Annotation of libwww/Library/src/HTTP.c, revision 1.76.2.3
1.74 frystyk 1: /* HTTP.c
2: ** MULTITHREADED IMPLEMENTATION OF HTTP CLIENT
1.2 timbl 3: **
1.74 frystyk 4: ** (c) COPYRIGHT CERN 1994.
5: ** Please first read the full copyright statement in the file COPYRIGH.
6: **
7: ** This module implments the HTTP protocol as a state machine
1.55 frystyk 8: **
9: ** History:
1.59 frystyk 10: ** < May 24 94 ?? Unknown - but obviously written
1.56 frystyk 11: ** May 24 94 HF Made reentrent and cleaned up a bit. Implemented
12: ** Forward, redirection, error handling and referer field
1.67 duns 13: ** 8 Jul 94 FM Insulate free() from _free structure element.
1.71 frystyk 14: ** Jul 94 HFN Written on top of HTTP.c, Henrik Frystyk
1.55 frystyk 15: **
1.1 timbl 16: */
17:
1.71 frystyk 18: /* Platform dependent stuff */
1.1 timbl 19: #include "HTUtils.h"
20: #include "tcp.h"
1.68 frystyk 21:
1.71 frystyk 22: /* Libray Includes */
23: #include "HTParse.h"
1.1 timbl 24: #include "HTTCP.h"
25: #include "HTFormat.h"
1.2 timbl 26: #include "HTAlert.h"
27: #include "HTMIME.h"
1.21 luotonen 28: #include "HTAccess.h" /* HTRequest */
1.14 luotonen 29: #include "HTAABrow.h" /* Access Authorization */
1.20 timbl 30: #include "HTTee.h" /* Tee off a cache stream */
31: #include "HTFWriter.h" /* Write to cache file */
1.54 luotonen 32: #include "HTError.h"
1.55 frystyk 33: #include "HTChunk.h"
1.71 frystyk 34: #include "HTGuess.h"
35: #include "HTThread.h"
1.55 frystyk 36: #include "HTTP.h" /* Implements */
37:
38: /* Macros and other defines */
1.71 frystyk 39: #define HTTP_VERSION "HTTP/1.0"
40: #define PUTC(c) (*me->target->isa->put_character)(me->target, c)
41: #define PUTS(s) (*me->target->isa->put_string)(me->target, s)
42: #define PUTBLOCK(b, l) (*me->target->isa->put_block)(me->target, b, l)
43: #define FREE_TARGET (*me->target->isa->_free)(me->target)
1.74 frystyk 44: #define ABORT_TARGET (*me->target->isa->abort)(me->target, e)
1.2 timbl 45:
1.55 frystyk 46: /* Globals */
1.59 frystyk 47: extern char * HTAppName; /* Application name: please supply */
48: extern char * HTAppVersion; /* Application version: please supply */
1.72 frystyk 49: extern BOOL using_proxy; /* are we using a proxy gateway? */
1.71 frystyk 50:
1.64 frystyk 51: PUBLIC int HTMaxRedirections = 10; /* Max number of redirections */
52: PUBLIC BOOL HTEnableFrom = NO; /* Enable From header? */
1.71 frystyk 53: PUBLIC char * HTProxyHeaders = NULL; /* Headers to pass as-is */
1.23 luotonen 54:
1.59 frystyk 55: /* Type definitions and global variables etc. local to this module */
56: /* This is the local definition of HTRequest->net_info */
57: typedef enum _HTTPState {
1.71 frystyk 58: HTTP_ERROR = -3,
59: HTTP_NO_DATA = -2,
60: HTTP_GOT_DATA = -1,
61: HTTP_BEGIN = 0,
62: HTTP_NEED_CONNECTION,
63: HTTP_NEED_REQUEST,
64: HTTP_SENT_REQUEST,
65: HTTP_NEED_BODY,
66: HTTP_REDIRECTION,
67: HTTP_AA
1.59 frystyk 68: } HTTPState;
1.55 frystyk 69:
70: typedef struct _http_info {
1.71 frystyk 71: int sockfd; /* Socket descripter */
72: SockA sock_addr; /* SockA is defined in tcp.h */
1.68 frystyk 73: HTInputSocket * isoc; /* Input buffer */
1.71 frystyk 74: HTStream * target; /* Output stream */
75: HTChunk * transmit; /* Line to be send */
76: int addressCount; /* Attempts if multi-homed host */
77: time_t connecttime; /* Used on multihomed hosts */
78: struct _HTRequest * request; /* Link back to request structure */
1.68 frystyk 79:
80: HTTPState state; /* State of the connection */
1.55 frystyk 81: } http_info;
82:
1.71 frystyk 83: #define MAX_STATUS_LEN 150 /* Max number of chars to look at */
1.55 frystyk 84:
1.71 frystyk 85: struct _HTStream {
86: CONST HTStreamClass * isa;
87: HTStream * target;
88: HTRequest * request;
89: http_info * http;
90: int cnt;
91: HTSocketEOL state;
92: BOOL transparent;
93: float version;
94: int status;
95: char buffer[MAX_STATUS_LEN+1];
96: char * bufptr;
97: };
1.21 luotonen 98:
1.71 frystyk 99: /* ------------------------------------------------------------------------- */
100: /* Help Functions */
101: /* ------------------------------------------------------------------------- */
1.21 luotonen 102:
1.71 frystyk 103: /* HTTPCleanup
1.1 timbl 104: **
1.55 frystyk 105: ** This function closes the connection and frees memory.
1.1 timbl 106: **
1.55 frystyk 107: ** Returns 0 on OK, else -1
1.1 timbl 108: */
1.71 frystyk 109: PRIVATE int HTTPCleanup ARGS1(HTRequest *, request)
1.1 timbl 110: {
1.71 frystyk 111: http_info *http;
1.55 frystyk 112: int status = 0;
1.71 frystyk 113: if (!request || !request->net_info) {
114: if (PROT_TRACE) fprintf(stderr, "HTTPCleanup. Bad argument!\n");
1.55 frystyk 115: status = -1;
116: } else {
1.71 frystyk 117: http = (http_info *) request->net_info;
1.59 frystyk 118: if (http->sockfd >= 0) {
1.71 frystyk 119: if (PROT_TRACE) fprintf(stderr, "HTTP........ Closing socket %d\n",
1.59 frystyk 120: http->sockfd);
121: if ((status = NETCLOSE(http->sockfd)) < 0)
122: HTErrorSysAdd(http->request, ERR_FATAL, NO, "NETCLOSE");
1.71 frystyk 123: HTThreadState(http->sockfd, THD_CLOSE);
124: HTThread_clear((HTNetInfo *) http);
125: http->sockfd = -1;
126: }
127: if (http->isoc)
128: HTInputSocket_free(http->isoc);
129: if (http->transmit)
130: HTChunkFree(http->transmit);
1.55 frystyk 131: }
132: free(http);
1.71 frystyk 133: request->net_info = NULL;
1.55 frystyk 134: return status;
135: }
1.36 frystyk 136:
1.23 luotonen 137:
1.55 frystyk 138: /* HTTPSendRequest
139: **
140: ** This function composes and sends a request to the connected server
141: ** specified.
142: **
1.71 frystyk 143: ** Returns <0 Error has occured or interrupted
144: ** HT_WOULD_BLOCK if operation would have blocked
145: ** HT_INTERRUPTED if interrupted
146: **
147: ** Note: The function does NEVER close the connection
1.1 timbl 148: */
1.55 frystyk 149: PRIVATE int HTTPSendRequest ARGS3(HTRequest *, request,
150: http_info *, http, char *, url)
151: {
152: int status = 0;
1.1 timbl 153:
1.71 frystyk 154: /* If first time through then generate HTTP request */
155: if (!http->transmit) {
156: HTChunk *command = HTChunkCreate(2048);
157: http->transmit = command;
158: if (request->method != METHOD_INVALID) {
159: HTChunkPuts(command, HTMethod_name(request->method));
160: HTChunkPutc(command, ' ');
161: }
1.21 luotonen 162: else
1.71 frystyk 163: HTChunkPuts(command, "GET ");
164:
165: /* if we are using a proxy gateway don't copy in the first slash
166: ** of say: /gopher://a;lkdjfl;ajdf;lkj/;aldk/adflj
167: ** so that just gohper://.... is sent. */
168: {
169: char *p1 = HTParse(url, "", PARSE_PATH|PARSE_PUNCTUATION);
170: if (using_proxy)
171: HTChunkPuts(command, p1+1);
172: else
173: HTChunkPuts(command, p1);
174: free(p1);
175: }
1.55 frystyk 176: HTChunkPutc(command, ' ');
177: HTChunkPuts(command, HTTP_VERSION);
1.71 frystyk 178: HTChunkPutc(command, CR); /* CR LF, as in rfc 977 */
179: HTChunkPutc(command, LF);
180:
181: if (HTImProxy && HTProxyHeaders) {
182: HTChunkPuts(command, HTProxyHeaders);
183: } else {
184: char line[256]; /*@@@@ */
185:
186: /* If no conversion list, then put it up, but leave initialization
187: to the client */
188: if (!HTConversions)
189: HTConversions = HTList_new();
1.34 frystyk 190:
1.71 frystyk 191: /* Run through both lists and generate `accept' lines */
192: {
193: int i;
194: HTList *conversions[2];
195: conversions[0] = HTConversions;
196: conversions[1] = request->conversions;
197:
198: for (i=0; i<2; i++) {
199: HTList *cur = conversions[i];
200: HTPresentation *pres;
201: while ((pres =(HTPresentation *) HTList_nextObject(cur))) {
202: if (pres->rep_out == WWW_PRESENT) {
203: if (pres->quality != 1.0) {
204: sprintf(line, "Accept: %s; q=%.3f%c%c",
205: HTAtom_name(pres->rep),
206: pres->quality, CR, LF);
207: } else {
208: sprintf(line, "Accept: %s%c%c",
209: HTAtom_name(pres->rep), CR, LF);
210: }
211: HTChunkPuts(command, line);
1.21 luotonen 212: }
1.17 timbl 213: }
214: }
1.2 timbl 215: }
1.71 frystyk 216:
217: /* Put out referer field if any parent */
218: if (request->parentAnchor) {
219: char *me = HTAnchor_address((HTAnchor *) request->anchor);
220: char *parent = HTAnchor_address((HTAnchor *)
221: request->parentAnchor);
222: char *relative = HTParse(parent, me,
223: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
224: if (relative && *relative) {
225: sprintf(line, "Referer: %s%c%c", parent, CR, LF);
226: HTChunkPuts(command, line);
227: }
228: free(me);
229: free(parent);
230: free(relative);
231: }
232:
233: /* Put out from field if enabled by client */
234: if (HTEnableFrom) {
235: CONST char *mailaddress = HTGetMailAddress();
236: if (mailaddress != NULL) {
237: sprintf(line, "From: %s%c%c", mailaddress, CR, LF);
238: HTChunkPuts(command, line);
239: }
240: }
241:
242: /* Put out user-agent */
243: sprintf(line, "User-Agent: %s/%s libwww/%s%c%c",
244: HTAppName ? HTAppName : "unknown",
245: HTAppVersion ? HTAppVersion : "0.0",
246: HTLibraryVersion, CR, LF);
247: HTChunkPuts(command, line);
248:
249: /* Put out authorization */
250: if (request->authorization != NULL) {
251: HTChunkPuts(command, "Authorization: ");
252: HTChunkPuts(command, request->authorization);
253: HTChunkPutc(command, CR);
254: HTChunkPutc(command, LF);
1.63 frystyk 255: }
256: }
1.71 frystyk 257: HTChunkPutc(command, CR); /* Blank line means "end" */
258: HTChunkPutc(command, LF);
259: HTChunkTerminate(command);
260: if (PROT_TRACE) fprintf(stderr, "HTTP Tx..... %s", command->data);
261:
262: /* Translate into ASCII if necessary */
1.4 timbl 263: #ifdef NOT_ASCII
1.71 frystyk 264: {
265: char * p;
266: for(p = command->data; *p; p++) {
267: *p = TOASCII(*p);
268: }
1.1 timbl 269: }
1.71 frystyk 270: #endif
1.55 frystyk 271: }
1.71 frystyk 272:
273: /* Now, we are ready for sending the request */
274: status = NETWRITE(http->sockfd, http->transmit->data,
275: http->transmit->size-1);
276: if (status < 0) {
277: #ifdef EAGAIN
278: if (errno == EAGAIN || errno == EWOULDBLOCK)
279: #else
280: if (errno == EWOULDBLOCK)
1.3 timbl 281: #endif
1.71 frystyk 282: {
283: if (PROT_TRACE)
284: fprintf(stderr, "HTTP Tx..... Write operation would block\n");
285: HTThreadState(http->sockfd, THD_SET_WRITE);
286: return HT_WOULD_BLOCK;
287: } else { /* A real error has occured */
1.55 frystyk 288: char *unescaped = NULL;
1.71 frystyk 289: HTErrorSysAdd(request, ERR_FATAL, NO, "NETWRITE");
1.55 frystyk 290: StrAllocCopy(unescaped, url);
291: HTUnEscape(unescaped);
292: HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL,
293: (void *) unescaped, (int) strlen(unescaped),
294: "HTTPSendRequest");
295: free(unescaped);
1.71 frystyk 296: return -1;
1.55 frystyk 297: }
298: }
1.71 frystyk 299: HTThreadState(http->sockfd, THD_CLR_WRITE); /* Write OK */
1.55 frystyk 300: return status;
301: }
302:
303:
1.71 frystyk 304: PRIVATE BOOL HTTPAuthentication ARGS1(HTRequest *, request)
305: {
306: HTAAScheme scheme;
307: HTList *valid_schemes = HTList_new();
308: HTAssocList **scheme_specifics = NULL;
1.76 frystyk 309: char *tmplate = NULL;
1.71 frystyk 310:
311: if (request->WWWAAScheme) {
312: if ((scheme = HTAAScheme_enum(request->WWWAAScheme)) != HTAA_UNKNOWN) {
313: HTList_addObject(valid_schemes, (void *) scheme);
314: if (!scheme_specifics) {
315: int i;
316: scheme_specifics = (HTAssocList**)
317: malloc(HTAA_MAX_SCHEMES * sizeof(HTAssocList*));
318: if (!scheme_specifics)
319: outofmem(__FILE__, "HTTPAuthentication");
320: for (i=0; i < HTAA_MAX_SCHEMES; i++)
321: scheme_specifics[i] = NULL;
322: }
323: scheme_specifics[scheme] = HTAA_parseArgList(request->WWWAARealm);
324: } else if (PROT_TRACE) {
325: HTErrorAdd(request, ERR_INFO, NO, HTERR_UNKNOWN_AA,
326: (void *) request->WWWAAScheme, 0, "HTTPAuthentication");
327: return NO;
328: }
329: }
330: if (request->WWWprotection) {
331: if (PROT_TRACE)
332: fprintf(stderr, "Protection template set to `%s'\n",
333: request->WWWprotection);
1.76 frystyk 334: StrAllocCopy(tmplate, request->WWWprotection);
1.71 frystyk 335: }
336: request->valid_schemes = valid_schemes;
337: request->scheme_specifics = scheme_specifics;
1.76 frystyk 338: request->prot_template = tmplate;
1.71 frystyk 339: return YES;
340: }
341:
342:
343: /*
344: ** This is a big switch handling all HTTP return codes. It puts in any
345: ** appropiate error message and decides whether we should expect data
1.76.2.1 frystyk 346: ** or not.
1.55 frystyk 347: */
1.71 frystyk 348: PRIVATE void HTTPResponse ARGS1(HTStream *, me)
1.55 frystyk 349: {
1.71 frystyk 350: switch (me->status) {
351:
352: case 200:
1.76.2.1 frystyk 353: case 201:
354: case 202:
355: case 203:
356: case 205:
357: case 206:
1.71 frystyk 358: break;
1.76.2.1 frystyk 359:
360: case 204: /* No Response */
361: me->http->state = HTTP_NO_DATA;
362: break;
363:
1.71 frystyk 364: case 301: /* Moved */
365: case 302: /* Found */
366: me->http->state = HTTP_REDIRECTION;
367: break;
1.55 frystyk 368:
1.71 frystyk 369: case 303: /* Method */
370: HTAlert("This client doesn't support automatic redirection of type `Method'");
371: me->http->state = HTTP_ERROR;
372: break;
1.55 frystyk 373:
1.76.2.1 frystyk 374: case 400: /* Bad Request */
375: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_BAD_REQUEST,
376: NULL, 0, "HTLoadHTTP");
1.71 frystyk 377: me->http->state = HTTP_ERROR;
378: break;
1.70 howcome 379:
1.71 frystyk 380: case 401:
1.76.2.1 frystyk 381: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_UNAUTHORIZED,
382: NULL, 0, "HTLoadHTTP");
1.71 frystyk 383: me->http->state = HTTP_AA;
384: break;
385:
386: case 402: /* Payment required */
1.76.2.1 frystyk 387: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_PAYMENT_REQUIRED,
388: NULL, 0, "HTLoadHTTP");
389: me->http->state = HTTP_ERROR;
1.71 frystyk 390: break;
1.55 frystyk 391:
1.71 frystyk 392: case 403: /* Forbidden */
1.76.2.1 frystyk 393: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_FORBIDDEN,
394: NULL, 0, "HTLoadHTTP");
1.71 frystyk 395: me->http->state = HTTP_ERROR;
396: break;
1.55 frystyk 397:
1.71 frystyk 398: case 404: /* Not Found */
1.76.2.1 frystyk 399: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_NOT_FOUND,
400: NULL, 0, "HTLoadHTTP");
1.71 frystyk 401: me->http->state = HTTP_ERROR;
402: break;
403:
1.76.2.1 frystyk 404: case 405: /* Not Allowed */
405: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_NOT_ALLOWED,
406: NULL, 0, "HTLoadHTTP");
407: me->http->state = HTTP_ERROR;
408: break;
409:
410: case 406: /* None Acceptable */
411: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_NONE_ACCEPTABLE,
412: NULL, 0, "HTLoadHTTP");
413: me->http->state = HTTP_ERROR;
414: break;
415:
416: case 407: /* Proxy Authentication Required */
417: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_PROXY,
418: NULL, 0, "HTLoadHTTP");
419: me->http->state = HTTP_ERROR;
420: break;
421:
422: case 408: /* Request Timeout */
423: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_TIMEOUT,
424: NULL, 0, "HTLoadHTTP");
425: me->http->state = HTTP_ERROR;
426: break;
427:
1.71 frystyk 428: case 500:
1.76.2.1 frystyk 429: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_INTERNAL,
430: NULL, 0, "HTLoadHTTP");
1.71 frystyk 431: me->http->state = HTTP_ERROR;
432: break;
433:
1.76.2.1 frystyk 434: case 501:
435: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_NOT_IMPLEMENTED,
436: NULL, 0, "HTLoadHTTP");
437: me->http->state = HTTP_ERROR;
438: break;
439:
440: case 502:
441: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_BAD_GATE,
442: NULL, 0, "HTLoadHTTP");
443: me->http->state = HTTP_ERROR;
444: break;
445:
446: case 503:
447: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_DOWN,
448: NULL, 0, "HTLoadHTTP");
449: me->http->state = HTTP_ERROR;
450: break;
451:
452: case 504:
453: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_GATE_TIMEOUT,
454: NULL, 0, "HTLoadHTTP");
455: me->http->state = HTTP_ERROR;
456: break;
457:
1.71 frystyk 458: default: /* bad number */
459: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_BAD_REPLY,
460: (void *) me->buffer, me->cnt, "HTLoadHTTP");
461: me->http->state = HTTP_ERROR;
462: break;
1.55 frystyk 463: }
464: }
465:
1.71 frystyk 466: /* ------------------------------------------------------------------------- */
467: /* HTTP Status Line Stream */
468: /* ------------------------------------------------------------------------- */
1.55 frystyk 469:
1.71 frystyk 470: /*
471: ** Analyse the string we have read. If it is a HTTP 1.0 or higher
472: ** then create a MIME-stream, else create a Guess stream to find out
473: ** what the 0.9 server is sending. We need to copy the buffer as we don't
474: ** know if we can modify the contents or not.
1.76.2.1 frystyk 475: **
476: ** Stream handling is a function of the status code returned from the
477: ** server:
478: ** 200: Use `output_stream' in HTRequest structure
479: ** else: Use `output_flush' in HTRequest structure
1.56 frystyk 480: */
1.71 frystyk 481: PRIVATE void flush ARGS1(HTStream *, me)
1.56 frystyk 482: {
1.71 frystyk 483: HTRequest *req = me->request;
484: me->transparent = YES; /* Only do this once */
485: if (me->state == EOL_FLF) {
486: if (strncasecomp(me->buffer, "http/", 5) ||
487: sscanf(me->buffer+5, "%f %d", &me->version, &me->status) < 2) {
488: HTErrorAdd(req, ERR_INFO, NO, HTERR_HTTP09,
489: (void *) me->buffer, me->cnt, "HTTPStatusStream");
490: me->target = HTGuess_new(req, NULL, WWW_UNKNOWN,
491: req->output_format, req->output_stream);
492: PUTBLOCK(me->buffer, me->cnt);
493: } else {
494: if (req->output_format == WWW_SOURCE) {
495: me->target = HTMIMEConvert(req, NULL, WWW_MIME,
496: req->output_format,
497: req->output_stream);
1.76.2.1 frystyk 498: } else if (me->status==200 && req->method==METHOD_GET) {
1.76.2.3! howcome 499: HTStream *s;
! 500:
1.71 frystyk 501: me->target = HTStreamStack(WWW_MIME,req->output_format,
1.76.2.1 frystyk 502: req->output_stream, req, NO);
1.76.2.3! howcome 503:
! 504: /* Cache HTTP 1.0 responses */
! 505: /* howcome added test for return value from HTCacheWriter 12/1/95 */
! 506:
! 507: if (HTCacheDir && (s = HTCacheWriter(req, NULL, WWW_MIME,
1.76.2.1 frystyk 508: req->output_format,
1.76.2.3! howcome 509: req->output_stream)))
! 510: {
! 511: me->target = HTTee(me->target, s);
! 512: }
1.71 frystyk 513: } else {
514: me->target = HTMIMEConvert(req, NULL, WWW_MIME,
1.76.2.1 frystyk 515: WWW_SOURCE, req->output_flush ?
516: req->output_flush : HTBlackHole());
1.56 frystyk 517: }
1.76.2.1 frystyk 518: if (!me->target)
1.71 frystyk 519: me->target = HTBlackHole(); /* What else */
1.56 frystyk 520: }
1.71 frystyk 521: } else {
522: me->target = HTGuess_new(req, NULL, WWW_UNKNOWN, req->output_format,
523: req->output_stream);
524: PUTBLOCK(me->buffer, me->cnt);
1.56 frystyk 525: }
1.71 frystyk 526: }
1.56 frystyk 527:
1.71 frystyk 528: PRIVATE void HTTPStatus_put_character ARGS2(HTStream *, me, char, c)
529: {
530: if (me->transparent)
531: PUTC(c);
532: else {
533: if (me->state == EOL_FCR) {
534: if (c == LF) {
535: me->state = EOL_FLF; /* Line found */
536: flush(me);
537: } else {
538: me->state = EOL_BEGIN;
539: me->cnt += 2;
540: *me->bufptr++ = CR; /* Need to put it back */
541: *me->bufptr++ = c;
542: }
543: } else if (c == CR) {
544: me->state = EOL_FCR;
545: } else if (c == LF) {
546: me->state = EOL_FLF; /* Line found */
547: me->cnt++;
548: *me->bufptr++ = LF;
549: flush(me);
550: } else {
551: me->cnt++;
552: *me->bufptr++ = c;
553: if (me->cnt >= MAX_STATUS_LEN)
554: flush(me);
555: }
1.56 frystyk 556: }
557: }
558:
1.71 frystyk 559: PRIVATE void HTTPStatus_put_string ARGS2(HTStream *, me, CONST char*, s)
560: {
561: while (!me->transparent && *s)
562: HTTPStatus_put_character(me, *s++);
563: if (*s) PUTS(s);
564: }
1.56 frystyk 565:
1.71 frystyk 566: PRIVATE void HTTPStatus_put_block ARGS3(HTStream *, me, CONST char*, b, int, l)
567: {
568: while (!me->transparent && l-- > 0)
569: HTTPStatus_put_character(me, *b++);
570: if (l > 0) PUTBLOCK(b, l);
571: }
572:
573: PRIVATE int HTTPStatus_free ARGS1(HTStream *, me)
574: {
575: int status = me->status;
1.76.2.1 frystyk 576: HTTPResponse(me); /* Get next state */
1.71 frystyk 577: if (!me->transparent)
578: flush(me);
579: if (me->target)
580: FREE_TARGET;
581: free(me);
582: return status; /* Return the HTTP status value - 0 if HTTP 0.9 */
583: }
584:
585: PRIVATE int HTTPStatus_abort ARGS2(HTStream *, me, HTError, e)
586: {
587: if (me->target)
1.74 frystyk 588: ABORT_TARGET;
1.71 frystyk 589: free(me);
1.74 frystyk 590: if (PROT_TRACE)
591: fprintf(stderr, "HTTPStatus.. ABORTING LOAD...\n");
1.71 frystyk 592: return EOF;
593: }
594:
595: /* HTTPStatus Stream
596: ** -----------------
597: */
598: PRIVATE CONST HTStreamClass HTTPStatusClass =
599: {
600: "HTTPStatus",
601: HTTPStatus_free,
602: HTTPStatus_abort,
603: HTTPStatus_put_character,
604: HTTPStatus_put_string,
605: HTTPStatus_put_block
606: };
607:
608: PUBLIC HTStream * HTTPStatus_new ARGS2(HTRequest *, request,
609: http_info *, http)
610: {
611: HTStream * me = (HTStream *) calloc(1, sizeof(HTStream));
612: if (!me) outofmem(__FILE__, "HTTPStatus_new");
613: me->isa = &HTTPStatusClass;
614: me->request = request;
615: me->http = http;
616: me->bufptr = me->buffer;
617: me->state = EOL_BEGIN;
618: return me;
619: }
620:
621: /* ------------------------------------------------------------------------- */
622:
623: /* Load Document from HTTP Server HTLoadHTTP
1.55 frystyk 624: ** ==============================
625: **
626: ** Given a hypertext address, this routine loads a document.
627: **
628: ** On entry,
629: ** request This is the request structure
630: ** On exit,
1.71 frystyk 631: ** returns <0 Error has occured or interrupted
632: ** HT_WOULD_BLOCK if operation would have blocked
1.58 frystyk 633: ** HT_LOADED if return status 200 OK
634: ** HT_NO_DATA if return status 204 No Response
1.55 frystyk 635: */
636: PUBLIC int HTLoadHTTP ARGS1 (HTRequest *, request)
637: {
1.71 frystyk 638: int status = HT_ERROR;
639: char *url; /* Gets initialized on every entry */
1.55 frystyk 640: http_info *http; /* Specific protocol information */
641:
642: if (!request || !request->anchor) {
1.71 frystyk 643: if (PROT_TRACE) fprintf(stderr, "HTLoadHTTP.. Bad argument\n");
644: return HT_ERROR;
1.55 frystyk 645: }
646: url = HTAnchor_physical(request->anchor);
1.17 timbl 647:
1.71 frystyk 648: /* Only do the setup first time through. This is actually state HTTP_BEGIN
649: but it can't be in the state machine as we need the structure first */
650: if (!request->net_info) {
1.22 luotonen 651: /*
1.71 frystyk 652: ** Initiate a new http structure and bind to request structure
653: ** This is actually state HTTP_BEGIN, but it can't be in the state
654: ** machine as we need the structure first.
1.22 luotonen 655: */
1.71 frystyk 656: if (PROT_TRACE) fprintf(stderr, "HTTP........ Looking for `%s\'\n", url);
657: if ((http = (http_info *) calloc(1, sizeof(http_info))) == NULL)
658: outofmem(__FILE__, "HTLoadHTTP");
659: http->sockfd = -1; /* Invalid socket number */
660: http->request = request;
661: http->state = HTTP_BEGIN;
662: request->net_info = (HTNetInfo *) http;
663: HTThread_new((HTNetInfo *) http);
664: } else
665: http = (http_info *) request->net_info; /* Get existing copy */
666:
667: /* Now jump into the machine. We know the state from the previous run */
668: while (1) {
669: switch (http->state) {
670: case HTTP_BEGIN:
671: /*
672: ** Compose authorization information (this was moved here
673: ** from after the making of the connection so that the connection
674: ** wouldn't have to wait while prompting username and password
675: ** from the user). -- AL 13.10.93
676: */
677: HTAA_composeAuth(request);
678: if (PROT_TRACE) {
679: if (request->authorization)
680: fprintf(stderr, "HTTP........ Sending Authorization: %s\n",
681: request->authorization);
682: else
683: fprintf(stderr,
684: "HTTP........ Not sending authorization (yet)\n");
685: }
686: http->state = HTTP_NEED_CONNECTION;
687: break;
688:
689: case HTTP_NEED_CONNECTION: /* Now let's set up a connection */
690: status = HTDoConnect((HTNetInfo *) http, url, TCP_PORT,
691: NULL, NO);
692: if (!status) {
693: if (PROT_TRACE)
694: fprintf(stderr, "HTTP........ Connected, socket %d\n",
695: http->sockfd);
696: http->isoc = HTInputSocket_new(http->sockfd);
697: http->state = HTTP_NEED_REQUEST;
698: } else if (status == HT_WOULD_BLOCK)
699: return status;
700: else
701: http->state = HTTP_ERROR; /* Error or interrupt */
702: break;
703:
704: case HTTP_NEED_REQUEST: /* Compose the request and send it */
705: if ((status = HTTPSendRequest(request, http, url)) < 0) {
706: if (status == HT_WOULD_BLOCK)
1.55 frystyk 707: return status;
1.71 frystyk 708: else
709: http->state = HTTP_ERROR;
1.55 frystyk 710: } else {
1.71 frystyk 711: http->state = HTTP_SENT_REQUEST;
1.21 luotonen 712: }
1.71 frystyk 713: break;
1.21 luotonen 714:
1.76.2.1 frystyk 715: case HTTP_SENT_REQUEST: /* Put up stream */
716: http->target = HTImProxy ?
717: request->output_stream : HTTPStatus_new(request, http);
718: http->state = HTTP_NEED_BODY;
719: break;
720:
721: case HTTP_NEED_BODY: /* Read the response */
1.74 frystyk 722: status = HTInputSocket_read(http->isoc, http->target);
723: if (status == HT_WOULD_BLOCK)
1.71 frystyk 724: return HT_WOULD_BLOCK;
1.75 frystyk 725: else if (status == HT_INTERRUPTED) {
726: (*http->target->isa->abort)(http->target, NULL);
1.74 frystyk 727: http->state = HTTP_ERROR;
1.76.2.1 frystyk 728: } else if (status == HT_LOADED) {
1.75 frystyk 729: (*http->target->isa->_free)(http->target);
730: if (http->state == HTTP_NEED_BODY)
731: http->state = HTTP_GOT_DATA;
1.76.2.1 frystyk 732: } else {
733: (*http->target->isa->_free)(http->target);
734: http->state = HTTP_ERROR;
1.75 frystyk 735: }
1.71 frystyk 736: break;
737:
738: case HTTP_REDIRECTION:
739: if (request->redirect) {
740: HTAnchor *anchor;
741: if (status == 301) {
742: HTErrorAdd(request, ERR_INFO, NO, HTERR_MOVED,
743: (void *) request->redirect,
744: (int) strlen(request->redirect), "HTLoadHTTP");
745: } else if (status == 302) {
746: HTErrorAdd(request, ERR_INFO, NO, HTERR_FOUND,
747: (void *) request->redirect,
748: (int) strlen(request->redirect), "HTLoadHTTP");
1.55 frystyk 749: }
1.71 frystyk 750: anchor = HTAnchor_findAddress(request->redirect);
751: if (++request->redirections < HTMaxRedirections) {
752: HTTPCleanup(request);
753: return HTLoadAnchorRecursive((HTAnchor *) anchor, request);
754: } else {
755: HTErrorAdd(request, ERR_FATAL, NO, HTERR_MAX_REDIRECT,
1.58 frystyk 756: NULL, 0, "HTLoadHTTP");
1.71 frystyk 757: http->state = HTTP_ERROR;
1.58 frystyk 758: }
1.71 frystyk 759: } else {
760: HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
761: NULL, 0, "HTLoadHTTP");
762: http->state = HTTP_ERROR;
763: }
764: break;
765:
766: case HTTP_AA:
767: if (HTTPAuthentication(request) == YES &&
768: HTAA_retryWithAuth(request) == YES) {
1.76.2.2 frystyk 769: HTTPCleanup(request);
1.71 frystyk 770: return HTLoadAnchor((HTAnchor *) request->anchor, request);
771: } else {
772: char *unescaped = NULL;
773: StrAllocCopy(unescaped, url);
774: HTUnEscape(unescaped);
775: HTErrorAdd(request, ERR_FATAL, NO, HTERR_UNAUTHORIZED,
776: (void *) unescaped,
777: (int) strlen(unescaped), "HTLoadHTTP");
778: free(unescaped);
779: http->state = HTTP_ERROR;
780: }
781: break;
782:
783: case HTTP_GOT_DATA:
1.74 frystyk 784: HTTPCleanup(request);
1.71 frystyk 785: return HT_LOADED;
786: break;
787:
788: case HTTP_NO_DATA:
789: HTTPCleanup(request);
790: return HT_NO_DATA;
791: break;
792:
793: case HTTP_ERROR:
794: HTTPCleanup(request);
795: return HT_ERROR;
796: break;
797: }
798: } /* End of while(1) */
799: }
800:
801: /* Protocol descriptor */
802:
803: GLOBALDEF PUBLIC HTProtocol HTTP = {
804: "http", SOC_NON_BLOCK, HTLoadHTTP, NULL, NULL
805: };
1.21 luotonen 806:
Webmaster