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