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