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

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

1.1 timbl 1: /*   HyperText Tranfer Protocol   - Client implementation     HTTP.c
 2: **   ==========================
1.2 timbl 3: **
 4: ** Bugs:
 5: **   Not implemented:
 6: **       Forward
 7: **       Redirection
 8: **       Error handling
1.1 timbl 9: */
 10: 
 11: /*   Module parameters:
 12: **   -----------------
 13: **
 14: ** These may be undefined and redefined by syspec.h
 15: */
1.2 timbl 16: 
 17: /* Implements:
 18: */
 19: #include "HTTP.h"
 20: 
 21: #define HTTP_VERSION  "HTTP/1.0"
 22: #define HTTP2             /* Version is greater than 0.9 */
 23: 
 24: #define INIT_LINE_SIZE     1024  /* Start with line buffer this big */
 25: #define LINE_EXTEND_THRESH   256   /* Minimum read size */
 26: #define VERSION_LENGTH         20   /* for returned protocol version */
 27: 
 28: /* Uses:
 29: */
1.1 timbl 30: #include "HTParse.h"
 31: #include "HTUtils.h"
 32: #include "tcp.h"
 33: #include "HTTCP.h"
 34: #include "HTFormat.h"
1.2 timbl 35: #include <ctype.h>
 36: #include "HTAlert.h"
 37: #include "HTMIME.h"
1.5 timbl 38: #include "HTML.h"       /* SCW */
 39: #include "HTInit.h"      /* SCW */
1.21 luotonen 40: #include "HTAccess.h"     /* HTRequest */
1.14 luotonen 41: #include "HTAABrow.h"     /* Access Authorization */
1.20 timbl 42: #include "HTTee.h"       /* Tee off a cache stream */
 43: #include "HTFWriter.h"     /* Write to cache file */
1.1 timbl 44: 
1.2 timbl 45: struct _HTStream {
 46:    HTStreamClass * isa;      /* all we need to know */
 47: };
 48: 
 49: 
1.6 timbl 50: extern char * HTAppName;    /* Application name: please supply */
 51: extern char * HTAppVersion;  /* Application version: please supply */
 52: 
1.19 timbl 53: PUBLIC BOOL HTCacheHTTP = YES; /* Enable caching of HTTP-retrieved files */
1.24 ! luotonen 54: PUBLIC char * HTGatewayCacheRoot = NULL;
 ! 55: PUBLIC unsigned long HTBytesCached = 0;
1.19 timbl 56: 
1.23 luotonen 57: PRIVATE char * gateway_cache_filename ARGS1(CONST char *, name)
 58: {
 59:   char * access;
 60:   char * host;
 61:   char * path;
 62:   char * cache_filename;
 63: 
1.24 ! luotonen 64:   if (HTGatewayCacheRoot && name && !strchr(name, '?') &&
1.23 luotonen 65:    (access = HTParse(name, "", PARSE_ACCESS))) {
 66: 
 67:    if (!strcmp(access, "file")) {
 68:      free(access);
 69:      return NULL;
 70:    }
 71: 
 72:    host  = HTParse(name, "", PARSE_HOST);
 73:    path  = HTParse(name, "", PARSE_PATH | PARSE_PUNCTUATION);
1.24 ! luotonen 74:    cache_filename = (char*)malloc(strlen(HTGatewayCacheRoot) +
 ! 75:                    strlen(access) +
1.23 luotonen 76:                    (host ? strlen(host) : 0) +
1.24 ! luotonen 77:                    (path ? strlen(path) : 0) + 20);
1.23 luotonen 78:    if (!cache_filename) outofmem(__FILE__, "cache_filename");
1.24 ! luotonen 79:    sprintf(cache_filename, "%s/%s/%s%s", 
 ! 80:        HTGatewayCacheRoot, access, host, path);
 ! 81: 
 ! 82:    if (path[ strlen(path)-1 ] == '/')
 ! 83:      strcat(cache_filename, "CERNhttpdINDEX");
1.23 luotonen 84: 
 85:    FREE(access); FREE(host); FREE(path);
 86:    return cache_filename;
 87:   }
 88:   return NULL;
 89: }
 90: 
 91: 
 92: PRIVATE FILE * gateway_cache_lookup ARGS1(CONST char *, name)
 93: {
 94:   char * cache_filename = gateway_cache_filename(name);
 95: 
 96:   if (cache_filename) {
1.24 ! luotonen 97:    struct stat stat_info;
 ! 98: 
 ! 99:    if (stat(cache_filename, &stat_info) != -1) {
 ! 100:      if (S_ISDIR(stat_info.st_mode)) {
 ! 101:        StrAllocCat(cache_filename, "/CERNhttpdINDEX");
 ! 102:        if (stat(cache_filename, &stat_info) == -1) {
 ! 103:          free(cache_filename);
 ! 104:          return NULL;
 ! 105:        }
 ! 106:      }
 ! 107:      if (S_ISREG(stat_info.st_mode)) {
 ! 108:        FILE * cache_file = fopen(cache_filename, "r");
 ! 109:        if (cache_file && TRACE)
 ! 110:          fprintf(stderr, "Cache: HIT \"%s\"\n", cache_filename);
 ! 111:        free(cache_filename);
 ! 112:        return cache_file;
 ! 113:      }
 ! 114:    }
1.23 luotonen 115:    free(cache_filename);
 116:   }
 117:   return NULL;
 118: }
 119: 
 120: 
