Annotation of libwww/Library/src/HTTP.c, revision 1.71
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.71 ! frystyk 45: extern BOOL using_proxy; / * are we using a proxy gateway? */
! 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