[BACK] Return to HTTP.c CVS log [TXT] [DIR] Up to [Public] / libwww / Library / src

Annotation of libwww/Library/src/HTTP.c, revision 1.79

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.55 frystyk 35: #include "HTTP.h"                       /* Implements */
 36: 
 37: /* Macros and other defines */
1.71 frystyk 38: #define HTTP_VERSION  "HTTP/1.0"
 39: #define PUTC(c)        (*me->target->isa->put_character)(me->target, c)
 40: #define PUTS(s)        (*me->target->isa->put_string)(me->target, s)
 41: #define PUTBLOCK(b, l) (*me->target->isa->put_block)(me->target, b, l)
 42: #define FREE_TARGET  (*me->target->isa->_free)(me->target)
1.74 frystyk 43: #define ABORT_TARGET  (*me->target->isa->abort)(me->target, e)
1.2 timbl 44: 
1.55 frystyk 45: /* Globals */
1.59 frystyk 46: extern char * HTAppName;         /* Application name: please supply */
 47: extern char * HTAppVersion;      /* Application version: please supply */
1.72 frystyk 48: extern BOOL using_proxy;          /* are we using a proxy gateway? */
1.71 frystyk 49: 
1.64 frystyk 50: PUBLIC int HTMaxRedirections = 10;      /* Max number of redirections */
 51: PUBLIC BOOL HTEnableFrom = NO;            /* Enable From header? */
1.71 frystyk 52: PUBLIC char * HTProxyHeaders = NULL;        /* Headers to pass as-is */
1.23 luotonen 53: 
1.59 frystyk 54: /* Type definitions and global variables etc. local to this module */
 55: /* This is the local definition of HTRequest->net_info */
 56: typedef enum _HTTPState {
1.71 frystyk 57:   HTTP_ERROR     = -3,
 58:   HTTP_NO_DATA    = -2,
 59:   HTTP_GOT_DATA   = -1,
 60:   HTTP_BEGIN     = 0,
 61:   HTTP_NEED_CONNECTION,
 62:   HTTP_NEED_REQUEST,
 63:   HTTP_SENT_REQUEST,
 64:   HTTP_NEED_BODY,
 65:   HTTP_REDIRECTION,
 66:   HTTP_AA
1.59 frystyk 67: } HTTPState;
1.55 frystyk 68: 
 69: typedef struct _http_info {
1.78 frystyk 70:   SOCKFD       sockfd;             /* Socket descripter */
1.71 frystyk 71:   SockA       sock_addr;       /* SockA is defined in tcp.h */
1.68 frystyk 72:   HTInputSocket *  isoc;                /* Input buffer */
1.71 frystyk 73:   HTStream *     target;               /* Output stream */
 74:   HTChunk *     transmit;             /* Line to be send */
 75:   int        addressCount;    /* Attempts if multi-homed host */
 76:   time_t       connecttime;       /* Used on multihomed hosts */
 77:   struct _HTRequest *    request;      /* Link back to request structure */
1.68 frystyk 78: 
 79:   HTTPState     state;          /* State of the connection */
1.55 frystyk 80: } http_info;
 81: 
1.71 frystyk 82: #define MAX_STATUS_LEN     150    /* Max number of chars to look at */
1.55 frystyk 83: 
1.71 frystyk 84: struct _HTStream {
 85:   CONST HTStreamClass *   isa;
 86:   HTStream *         target;
 87:   HTRequest *            request;
 88:   http_info *            http;
 89:   int                cnt;
 90:   HTSocketEOL            state;
 91:   BOOL            transparent;
1.79 ! frystyk 92:   double           version;         /* @@@ DOESN'T WORK */
1.71 frystyk 93:   int                status;
 94:   char            buffer[MAX_STATUS_LEN+1];
 95:   char *           bufptr;
 96: };
1.21 luotonen 97: 
1.71 frystyk 98: /* ------------------------------------------------------------------------- */
 99: /*                Help Functions               */
 100: /* ------------------------------------------------------------------------- */