1.24 ! luotonen 121: PRIVATE FILE * gateway_cache_create ARGS1(char *, cache_filename)
1.23 luotonen 122: {
1.24 ! luotonen 123:   if (HTGatewayCacheRoot && cache_filename) {
 ! 124:    char * cur = cache_filename + strlen(HTGatewayCacheRoot) + 1;
1.23 luotonen 125:    FILE * cache_file;
 126:    struct stat stat_info;
1.24 ! luotonen 127:    BOOL create = NO;
1.23 luotonen 128: 
 129:    while ((cur = strchr(cur, '/'))) {
 130:      *cur = 0;
1.24 ! luotonen 131:      if (create || -1 == stat(cache_filename, &stat_info)) {
 ! 132:        create = YES;  /* To avoid doing stat()s in vain */
1.23 luotonen 133:        CTRACE(stderr, "Gateway cache: creating cache dir \"%s\"\n",
 134:            cache_filename);
 135:        if (-1 == mkdir(cache_filename, 0775)) {
 136:          CTRACE(stderr, "Gateway cache: can't create dir \"%s\"\n",
 137:              cache_filename);
 138:          return NULL;
 139:        }
 140:        CTRACE(stderr, "Gateway cache: created directory \"%s\"\n",
 141:            cache_filename);
 142:      }
1.24 ! luotonen 143:      else {
 ! 144:        if (S_ISREG(stat_info.st_mode)) {
 ! 145:          char * tmp1 = (char*)malloc(strlen(cache_filename)+25);
 ! 146:          char * tmp2 = (char*)malloc(strlen(cache_filename)+25);
 ! 147: 
 ! 148:          sprintf(tmp1, "%s.CERNhttpdINTERNAL", cache_filename);
 ! 149:          sprintf(tmp2, "%s/CERNhttpdINDEX", cache_filename);
 ! 150: 
 ! 151:          CTRACE(stderr, "Moving \"%s\" to \"%s\"\n",
 ! 152:              cache_filename, tmp1);
 ! 153:          rename(cache_filename, tmp1);
 ! 154: 
 ! 155:          CTRACE(stderr, "Creating dir \"%s\"\n", cache_filename);
 ! 156:          mkdir(cache_filename, 0775);
 ! 157: 
 ! 158:          CTRACE(stderr, "Moving \"%s\" to \"%s\"\n", tmp1, tmp2);
 ! 159:          rename(tmp1, tmp2);
 ! 160: 
 ! 161:          free(tmp1);
 ! 162:          free(tmp2);
 ! 163:        }
 ! 164:        else {
 ! 165:          CTRACE(stderr,
 ! 166:              "Gateway cache: dir \"%s\" already exists\n",
 ! 167:              cache_filename);
 ! 168:        }
 ! 169:      }
1.23 luotonen 170:      *cur = '/';
 171:      cur++;
 172:    }
 173:    cache_file = fopen(cache_filename, "w");
 174:    if (!cache_file) {
 175:      CTRACE(stderr, "Gateway cache: can't create cache file \"%s\"\n",
 176:          cache_filename);
 177:    }
 178:    return cache_file;
 179:   }
 180:   return NULL;
 181: }
 182: 
 183: 
 184: 
 185: 
