Annotation of libwww/Library/src/HTMIME.c, revision 2.96
2.15 frystyk 1: /* HTMIME.c
2: ** MIME MESSAGE PARSE
3: **
2.22 frystyk 4: ** (c) COPYRIGHT MIT 1995.
2.15 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
2.96 ! frystyk 6: ** @(#) $Id: HTMIME.c,v 2.95 1999/02/27 01:28:40 frystyk Exp $
2.1 timbl 7: **
8: ** This is RFC 1341-specific code.
9: ** The input stream pushed into this parser is assumed to be
10: ** stripped on CRs, ie lines end with LF, not CR LF.
11: ** (It is easy to change this except for the body part where
12: ** conversion can be slow.)
13: **
14: ** History:
15: ** Feb 92 Written Tim Berners-Lee, CERN
2.13 duns 16: ** 8 Jul 94 FM Insulate free() from _free structure element.
2.71 frystyk 17: ** 14 Mar 95 HFN Now using response for storing data. No more `\n',
2.18 frystyk 18: ** static buffers etc.
2.1 timbl 19: */
2.17 frystyk 20:
21: /* Library include files */
2.86 frystyk 22: #include "wwwsys.h"
2.60 frystyk 23: #include "WWWUtil.h"
2.61 frystyk 24: #include "WWWCore.h"
25: #include "HTReqMan.h"
26: #include "HTNetMan.h"
2.36 frystyk 27: #include "HTHeader.h"
2.64 eric 28: #include "HTWWWStr.h"
2.93 frystyk 29:
30: #ifndef NO_CACHE
31: #include "HTTee.h"
32: #include "HTConLen.h"
33: #include "HTMerge.h"
34: #include "WWWCache.h"
35: #endif
36:
2.14 frystyk 37: #include "HTMIME.h" /* Implemented here */
2.1 timbl 38:
2.70 frystyk 39: typedef enum _HTMIMEMode {
40: HT_MIME_HEADER = 0x1,
2.71 frystyk 41: HT_MIME_FOOTER = 0x2,
2.77 frystyk 42: HT_MIME_PARTIAL = 0x4,
2.95 frystyk 43: HT_MIME_CONT = 0x8,
44: HT_MIME_UPGRADE = 0x10
2.70 frystyk 45: } HTMIMEMode;
46:
2.1 timbl 47: struct _HTStream {
2.57 frystyk 48: const HTStreamClass * isa;
2.18 frystyk 49: HTRequest * request;
2.71 frystyk 50: HTResponse * response;
2.32 frystyk 51: HTNet * net;
2.18 frystyk 52: HTStream * target;
2.93 frystyk 53: HTConverter * save_stream;
2.18 frystyk 54: HTFormat target_format;
2.64 eric 55: HTChunk * token;
56: HTChunk * value;
57: int hash;
2.59 frystyk 58: HTEOLState EOLstate;
2.70 frystyk 59: HTMIMEMode mode;
2.18 frystyk 60: BOOL transparent;
2.64 eric 61: BOOL haveToken;
2.78 frystyk 62: BOOL hasBody;
2.1 timbl 63: };
64:
2.93 frystyk 65: PRIVATE HTConverter * LocalSaveStream = NULL; /* Where to save unknown stuff */
66:
2.18 frystyk 67: /* ------------------------------------------------------------------------- */
2.1 timbl 68:
2.64 eric 69: PRIVATE int pumpData (HTStream * me)
2.18 frystyk 70: {
2.64 eric 71: HTRequest * request = me->request;
2.71 frystyk 72: HTResponse * response = me->response;
73: HTFormat format = HTResponse_format(response);
2.83 frystyk 74: HTList * te = HTResponse_transfer(response);
75: HTList * ce = HTResponse_encoding(response);
2.71 frystyk 76: long length = HTResponse_length(response);
2.83 frystyk 77: HTStream * BlackHole = HTBlackHole();
78: BOOL savestream = NO;
2.48 frystyk 79: me->transparent = YES; /* Pump rest of data right through */
2.47 frystyk 80:
2.71 frystyk 81: /*
2.77 frystyk 82: ** Cache the metainformation in the anchor object by copying
2.71 frystyk 83: ** it from the response object. This we do regardless if
84: ** we have a persistent cache or not as the memory cache will
85: ** use it as well. If we are updating a cache entry using
2.77 frystyk 86: ** byte ranges then we already have the metainformation and
2.71 frystyk 87: ** hence we can ignore the new one as it'd better be the same.
88: */
2.90 frystyk 89: if (!(me->mode & HT_MIME_PARTIAL) &&
90: HTResponse_isCachable(me->response) != HT_NO_CACHE)
2.71 frystyk 91: HTAnchor_update(HTRequest_anchor(request), me->response);
92:
93: /*
94: ** If we asked only to read the header or footer or we used a HEAD
95: ** method then we stop here as we don't expect any body part.
96: */
2.70 frystyk 97: if (me->mode & (HT_MIME_HEADER | HT_MIME_FOOTER) ||
2.71 frystyk 98: HTRequest_method(request) == METHOD_HEAD) {
2.88 frystyk 99: HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE);
100: if (cbf) (*cbf)(request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL);
2.78 frystyk 101: return HT_LOADED;
2.70 frystyk 102: }
2.43 frystyk 103:
2.60 frystyk 104: /*
2.77 frystyk 105: ** If we are paring a 1xx response then return HT_CONTINUE
106: */
107: if (me->mode & HT_MIME_CONT)
108: return HT_CONTINUE;
109:
110: /*
2.95 frystyk 111: ** If we get a 101 Protocol Switch then we are done here
112: ** but not done with the response (which we don't know
113: ** how to go about parsing
114: */
115: if (me->mode & HT_MIME_UPGRADE) {
116: me->hasBody = YES;
117: return HT_OK;
118: }
119:
120: /*
2.71 frystyk 121: ** If there is no content-length, no transfer encoding and no
122: ** content type then we assume that there is no body part in
123: ** the message and we can return HT_LOADED
2.68 frystyk 124: */
2.79 frystyk 125: {
126: HTHost * host = HTNet_host(me->net);
2.84 frystyk 127: if (length<0 && te==NULL &&
2.79 frystyk 128: HTHost_isPersistent(host) && !HTHost_closeNotification(host)) {
2.81 frystyk 129: if (format != WWW_UNKNOWN) {
2.94 frystyk 130: HTTRACE(STREAM_TRACE, "MIME Parser. BAD - there seems to be a body but no length. This must be an HTTP/1.0 server pretending that it is HTTP/1.1\n");
2.81 frystyk 131: HTHost_setCloseNotification(host, YES);
132: } else {
2.88 frystyk 133: HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE);
134: if (cbf) (*cbf)(request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL);
2.94 frystyk 135: HTTRACE(STREAM_TRACE, "MIME Parser. No body in this messsage\n");
2.81 frystyk 136: return HT_LOADED;
137: }
2.79 frystyk 138: }
2.68 frystyk 139: }
140:
141: /*
2.78 frystyk 142: ** Deal with the body
143: */
144: me->hasBody = YES;
145:
146: /*
2.71 frystyk 147: ** Handle any Content Type
2.60 frystyk 148: */
2.71 frystyk 149: if (!(me->mode & HT_MIME_PARTIAL) &&
2.83 frystyk 150: (format != WWW_UNKNOWN || length > 0 || te)) {
151: HTStream * target;
2.94 frystyk 152: HTTRACE(STREAM_TRACE, "Building.... C-T stack from %s to %s\n" _
153: HTAtom_name(format) _
2.71 frystyk 154: HTAtom_name(me->target_format));
2.83 frystyk 155: if ((target = HTStreamStack(format, me->target_format,
156: me->target, request, YES))==BlackHole) {
157: if (!savestream) {
2.90 frystyk 158: if (me->target) (*me->target->isa->abort)(me->target, NULL);
2.93 frystyk 159: me->target = me->save_stream(request, NULL,
160: format, me->target_format, me->target);
2.90 frystyk 161: savestream = YES;
162: }
163: } else
164: me->target = target;
165: }
166:
167: /*
168: ** Handle any Content Encodings
169: */
2.94 frystyk 170: HTTRACE(STREAM_TRACE, "Building.... Content-Decoding stack\n");
2.90 frystyk 171: if (ce) {
172: HTStream * target = HTContentDecodingStack(ce, me->target, request, NULL);
173: if (target == BlackHole) {
174: if (!savestream) {
2.83 frystyk 175: if (me->target) (*me->target->isa->abort)(me->target, NULL);
2.93 frystyk 176: me->target = me->save_stream(request, NULL,
177: format, me->target_format, me->target);
2.83 frystyk 178: savestream = YES;
179: }
180: } else
181: me->target = target;
2.18 frystyk 182: }
2.60 frystyk 183:
2.71 frystyk 184: /*
185: ** Can we cache the data object? If so then create a T stream and hook it
186: ** into the stream pipe. We do it before the transfer decoding so that we
187: ** don't have to deal with that when we retrieve the object from cache.
188: ** If we are appending to a cache entry then use a different stream than
189: ** if creating a new entry.
190: */
2.93 frystyk 191: #ifndef NO_CACHE
2.90 frystyk 192: if (HTCacheMode_enabled()) {
2.71 frystyk 193: if (me->mode & HT_MIME_PARTIAL) {
194: HTStream * append = HTStreamStack(WWW_CACHE_APPEND,
195: me->target_format,
196: me->target, request, NO);
2.90 frystyk 197: me->target = append;
198: } else if (HTResponse_isCachable(me->response) == HT_CACHE_ALL) {
2.71 frystyk 199: HTStream * cache = HTStreamStack(WWW_CACHE, me->target_format,
200: me->target, request, NO);
201: if (cache) me->target = HTTee(me->target, cache, NULL);
202: }
2.70 frystyk 203: }
2.93 frystyk 204: #endif
2.90 frystyk 205:
2.71 frystyk 206: /*
2.83 frystyk 207: ** Handle any Transfer Encodings
2.71 frystyk 208: */
2.94 frystyk 209: HTTRACE(STREAM_TRACE, "Building.... Transfer-Decoding stack\n");
2.83 frystyk 210: if (te) {
211: HTStream * target = HTTransferDecodingStack(te, me->target, request, NULL);
212: if (target == BlackHole) {
213: if (!savestream) {
214: if (me->target) (*me->target->isa->abort)(me->target, NULL);
2.93 frystyk 215: me->target = me->save_stream(request, NULL,
216: format, me->target_format, me->target);
2.83 frystyk 217: savestream = YES;
218: }
219: } else
220: me->target = target;
2.61 frystyk 221: }
2.71 frystyk 222:
2.85 frystyk 223:
224: /*
225: ** If we for some reason couldn't find a target stream
226: */
227: if (!me->target) me->target = HTBlackHole();
2.27 frystyk 228: return HT_OK;
2.1 timbl 229: }
230:
2.65 eric 231: /* _dispatchParsers - call request's MIME header parser.
232: ** Use global parser if no appropriate one is found for request.
233: */
234: PRIVATE int _dispatchParsers (HTStream * me)
235: {
236: int status;
237: char * token = HTChunk_data(me->token);
238: char * value = HTChunk_data(me->value);
2.71 frystyk 239: BOOL found = NO;
240: BOOL local = NO;
2.65 eric 241: HTMIMEParseSet * parseSet;
242:
243: /* In case we get an empty header consisting of a CRLF, we fall thru */
2.94 frystyk 244: HTTRACE(STREAM_TRACE, "MIME header. %s: %s\n" _
245: token ? token : "<null>" _
2.66 frystyk 246: value ? value : "<null>");
247: if (!token) return HT_OK; /* Ignore noop token */
2.65 eric 248:
2.70 frystyk 249: /*
2.71 frystyk 250: ** Remember the original header
251: */
252: HTResponse_addHeader(me->response, token, value);
253:
254: /*
2.70 frystyk 255: ** Search the local set of MIME parsers
256: */
2.65 eric 257: if ((parseSet = HTRequest_MIMEParseSet(me->request, &local)) != NULL) {
258: status = HTMIMEParseSet_dispatch(parseSet, me->request,
2.71 frystyk 259: token, value, &found);
260: if (found) return status;
2.65 eric 261: }
262:
2.70 frystyk 263: /*
264: ** Search the global set of MIME parsers
265: */
2.71 frystyk 266: if (local==NO && (parseSet = HTHeader_MIMEParseSet()) != NULL) {
267: status = HTMIMEParseSet_dispatch(parseSet, me->request,
268: token, value, &found);
269: if (found) return status;
270: }
271:
2.65 eric 272: return HT_OK;
273: }
274:
2.18 frystyk 275: /*
276: ** Header is terminated by CRCR, LFLF, CRLFLF, CRLFCRLF
277: ** Folding is either of CF LWS, LF LWS, CRLF LWS
278: */
2.57 frystyk 279: PRIVATE int HTMIME_put_block (HTStream * me, const char * b, int l)
2.18 frystyk 280: {
2.57 frystyk 281: const char * start = b;
282: const char * end = start;
2.96 ! frystyk 283: const char * value = HTChunk_size(me->value) > 0 ? b : NULL;
2.73 frystyk 284: int length = l;
2.64 eric 285: int status;
286:
287: while (!me->transparent) {
2.18 frystyk 288: if (me->EOLstate == EOL_FCR) {
2.64 eric 289: if (*b == CR) /* End of header */
290: me->EOLstate = EOL_END;
291: else if (*b == LF) /* CRLF */
2.18 frystyk 292: me->EOLstate = EOL_FLF;
2.80 frystyk 293: else if (isspace((int) *b)) /* Folding: CR SP */
2.64 eric 294: me->EOLstate = EOL_FOLD;
295: else /* New line */
296: me->EOLstate = EOL_LINE;
2.18 frystyk 297: } else if (me->EOLstate == EOL_FLF) {
298: if (*b == CR) /* LF CR or CR LF CR */
299: me->EOLstate = EOL_SCR;
2.64 eric 300: else if (*b == LF) /* End of header */
301: me->EOLstate = EOL_END;
2.80 frystyk 302: else if (isspace((int) *b)) /* Folding: LF SP or CR LF SP */
2.64 eric 303: me->EOLstate = EOL_FOLD;
304: else /* New line */
305: me->EOLstate = EOL_LINE;
306: } else if (me->EOLstate == EOL_SCR) {
307: if (*b==CR || *b==LF) /* End of header */
308: me->EOLstate = EOL_END;
2.80 frystyk 309: else if (isspace((int) *b)) /* Folding: LF CR SP or CR LF CR SP */
2.64 eric 310: me->EOLstate = EOL_FOLD;
311: else /* New line */
312: me->EOLstate = EOL_LINE;
313: } else if (*b == CR)
314: me->EOLstate = EOL_FCR;
315: else if (*b == LF)
316: me->EOLstate = EOL_FLF; /* Line found */
317: else {
318: if (!me->haveToken) {
2.80 frystyk 319: if (*b == ':' || isspace((int) *b)) {
2.64 eric 320: HTChunk_putb(me->token, start, end-start);
321: HTChunk_putc(me->token, '\0');
322: me->haveToken = YES;
323: } else {
324: unsigned char ch = *(unsigned char *) b;
2.92 frystyk 325: ch = TOLOWER(ch);
2.64 eric 326: me->hash = (me->hash * 3 + ch) % MIME_HASH_SIZE;
327: }
2.80 frystyk 328: } else if (value == NULL && *b != ':' && !isspace((int) *b))
2.64 eric 329: value = b;
330: end++;
331: }
332: switch (me->EOLstate) {
333: case EOL_LINE:
2.73 frystyk 334: case EOL_END:
335: {
2.78 frystyk 336: int ret = HT_ERROR;
2.64 eric 337: HTChunk_putb(me->value, value, end-value);
338: HTChunk_putc(me->value, '\0');
2.75 frystyk 339: ret = _dispatchParsers(me);
2.73 frystyk 340: HTNet_addBytesRead(me->net, b-start);
2.64 eric 341: start=b, end=b;
342: if (me->EOLstate == EOL_END) { /* EOL_END */
2.75 frystyk 343: if (ret == HT_OK) {
2.67 frystyk 344: b++, l--;
2.78 frystyk 345: ret = pumpData(me);
2.73 frystyk 346: HTNet_addBytesRead(me->net, 1);
2.82 frystyk 347: if (me->mode & (HT_MIME_FOOTER | HT_MIME_CONT)) {
2.73 frystyk 348: HTHost_setConsumed(HTNet_host(me->net), length - l);
2.75 frystyk 349: return ret;
2.78 frystyk 350: } else {
2.89 frystyk 351: HTNet_setHeaderBytesRead(me->net, HTNet_bytesRead(me->net));
2.78 frystyk 352: }
2.67 frystyk 353: }
2.64 eric 354: } else { /* EOL_LINE */
355: HTChunk_clear(me->token);
356: HTChunk_clear(me->value);
357: me->haveToken = NO;
358: me->hash = 0;
359: value = NULL;
360: }
2.18 frystyk 361: me->EOLstate = EOL_BEGIN;
2.78 frystyk 362: if (ret != HT_OK && ret != HT_LOADED) return ret;
2.64 eric 363: break;
2.73 frystyk 364: }
2.64 eric 365: case EOL_FOLD:
2.18 frystyk 366: me->EOLstate = EOL_BEGIN;
2.64 eric 367: if (!me->haveToken) {
368: HTChunk_putb(me->token, start, end-start);
369: HTChunk_putc(me->token, '\0');
370: me->haveToken = YES;
371: } else if (value) {
372: HTChunk_putb(me->value, value, end-value);
373: HTChunk_putc(me->value, ' ');
374: }
375: start=b, end=b;
376: break;
377: default:
2.73 frystyk 378: b++, l--;
2.64 eric 379: if (!l) {
2.87 frystyk 380: BOOL stop = NO;
381: if (!me->haveToken) {
382: /* If empty header then prepare to stop */
383: if (end-start)
384: HTChunk_putb(me->token, start, end-start);
385: else
386: stop = YES;
387: } else if (value)
2.64 eric 388: HTChunk_putb(me->value, value, end-value);
2.78 frystyk 389: HTHost_setConsumed(HTNet_host(me->net), length - l);
2.87 frystyk 390: return stop ? pumpData(me) : HT_OK;
2.64 eric 391: }
392: }
2.18 frystyk 393: }
2.32 frystyk 394:
2.78 frystyk 395: if (length != l) HTHost_setConsumed(HTNet_host(me->net), length - l);
396:
2.32 frystyk 397: /*
398: ** Put the rest down the stream without touching the data but make sure
2.73 frystyk 399: ** that we get the correct content length of data. If we have a CL in
400: ** the headers then this stream is responsible for the accountance.
2.32 frystyk 401: */
2.82 frystyk 402: if (me->hasBody) {
2.73 frystyk 403: HTNet * net = me->net;
2.66 frystyk 404: /* Check if CL at all - thanks to jwei@hal.com (John Wei) */
2.73 frystyk 405: long cl = HTResponse_length(me->response);
2.82 frystyk 406: if (cl >= 0) {
2.89 frystyk 407: long bodyRead = HTNet_bytesRead(net) - HTNet_headerBytesRead(net);
2.73 frystyk 408:
409: /*
410: ** If we have more than we need then just take what belongs to us.
411: */
412: if (bodyRead + l >= cl) {
413: int consume = cl - bodyRead;
414: if ((status = (*me->target->isa->put_block)(me->target, b, consume)) < 0)
415: return status;
2.88 frystyk 416: else {
417: HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE);
418: HTNet_addBytesRead(net, consume);
419: HTHost_setConsumed(HTNet_host(net), consume);
420: if (cbf) (*cbf)(me->request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL);
421: return HT_LOADED;
422: }
423: } else {
2.73 frystyk 424: if ((status = (*me->target->isa->put_block)(me->target, b, l)) < 0)
2.78 frystyk 425: return status;
2.73 frystyk 426: HTNet_addBytesRead(net, l);
2.78 frystyk 427: HTHost_setConsumed(HTNet_host(net), l);
2.73 frystyk 428: return status;
429: }
2.78 frystyk 430: }
2.73 frystyk 431: return (*me->target->isa->put_block)(me->target, b, l);
2.88 frystyk 432: } else {
433: HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE);
434: if (cbf) (*cbf)(me->request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL);
2.66 frystyk 435: }
436: return HT_LOADED;
2.18 frystyk 437: }
438:
439:
440: /* Character handling
441: ** ------------------
442: */
2.36 frystyk 443: PRIVATE int HTMIME_put_character (HTStream * me, char c)
2.18 frystyk 444: {
445: return HTMIME_put_block(me, &c, 1);
446: }
447:
2.1 timbl 448:
449: /* String handling
450: ** ---------------
451: */
2.57 frystyk 452: PRIVATE int HTMIME_put_string (HTStream * me, const char * s)
2.1 timbl 453: {
2.18 frystyk 454: return HTMIME_put_block(me, s, (int) strlen(s));
2.1 timbl 455: }
456:
457:
2.18 frystyk 458: /* Flush an stream object
459: ** ---------------------
2.1 timbl 460: */
2.36 frystyk 461: PRIVATE int HTMIME_flush (HTStream * me)
2.1 timbl 462: {
2.47 frystyk 463: return me->target ? (*me->target->isa->flush)(me->target) : HT_OK;
2.1 timbl 464: }
465:
2.18 frystyk 466: /* Free a stream object
467: ** --------------------
2.1 timbl 468: */
2.36 frystyk 469: PRIVATE int HTMIME_free (HTStream * me)
2.1 timbl 470: {
2.18 frystyk 471: int status = HT_OK;
2.64 eric 472: if (!me->transparent)
2.65 eric 473: if (_dispatchParsers(me) == HT_OK)
2.64 eric 474: pumpData(me);
2.25 frystyk 475: if (me->target) {
476: if ((status = (*me->target->isa->_free)(me->target))==HT_WOULD_BLOCK)
477: return HT_WOULD_BLOCK;
478: }
2.94 frystyk 479: HTTRACE(PROT_TRACE, "MIME........ FREEING....\n");
2.64 eric 480: HTChunk_delete(me->token);
481: HTChunk_delete(me->value);
2.52 frystyk 482: HT_FREE(me);
2.18 frystyk 483: return status;
2.1 timbl 484: }
485:
486: /* End writing
487: */
2.38 frystyk 488: PRIVATE int HTMIME_abort (HTStream * me, HTList * e)
2.1 timbl 489: {
2.18 frystyk 490: int status = HT_ERROR;
2.41 frystyk 491: if (me->target) status = (*me->target->isa->abort)(me->target, e);
2.94 frystyk 492: HTTRACE(PROT_TRACE, "MIME........ ABORTING...\n");
2.64 eric 493: HTChunk_delete(me->token);
494: HTChunk_delete(me->value);
2.52 frystyk 495: HT_FREE(me);
2.18 frystyk 496: return status;
2.1 timbl 497: }
498:
499:
500:
501: /* Structured Object Class
502: ** -----------------------
503: */
2.57 frystyk 504: PRIVATE const HTStreamClass HTMIME =
2.1 timbl 505: {
506: "MIMEParser",
2.18 frystyk 507: HTMIME_flush,
2.1 timbl 508: HTMIME_free,
2.6 timbl 509: HTMIME_abort,
510: HTMIME_put_character,
511: HTMIME_put_string,
2.18 frystyk 512: HTMIME_put_block
2.1 timbl 513: };
514:
515:
2.48 frystyk 516: /* MIME header parser stream.
2.1 timbl 517: ** -------------------------
2.48 frystyk 518: ** This stream parses a complete MIME header and if a content type header
519: ** is found then the stream stack is called. Any left over data is pumped
520: ** right through the stream
2.1 timbl 521: */
2.36 frystyk 522: PUBLIC HTStream* HTMIMEConvert (HTRequest * request,
523: void * param,
524: HTFormat input_format,
525: HTFormat output_format,
526: HTStream * output_stream)
2.1 timbl 527: {
2.62 frystyk 528: HTStream * me;
2.52 frystyk 529: if ((me = (HTStream *) HT_CALLOC(1, sizeof(* me))) == NULL)
530: HT_OUTOFMEM("HTMIMEConvert");
2.1 timbl 531: me->isa = &HTMIME;
2.18 frystyk 532: me->request = request;
2.71 frystyk 533: me->response = HTRequest_response(request);
2.70 frystyk 534: me->net = HTRequest_net(request);
2.49 frystyk 535: me->target = output_stream;
2.18 frystyk 536: me->target_format = output_format;
2.93 frystyk 537: me->save_stream = LocalSaveStream ? LocalSaveStream : HTBlackHoleConverter;
2.64 eric 538: me->token = HTChunk_new(256);
539: me->value = HTChunk_new(256);
540: me->hash = 0;
2.18 frystyk 541: me->EOLstate = EOL_BEGIN;
2.64 eric 542: me->haveToken = NO;
2.1 timbl 543: return me;
544: }
2.32 frystyk 545:
2.48 frystyk 546: /* MIME header ONLY parser stream
547: ** ------------------------------
548: ** This stream parses a complete MIME header and then returnes HT_PAUSE.
549: ** It does not set up any streams and resting data stays in the buffer.
550: ** This can be used if you only want to parse the headers before you
551: ** decide what to do next. This is for example the case in a server app.
552: */
553: PUBLIC HTStream * HTMIMEHeader (HTRequest * request,
554: void * param,
555: HTFormat input_format,
556: HTFormat output_format,
557: HTStream * output_stream)
558: {
2.62 frystyk 559: HTStream * me = HTMIMEConvert(request, param, input_format,
560: output_format, output_stream);
2.70 frystyk 561: me->mode |= HT_MIME_HEADER;
2.48 frystyk 562: return me;
563: }
2.77 frystyk 564:
565: PUBLIC HTStream * HTMIMEContinue (HTRequest * request,
566: void * param,
567: HTFormat input_format,
568: HTFormat output_format,
569: HTStream * output_stream)
570: {
571: HTStream * me = HTMIMEConvert(request, param, input_format,
572: output_format, output_stream);
573: me->mode |= HT_MIME_CONT;
2.95 frystyk 574: return me;
575: }
576:
577: PUBLIC HTStream * HTMIMEUpgrade (HTRequest * request,
578: void * param,
579: HTFormat input_format,
580: HTFormat output_format,
581: HTStream * output_stream)
582: {
583: HTStream * me = HTMIMEConvert(request, param, input_format,
584: output_format, output_stream);
585: me->mode |= HT_MIME_UPGRADE;
2.77 frystyk 586: return me;
587: }
2.62 frystyk 588:
589: /* MIME footer ONLY parser stream
590: ** ------------------------------
591: ** Parse only a footer, for example after a chunked encoding.
592: */
593: PUBLIC HTStream * HTMIMEFooter (HTRequest * request,
594: void * param,
595: HTFormat input_format,
596: HTFormat output_format,
597: HTStream * output_stream)
598: {
599: HTStream * me = HTMIMEConvert(request, param, input_format,
600: output_format, output_stream);
2.70 frystyk 601: me->mode |= HT_MIME_FOOTER;
2.62 frystyk 602: return me;
603: }
2.71 frystyk 604:
2.93 frystyk 605: #ifndef NO_CACHE
2.72 frystyk 606: /*
607: ** A small BEFORE filter that just finds a cache entry unconditionally
608: ** and loads the entry. All freshness and any other constraints are
609: ** ignored.
610: */
611: PRIVATE int HTCacheLoadFilter (HTRequest * request, void * param, int mode)
612: {
613: HTParentAnchor * anchor = HTRequest_anchor(request);
614: HTCache * cache = HTCache_find(anchor);
2.94 frystyk 615: HTTRACE(STREAM_TRACE, "Cache Load.. loading partial cache entry\n");
2.72 frystyk 616: if (cache) {
617: char * name = HTCache_name(cache);
618: HTAnchor_setPhysical(anchor, name);
619: HTCache_addHit(cache);
620: HT_FREE(name);
621: }
622: return HT_OK;
623: }
624:
625: /*
626: ** A small AFTER filter that flushes the PIPE buffer so that we can
627: ** get the rest of the data
628: */
629: PRIVATE int HTCacheFlushFilter (HTRequest * request, HTResponse * response,
630: void * param, int mode)
631: {
632: HTStream * pipe = (HTStream *) param;
633: if (pipe) {
2.94 frystyk 634: HTTRACE(STREAM_TRACE, "Cache Flush. Flushing and freeing PIPE buffer\n");
2.72 frystyk 635: (*pipe->isa->flush)(pipe);
636: (*pipe->isa->_free)(pipe);
637: }
638:
639: /*
640: ** We also delete the request obejct and stop more filters from being called.
641: ** As this is our own request, it's OK to do that
642: */
643: HTRequest_delete(request);
644: return HT_ERROR;
645: }
2.93 frystyk 646: #endif
2.72 frystyk 647:
2.71 frystyk 648: /* Partial Response MIME parser stream
649: ** -----------------------------------
650: ** In case we sent a Range conditional GET we may get back a partial
651: ** response. This response must be appended to the already existing
652: ** cache entry before presented to the user.
653: ** We do this by continuing to load the new object into a temporary
654: ** buffer and at the same time start the cache load of the already
655: ** existing object. When we have loaded the cache we merge the two
656: ** buffers.
657: */
658: PUBLIC HTStream * HTMIMEPartial (HTRequest * request,
659: void * param,
660: HTFormat input_format,
661: HTFormat output_format,
662: HTStream * output_stream)
663: {
2.93 frystyk 664: #ifndef NO_CACHE
2.71 frystyk 665: HTParentAnchor * anchor = HTRequest_anchor(request);
2.72 frystyk 666: HTFormat format = HTAnchor_format(anchor);
667: HTStream * pipe = NULL;
668:
2.71 frystyk 669: /*
670: ** The merge stream is a place holder for where we can put data when it
671: ** arrives. We have two feeds: one from the cache and one from the net.
672: ** We call the stream stack already now to get the right output stream.
673: ** We can do this as we already know the content type from when we got the
674: ** first part of the object.
675: */
2.72 frystyk 676: HTStream * merge = HTMerge(HTStreamStack(format,
677: output_format, output_stream,
678: request, YES), 2);
2.71 frystyk 679:
680: /*
2.72 frystyk 681: ** Now we create the MIME parser stream in partial data mode. We also
682: ** set the target to our merge stream.
2.71 frystyk 683: */
684: HTStream * me = HTMIMEConvert(request, param, input_format,
685: output_format, output_stream);
686: me->mode |= HT_MIME_PARTIAL;
2.72 frystyk 687: me->target = merge;
688:
689: /*
690: ** Create the cache append stream, and a Tee stream
691: */
692: {
693: HTStream * append = HTStreamStack(WWW_CACHE_APPEND, output_format,
694: output_stream, request, NO);
695: if (append) me->target = HTTee(me->target, append, NULL);
696: }
697:
698: /*
699: ** Create the pipe buffer stream to buffer the data that we read
700: ** from the network
701: */
2.74 frystyk 702: if ((pipe = HTPipeBuffer(me->target, 0))) me->target = pipe;
2.71 frystyk 703:
704: /*
705: ** Now start the second load from the cache. First we read this data from
706: ** the cache and then we flush the data that we have read from the net.
707: */
708: {
2.72 frystyk 709: HTRequest * cache_request = HTRequest_new();
2.71 frystyk 710:
2.72 frystyk 711: /*
712: ** Set the output format to source and the output stream to the
713: ** merge stream. As we have already set up the stream pipe, we just
714: ** load it as source.
715: */
716: HTRequest_setOutputFormat(cache_request, WWW_SOURCE);
717: HTRequest_setOutputStream(cache_request, merge);
718:
719: /*
720: ** Bind the anchor to the new request and also register a local
721: ** AFTER filter to flush the pipe buffer so that we can get
722: ** rest of the data through.
723: */
724: HTRequest_setAnchor(cache_request, (HTAnchor *) anchor);
725: HTRequest_addBefore(cache_request, HTCacheLoadFilter, NULL, NULL,
726: HT_FILTER_FIRST, YES);
727: HTRequest_addAfter(cache_request, HTCacheFlushFilter, NULL, pipe,
728: HT_ALL, HT_FILTER_FIRST, YES);
2.71 frystyk 729:
2.94 frystyk 730: HTTRACE(STREAM_TRACE, "Partial..... Starting cache load\n");
2.72 frystyk 731: HTLoad(cache_request, NO);
2.71 frystyk 732: }
733: return me;
2.93 frystyk 734: #else
735: return NULL;
736: #endif
2.71 frystyk 737: }
738:
2.93 frystyk 739: PUBLIC void HTMIME_setSaveStream (HTConverter * save_stream)
740: {
741: LocalSaveStream = save_stream;
742: }
743:
744: PUBLIC HTConverter * HTMIME_saveStream (void)
745: {
746: return LocalSaveStream;
747: }
Webmaster