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