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