Annotation of libwww/Library/src/HTTPReq.c, revision 2.43
2.1 frystyk 1: /* HTTPReq.c
2.23 frystyk 2: ** HTTP REQUEST GENERATION
2.1 frystyk 3: **
2.32 frystyk 4: ** (c) COPYRIGHT MIT 1995.
5: ** Please first read the full copyright statement in the file COPYRIGH.
2.43 ! frystyk 6: ** @(#) $Id: HTTPReq.c,v 2.42 1996/08/24 18:10:20 frystyk Exp $
2.32 frystyk 7: **
2.1 frystyk 8: ** This module implements the output stream for HTTP used for sending
9: ** requests with or without a entity body.
10: **
11: ** History:
12: ** Jan 95 HFN Written from scratch
13: */
14:
15: /* Library Includes */
2.29 frystyk 16: #include "sysdep.h"
2.28 frystyk 17: #include "WWWUtil.h"
2.32 frystyk 18: #include "WWWCore.h"
19:
2.23 frystyk 20: #include "HTTPGen.h"
2.21 frystyk 21: #include "HTTPUtil.h"
2.1 frystyk 22: #include "HTTPReq.h" /* Implements */
23:
2.24 frystyk 24: #define PUTC(c) (*me->target->isa->put_character)(me->target, c)
25: #define PUTS(s) (*me->target->isa->put_string)(me->target, s)
2.1 frystyk 26: #define PUTBLOCK(b, l) (*me->target->isa->put_block)(me->target, b, l)
27:
28: struct _HTStream {
2.29 frystyk 29: const HTStreamClass * isa;
2.1 frystyk 30: HTStream * target;
31: HTRequest * request;
2.18 frystyk 32: SOCKET sockfd;
2.11 frystyk 33: int version;
2.38 frystyk 34: int state;
35: char * url;
2.1 frystyk 36: BOOL transparent;
37: };
38:
39: /* ------------------------------------------------------------------------- */
40: /* HTTP Output Request Stream */
41: /* ------------------------------------------------------------------------- */
42:
2.11 frystyk 43: /* HTTP09Request
44: ** -------------
45: ** Makes a HTTP/0.9 request
2.1 frystyk 46: */
2.38 frystyk 47: PRIVATE int HTTP09Request (HTStream * me, HTRequest * request)
2.11 frystyk 48: {
2.34 frystyk 49: HTParentAnchor * anchor = HTRequest_anchor(request);
50: char * addr = HTAnchor_physical(anchor);
2.38 frystyk 51: if (!me->url) me->url = HTParse(addr, "", PARSE_PATH|PARSE_PUNCTUATION);
52: if (me->state == 0) {
53: PUTS("GET ");
54: me->state++;
55: }
56: if (me->state == 1) {
57: int status = PUTS(me->url);
58: if (status != HT_OK) return status;
59: me->state++;
60: }
2.24 frystyk 61: PUTC(CR);
62: PUTC(LF);
2.38 frystyk 63: return HT_OK;
2.11 frystyk 64: }
65:
66: /* HTTPMakeRequest
67: ** ---------------
68: ** Makes a HTTP/1.0-1.1 request header.
69: */
2.38 frystyk 70: PRIVATE int HTTPMakeRequest (HTStream * me, HTRequest * request)
2.1 frystyk 71: {
2.39 frystyk 72: HTMethod method = HTRequest_method(request);
73: HTRqHd request_mask = HTRequest_rqHd(request);
74: HTParentAnchor * anchor = HTRequest_anchor(request);
2.43 ! frystyk 75: char * etag = HTAnchor_etag(anchor);
2.24 frystyk 76: char crlf[3];
77: char qstr[10];
78: *crlf = CR; *(crlf+1) = LF; *(crlf+2) = '\0';
2.1 frystyk 79:
2.41 frystyk 80: /* Generate the HTTP/1.x RequestLine */
2.38 frystyk 81: if (me->state == 0) {
2.34 frystyk 82: if (method != METHOD_INVALID) {
83: PUTS(HTMethod_name(method));
84: PUTC(' ');
85: } else
86: PUTS("GET ");
2.38 frystyk 87: me->state++;
2.34 frystyk 88: }
2.1 frystyk 89:
2.39 frystyk 90: /*
91: ** Generate the Request URI. If we are using full request URI then it's
92: ** easy. Otherwise we must filter out the path part of the URI.
93: ** In case it's a OPTIONS request then if there is no pathinfo then use
94: ** a * instead.
95: */
96: if (me->state == 1) {
2.38 frystyk 97: char * addr = HTAnchor_physical(anchor);
2.39 frystyk 98: int status = HT_OK;
2.35 frystyk 99: if (HTRequest_fullURI(request)) {
2.40 frystyk 100: status = PUTS(addr);
2.1 frystyk 101: } else {
2.39 frystyk 102: if (method == METHOD_OPTIONS) {
103: /*
104: ** We don't preserve the final slash or lack of same through
105: ** out the code. This is mainly for optimization reasons
106: ** but it gives a problem OPTIONS
107: */
108: if (!me->url) me->url = HTParse(addr, "", PARSE_PATH);
109: if (!*me->url) StrAllocCopy(me->url, "*");
110: status = PUTS(me->url);
111: } else {
112: if (!me->url)
113: me->url = HTParse(addr, "", PARSE_PATH|PARSE_PUNCTUATION);
114: status = PUTS(me->url);
115: }
2.1 frystyk 116: }
2.38 frystyk 117: if (status != HT_OK) return status;
118: me->state++;
2.1 frystyk 119: }
2.25 frystyk 120: PUTC(' ');
2.41 frystyk 121:
122: /*
123: ** Send out the version number. If we know it is a HTTP/1.0 server we
124: ** are talking to then use HTTP/1.0, else use HTTP/1.1 as default version
125: ** number
126: */
127: if (me->version == HTTP_10)
128: PUTS(HTTP_VERSION_10);
129: else
130: PUTS(HTTP_VERSION);
2.24 frystyk 131: PUTBLOCK(crlf, 2);
2.1 frystyk 132:
2.4 frystyk 133: /* Request Headers */
2.34 frystyk 134: if (request_mask & HT_C_ACCEPT_TYPE) {
2.37 frystyk 135: /*
136: ** If caller has specified a specific output format then use this.
137: ** Otherwise use all the registered converters to generate the
138: ** accept header
139: */
140: if (HTRequest_outputFormat(request) == WWW_PRESENT) {
141: int list;
142: HTList *cur;
143: BOOL first=YES;
144: for (list=0; list<2; list++) {
145: if ((!list && ((cur = HTFormat_conversion()) != NULL)) ||
146: (list && ((cur = HTRequest_conversion(request))!=NULL))) {
147: HTPresentation * pres;
148: while ((pres=(HTPresentation *) HTList_nextObject(cur))) {
149: if (pres->rep_out==WWW_PRESENT && pres->quality<=1.0) {
150: if (first) {
151: PUTS("Accept: ");
152: first=NO;
153: } else
154: PUTC(',');
155: PUTS(HTAtom_name(pres->rep));
156: if (pres->quality != 1.0) {
157: sprintf(qstr, ";q=%1.1f", pres->quality);
158: PUTS(qstr);
159: }
2.1 frystyk 160: }
161: }
162: }
163: }
2.37 frystyk 164: if (!first) PUTBLOCK(crlf, 2);
165: } else {
166: PUTS("Accept: ");
167: PUTS(HTAtom_name(HTRequest_outputFormat(request)));
168: PUTBLOCK(crlf, 2);
169: }
2.1 frystyk 170: }
2.34 frystyk 171: if (request_mask & HT_C_ACCEPT_CHAR) {
2.4 frystyk 172: int list;
173: HTList *cur;
2.24 frystyk 174: BOOL first=YES;
2.4 frystyk 175: for (list=0; list<2; list++) {
2.14 frystyk 176: if ((!list && ((cur = HTFormat_charset()) != NULL)) ||
177: (list && ((cur = HTRequest_charset(request)) != NULL))) {
2.4 frystyk 178: HTAcceptNode *pres;
179: while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
2.5 frystyk 180: if (first) {
2.24 frystyk 181: PUTS("Accept-Charset: ");
2.5 frystyk 182: first=NO;
2.24 frystyk 183: } else
184: PUTC(',');
185: PUTS(HTAtom_name(pres->atom));
2.4 frystyk 186: }
187: }
188: }
2.31 frystyk 189: if (!first) PUTBLOCK(crlf, 2);
2.4 frystyk 190: }
2.34 frystyk 191: if (request_mask & HT_C_ACCEPT_ENC) {
2.4 frystyk 192: int list;
193: HTList *cur;
2.24 frystyk 194: BOOL first=YES;
2.4 frystyk 195: for (list=0; list<2; list++) {
2.33 frystyk 196: if ((!list && ((cur = HTFormat_contentCoding()) != NULL)) ||
2.14 frystyk 197: (list && ((cur = HTRequest_encoding(request)) != NULL))) {
2.33 frystyk 198: HTCoding * pres;
199: while ((pres = (HTCoding *) HTList_nextObject(cur))) {
2.5 frystyk 200: if (first) {
2.24 frystyk 201: PUTS("Accept-Encoding: ");
2.32 frystyk 202: first = NO;
2.24 frystyk 203: } else
204: PUTC(',');
2.33 frystyk 205: PUTS(HTCoding_name(pres));
2.4 frystyk 206: }
207: }
208: }
2.31 frystyk 209: if (!first) PUTBLOCK(crlf, 2);
2.4 frystyk 210: }
2.34 frystyk 211: if (request_mask & HT_C_ACCEPT_LAN) {
2.4 frystyk 212: int list;
213: HTList *cur;
2.24 frystyk 214: BOOL first=YES;
2.4 frystyk 215: for (list=0; list<2; list++) {
2.14 frystyk 216: if ((!list && ((cur = HTFormat_language()) != NULL)) ||
217: (list && ((cur = HTRequest_language(request)) != NULL))) {
2.4 frystyk 218: HTAcceptNode *pres;
219: while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
2.5 frystyk 220: if (first) {
2.24 frystyk 221: PUTS("Accept-Language: ");
2.5 frystyk 222: first=NO;
2.24 frystyk 223: } else
224: PUTC(',');
225: PUTS(HTAtom_name(pres->atom));
226: if (pres->quality != 1.0) {
227: sprintf(qstr, ";q=%1.1f", pres->quality);
228: PUTS(qstr);
2.5 frystyk 229: }
2.4 frystyk 230: }
231: }
232: }
2.31 frystyk 233: if (!first) PUTBLOCK(crlf, 2);
2.4 frystyk 234: }
2.36 frystyk 235: if (request_mask & HT_C_AUTH) {
2.34 frystyk 236: HTAssocList * cur = HTRequest_credentials(request);
237: if (cur) { /* Access authentication */
238: HTAssoc * pres;
239: while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) {
240: PUTS(HTAssoc_name(pres));
241: PUTS(": ");
242: PUTS(HTAssoc_value(pres));
243: PUTBLOCK(crlf, 2);
244: }
2.28 frystyk 245: }
2.1 frystyk 246: }
2.34 frystyk 247: if (request_mask & HT_C_FROM) {
248: HTUserProfile * up = HTRequest_userProfile(request);
249: const char * mailaddress = HTUserProfile_email(up);
2.1 frystyk 250: if (mailaddress) {
2.24 frystyk 251: PUTS("From: ");
252: PUTS(mailaddress);
253: PUTBLOCK(crlf, 2);
2.1 frystyk 254: }
255: }
2.34 frystyk 256: if (request_mask & HT_C_HOST) {
2.13 frystyk 257: char *orig = HTAnchor_address((HTAnchor *) anchor);
2.12 frystyk 258: char *host = HTParse(orig, "", PARSE_HOST);
2.34 frystyk 259: #if 0
260: /* Keep the port number for HTTP/1.1 compliance */
2.12 frystyk 261: char *ptr = strchr(host, ':'); /* Chop off port number */
262: if (ptr) *ptr = '\0';
2.34 frystyk 263: #endif
2.24 frystyk 264: PUTS("Host: ");
265: PUTS(host);
266: PUTBLOCK(crlf, 2);
2.26 frystyk 267: HT_FREE(orig);
268: HT_FREE(host);
2.39 frystyk 269: }
2.42 frystyk 270:
271: /*
272: ** In the "If-*" series of headers, the ones related to etags have higher
273: ** priority than the date relates ones. That is, if we have a etag then
274: ** use that, otherwise use the date
275: */
2.43 ! frystyk 276: if (request_mask & HT_C_IF_MATCH && etag) {
! 277: PUTS("If-Match: \"");
! 278: PUTS(etag);
! 279: PUTC('"');
! 280: PUTBLOCK(crlf, 2);
! 281: if (PROT_TRACE) HTTrace("HTTP........ If-Match using etag `%s\'\n", etag);
! 282: } else if (request_mask & HT_C_IF_UNMOD_SINCE) {
2.42 frystyk 283: time_t lm = HTAnchor_lastModified(anchor);
284: if (lm > 0) {
2.43 ! frystyk 285: PUTS("If-Unmodified-Since: ");
2.42 frystyk 286: PUTS(HTDateTimeStr(&lm, NO));
287: PUTBLOCK(crlf, 2);
2.43 ! frystyk 288: if (PROT_TRACE) HTTrace("HTTP........ If-Unmodified-Since\n");
2.42 frystyk 289: }
290: }
2.43 ! frystyk 291: if (request_mask & HT_C_IF_NONE_MATCH && etag) {
! 292: PUTS("If-None-Match: \"");
! 293: PUTS(etag);
! 294: PUTC('"');
! 295: PUTBLOCK(crlf, 2);
! 296: if (PROT_TRACE) HTTrace("HTTP........ If-None-Match using etag `%s\'\n", etag);
! 297: } else if (request_mask & HT_C_IMS) {
2.42 frystyk 298: time_t lm = HTAnchor_lastModified(anchor);
299: if (lm > 0) {
2.43 ! frystyk 300: PUTS("If-Modified-Since: ");
2.42 frystyk 301: PUTS(HTDateTimeStr(&lm, NO));
302: PUTBLOCK(crlf, 2);
2.43 ! frystyk 303: if (PROT_TRACE) HTTrace("HTTP........ If-Modified-Since\n");
2.42 frystyk 304: }
305: }
306:
307: if (request_mask & HT_C_IF_RANGE) {
308:
309: /* Not supported */
310:
311: }
2.39 frystyk 312: if (request_mask & HT_C_MAX_FORWARDS) {
313: int hops = HTRequest_maxForwards(request);
314: if (hops >= 0) {
315: sprintf(qstr, "%d", hops);
316: PUTS("Max-Forwards: ");
317: PUTS(qstr);
318: PUTBLOCK(crlf, 2);
319: }
2.42 frystyk 320: }
321: if (request_mask & HT_C_RANGE) {
322:
323: /* Not supported */
324:
2.1 frystyk 325: }
2.34 frystyk 326: if (request_mask & HT_C_REFERER) {
327: HTParentAnchor * parent_anchor = HTRequest_parent(request);
328: if (parent_anchor) {
329: char * act = HTAnchor_address((HTAnchor *) anchor);
330: char * parent = HTAnchor_address((HTAnchor *) parent_anchor);
331: char * relative = HTParse(parent, act,
332: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
333: if (relative && *relative) {
334: PUTS("Referer: ");
335: PUTS(parent);
336: PUTBLOCK(crlf, 2);
337: }
338: HT_FREE(act);
339: HT_FREE(parent);
340: HT_FREE(relative);
2.1 frystyk 341: }
342: }
2.34 frystyk 343: if (request_mask & HT_C_USER_AGENT) {
2.24 frystyk 344: PUTS("User-Agent: ");
345: PUTS(HTLib_appName());
346: PUTC('/');
347: PUTS(HTLib_appVersion());
348: PUTC(' ');
349: PUTS(HTLib_name());
350: PUTC('/');
351: PUTS(HTLib_version());
352: PUTBLOCK(crlf, 2);
2.1 frystyk 353: }
2.27 eric 354: if (PROT_TRACE)HTTrace("HTTP........ Generating Request Headers\n");
2.38 frystyk 355: return HT_OK;
2.1 frystyk 356: }
357:
2.29 frystyk 358: PRIVATE int HTTPRequest_put_block (HTStream * me, const char * b, int l)
2.1 frystyk 359: {
2.7 frystyk 360: if (!me->target) {
2.1 frystyk 361: return HT_WOULD_BLOCK;
2.7 frystyk 362: } else if (me->transparent)
2.11 frystyk 363: return b ? PUTBLOCK(b, l) : HT_OK;
2.1 frystyk 364: else {
2.38 frystyk 365: int status = HT_OK;
366: if (me->version == HTTP_09) {
367: status = HTTP09Request(me, me->request);
368: if (status != HT_OK) return status;
369: } else {
370: status = HTTPMakeRequest(me, me->request);
371: if (status != HT_OK) return status;
2.1 frystyk 372: me->transparent = YES;
2.11 frystyk 373: return b ? PUTBLOCK(b, l) : HT_OK;
2.1 frystyk 374: }
375: return status;
376: }
377: }
378:
2.16 frystyk 379: PRIVATE int HTTPRequest_put_character (HTStream * me, char c)
2.1 frystyk 380: {
2.11 frystyk 381: return HTTPRequest_put_block(me, &c, 1);
2.1 frystyk 382: }
383:
2.29 frystyk 384: PRIVATE int HTTPRequest_put_string (HTStream * me, const char * s)
2.1 frystyk 385: {
2.11 frystyk 386: return HTTPRequest_put_block(me, s, strlen(s));
2.1 frystyk 387: }
388:
389: /*
390: ** Flushes data but doesn't free stream object
391: */
2.16 frystyk 392: PRIVATE int HTTPRequest_flush (HTStream * me)
2.1 frystyk 393: {
2.13 frystyk 394: int status = HTTPRequest_put_block(me, NULL, 0);
395: return status==HT_OK ? (*me->target->isa->flush)(me->target) : status;
2.1 frystyk 396: }
397:
398: /*
399: ** Flushes data and frees stream object
400: */
2.16 frystyk 401: PRIVATE int HTTPRequest_free (HTStream * me)
2.1 frystyk 402: {
2.11 frystyk 403: int status = HTTPRequest_flush(me);
404: if (status != HT_WOULD_BLOCK) {
405: if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
406: return HT_WOULD_BLOCK;
2.38 frystyk 407: HT_FREE(me->url);
2.26 frystyk 408: HT_FREE(me);
2.1 frystyk 409: }
2.7 frystyk 410: return status;
2.1 frystyk 411: }
412:
2.16 frystyk 413: PRIVATE int HTTPRequest_abort (HTStream * me, HTList * e)
2.1 frystyk 414: {
2.13 frystyk 415: if (me->target) (*me->target->isa->abort)(me->target, e);
2.38 frystyk 416: HT_FREE(me->url);
2.26 frystyk 417: HT_FREE(me);
2.27 eric 418: if (PROT_TRACE) HTTrace("HTTPRequest. ABORTING...\n");
2.1 frystyk 419: return HT_ERROR;
420: }
421:
422: /* HTTPRequest Stream
423: ** -----------------
424: */
2.29 frystyk 425: PRIVATE const HTStreamClass HTTPRequestClass =
2.1 frystyk 426: {
427: "HTTPRequest",
428: HTTPRequest_flush,
429: HTTPRequest_free,
430: HTTPRequest_abort,
431: HTTPRequest_put_character,
432: HTTPRequest_put_string,
433: HTTPRequest_put_block
434: };
435:
2.23 frystyk 436: PUBLIC HTStream * HTTPRequest_new (HTRequest * request, HTStream * target,
437: BOOL endHeader)
2.1 frystyk 438: {
2.34 frystyk 439: HTNet * net = HTRequest_net(request);
440: HTHost * host = HTNet_host(net);
2.26 frystyk 441: HTStream * me;
442: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
443: HT_OUTOFMEM("HTTPRequest_new");
2.1 frystyk 444: me->isa = &HTTPRequestClass;
445: me->target = target;
446: me->request = request;
2.32 frystyk 447: me->version = HTHost_version(host);
2.1 frystyk 448: me->transparent = NO;
2.23 frystyk 449:
450: /* Return general HTTP header stream */
451: return HTTPGen_new(request, me, endHeader);
2.1 frystyk 452: }
Webmaster