Annotation of libwww/Library/src/HTTPReq.c, revision 2.8
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.4 frystyk 109: /* Request Headers */
110: if (request->RequestMask & HT_ACCEPT_TYPE) {
2.1 frystyk 111: int list;
112: HTList *cur;
113: for (list=0; list<2; list++) {
114: if ((!list && ((cur=HTConversions) != NULL)) ||
115: (list && ((cur=request->conversions) != NULL))) {
2.4 frystyk 116: HTPresentation *pres;
2.1 frystyk 117: while ((pres =(HTPresentation *) HTList_nextObject(cur))) {
118: if (pres->rep_out == WWW_PRESENT) {
119: if (pres->quality != 1.0) {
120: sprintf(linebuf, "Accept: %s; q=%1.1f%c%c",
121: HTAtom_name(pres->rep),
122: pres->quality, CR, LF);
123: } else {
124: sprintf(linebuf, "Accept: %s%c%c",
125: HTAtom_name(pres->rep), CR, LF);
126: }
127: HTChunkPuts(header, linebuf);
128: }
129: }
130: }
131: }
132: }
2.4 frystyk 133: if (request->RequestMask & HT_ACCEPT_CHAR) {
134: BOOL first=YES;
135: int list;
136: HTList *cur;
137: for (list=0; list<2; list++) {
138: if ((!list && ((cur=HTCharsets) != NULL)) ||
139: (list && ((cur=request->charsets) != NULL))) {
140: HTAcceptNode *pres;
141: while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
2.5 frystyk 142: if (first) {
143: HTChunkPuts(header, "Accept-Charset: ");
144: first=NO;
145: }
2.4 frystyk 146: if (cur->next)
147: sprintf(linebuf, "%s,", HTAtom_name(pres->atom));
148: else
149: sprintf(linebuf, "%s%c%c", HTAtom_name(pres->atom),
150: CR, LF);
151: HTChunkPuts(header, linebuf);
152: }
153: }
154: }
155: }
156: if (request->RequestMask & HT_ACCEPT_ENC) {
157: BOOL first=YES;
158: int list;
159: HTList *cur;
160: for (list=0; list<2; list++) {
161: if ((!list && ((cur=HTEncodings) != NULL)) ||
162: (list && ((cur=request->encodings) != NULL))) {
163: HTAcceptNode *pres;
164: while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
2.5 frystyk 165: if (first) {
166: HTChunkPuts(header, "Accept-Encoding: ");
167: first=NO;
168: }
2.4 frystyk 169: if (cur->next)
170: sprintf(linebuf, "%s,", HTAtom_name(pres->atom));
171: else
172: sprintf(linebuf, "%s%c%c", HTAtom_name(pres->atom),
173: CR, LF);
174: HTChunkPuts(header, linebuf);
175: }
176: }
177: }
178: }
179: if (request->RequestMask & HT_ACCEPT_LAN) {
180: BOOL first=YES;
181: int list;
182: HTList *cur;
183: for (list=0; list<2; list++) {
184: if ((!list && ((cur=HTLanguages) != NULL)) ||
185: (list && ((cur=request->languages) != NULL))) {
186: HTAcceptNode *pres;
187: while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
2.5 frystyk 188: if (first) {
189: HTChunkPuts(header, "Accept-Language: ");
190: first=NO;
191: }
2.4 frystyk 192: if (cur->next)
193: sprintf(linebuf, "%s,", HTAtom_name(pres->atom));
194: else
195: sprintf(linebuf, "%s%c%c", HTAtom_name(pres->atom),
196: CR, LF);
197: HTChunkPuts(header, linebuf);
198: }
199: }
200: }
201: }
202: if (request->authorization != NULL) { /* Put out authorization */
2.1 frystyk 203: sprintf(linebuf, "Authorization: %s%c%c", request->authorization,
204: CR, LF);
205: HTChunkPuts(header, linebuf);
206: }
2.4 frystyk 207: if (request->RequestMask & HT_FROM) {
2.1 frystyk 208: CONST char *mailaddress = HTGetMailAddress();
209: if (mailaddress) {
210: sprintf(linebuf, "From: %s%c%c", mailaddress, CR, LF);
211: HTChunkPuts(header, linebuf);
212: }
213: }
2.8 ! frystyk 214: if (request->RequestMask & HT_IMS) {
! 215: if (entity->last_modified != -1) {
! 216: sprintf(linebuf, "If-Modified-Since: %s%c%c",
! 217: HTDateTimeStr(&entity->last_modified, NO), CR,LF);
! 218: HTChunkPuts(header, linebuf);
! 219: }
! 220: }
! 221: if (request->RequestMask & HT_NO_CACHE) {
2.1 frystyk 222: sprintf(linebuf, "Pragma: %s%c%c", "no-cache", CR, LF);
223: HTChunkPuts(header, linebuf);
224: }
2.4 frystyk 225: if (request->RequestMask & HT_REFERER && request->parentAnchor) {
2.1 frystyk 226: char *act = HTAnchor_address((HTAnchor *) request->anchor);
227: char *parent = HTAnchor_address((HTAnchor *) request->parentAnchor);
228: char *relative = HTParse(parent, act,
229: PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
230: if (relative && *relative) {
231: sprintf(linebuf, "Referer: %s%c%c", parent, CR, LF);
232: HTChunkPuts(header, linebuf);
233: }
234: free(act);
235: free(parent);
236: free(relative);
237: }
2.4 frystyk 238: if (request->RequestMask & HT_USER_AGENT) {
2.1 frystyk 239: sprintf(linebuf, "User-Agent: %s/%s libwww/%s%c%c",
240: HTAppName ? HTAppName : "unknown",
241: HTAppVersion ? HTAppVersion : "0.0",
242: HTLibraryVersion, CR, LF);
243: HTChunkPuts(header, linebuf);
244: }
245:
246: /* Now put out entity headers if we are using PUT or POST. If we have a
247: ** PostAnchor then we take the information from this and uses the
248: ** destination anchor to contain the reply. Otherwise, we have created an
249: ** anchor (using internal editing etc) and we can use the destination
250: ** anchor directly.
251: */
252: if (request->method==METHOD_PUT || request->method==METHOD_POST) {
2.2 frystyk 253: if (request->EntityMask & HT_ALLOW) { /* @@@@@@@@@@ */
2.1 frystyk 254:
255: }
256: if (request->EntityMask & HT_CONTENT_ENCODING &&
257: entity->content_encoding) {
258: sprintf(linebuf, "Content-Encoding: %s%c%c",
259: HTAtom_name(entity->content_encoding), CR, LF);
260: HTChunkPuts(header, linebuf);
261: }
2.6 frystyk 262:
263: /* @@@ SHOULD BE A LIST @@@ */
264: if (request->EntityMask & HT_CONTENT_LANGUAGE &&
265: entity->content_language) {
2.3 frystyk 266: sprintf(linebuf, "Content-Language: %s%c%c",
267: HTAtom_name(entity->content_language), CR, LF);
268: HTChunkPuts(header, linebuf);
2.1 frystyk 269: }
270: if (request->EntityMask & HT_CONTENT_LENGTH) { /* Must be there!!! */
271: sprintf(linebuf, "Content-Length: %ld%c%c",
272: entity->content_length, CR, LF);
273: HTChunkPuts(header, linebuf);
274: }
275: if (request->EntityMask & HT_CTE && entity->cte) {
276: sprintf(linebuf, "Content-Transfer-Encoding: %s%c%c",
277: HTAtom_name(entity->cte), CR, LF);
278: HTChunkPuts(header, linebuf);
279: }
280: if (request->EntityMask & HT_CONTENT_TYPE && entity->content_type) {
2.2 frystyk 281: sprintf(linebuf, "Content-Type: %s",
282: HTAtom_name(entity->content_type));
283: if (entity->charset) {
284: strcat(linebuf, "; charset=");
285: strcat(linebuf, HTAtom_name(entity->charset));
286: }
287: if (entity->level) {
288: strcat(linebuf, "; level=");
289: strcat(linebuf, HTAtom_name(entity->level));
290: }
2.1 frystyk 291: HTChunkPuts(header, linebuf);
2.2 frystyk 292: HTChunkPutc(header, CR);
293: HTChunkPutc(header, LF);
2.1 frystyk 294: }
295: if (request->EntityMask & HT_DERIVED_FROM && entity->derived_from) {
296: sprintf(linebuf, "Derived-From: %s%c%c", entity->derived_from,
297: CR, LF);
298: HTChunkPuts(header, linebuf);
299: }
2.8 ! frystyk 300: if (request->EntityMask & HT_EXPIRES) {
! 301: if (entity->expires != -1) {
! 302: sprintf(linebuf, "Expires: %s%c%c",
! 303: HTDateTimeStr(&entity->expires, NO), CR,LF);
! 304: HTChunkPuts(header, linebuf);
! 305: }
2.1 frystyk 306: }
2.8 ! frystyk 307: if (request->EntityMask & HT_LAST_MODIFIED) {
! 308: if (entity->last_modified != -1) {
! 309: sprintf(linebuf, "Last-Modified: %s%c%c",
! 310: HTDateTimeStr(&entity->last_modified, NO), CR,LF);
! 311: HTChunkPuts(header, linebuf);
! 312: }
2.1 frystyk 313: }
2.2 frystyk 314: if (request->EntityMask & HT_LINK) { /* @@@@@@@@@@ */
2.1 frystyk 315:
316: }
2.2 frystyk 317: if (request->EntityMask & HT_TITLE) { /* @@@@@@@@@@ */
2.1 frystyk 318:
319: }
2.2 frystyk 320: if (request->EntityMask & HT_URI) { /* @@@@@@@@@@ */
2.1 frystyk 321:
322: }
323: if (request->EntityMask & HT_VERSION && entity->version) {
324: sprintf(linebuf, "Version: %s%c%c", entity->version, CR, LF);
325: HTChunkPuts(header, linebuf);
326: }
327: }
328:
329: /* Put out extra information if any */
330: if (request->ExtraHeaders)
331: HTChunkPuts(header, request->ExtraHeaders);
332:
333: HTChunkPutc(header, CR); /* Blank line means "end" */
334: HTChunkPutc(header, LF);
335: HTChunkTerminate(header);
336: if (PROT_TRACE)
337: fprintf(TDEST, "HTTP Tx..... %s", header->data);
338: }
339:
340: PRIVATE int HTTPRequest_put_character ARGS2(HTStream *, me, char, c)
341: {
2.7 frystyk 342: if (!me->target) {
2.1 frystyk 343: return HT_WOULD_BLOCK;
2.7 frystyk 344: } else if (me->transparent)
2.1 frystyk 345: return PUTC(c);
346: else {
347: int status;
348: HTTPMakeRequest(me, me->request); /* Generate header */
349: if ((status=PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK) {
350: me->transparent = YES;
351: return PUTC(c);
352: }
353: return status;
354: }
355: }
356:
357: PRIVATE int HTTPRequest_put_string ARGS2(HTStream *, me, CONST char*, s)
358: {
2.7 frystyk 359: if (!me->target) {
2.1 frystyk 360: return HT_WOULD_BLOCK;
2.7 frystyk 361: } else if (me->transparent)
2.1 frystyk 362: return PUTS(s);
363: else {
364: int status;
365: HTTPMakeRequest(me, me->request); /* Generate header */
366: if ((status=PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK) {
367: me->transparent = YES;
368: return PUTS(s);
369: }
370: return status;
371: }
372: }
373:
374: PRIVATE int HTTPRequest_put_block ARGS3(HTStream *, me, CONST char*, b, int, l)
375: {
2.7 frystyk 376: if (!me->target) {
2.1 frystyk 377: return HT_WOULD_BLOCK;
2.7 frystyk 378: } else if (me->transparent)
2.1 frystyk 379: return PUTBLOCK(b, l);
380: else {
381: int status;
382: HTTPMakeRequest(me, me->request); /* Generate header */
383: if ((status=PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK) {
384: me->transparent = YES;
385: return PUTBLOCK(b, l);
386: }
387: return status;
388: }
389: }
390:
391: /*
392: ** Flushes data but doesn't free stream object
393: */
394: PRIVATE int HTTPRequest_flush ARGS1(HTStream *, me)
395: {
2.7 frystyk 396: if (!me->target) {
2.1 frystyk 397: return HT_WOULD_BLOCK;
2.7 frystyk 398: } else if (!me->transparent) {
2.1 frystyk 399: int status;
400: HTTPMakeRequest(me, me->request); /* Generate header */
401: if ((status=PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK)
402: me->transparent = YES;
403: else
404: return status;
405: }
406: return HT_OK;
407: }
408:
409: /*
410: ** Flushes data and frees stream object
411: */
412: PRIVATE int HTTPRequest_free ARGS1(HTStream *, me)
413: {
2.7 frystyk 414: int status;
415: if (!me->target) {
2.1 frystyk 416: return HT_WOULD_BLOCK;
2.7 frystyk 417: } else if (!me->transparent) {
2.1 frystyk 418: HTTPMakeRequest(me, me->request); /* Generate header */
2.6 frystyk 419: if ((status = PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK)
2.1 frystyk 420: me->transparent = YES;
421: else
422: return status;
423: }
2.7 frystyk 424: if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
2.6 frystyk 425: return HT_WOULD_BLOCK;
2.1 frystyk 426: HTChunkFree(me->buffer);
427: free(me);
2.7 frystyk 428: return status;
2.1 frystyk 429: }
430:
431: PRIVATE int HTTPRequest_abort ARGS2(HTStream *, me, HTError, e)
432: {
433: if (me->target)
434: ABORT_TARGET;
435: HTChunkFree(me->buffer);
436: free(me);
437: if (PROT_TRACE)
438: fprintf(TDEST, "HTTPRequest. ABORTING...\n");
439: return HT_ERROR;
440: }
441:
442: /* HTTPRequest Stream
443: ** -----------------
444: */
445: PRIVATE CONST HTStreamClass HTTPRequestClass =
446: {
447: "HTTPRequest",
448: HTTPRequest_flush,
449: HTTPRequest_free,
450: HTTPRequest_abort,
451: HTTPRequest_put_character,
452: HTTPRequest_put_string,
453: HTTPRequest_put_block
454: };
455:
456: PUBLIC HTStream * HTTPRequest_new ARGS2(HTRequest *, request,
457: HTStream *, target)
458: {
459: HTStream * me = (HTStream *) calloc(1, sizeof(HTStream));
460: if (!me) outofmem(__FILE__, "HTTPRequest_new");
461: me->isa = &HTTPRequestClass;
462: me->target = target;
463: me->request = request;
464: me->buffer = HTChunkCreate(512);
465: me->transparent = NO;
466: return me;
467: }
468:
469:
Webmaster