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

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

Webmaster