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