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