1.21 luotonen 101: 
1.71 frystyk 102: /*                                 HTTPCleanup
1.1 timbl 103: **
1.55 frystyk 104: **   This function closes the connection and frees memory.
1.1 timbl 105: **
1.55 frystyk 106: **   Returns 0 on OK, else -1
1.1 timbl 107: */
1.71 frystyk 108: PRIVATE int HTTPCleanup ARGS1(HTRequest *, request)
1.1 timbl 109: {
1.71 frystyk 110:   http_info *http;
1.55 frystyk 111:   int status = 0;
1.71 frystyk 112:   if (!request || !request->net_info) {
1.78 frystyk 113:    if (PROT_TRACE) fprintf(TDEST, "HTTPCleanup. Bad argument!\n");
1.55 frystyk 114:    status = -1;
 115:   } else {
1.71 frystyk 116:    http = (http_info *) request->net_info;
1.78 frystyk 117:    if (http->sockfd != INVSOC) {
 118:      if (PROT_TRACE)
 119:        fprintf(TDEST,"HTTP........ Closing socket %d\n",http->sockfd);
1.59 frystyk 120:      if ((status = NETCLOSE(http->sockfd)) < 0)
1.78 frystyk 121:        HTErrorSysAdd(http->request, ERR_FATAL, socerrno, NO,
 122:               "NETCLOSE");
1.71 frystyk 123:      HTThreadState(http->sockfd, THD_CLOSE);
 124:      HTThread_clear((HTNetInfo *) http);
1.78 frystyk 125:      http->sockfd = INVSOC;
1.71 frystyk 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);
1.78 frystyk 260:    if (PROT_TRACE) fprintf(TDEST, "HTTP Tx..... %s", command->data);
1.71 frystyk 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
1.78 frystyk 278:    if (socerrno == EAGAIN || socerrno == EWOULDBLOCK) 
1.71 frystyk 279: #else
1.78 frystyk 280:    if (socerrno == EWOULDBLOCK)
1.3 timbl 281: #endif
1.71 frystyk 282:    {
 283:      if (PROT_TRACE)
1.78 frystyk 284:        fprintf(TDEST, "HTTP Tx..... Write operation would block\n");
1.71 frystyk 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.78 frystyk 289:      HTErrorSysAdd(request, ERR_FATAL, socerrno, 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)
1.78 frystyk 332:      fprintf(TDEST, "Protection template set to `%s'\n",
1.71 frystyk 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.78 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.78 frystyk 352:    case 0:                        /* 0.9 response */
 353:    case 200:
 354:    case 201:
 355:    case 202:
 356:    case 203:
 357:    case 205:
 358:    case 206:
1.71 frystyk 359:    break;
1.78 frystyk 360: 
 361:    case 204:                           /* No Response */
 362:    me->http->state = HTTP_NO_DATA;
1.71 frystyk 363:    break;
1.78 frystyk 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.78 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.78 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.78 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.78 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.78 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.78 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.78 frystyk 430:    HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_INTERNAL,
 431:          NULL, 0, "HTLoadHTTP");
 432:    me->http->state = HTTP_ERROR;
 433:    break;
 434:    
1.71 frystyk 435:    case 501:
1.78 frystyk 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");
1.71 frystyk 456:    me->http->state = HTTP_ERROR;
 457:    break;
1.78 frystyk 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.78 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) ||
1.79 ! frystyk 488:      sscanf(me->buffer+5, "%lf %d", &me->version, &me->status) < 2) {
1.71 frystyk 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.78 frystyk 499:      } else if (me->status==200 && req->method==METHOD_GET) {
 500:        HTStream *s;
 501: 
1.71 frystyk 502:        me->target = HTStreamStack(WWW_MIME,req->output_format,
1.78 frystyk 503:                      req->output_stream, req, NO);
 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,
 509:                            req->output_format,
 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.78 frystyk 516:                      WWW_SOURCE, req->output_flush ?
 517:                      req->output_flush : HTBlackHole());
1.56 frystyk 518:      }
1.78 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.78 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)
1.78 frystyk 592:    fprintf(TDEST, "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.78 frystyk 644:     if (PROT_TRACE) fprintf(TDEST, "HTLoadHTTP.. Bad argument\n");
1.71 frystyk 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.78 frystyk 657:    if (PROT_TRACE) fprintf(TDEST, "HTTP........ Looking for `%s\'\n", url);
1.71 frystyk 658:    if ((http = (http_info *) calloc(1, sizeof(http_info))) == NULL)
 659:      outofmem(__FILE__, "HTLoadHTTP");
1.78 frystyk 660:    http->sockfd = INVSOC;           /* Invalid socket number */
1.71 frystyk 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)
1.78 frystyk 681:          fprintf(TDEST, "HTTP........ Sending Authorization: %s\n",
1.71 frystyk 682:              request->authorization);
 683:        else
1.78 frystyk 684:          fprintf(TDEST,
1.71 frystyk 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)
1.78 frystyk 695:          fprintf(TDEST, "HTTP........ Connected, socket %d\n",
1.71 frystyk 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.78 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.78 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.78 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.78 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);
1.78 frystyk 780:        http->state = HT_ERROR;
1.71 frystyk 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

AltStyle によって変換されたページ (->オリジナル) /