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