Annotation of libwww/Library/src/HTTPReq.c, revision 2.10

2.1       frystyk     1: /*                                                                   HTTPReq.c
                      2: **     MULTITHREADED IMPLEMENTATION OF HTTP CLIENT
                      3: **
                      4: **     This module implements the output stream for HTTP used for sending
                      5: **     requests with or without a entity body.
                      6: **
                      7: ** History:
                      8: **     Jan 95 HFN      Written from scratch
                      9: **
                     10: */
                     11: 
                     12: /* Library Includes */
                     13: #include "tcp.h"
                     14: #include "HTUtils.h"
                     15: #include "HTString.h"
                     16: #include "HTParse.h"
2.4       frystyk    17: #include "HTFormat.h"
2.10    ! frystyk    18: #include "HTNet.h"
2.1       frystyk    19: #include "HTTCP.h"
                     20: #include "HTWriter.h"
2.10    ! frystyk    21: #include "HTReqMan.h"
2.1       frystyk    22: #include "HTChunk.h"
                     23: #include "HTTPReq.h"                                          /* Implements */
                     24: 
                     25: /* Type definitions and global variables etc. local to this module */ 
                     26: extern char * HTAppName;                 /* Application name: please supply */
                     27: extern char * HTAppVersion;           /* Application version: please supply */
                     28: PUBLIC char * HTProxyHeaders = NULL;               /* Headers to pass as-is */
                     29: 
                     30: /* Macros and other defines */
                     31: #define HTTP_VERSION   "HTTP/1.0"
                     32: #define MIME_VERSION   "MIME/1.0"
                     33: #define PUTC(c)                (*me->target->isa->put_character)(me->target, c)
                     34: #define PUTS(s)                (*me->target->isa->put_string)(me->target, s)
                     35: #define PUTBLOCK(b, l) (*me->target->isa->put_block)(me->target, b, l)
