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