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

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

Webmaster