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