1.21 luotonen 186: PRIVATE void parse_401_headers ARGS2(HTRequest *,   req,
 187:                   HTInputSocket *,  isoc)
 188: {
 189:   HTAAScheme scheme;
 190:   char *line;
 191:   int num_schemes = 0;
 192:   HTList *valid_schemes = HTList_new();
 193:   HTAssocList **scheme_specifics = NULL;
 194:   char *template = NULL;
 195: 
 196:   /* Read server reply header lines */
 197: 
 198:   if (TRACE)
 199:    fprintf(stderr, "Server 401 reply header lines:\n");
 200: 
 201:   while (NULL != (line = HTInputSocket_getUnfoldedLine(isoc)) &&
 202:      *line != 0) {
 203: 
 204:    if (TRACE) fprintf(stderr, "%s\n", line);
 205: 
 206:    if (strchr(line, ':')) {    /* Valid header line */
 207: 
 208:      char *p = line;
 209:      char *fieldname = HTNextField(&p);
 210:      char *arg1 = HTNextField(&p);
 211:      char *args = p;
 212:      
 213:      if (0==strcasecomp(fieldname, "WWW-Authenticate:")) {
 214:        if (HTAA_UNKNOWN != (scheme = HTAAScheme_enum(arg1))) {
 215:          HTList_addObject(valid_schemes, (void*)scheme);
 216:          if (!scheme_specifics) {
 217:            int i;
 218:            scheme_specifics = (HTAssocList**)
 219:              malloc(HTAA_MAX_SCHEMES * sizeof(HTAssocList*));
 220:            if (!scheme_specifics)
 221:              outofmem(__FILE__, "parse_401_headers");
 222:            for (i=0; i < HTAA_MAX_SCHEMES; i++)
 223:              scheme_specifics[i] = NULL;
 224:          }
 225:          scheme_specifics[scheme] = HTAA_parseArgList(args);
 226:          num_schemes++;
 227:        }
 228:        else if (TRACE) {
 229:          fprintf(stderr, "Unknown scheme `%s' %s\n",
 230:              (arg1 ? arg1 : "(null)"),
 231:              "in WWW-Authenticate: field");
 232:        }
 233:      }
 234: 
 235:      else if (0==strcasecomp(fieldname, "WWW-Protection-Template:")) {
 236:        if (TRACE)
 237:          fprintf(stderr, "Protection template set to `%s'\n", arg1);
 238:        StrAllocCopy(template, arg1);
 239:      }
 240: 
 241:    } /* if a valid header line */
 242:    else if (TRACE) {
 243:      fprintf(stderr, "Invalid header line `%s' ignored\n", line);
 244:    } /* else invalid header line */
 245:   } /* while header lines remain */
 246: 
 247:   req->valid_schemes = valid_schemes;
 248:   req->scheme_specifics = scheme_specifics;
 249:   req->prot_template = template;
 250: }
 251: 
 252: 
 253: 
1.1 timbl 254: /*       Load Document from HTTP Server         HTLoadHTTP()
 255: **       ==============================
 256: **
 257: **   Given a hypertext address, this routine loads a document.
 258: **
 259: **
 260: ** On entry,
 261: **   arg   is the hypertext reference of the article to be loaded.
 262: **
 263: ** On exit,
 264: **   returns >=0   If no error, a good socket number
 265: **       <0   Error.
 266: **
 267: **   The socket must be closed by the caller after the document has been
 268: **   read.
 269: **
 270: */