2.7       frystyk    36: #define FREE_TARGET    
2.1       frystyk    37: #define ABORT_TARGET   (*me->target->isa->abort)(me->target, e)
                     38: 
                     39: /* Type definitions and global variables etc. local to this module */
                     40: 
                     41: PRIVATE char linebuf[256];     /* @@@ */
                     42: 
                     43: struct _HTStream {
                     44:     CONST HTStreamClass *      isa;
                     45:     HTStream *                 target;
                     46:     HTRequest *                        request;
2.7       frystyk    47:     SOCKFD                     sockfd;
2.1       frystyk    48:     HTChunk *                          buffer;
                     49:     BOOL                       transparent;
                     50: };
                     51: 
                     52: /* ------------------------------------------------------------------------- */
                     53: /*                         HTTP Output Request Stream                       */
                     54: /* ------------------------------------------------------------------------- */
                     55: 
                     56: /*                                                              HTTPMakeRequest
                     57: **
                     58: **     This function composes the HTTP request header.
                     59: */
                     60: PRIVATE void HTTPMakeRequest ARGS2(HTStream *, me, HTRequest *, request)
                     61: {
                     62:     HTChunk *header = me->buffer;
                     63:     HTParentAnchor *entity =
2.7       frystyk    64:        (request->source && request->source->anchor) ?
                     65:            request->source->anchor : request->anchor;
2.1       frystyk    66: 
                     67:     /* Generate the HTTP/1.0 RequestLine */
                     68:     if (request->method != METHOD_INVALID) {
                     69:        HTChunkPuts(header, HTMethod_name(request->method));
                     70:        HTChunkPutc(header, ' ');
                     71:     } else
                     72:        HTChunkPuts(header, "GET ");
                     73: 
                     74:     /* If we are using a proxy then only take the `path' info in the URL */
                     75:     {
2.6       frystyk    76:        char *addr = HTAnchor_physical(request->anchor);
                     77:        char *fullurl = HTParse(addr, "", PARSE_PATH|PARSE_PUNCTUATION);
2.1       frystyk    78:        if (request->using_proxy) {
                     79:            HTChunkPuts(header, fullurl+1);
                     80:        } else {
                     81:            HTChunkPuts(header, fullurl);
                     82:        }
                     83:        free(fullurl);
                     84:     }
                     85:     HTChunkPutc(header, ' ');
                     86:     HTChunkPuts(header, HTTP_VERSION);
                     87:     HTChunkPutc(header, CR);
                     88:     HTChunkPutc(header, LF);
                     89: 
2.4       frystyk    90:     /* General Headers */
                     91:     if (request->GenMask & HT_DATE) {
2.1       frystyk    92:        time_t local = time(NULL);
                     93:        sprintf(linebuf, "Date: %s%c%c", HTDateTimeStr(&local, NO), CR,LF);
                     94:        HTChunkPuts(header, linebuf);
                     95:     }
2.4       frystyk    96:     if (request->GenMask & HT_FORWARDED) {             /* @@@@@@ */
                     97:     }
                     98:     if (request->GenMask & HT_MESSAGE_ID) {
2.1       frystyk    99:        CONST char *msgid = HTMessageIdStr();
                    100:        if (msgid) {
                    101:            sprintf(linebuf, "Message-ID: %s%c%c", msgid, CR, LF);
                    102:            HTChunkPuts(header, linebuf);
                    103:        }
                    104:     }
2.4       frystyk   105:     if (request->GenMask & HT_MIME) {
2.1       frystyk   106:        sprintf(linebuf, "MIME-Version: %s%c%c", MIME_VERSION, CR, LF);
                    107:        HTChunkPuts(header, linebuf);
                    108:     }
                    109: 
2.9       frystyk   110:     /* Various PRAGMA headers */
                    111:     if (request->RequestMask & HT_NO_CACHE) {
                    112:        sprintf(linebuf, "Pragma: %s%c%c", "no-cache", CR, LF);
                    113:        HTChunkPuts(header, linebuf);
                    114:     }
                    115: 
2.4       frystyk   116:     /* Request Headers */
                    117:     if (request->RequestMask & HT_ACCEPT_TYPE) {
2.1       frystyk   118:        int list;
                    119:        HTList *cur;
                    120:        for (list=0; list<2; list++) {
                    121:            if ((!list && ((cur=HTConversions) != NULL)) ||
                    122:                (list && ((cur=request->conversions) != NULL))) {
2.4       frystyk   123:                HTPresentation  *pres;
2.1       frystyk   124:                while ((pres =(HTPresentation *) HTList_nextObject(cur))) {
                    125:                    if (pres->rep_out == WWW_PRESENT) {
                    126:                        if (pres->quality != 1.0) {
                    127:                            sprintf(linebuf, "Accept: %s; q=%1.1f%c%c",
                    128:                                    HTAtom_name(pres->rep),
                    129:                                    pres->quality, CR, LF);
                    130:                        } else {
                    131:                            sprintf(linebuf, "Accept: %s%c%c",
                    132:                                    HTAtom_name(pres->rep), CR, LF);
                    133:                        }
                    134:                        HTChunkPuts(header, linebuf);
                    135:                    }
                    136:                }
                    137:            }
                    138:        }
                    139:     }
2.4       frystyk   140:     if (request->RequestMask & HT_ACCEPT_CHAR) {
                    141:        BOOL first=YES;
                    142:        int list;
                    143:        HTList *cur;
                    144:        for (list=0; list<2; list++) {
                    145:            if ((!list && ((cur=HTCharsets) != NULL)) ||
                    146:                (list && ((cur=request->charsets) != NULL))) {
                    147:                HTAcceptNode *pres;
                    148:                while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
2.5       frystyk   149:                    if (first) {
                    150:                        HTChunkPuts(header, "Accept-Charset: ");
                    151:                        first=NO;
                    152:                    }
2.4       frystyk   153:                    if (cur->next)
                    154:                        sprintf(linebuf, "%s,", HTAtom_name(pres->atom));
                    155:                    else
                    156:                        sprintf(linebuf, "%s%c%c", HTAtom_name(pres->atom),
                    157:                                CR, LF);
                    158:                    HTChunkPuts(header, linebuf);
                    159:                }
                    160:            }
                    161:        }
                    162:     }
                    163:     if (request->RequestMask & HT_ACCEPT_ENC) {
                    164:        BOOL first=YES;
                    165:        int list;
                    166:        HTList *cur;
                    167:        for (list=0; list<2; list++) {
                    168:            if ((!list && ((cur=HTEncodings) != NULL)) ||
                    169:                (list && ((cur=request->encodings) != NULL))) {
                    170:                HTAcceptNode *pres;
                    171:                while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
2.5       frystyk   172:                    if (first) {
                    173:                        HTChunkPuts(header, "Accept-Encoding: ");
                    174:                        first=NO;
                    175:                    }
2.4       frystyk   176:                    if (cur->next)
                    177:                        sprintf(linebuf, "%s,", HTAtom_name(pres->atom));
                    178:                    else
                    179:                        sprintf(linebuf, "%s%c%c", HTAtom_name(pres->atom),
                    180:                                CR, LF);
                    181:                    HTChunkPuts(header, linebuf);
                    182:                }
                    183:            }
                    184:        }
                    185:     }
                    186:     if (request->RequestMask & HT_ACCEPT_LAN) {
                    187:        BOOL first=YES;
                    188:        int list;
                    189:        HTList *cur;
                    190:        for (list=0; list<2; list++) {
                    191:            if ((!list && ((cur=HTLanguages) != NULL)) ||
                    192:                (list && ((cur=request->languages) != NULL))) {
                    193:                HTAcceptNode *pres;
                    194:                while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
2.5       frystyk   195:                    if (first) {
                    196:                        HTChunkPuts(header, "Accept-Language: ");
                    197:                        first=NO;
                    198:                    }
2.4       frystyk   199:                    if (cur->next)
                    200:                        sprintf(linebuf, "%s,", HTAtom_name(pres->atom));
                    201:                    else
                    202:                        sprintf(linebuf, "%s%c%c", HTAtom_name(pres->atom),
                    203:                                CR, LF);
                    204:                    HTChunkPuts(header, linebuf);
                    205:                }
                    206:            }
                    207:        }
                    208:     }
                    209:     if (request->authorization != NULL) {          /* Put out authorization */
2.1       frystyk   210:        sprintf(linebuf, "Authorization: %s%c%c", request->authorization,
                    211:                CR, LF);
                    212:        HTChunkPuts(header, linebuf);
                    213:     }
