Annotation of libwww/Library/src/HTTP.c, revision 1.75
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;
309: char *template = NULL;
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);
334: StrAllocCopy(template, request->WWWprotection);
335: }
336: request->valid_schemes = valid_schemes;
337: request->scheme_specifics = scheme_specifics;
338: request->prot_template = template;
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
346: ** or not. If we are not interested in the body (for example in 3xx
347: ** codes) then turn the stream into a black hole.
1.55 frystyk 348: */
1.71 frystyk 349: PRIVATE void HTTPResponse ARGS1(HTStream *, me)
1.55 frystyk 350: {
1.71 frystyk 351: char *url = HTAnchor_physical(me->request->anchor);
352: switch (me->status) {
353:
354: case 204: /* No response */
355: HTErrorAdd(me->request, ERR_INFO, NO, HTERR_NO_RESPONSE,
356: NULL, 0, "HTLoadHTTP");
357: me->http->state = HTTP_NO_DATA;
358: break;
359:
360: case 203: /* Partial */
361: HTErrorAdd(me->request, ERR_INFO, NO, HTERR_PARTIAL,
362: NULL, 0, "HTLoadHTTP");
363: /* Drop through to 200 to get the body */
364:
365: case 200:
366: me->http->state = HTTP_NEED_BODY;
367: break;
368:
369: case 301: /* Moved */
370: case 302: /* Found */
371: me->http->state = HTTP_REDIRECTION;
372: break;
1.55 frystyk 373:
1.71 frystyk 374: case 303: /* Method */
375: HTAlert("This client doesn't support automatic redirection of type `Method'");
376: me->http->state = HTTP_ERROR;
377: break;
1.55 frystyk 378:
1.71 frystyk 379: case 304: /* Not modified Since */
380: {
381: char *unescaped = NULL;
382: StrAllocCopy(unescaped, url);
383: HTUnEscape(unescaped);
384: HTErrorAdd(me->request, ERR_INFO, NO, HTERR_NOT_MODIFIED,
385: (void *) unescaped, (int) strlen(unescaped),
386: "HTLoadHTTP");
387: free(unescaped);
388: }
389: me->http->state = HTTP_NO_DATA;
390: break;
391:
392: case 400: /* Bad me->request */
393: {
394: char *unescaped = NULL;
395: StrAllocCopy(unescaped, url);
396: HTUnEscape(unescaped);
397: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_BAD_REQUEST,
398: (void *) unescaped, (int) strlen(unescaped),
399: "HTLoadHTTP");
400: free(unescaped);
401: }
402: me->http->state = HTTP_ERROR;
403: break;
1.70 howcome 404:
1.71 frystyk 405: case 401:
406: me->http->state = HTTP_AA;
407: break;
408:
409: case 402: /* Payment required */
410: {
411: char *unescaped = NULL;
412: StrAllocCopy(unescaped, url);
413: HTUnEscape(unescaped);
414: HTErrorAdd(me->request, ERR_FATAL, NO,
415: HTERR_PAYMENT_REQUIRED, (void *) unescaped,
416: (int) strlen(unescaped), "HTLoadHTTP");
417: free(unescaped);
1.17 timbl 418: }
1.71 frystyk 419: me->http->state = HTTP_NO_DATA;
420: break;
1.55 frystyk 421:
1.71 frystyk 422: case 403: /* Forbidden */
423: {
424: char *unescaped = NULL;
425: StrAllocCopy(unescaped, url);
426: HTUnEscape(unescaped);
427: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_FORBIDDEN,
428: (void *) unescaped, (int) strlen(unescaped),
429: "HTLoadHTTP");
430: free(unescaped);
431: }
432: me->http->state = HTTP_ERROR;
433: break;
1.55 frystyk 434:
1.71 frystyk 435: case 404: /* Not Found */
436: {
437: char *unescaped = NULL;
438: StrAllocCopy(unescaped, url);
439: HTUnEscape(unescaped);
440: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_NOT_FOUND,
441: (void *) unescaped, (int) strlen(unescaped),
442: "HTLoadHTTP");
443: free(unescaped);
444: }
445: me->http->state = HTTP_ERROR;
446: break;
447:
448: case 500:
449: case 501:
450: {
451: char *unescaped = NULL;
452: StrAllocCopy(unescaped, url);
453: HTUnEscape(unescaped);
454: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_INTERNAL,
455: (void *) unescaped, (int) strlen(unescaped),
456: "HTLoadHTTP");
457: free(unescaped);
458: }
459: me->http->state = HTTP_ERROR;
460: break;
461:
462: default: /* bad number */
463: HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_BAD_REPLY,
464: (void *) me->buffer, me->cnt, "HTLoadHTTP");
465: me->http->state = HTTP_ERROR;
466: break;
1.55 frystyk 467: }
468: }
469:
1.71 frystyk 470: /* ------------------------------------------------------------------------- */
471: /* HTTP Status Line Stream */
472: /* ------------------------------------------------------------------------- */
1.55 frystyk 473:
1.71 frystyk 474: /*
475: ** Analyse the string we have read. If it is a HTTP 1.0 or higher
476: ** then create a MIME-stream, else create a Guess stream to find out
477: ** what the 0.9 server is sending. We need to copy the buffer as we don't
478: ** know if we can modify the contents or not.
1.56 frystyk 479: */
1.71 frystyk 480: PRIVATE void flush ARGS1(HTStream *, me)
1.56 frystyk 481: {
1.71 frystyk 482: HTRequest *req = me->request;
483: me->transparent = YES; /* Only do this once */
484: if (me->state == EOL_FLF) {
485: if (strncasecomp(me->buffer, "http/", 5) ||
486: sscanf(me->buffer+5, "%f %d", &me->version, &me->status) < 2) {
487: HTErrorAdd(req, ERR_INFO, NO, HTERR_HTTP09,
488: (void *) me->buffer, me->cnt, "HTTPStatusStream");
489: me->target = HTGuess_new(req, NULL, WWW_UNKNOWN,
490: req->output_format, req->output_stream);
491: PUTBLOCK(me->buffer, me->cnt);
492: } else {
493: HTTPResponse(me); /* Get next state */
494: if (req->output_format == WWW_SOURCE) {
495: me->target = HTMIMEConvert(req, NULL, WWW_MIME,
496: req->output_format,
497: req->output_stream);
498: } else if (me->http->state == HTTP_NEED_BODY) {
499: me->target = HTStreamStack(WWW_MIME,req->output_format,
500: req->output_stream,
501: req, NO);
502: if (HTCacheDir) { /* Cache HTTP 1.0 responses */
503: me->target =
504: HTTee(me->target,
505: HTCacheWriter(req, NULL, WWW_MIME,
506: req->output_format,
507: req->output_stream));
1.56 frystyk 508: }
1.71 frystyk 509: } else {
510: me->target = HTMIMEConvert(req, NULL, WWW_MIME,
511: WWW_SOURCE, HTBlackHole());
1.56 frystyk 512: }
1.71 frystyk 513: if (!me->target) {
514: if (PROT_TRACE)
515: fprintf(stderr, "HTTPStream.. Using a black hole\n");
516: me->target = HTBlackHole(); /* What else */
517: }
1.56 frystyk 518: }
1.71 frystyk 519: } else {
520: me->target = HTGuess_new(req, NULL, WWW_UNKNOWN, req->output_format,
521: req->output_stream);
522: PUTBLOCK(me->buffer, me->cnt);
1.56 frystyk 523: }
1.71 frystyk 524: }
1.56 frystyk 525:
1.71 frystyk 526: PRIVATE void HTTPStatus_put_character ARGS2(HTStream *, me, char, c)
527: {
528: if (me->transparent)
529: PUTC(c);
530: else {
531: if (me->state == EOL_FCR) {
532: if (c == LF) {
533: me->state = EOL_FLF; /* Line found */
534: flush(me);
535: } else {
536: me->state = EOL_BEGIN;
537: me->cnt += 2;
538: *me->bufptr++ = CR; /* Need to put it back */
539: *me->bufptr++ = c;
540: }
541: } else if (c == CR) {
542: me->state = EOL_FCR;
543: } else if (c == LF) {
544: me->state = EOL_FLF; /* Line found */
545: me->cnt++;
546: *me->bufptr++ = LF;
547: flush(me);
548: } else {
549: me->cnt++;
550: *me->bufptr++ = c;
551: if (me->cnt >= MAX_STATUS_LEN)
552: flush(me);
553: }
1.56 frystyk 554: }
555: }
556:
1.71 frystyk 557: PRIVATE void HTTPStatus_put_string ARGS2(HTStream *, me, CONST char*, s)
558: {
559: while (!me->transparent && *s)
560: HTTPStatus_put_character(me, *s++);
561: if (*s) PUTS(s);
562: }
1.56 frystyk 563:
1.71 frystyk 564: PRIVATE void HTTPStatus_put_block ARGS3(HTStream *, me, CONST char*, b, int, l)
565: {
566: while (!me->transparent && l-- > 0)
567: HTTPStatus_put_character(me, *b++);
568: if (l > 0) PUTBLOCK(b, l);
569: }
570:
571: PRIVATE int HTTPStatus_free ARGS1(HTStream *, me)
572: {
573: int status = me->status;
574: if (!me->transparent)
575: flush(me);
576: if (me->target)
577: FREE_TARGET;
578: free(me);
579: return status; /* Return the HTTP status value - 0 if HTTP 0.9 */
580: }
581:
582: PRIVATE int HTTPStatus_abort ARGS2(HTStream *, me, HTError, e)
583: {
584: if (me->target)
1.74 frystyk 585: ABORT_TARGET;
1.71 frystyk 586: free(me);
1.74 frystyk 587: if (PROT_TRACE)
588: fprintf(stderr, "HTTPStatus.. ABORTING LOAD...\n");
1.71 frystyk 589: return EOF;
590: }
591:
592: /* HTTPStatus Stream
593: ** -----------------
594: */
595: PRIVATE CONST HTStreamClass HTTPStatusClass =
596: {
597: "HTTPStatus",
598: HTTPStatus_free,
599: HTTPStatus_abort,
600: HTTPStatus_put_character,
601: HTTPStatus_put_string,
602: HTTPStatus_put_block
603: };
604:
605: PUBLIC HTStream * HTTPStatus_new ARGS2(HTRequest *, request,
606: http_info *, http)
607: {
608: HTStream * me = (HTStream *) calloc(1, sizeof(HTStream));
609: if (!me) outofmem(__FILE__, "HTTPStatus_new");
610: me->isa = &HTTPStatusClass;
611: me->request = request;
612: me->http = http;
613: me->bufptr = me->buffer;
614: me->state = EOL_BEGIN;
615: return me;
616: }
617:
618: /* ------------------------------------------------------------------------- */
619:
620: /* Load Document from HTTP Server HTLoadHTTP
1.55 frystyk 621: ** ==============================
622: **
623: ** Given a hypertext address, this routine loads a document.
624: **
625: ** On entry,
626: ** request This is the request structure
627: ** On exit,
1.71 frystyk 628: ** returns <0 Error has occured or interrupted
629: ** HT_WOULD_BLOCK if operation would have blocked
1.58 frystyk 630: ** HT_LOADED if return status 200 OK
631: ** HT_NO_DATA if return status 204 No Response
1.55 frystyk 632: */
633: PUBLIC int HTLoadHTTP ARGS1 (HTRequest *, request)
634: {
1.71 frystyk 635: int status = HT_ERROR;
636: char *url; /* Gets initialized on every entry */
1.55 frystyk 637: http_info *http; /* Specific protocol information */
638:
639: if (!request || !request->anchor) {
1.71 frystyk 640: if (PROT_TRACE) fprintf(stderr, "HTLoadHTTP.. Bad argument\n");
641: return HT_ERROR;
1.55 frystyk 642: }
643: url = HTAnchor_physical(request->anchor);
1.17 timbl 644:
1.71 frystyk 645: /* Only do the setup first time through. This is actually state HTTP_BEGIN
646: but it can't be in the state machine as we need the structure first */
647: if (!request->net_info) {
1.22 luotonen 648: /*
1.71 frystyk 649: ** Initiate a new http structure and bind to request structure
650: ** This is actually state HTTP_BEGIN, but it can't be in the state
651: ** machine as we need the structure first.
1.22 luotonen 652: */
1.71 frystyk 653: if (PROT_TRACE) fprintf(stderr, "HTTP........ Looking for `%s\'\n", url);
654: if ((http = (http_info *) calloc(1, sizeof(http_info))) == NULL)
655: outofmem(__FILE__, "HTLoadHTTP");
656: http->sockfd = -1; /* Invalid socket number */
657: http->request = request;
658: http->state = HTTP_BEGIN;
659: request->net_info = (HTNetInfo *) http;
660: HTThread_new((HTNetInfo *) http);
661: } else
662: http = (http_info *) request->net_info; /* Get existing copy */
663:
664: /* Now jump into the machine. We know the state from the previous run */
665: while (1) {
666: switch (http->state) {
667: case HTTP_BEGIN:
668: /*
669: ** Compose authorization information (this was moved here
670: ** from after the making of the connection so that the connection
671: ** wouldn't have to wait while prompting username and password
672: ** from the user). -- AL 13.10.93
673: */
674: HTAA_composeAuth(request);
675: if (PROT_TRACE) {
676: if (request->authorization)
677: fprintf(stderr, "HTTP........ Sending Authorization: %s\n",
678: request->authorization);
679: else
680: fprintf(stderr,
681: "HTTP........ Not sending authorization (yet)\n");
682: }
683: http->state = HTTP_NEED_CONNECTION;
684: break;
685:
686: case HTTP_NEED_CONNECTION: /* Now let's set up a connection */
687: status = HTDoConnect((HTNetInfo *) http, url, TCP_PORT,
688: NULL, NO);
689: if (!status) {
690: if (PROT_TRACE)
691: fprintf(stderr, "HTTP........ Connected, socket %d\n",
692: http->sockfd);
693: http->isoc = HTInputSocket_new(http->sockfd);
694: http->state = HTTP_NEED_REQUEST;
695: } else if (status == HT_WOULD_BLOCK)
696: return status;
697: else
698: http->state = HTTP_ERROR; /* Error or interrupt */
699: break;
700:
701: case HTTP_NEED_REQUEST: /* Compose the request and send it */
702: if ((status = HTTPSendRequest(request, http, url)) < 0) {
703: if (status == HT_WOULD_BLOCK)
1.55 frystyk 704: return status;
1.71 frystyk 705: else
706: http->state = HTTP_ERROR;
1.55 frystyk 707: } else {
1.71 frystyk 708: http->state = HTTP_SENT_REQUEST;
1.21 luotonen 709: }
1.71 frystyk 710: break;
1.21 luotonen 711:
1.71 frystyk 712: case HTTP_SENT_REQUEST: /* Read the response */
713: case HTTP_NEED_BODY:
714: if (!http->target) {
715: if (HTImProxy)
716: http->target = request->output_stream;
717: else
718: http->target = HTTPStatus_new(request, http);
719: }
1.74 frystyk 720: status = HTInputSocket_read(http->isoc, http->target);
721: if (status == HT_WOULD_BLOCK)
1.71 frystyk 722: return HT_WOULD_BLOCK;
1.75 ! frystyk 723: else if (status == HT_INTERRUPTED) {
! 724: (*http->target->isa->abort)(http->target, NULL);
1.74 frystyk 725: http->state = HTTP_ERROR;
1.75 ! frystyk 726: } else {
! 727: (*http->target->isa->_free)(http->target);
! 728: if (http->state == HTTP_NEED_BODY)
! 729: http->state = HTTP_GOT_DATA;
! 730: }
1.71 frystyk 731: break;
732:
733: case HTTP_REDIRECTION:
734: if (request->redirect) {
735: HTAnchor *anchor;
736: if (status == 301) {
737: HTErrorAdd(request, ERR_INFO, NO, HTERR_MOVED,
738: (void *) request->redirect,
739: (int) strlen(request->redirect), "HTLoadHTTP");
740: } else if (status == 302) {
741: HTErrorAdd(request, ERR_INFO, NO, HTERR_FOUND,
742: (void *) request->redirect,
743: (int) strlen(request->redirect), "HTLoadHTTP");
1.55 frystyk 744: }
1.71 frystyk 745: anchor = HTAnchor_findAddress(request->redirect);
746: if (++request->redirections < HTMaxRedirections) {
747: HTTPCleanup(request);
748: return HTLoadAnchorRecursive((HTAnchor *) anchor, request);
749: } else {
750: HTErrorAdd(request, ERR_FATAL, NO, HTERR_MAX_REDIRECT,
1.58 frystyk 751: NULL, 0, "HTLoadHTTP");
1.71 frystyk 752: http->state = HTTP_ERROR;
1.58 frystyk 753: }
1.71 frystyk 754: } else {
755: HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
756: NULL, 0, "HTLoadHTTP");
757: http->state = HTTP_ERROR;
758: }
759: break;
760:
761: case HTTP_AA:
1.74 frystyk 762: HTTPCleanup(request);
1.71 frystyk 763: if (HTTPAuthentication(request) == YES &&
764: HTAA_retryWithAuth(request) == YES) {
765: return HTLoadAnchor((HTAnchor *) request->anchor, request);
766: } else {
767: char *unescaped = NULL;
768: StrAllocCopy(unescaped, url);
769: HTUnEscape(unescaped);
770: HTErrorAdd(request, ERR_FATAL, NO, HTERR_UNAUTHORIZED,
771: (void *) unescaped,
772: (int) strlen(unescaped), "HTLoadHTTP");
773: free(unescaped);
774: http->state = HTTP_ERROR;
775: }
776: break;
777:
778: case HTTP_GOT_DATA:
1.74 frystyk 779: HTTPCleanup(request);
1.71 frystyk 780: return HT_LOADED;
781: break;
782:
783: case HTTP_NO_DATA:
784: HTTPCleanup(request);
785: return HT_NO_DATA;
786: break;
787:
788: case HTTP_ERROR:
789: HTTPCleanup(request);
790: return HT_ERROR;
791: break;
792: }
793: } /* End of while(1) */
794: }
795:
796: /* Protocol descriptor */
797:
798: #ifdef EVENT_LOOP
799: GLOBALDEF PUBLIC HTProtocol HTTP = {
800: "http", SOC_NON_BLOCK, HTLoadHTTP, NULL, NULL
801: };
802: #else
803: GLOBALDEF PUBLIC HTProtocol HTTP = {
804: "http", SOC_BLOCK, HTLoadHTTP, NULL, NULL
805: };
1.55 frystyk 806: #endif
1.21 luotonen 807:
Webmaster