1.19 timbl 271: PUBLIC int HTLoadHTTP ARGS1 (HTRequest *, request)
1.1 timbl 272: {
1.22 luotonen 273:   CONST char * arg = NULL;
1.1 timbl 274:   int s;               /* Socket number for returned data */
 275:   int status;                /* tcp return */
1.10 timbl 276:   char crlf[3];           /* A CR LF equivalent string */
1.3 timbl 277:   HTStream * target = NULL;     /* Unconverted data */
 278:   
1.2 timbl 279:   CONST char* gate = 0;       /* disable this feature */
1.1 timbl 280:   SockA soc_address;         /* Binary network address */
 281:   SockA * sin = &soc_address;
1.2 timbl 282:   BOOL extensions = YES;       /* Assume good HTTP server */
1.17 timbl 283: 
1.22 luotonen 284:   if (request->reason == HTAA_OK_GATEWAY) {
 285:    arg = request->translated;
1.24 ! luotonen 286:    HTBytesCached = 0;
1.23 luotonen 287: 
 288:    /*
 289:    ** Cache lookup
 290:    */
1.24 ! luotonen 291:    if (HTGatewayCacheRoot && request->method == METHOD_GET &&
1.23 luotonen 292:      !request->authorization && !request->arg_keywords) {
 293: 
 294:      FILE * cache_file = gateway_cache_lookup(arg);
 295: 
 296:      if (cache_file) {
 297:        HTFileCopy(cache_file, request->output_stream);
 298:        return HT_LOADED;
 299:      }
 300: 
 301:    } /* Cache lookup */
 302: 
1.22 luotonen 303:   }
 304:   else {
 305:    arg = HTAnchor_physical(request->anchor);
 306:    StrAllocCopy(request->argument, arg);
 307:   }
 308: 
1.1 timbl 309:   if (!arg) return -3;        /* Bad if no name sepcified   */
 310:   if (!*arg) return -2;       /* Bad if name had zero length */
 311: 
 312: /* Set up defaults:
 313: */
 314: #ifdef DECNET
1.2 timbl 315:   sin->sdn_family = AF_DECnet;      /* Family = DECnet, host order */
 316:   sin->sdn_objnum = DNP_OBJ;     /* Default: http object number */
1.1 timbl 317: #else /* Internet */
1.2 timbl 318:   sin->sin_family = AF_INET;   /* Family = internet, host order */
 319:   sin->sin_port = htons(TCP_PORT);  /* Default: http port  */
1.1 timbl 320: #endif
 321: 
1.10 timbl 322:   sprintf(crlf, "%c%c", CR, LF);   /* To be corect on Mac, VM, etc */
 323:   
1.1 timbl 324:   if (TRACE) {
 325:     if (gate) fprintf(stderr,
 326:        "HTTPAccess: Using gateway %s for %s\n", gate, arg);
 327:     else fprintf(stderr, "HTTPAccess: Direct access for %s\n", arg);
 328:   }
 329:   
 330: /* Get node name and optional port number:
 331: */
 332:   {
 333:    char *p1 = HTParse(gate ? gate : arg, "", PARSE_HOST);
 334:    int status = HTParseInet(sin, p1); /* TBL 920622 */
 335:     free(p1);
 336:    if (status) return status;  /* No such host for example */
 337:   }
 338:   
1.15 luotonen 339: /*
 340: ** Compose authorization information (this was moved here
 341: ** from after the making of the connection so that the connection
 342: ** wouldn't have to wait while prompting username and password
 343: ** from the user).               -- AL 13.10.93
 344: */
 345: #ifdef ACCESS_AUTH
1.21 luotonen 346:   HTAA_composeAuth(request);
 347:   if (TRACE) {
 348:    if (request->authorization)
 349:      fprintf(stderr, "HTTP: Sending Authorization: %s\n",
 350:          request->authorization);
 351:    else
 352:      fprintf(stderr, "HTTP: Not sending authorization (yet)\n");
1.15 luotonen 353:   }
 354: #endif /* ACCESS_AUTH */
1.1 timbl 355:  
1.10 timbl 356: /*   Now, let's get a socket set up from the server for the data:
1.1 timbl 357: */   
 358: #ifdef DECNET
 359:   s = socket(AF_DECnet, SOCK_STREAM, 0);
 360: #else
 361:   s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 362: #endif
 363:   status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
 364:   if (status < 0) {
 365:      if (TRACE) fprintf(stderr, 
 366:       "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", arg, errno);
1.17 timbl 367: 
1.1 timbl 368:      return HTInetStatus("connect");
 369:    }
 370:   
 371:   if (TRACE) fprintf(stderr, "HTTP connected, socket %d\n", s);
 372: 
1.17 timbl 373: 
 374: /*   Compose and send command
 375: **   ------------------------
 376: */
 377:   {
 378:     char *command;         /* The whole command */
 379:    
1.1 timbl 380: /*   Ask that node for the document,
 381: **   omitting the host name & anchor if not gatewayed.
 382: */    
1.17 timbl 383:    if (gate) {
 384:      command = malloc(4 + strlen(arg)+ 2 + 31);
 385:      if (command == NULL) outofmem(__FILE__, "HTLoadHTTP");
 386:      strcpy(command, "GET ");
 387:      strcat(command, arg);
 388:    } else { /* not gatewayed */
 389:      char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
 390:      command = malloc(4 + strlen(p1)+ 2 + 31);
 391:      if (command == NULL) outofmem(__FILE__, "HTLoadHTTP");
1.23 luotonen 392:      if (request->method != METHOD_INVALID) {
 393:        strcpy(command, HTMethod_name(request->method));
1.22 luotonen 394:        strcat(command, " ");
 395:      }
 396:      else {
 397:        strcpy(command, "GET ");
 398:      }
1.17 timbl 399:      strcat(command, p1);
 400:      free(p1);
 401:    }
1.2 timbl 402: #ifdef HTTP2
1.17 timbl 403:    if (extensions) {
 404:      strcat(command, " ");
 405:      strcat(command, HTTP_VERSION);
 406:    }
1.2 timbl 407: #endif
1.17 timbl 408:   
 409:    strcat(command, crlf); /* CR LF, as in rfc 977 */
 410:   
 411:    if (extensions) {
1.21 luotonen 412: 
1.17 timbl 413:      int i;
 414:      HTAtom * present = WWW_PRESENT;
 415:      char line[256];  /*@@@@ */
1.21 luotonen 416:      HTList *conversions[2];
 417: 
1.22 luotonen 418:      if (!HTConversions) {
 419:        HTConversions = HTList_new();
 420:        HTFormatInit(HTConversions);
 421:      }
1.21 luotonen 422:      conversions[0] = HTConversions;
 423:      conversions[1] = request->conversions;
 424: 
 425:      for (i=0; i<2; i++) {
 426:        HTList *cur = conversions[i];
 427:        HTPresentation *pres;
 428: 
 429:        while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
 430:          if (pres->rep_out == present) {
 431:            if (pres->quality != 1.0) {
 432:              sprintf(line, "Accept: %s q=%.3f%c%c",
 433:                  HTAtom_name(pres->rep),
 434:                  pres->quality, CR, LF);
 435:            } else {
 436:              sprintf(line, "Accept: %s%c%c",
 437:                  HTAtom_name(pres->rep), CR, LF);
 438:            }
 439:            StrAllocCat(command, line);
1.17 timbl 440:          }
 441:        }
1.2 timbl 442:      }
1.22 luotonen 443: 
 444:      sprintf(line, "User-Agent: %s%s %s/%s libwww/%s%c%c",
 445:          request->user_agent ? request->user_agent : "",
 446:          request->user_agent ? " VIA Gateway" : "",
1.17 timbl 447:          HTAppName ? HTAppName : "unknown",
 448:          HTAppVersion ? HTAppVersion : "0.0",
 449:          HTLibraryVersion, CR, LF);
 450:          StrAllocCat(command, line);
 451:   
1.22 luotonen 452:      if (request->from) {
 453:        sprintf(line, "From: %s%c%c", request->from, CR, LF);
 454:        StrAllocCat(command, line);
 455:      }
 456: 
1.14 luotonen 457: #ifdef ACCESS_AUTH
1.21 luotonen 458:      if (request->authorization != NULL) {
 459:        sprintf(line, "Authorization: %s%c%c",
 460:            request->authorization, CR, LF);
1.17 timbl 461:        StrAllocCat(command, line);
 462:      }
 463: #endif /* ACCESS_AUTH */
1.22 luotonen 464: 
 465:      if (request->content_type) {
 466:        sprintf(line, "Content-Type: %s%c%c",
 467:            HTAtom_name(request->content_type), CR, LF);
 468:        StrAllocCat(command, line);
 469:      }
 470: 
 471:      if (request->content_length > 0) {
 472:        sprintf(line, "Content-Length: %d%c%c",
 473:            request->content_length, CR, LF);
 474:        StrAllocCat(command, line);
 475:      }
 476: 
 477: 
1.14 luotonen 478:    }
1.17 timbl 479:   
 480:    StrAllocCat(command, crlf);   /* Blank line means "end" */
 481:   
 482:    if (TRACE) fprintf(stderr, "HTTP Tx: %s\n", command);
 483:   
 484:   /* Translate into ASCII if necessary
 485:   */
1.4 timbl 486: #ifdef NOT_ASCII
1.17 timbl 487:    {
 488:      char * p;
 489:      for(p = command; *p; p++) {
 490:        *p = TOASCII(*p);
 491:      }
1.1 timbl 492:    }
1.3 timbl 493: #endif
1.17 timbl 494:   
 495:    status = NETWRITE(s, command, (int)strlen(command));
 496:    free(command);
 497:    if (status<0) {
 498:      if (TRACE) fprintf(stderr,
 499:        "HTTPAccess: Unable to send command.\n");
1.1 timbl 500:      return HTInetStatus("send");
1.17 timbl 501:    }
 502:   } /* compose and send command */
 503:   
