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