2.4       frystyk   214:     if (request->RequestMask & HT_FROM) {
2.1       frystyk   215:        CONST char *mailaddress = HTGetMailAddress();
                    216:        if (mailaddress) {
                    217:            sprintf(linebuf, "From: %s%c%c", mailaddress, CR, LF);
                    218:            HTChunkPuts(header, linebuf);
                    219:        }
                    220:     }
2.8       frystyk   221:     if (request->RequestMask & HT_IMS) {
                    222:        if (entity->last_modified != -1) {
                    223:            sprintf(linebuf, "If-Modified-Since: %s%c%c",
2.9       frystyk   224:                    HTDateTimeStr(&entity->last_modified, NO), CR, LF);
2.8       frystyk   225:            HTChunkPuts(header, linebuf);
                    226:        }
                    227:     }
2.9       frystyk   228:     if (request->EntityMask & HT_ORIG_URI) {
                    229:        char *orig = HTAnchor_address((HTAnchor *) request->anchor);
                    230:        sprintf(linebuf, "Orig-URI: %s%c%c", orig, CR, LF);
                    231:        free(orig);
2.1       frystyk   232:     }
2.4       frystyk   233:     if (request->RequestMask & HT_REFERER && request->parentAnchor) {
2.1       frystyk   234:        char *act = HTAnchor_address((HTAnchor *) request->anchor);
                    235:        char *parent = HTAnchor_address((HTAnchor *) request->parentAnchor);
                    236:        char *relative = HTParse(parent, act,
                    237:                                 PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                    238:        if (relative && *relative) {
                    239:            sprintf(linebuf, "Referer: %s%c%c", parent, CR, LF);
                    240:            HTChunkPuts(header, linebuf);
                    241:        }
                    242:        free(act);
                    243:        free(parent);
                    244:            free(relative);
                    245:     }
2.4       frystyk   246:     if (request->RequestMask & HT_USER_AGENT) {
2.1       frystyk   247:        sprintf(linebuf, "User-Agent: %s/%s libwww/%s%c%c",
                    248:                HTAppName ? HTAppName : "unknown",
                    249:                HTAppVersion ? HTAppVersion : "0.0",
                    250:                HTLibraryVersion, CR, LF);
                    251:        HTChunkPuts(header, linebuf);
                    252:     }
                    253: 
                    254:     /* Now put out entity headers if we are using PUT or POST. If we have a
                    255:     ** PostAnchor then we take the information from this and uses the
                    256:     ** destination anchor to contain the reply. Otherwise, we have created an
                    257:     ** anchor (using internal editing etc) and we can use the destination
                    258:     ** anchor directly.
                    259:     */
                    260:     if (request->method==METHOD_PUT || request->method==METHOD_POST) {
2.2       frystyk   261:        if (request->EntityMask & HT_ALLOW) {           /* @@@@@@@@@@ */
2.1       frystyk   262: 
                    263:        }
                    264:        if (request->EntityMask & HT_CONTENT_ENCODING &&
                    265:            entity->content_encoding) {
                    266:            sprintf(linebuf, "Content-Encoding: %s%c%c",
                    267:                    HTAtom_name(entity->content_encoding), CR, LF);
                    268:            HTChunkPuts(header, linebuf);
                    269:        }
2.6       frystyk   270: 
                    271:        /* @@@ SHOULD BE A LIST @@@ */
                    272:        if (request->EntityMask & HT_CONTENT_LANGUAGE &&
                    273:            entity->content_language) {
2.3       frystyk   274:            sprintf(linebuf, "Content-Language: %s%c%c",
                    275:                    HTAtom_name(entity->content_language), CR, LF);
                    276:            HTChunkPuts(header, linebuf);
2.1       frystyk   277:        }
                    278:        if (request->EntityMask & HT_CONTENT_LENGTH) {   /* Must be there!!! */
                    279:            sprintf(linebuf, "Content-Length: %ld%c%c",
                    280:                    entity->content_length, CR, LF);
                    281:            HTChunkPuts(header, linebuf);       
                    282:        }
                    283:        if (request->EntityMask & HT_CTE && entity->cte) {
                    284:            sprintf(linebuf, "Content-Transfer-Encoding: %s%c%c",
                    285:                    HTAtom_name(entity->cte), CR, LF);
                    286:            HTChunkPuts(header, linebuf);
                    287:        }
                    288:        if (request->EntityMask & HT_CONTENT_TYPE && entity->content_type) {
2.2       frystyk   289:            sprintf(linebuf, "Content-Type: %s",
                    290:                    HTAtom_name(entity->content_type));
                    291:            if (entity->charset) {
                    292:                strcat(linebuf, "; charset=");
                    293:                strcat(linebuf, HTAtom_name(entity->charset));
                    294:            }
                    295:            if (entity->level) {
                    296:                strcat(linebuf, "; level=");
                    297:                strcat(linebuf, HTAtom_name(entity->level));
                    298:            }
2.1       frystyk   299:            HTChunkPuts(header, linebuf);
2.2       frystyk   300:            HTChunkPutc(header, CR);
                    301:            HTChunkPutc(header, LF);
2.1       frystyk   302:        }
                    303:        if (request->EntityMask & HT_DERIVED_FROM && entity->derived_from) {
                    304:            sprintf(linebuf, "Derived-From: %s%c%c", entity->derived_from,
                    305:                    CR, LF);
                    306:            HTChunkPuts(header, linebuf);
                    307:        }
2.8       frystyk   308:        if (request->EntityMask & HT_EXPIRES) {
                    309:            if (entity->expires != -1) {
                    310:                sprintf(linebuf, "Expires: %s%c%c",
                    311:                        HTDateTimeStr(&entity->expires, NO), CR,LF);
                    312:                HTChunkPuts(header, linebuf);
                    313:            }
2.1       frystyk   314:        }
2.8       frystyk   315:        if (request->EntityMask & HT_LAST_MODIFIED) {
                    316:            if (entity->last_modified != -1) {
                    317:                sprintf(linebuf, "Last-Modified: %s%c%c",
                    318:                        HTDateTimeStr(&entity->last_modified, NO), CR,LF);
                    319:                HTChunkPuts(header, linebuf);
                    320:            }
2.1       frystyk   321:        }
2.2       frystyk   322:        if (request->EntityMask & HT_LINK) {            /* @@@@@@@@@@ */
2.1       frystyk   323: 
                    324:        }
2.2       frystyk   325:        if (request->EntityMask & HT_TITLE) {           /* @@@@@@@@@@ */
2.1       frystyk   326: 
                    327:        }
2.2       frystyk   328:        if (request->EntityMask & HT_URI) {             /* @@@@@@@@@@ */
2.1       frystyk   329: 
                    330:        }
                    331:        if (request->EntityMask & HT_VERSION && entity->version) {
                    332:            sprintf(linebuf, "Version: %s%c%c", entity->version, CR, LF);
                    333:            HTChunkPuts(header, linebuf);
                    334:        }
                    335:     }
                    336: 
                    337:     /* Put out extra information if any */
                    338:     if (request->ExtraHeaders)
                    339:        HTChunkPuts(header, request->ExtraHeaders);
                    340:     
                    341:     HTChunkPutc(header, CR);                      /* Blank line means "end" */
                    342:     HTChunkPutc(header, LF);
                    343:     HTChunkTerminate(header);
                    344:     if (PROT_TRACE)
                    345:        fprintf(TDEST, "HTTP Tx..... %s", header->data);
                    346: }
                    347: 
                    348: PRIVATE int HTTPRequest_put_character ARGS2(HTStream *, me, char, c)
                    349: {
2.7       frystyk   350:     if (!me->target) {
2.1       frystyk   351:        return HT_WOULD_BLOCK;
2.7       frystyk   352:     } else if (me->transparent)
2.1       frystyk   353:        return PUTC(c);
                    354:     else {
                    355:        int status;
                    356:        HTTPMakeRequest(me, me->request);                 /* Generate header */
                    357:        if ((status=PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK) {
                    358:            me->transparent = YES;
                    359:            return PUTC(c);
                    360:        }
                    361:        return status;
                    362:     }
                    363: }
                    364: 
                    365: PRIVATE int HTTPRequest_put_string ARGS2(HTStream *, me, CONST char*, s)
                    366: {
2.7       frystyk   367:     if (!me->target) {
2.1       frystyk   368:        return HT_WOULD_BLOCK;
2.7       frystyk   369:     } else if (me->transparent)
2.1       frystyk   370:        return PUTS(s);
                    371:     else {
                    372:        int status;
                    373:        HTTPMakeRequest(me, me->request);                 /* Generate header */
                    374:        if ((status=PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK) {
                    375:            me->transparent = YES;
                    376:            return PUTS(s);
                    377:        }
                    378:        return status;
                    379:     }
                    380: }
                    381: 
                    382: PRIVATE int HTTPRequest_put_block ARGS3(HTStream *, me, CONST char*, b, int, l)
                    383: {
2.7       frystyk   384:     if (!me->target) {
2.1       frystyk   385:        return HT_WOULD_BLOCK;
2.7       frystyk   386:     } else if (me->transparent)
2.1       frystyk   387:        return PUTBLOCK(b, l);
                    388:     else {
                    389:        int status;
                    390:        HTTPMakeRequest(me, me->request);                 /* Generate header */
                    391:        if ((status=PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK) {
                    392:            me->transparent = YES;
                    393:            return PUTBLOCK(b, l);
                    394:        }
                    395:        return status;
                    396:     }
                    397: }
                    398: 
                    399: /*
                    400: **     Flushes data but doesn't free stream object
                    401: */
                    402: PRIVATE int HTTPRequest_flush ARGS1(HTStream *, me)
                    403: {
2.7       frystyk   404:     if (!me->target) {
2.1       frystyk   405:        return HT_WOULD_BLOCK;
2.7       frystyk   406:     } else if (!me->transparent) {
2.1       frystyk   407:        int status;
                    408:        HTTPMakeRequest(me, me->request);                 /* Generate header */
                    409:        if ((status=PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK)
                    410:            me->transparent = YES;
                    411:        else
                    412:            return status;
                    413:     }
                    414:     return HT_OK;
                    415: }
                    416: 
                    417: /*
                    418: **     Flushes data and frees stream object
                    419: */
                    420: PRIVATE int HTTPRequest_free ARGS1(HTStream *, me)
                    421: {
2.7       frystyk   422:     int status;
                    423:     if (!me->target) {
2.1       frystyk   424:        return HT_WOULD_BLOCK;
2.7       frystyk   425:     } else if (!me->transparent) {
2.1       frystyk   426:        HTTPMakeRequest(me, me->request);                 /* Generate header */
2.6       frystyk   427:        if ((status = PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK)
2.1       frystyk   428:            me->transparent = YES;
                    429:        else
                    430:            return status;
                    431:     }
2.7       frystyk   432:     if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
2.6       frystyk   433:        return HT_WOULD_BLOCK;
2.1       frystyk   434:     HTChunkFree(me->buffer);
                    435:     free(me);
2.7       frystyk   436:     return status;
2.1       frystyk   437: }
                    438: 
                    439: PRIVATE int HTTPRequest_abort ARGS2(HTStream *, me, HTError, e)
                    440: {
                    441:     if (me->target)
                    442:        ABORT_TARGET;
                    443:     HTChunkFree(me->buffer);
                    444:     free(me);
                    445:     if (PROT_TRACE)
                    446:        fprintf(TDEST, "HTTPRequest. ABORTING...\n");
                    447:     return HT_ERROR;
                    448: }
                    449: 
                    450: /*     HTTPRequest Stream
                    451: **     -----------------
                    452: */
                    453: PRIVATE CONST HTStreamClass HTTPRequestClass =
                    454: {              
                    455:     "HTTPRequest",
                    456:     HTTPRequest_flush,
                    457:     HTTPRequest_free,
                    458:     HTTPRequest_abort,
                    459:     HTTPRequest_put_character,
                    460:     HTTPRequest_put_string,
                    461:     HTTPRequest_put_block
                    462: };
                    463: 
                    464: PUBLIC HTStream * HTTPRequest_new ARGS2(HTRequest *,   request,
                    465:                                        HTStream *,     target)
                    466: {
                    467:     HTStream * me = (HTStream *) calloc(1, sizeof(HTStream));
                    468:     if (!me) outofmem(__FILE__, "HTTPRequest_new");
                    469:     me->isa = &HTTPRequestClass;
                    470:     me->target = target;
                    471:     me->request = request;
                    472:     me->buffer = HTChunkCreate(512);
                    473:     me->transparent = NO;
                    474:     return me;
                    475: }
                    476: 
                    477: 

Webmaster