1.2 timbl 504: 
1.17 timbl 505: /*   Read the response
 506: **   -----------------
1.11 timbl 507: **
 508: **   HTTP0 servers must return ASCII style text, though it can in
 509: **   principle be just text without any markup at all.
 510: **   Full HTTP servers must return a response
 511: **   line and RFC822 style header. The response must therefore in
 512: **   either case have a CRLF somewhere soon.
 513: **
 514: **   This is the theory. In practice, there are (1993) unfortunately
 515: **   many binary documents just served up with HTTP0.9. This
 516: **   means we have to preserve the binary buffer (on the assumption that
 517: **   conversion from ASCII may lose information) in case it turns
 518: **   out that we want the binary original.
1.2 timbl 519: */
1.3 timbl 520: 
1.22 luotonen 521:   if (request->reason == HTAA_OK_GATEWAY) {
1.24 ! luotonen 522: 
 ! 523:    char * cache_filename = NULL;
 ! 524: 
1.22 luotonen 525:    /*
 526:    ** Server as a gateway -- send body of the message
 527:    ** received from client (if any).
 528:    */
 529:    if (request->isoc && request->content_length > 0) {
 530:      int remain = request->content_length;
 531:      int i = remain;
 532:      char * buf;
 533: 
 534:      while (remain > 0 &&
 535:          (buf = HTInputSocket_getBlock(request->isoc, &i))) {
 536:        int status = NETWRITE(s, buf, i);
 537:        if (status < 0) {
 538:          CTRACE(stderr, "HTTPAccess: Unable to forward body\n");
 539:          return HTInetStatus("send");
 540:        }
 541:        remain -= i;
 542:        i = remain;
 543:      }
 544:    }
1.23 luotonen 545: 
 546:    /*
 547:    ** Cache the document if it a GET request,
 548:    ** not protected and not a search request.
 549:    */
1.24 ! luotonen 550:    if (HTGatewayCacheRoot && request->method == METHOD_GET &&
1.23 luotonen 551:      !request->authorization && !request->arg_keywords) {
 552: 
1.24 ! luotonen 553:      cache_filename = gateway_cache_filename(request->translated);
 ! 554:      if (cache_filename) {
 ! 555:        FILE * cache_file = gateway_cache_create(cache_filename);
 ! 556: 
 ! 557:        if (cache_file) {
 ! 558:          request->output_stream = HTTee(request->output_stream,
 ! 559:                          HTFWriter_new(cache_file));
 ! 560:          CTRACE(stderr, "Gateway cache: writing to cache file\n");
 ! 561:        }
 ! 562:        else {
 ! 563:          FREE(cache_filename);
 ! 564:          CTRACE(stderr,
 ! 565:              "Gateway cache: couldn't create cache file\n");
 ! 566:        }
1.23 luotonen 567:      }
 568:    }
 569:    else CTRACE(stderr, "Gateway cache: not caching\n");
 570: 
1.22 luotonen 571:    /*
 572:    ** Load results directly to client
 573:    */
 574:    HTCopy(s, request->output_stream);
1.24 ! luotonen 575:    if (cache_filename) {
 ! 576:      struct stat stat_info;
 ! 577:      if (stat(cache_filename, &stat_info) != -1) {
 ! 578:        HTBytesCached = stat_info.st_size;
 ! 579:        CTRACE(stderr, "Cached %lu bytes to \"%s\"\n",
 ! 580:            HTBytesCached, cache_filename);
 ! 581:      }
 ! 582:      else CTRACE(stderr, "Couldn't stat cache file \"%s\"\n",
 ! 583:            cache_filename);
 ! 584:    }
1.22 luotonen 585:    return HT_LOADED;
 586:   }
 587:   else {   /* read response */
1.21 luotonen 588: 
1.17 timbl 589:    HTFormat format_in;       /* Format arriving in the message */
1.21 luotonen 590:    HTInputSocket *isoc = HTInputSocket_new(s);
 591:    char * status_line = HTInputSocket_getStatusLine(isoc);
1.2 timbl 592: 
1.11 timbl 593: /* Kludge to trap binary responses from illegal HTTP0.9 servers.
 594: ** First time we have enough, look at the stub in ASCII
 595: ** and get out of here if it doesn't look right.
 596: **
 597: ** We also check for characters above 128 in the first few bytes, and
 598: ** if we find them we forget the html default.
 599: **
 600: ** Bugs: A HTTP0.9 server returning a document starting "HTTP/"
 601: **   will be taken as a HTTP 1.0 server. Failure.
 602: **   An HTTP 0.9 server returning a binary document with
 603: **   characters < 128 will be read as ASCII.
 604: */
1.21 luotonen 605:    if (!status_line) {   /* HTTP0 response */
 606:      if (HTInputSocket_seemsBinary(isoc)) {
 607:        format_in = HTAtom_for("www/unknown");
 608:      }
 609:      else {
 610:        format_in = WWW_HTML;
 611:      }
 612:      goto copy;
 613:    } /* end kludge */
 614: 
 615:    if (status_line) {   /* Decode full HTTP response */
 616:      /*
 617:      ** We now have a terminated server status line, and we have
 618:      ** checked that it is most probably a legal one. Parse it.
 619:      */
 620:      char server_version[VERSION_LENGTH+1];
 621:      int server_status;
 622: 
 623:      if (TRACE)
 624:        fprintf(stderr, "HTTP Status Line: Rx: %.70s\n", status_line);
1.17 timbl 625:   
1.21 luotonen 626:      sscanf(status_line, "%20s%d", server_version, &server_status);
1.2 timbl 627: 
1.21 luotonen 628:      format_in = HTAtom_for("www/mime");
1.7 timbl 629:   
1.21 luotonen 630:      switch (server_status / 100) {
1.2 timbl 631: 
1.21 luotonen 632:       default:     /* bad number */
 633:        HTAlert("Unknown status reply from server!");
 634:        break;
1.17 timbl 635:          
1.21 luotonen 636:       case 3:      /* Various forms of redirection */
 637:        HTAlert(
1.17 timbl 638:      "Redirection response from server is not handled by this client");
1.21 luotonen 639:        break;
1.17 timbl 640:          
1.21 luotonen 641:       case 4:      /* Access Authorization problem */
1.14 luotonen 642: #ifdef ACCESS_AUTH
1.21 luotonen 643:        switch (server_status) {
 644:         case 401:
 645:          parse_401_headers(request, isoc);
 646: 
 647:          if (TRACE) fprintf(stderr, "%s %d %s\n",
 648:                    "HTTP: close socket", s,
 649:                    "to retry with Access Authorization");
 650:          HTInputSocket_free(isoc);
 651:          (void)NETCLOSE(s);
1.24 ! luotonen 652:          if (HTAA_retryWithAuth(request, HTLoadHTTP)) {
1.21 luotonen 653:            status = HT_LOADED;/* @@ THIS ONLY WORKS ON LINEMODE */
 654:            goto clean_up;
 655:          }
 656:          /* else falltrough */
 657:         default:
1.14 luotonen 658:          {
1.21 luotonen 659:            char *p1 = HTParse(gate ? gate : arg, "",
 660:                      PARSE_HOST);
 661:            char * message;
 662: 
 663:            if (!(message = (char*)malloc(strlen(status_line) +
 664:                           strlen(p1) + 100)))
 665:              outofmem(__FILE__, "HTTP 4xx status");
1.14 luotonen 666:            sprintf(message,
1.21 luotonen 667:                "HTTP server at %s replies:\n%s\n\n%s\n",
 668:                p1, status_line,
 669:                ((server_status == 401) 
 670:                 ? "Access Authorization package giving up.\n"
 671:                 : ""));
1.22 luotonen 672:            status = HTLoadError(request, server_status, message);
1.14 luotonen 673:            free(message);
 674:            free(p1);
 675:            goto clean_up;
 676:          }
1.21 luotonen 677:        } /* switch */
 678:        goto clean_up;
 679:        break;
 680: #else
 681:        /* case 4 without Access Authorization falls through */
 682:        /* to case 5 (previously "I think I goofed"). -- AL */
 683: #endif /* ACCESS_AUTH */
 684: 
 685:       case 5:      /* I think you goofed */
 686:        {
 687:          char *p1 = HTParse(gate ? gate : arg, "", PARSE_HOST);
 688:          char * message = (char*)malloc(strlen(status_line) + 
 689:                          strlen(p1) + 100);
 690:          if (!message) outofmem(__FILE__, "HTTP 5xx status");
 691:          sprintf(message,
 692:              "HTTP server at %s replies:\n%s", p1, status_line);
1.22 luotonen 693:          status = HTLoadError(request, server_status, message);
1.21 luotonen 694:          free(message);
 695:          free(p1);
 696:          goto clean_up;
 697:        }
 698:        break;
1.17 timbl 699:          
1.21 luotonen 700:       case 2:      /* Good: Got MIME object */
 701:        break;
1.17 timbl 702:   
1.21 luotonen 703:      } /* switch on response code */
1.17 timbl 704:      
1.21 luotonen 705:    } /* Full HTTP reply */
1.17 timbl 706:      
 707:   
1.3 timbl 708: /*   Set up the stream stack to handle the body of the message
 709: */
1.21 luotonen 710: 
1.13 duns 711: copy:
1.21 luotonen 712: 
1.18 timbl 713:    target = HTStreamStack(format_in, request);
1.21 luotonen 714: 
1.17 timbl 715:    if (!target) {
 716:      char buffer[1024]; /* @@@@@@@@ */
 717:      sprintf(buffer, "Sorry, no known way of converting %s to %s.",
 718:          HTAtom_name(format_in), HTAtom_name(request->output_format));
 719:      fprintf(stderr, "HTTP: %s", buffer);
1.22 luotonen 720:      status = HTLoadError(request, 501, buffer);
1.17 timbl 721:      goto clean_up;
 722:    }
 723:   
1.19 timbl 724:     /* @@ Bug: The decision of whether or not to cache should also be
1.21 luotonen 725:    ** made contingent on a IP address match or non match.
 726:    */
1.19 timbl 727:     if (HTCacheHTTP) {
 728:      target = HTTee(target, HTCacheWriter(request, NULL, format_in,
1.21 luotonen 729:                         request->output_format,
 730:                         request->output_stream));
1.19 timbl 731:    }
 732:    
1.11 timbl 733: /*   Push the data down the stream
1.3 timbl 734: **   We have to remember the end of the first buffer we just read
1.2 timbl 735: */
1.17 timbl 736:    if (format_in == WWW_HTML) {
 737:      target = HTNetToText(target);    /* Pipe through CR stripper */
 738:    }
1.21 luotonen 739: 
1.17 timbl 740:    (*target->isa->put_block)(target,
1.21 luotonen 741:                 isoc->input_pointer,
 742:                 isoc->input_limit - isoc->input_pointer);
 743:    HTInputSocket_free(isoc);
1.17 timbl 744:    HTCopy(s, target);
 745:      
 746:    (*target->isa->free)(target);
 747:    status = HT_LOADED;
1.11 timbl 748:   
1.2 timbl 749: /*   Clean up
1.1 timbl 750: */
1.17 timbl 751:    
 752: clean_up: 
 753:    if (TRACE) fprintf(stderr, "HTTP: close socket %d.\n", s);
 754:    (void) NETCLOSE(s);
 755:   
 756:    return status;         /* Good return */
1.3 timbl 757:   
1.17 timbl 758:   } /* read response */
 759: } /* load HTTP */
1.1 timbl 760: 
 761: /*   Protocol descriptor
 762: */
 763: 
1.17 timbl 764: GLOBALDEF PUBLIC HTProtocol HTTP = { "http", HTLoadHTTP, 0, 0 };
1.21 luotonen 765: 

Webmaster

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