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