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