📄 htmime.c
字号:
** Put the rest down the stream without touching the data but make sure ** that we get the correct content length of data. If we have a CL in ** the headers then this stream is responsible for the accountance. */ if (me->hasBody) { HTNet * net = me->net; /* Check if CL at all - thanks to jwei@hal.com (John Wei) */ long cl = HTResponse_length(me->response); if (cl >= 0) { long bodyRead = HTNet_bytesRead(net) - HTNet_headerBytesRead(net); /* ** If we have more than we need then just take what belongs to us. */ if (bodyRead + l >= cl) { int consume = cl - bodyRead; if ((status = (*me->target->isa->put_block)(me->target, b, consume)) < 0) return status; else { HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE); HTNet_addBytesRead(net, consume); HTHost_setConsumed(HTNet_host(net), consume); if (cbf) (*cbf)(me->request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL); return HT_LOADED; } } else { if ((status = (*me->target->isa->put_block)(me->target, b, l)) < 0) return status; HTNet_addBytesRead(net, l); HTHost_setConsumed(HTNet_host(net), l); return status; } } return (*me->target->isa->put_block)(me->target, b, l); } else { HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE); if (cbf) (*cbf)(me->request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL); } return HT_LOADED;}/* Character handling** ------------------*/PRIVATE int HTMIME_put_character (HTStream * me, char c){ return HTMIME_put_block(me, &c, 1);}/* String handling** ---------------*/PRIVATE int HTMIME_put_string (HTStream * me, const char * s){ return HTMIME_put_block(me, s, (int) strlen(s));}/* Flush an stream object** ---------------------*/PRIVATE int HTMIME_flush (HTStream * me){ return me->target ? (*me->target->isa->flush)(me->target) : HT_OK;}/* Free a stream object** --------------------*/PRIVATE int HTMIME_free (HTStream * me){ int status = HT_OK; if (!me->transparent) if (_stream2dispatchParsers(me) == HT_OK) pumpData(me); if (me->target) { if ((status = (*me->target->isa->_free)(me->target))==HT_WOULD_BLOCK) return HT_WOULD_BLOCK; } HTTRACE(PROT_TRACE, "MIME........ FREEING....\n"); HTChunk_delete(me->token); HTChunk_delete(me->value); HT_FREE(me); return status;}/* End writing*/PRIVATE int HTMIME_abort (HTStream * me, HTList * e){ int status = HT_ERROR; if (me->target) status = (*me->target->isa->abort)(me->target, e); HTTRACE(PROT_TRACE, "MIME........ ABORTING...\n"); HTChunk_delete(me->token); HTChunk_delete(me->value); HT_FREE(me); return status;}/* Structured Object Class** -----------------------*/PRIVATE const HTStreamClass HTMIME ={ "MIMEParser", HTMIME_flush, HTMIME_free, HTMIME_abort, HTMIME_put_character, HTMIME_put_string, HTMIME_put_block}; /* MIME header parser stream.** -------------------------** This stream parses a complete MIME header and if a content type header** is found then the stream stack is called. Any left over data is pumped** right through the stream*/PUBLIC HTStream* HTMIMEConvert (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream){ HTStream * me; if ((me = (HTStream *) HT_CALLOC(1, sizeof(* me))) == NULL) HT_OUTOFMEM("HTMIMEConvert"); me->isa = &HTMIME; me->request = request; me->response = HTRequest_response(request); me->net = HTRequest_net(request); me->target = output_stream; me->target_format = output_format; me->save_stream = LocalSaveStream ? LocalSaveStream : HTBlackHoleConverter; me->token = HTChunk_new(256); me->value = HTChunk_new(256); me->hash = 0; me->EOLstate = EOL_BEGIN; me->haveToken = NO; return me;}/* MIME header ONLY parser stream** ------------------------------** This stream parses a complete MIME header and then returnes HT_PAUSE.** It does not set up any streams and resting data stays in the buffer.** This can be used if you only want to parse the headers before you** decide what to do next. This is for example the case in a server app.*/PUBLIC HTStream * HTMIMEHeader (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream){ HTStream * me = HTMIMEConvert(request, param, input_format, output_format, output_stream); me->mode |= HT_MIME_HEADER; return me;}PUBLIC HTStream * HTMIMEContinue (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream){ HTStream * me = HTMIMEConvert(request, param, input_format, output_format, output_stream); me->mode |= HT_MIME_CONT; return me;}PUBLIC HTStream * HTMIMEUpgrade (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream){ HTStream * me = HTMIMEConvert(request, param, input_format, output_format, output_stream); me->mode |= HT_MIME_UPGRADE; return me;}/* MIME footer ONLY parser stream** ------------------------------** Parse only a footer, for example after a chunked encoding.*/PUBLIC HTStream * HTMIMEFooter (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream){ HTStream * me = HTMIMEConvert(request, param, input_format, output_format, output_stream); me->mode |= HT_MIME_FOOTER; return me;}#ifndef NO_CACHE/*** A small BEFORE filter that just finds a cache entry unconditionally** and loads the entry. All freshness and any other constraints are ** ignored.*/PRIVATE int HTCacheLoadFilter (HTRequest * request, void * param, int mode){ HTParentAnchor * anchor = HTRequest_anchor(request); char * default_name; HTCache * cache; default_name = HTRequest_defaultPutName (request); cache = HTCache_find(anchor, default_name); HTTRACE(STREAM_TRACE, "Cache Load.. loading partial cache entry\n"); if (cache) { char * name = HTCache_name(cache); HTAnchor_setPhysical(anchor, name); HTCache_addHit(cache); HT_FREE(name); } return HT_OK;}/*** A small AFTER filter that flushes the PIPE buffer so that we can** get the rest of the data*/PRIVATE int HTCacheFlushFilter (HTRequest * request, HTResponse * response, void * param, int mode){ HTStream * pipe = (HTStream *) param; if (pipe) { HTTRACE(STREAM_TRACE, "Cache Flush. Flushing and freeing PIPE buffer\n"); (*pipe->isa->flush)(pipe);#if 0 /* @@ JK: flush converts the pipe to an open one, we shouldn't free it as we'll loose our references */ (*pipe->isa->_free)(pipe);#endif } /* ** We also delete the request obejct and stop more filters from being called. ** As this is our own request, it's OK to do that */ HTRequest_delete(request); return HT_ERROR;}#endif/* Partial Response MIME parser stream** -----------------------------------** In case we sent a Range conditional GET we may get back a partial** response. This response must be appended to the already existing** cache entry before presented to the user.** We do this by continuing to load the new object into a temporary ** buffer and at the same time start the cache load of the already** existing object. When we have loaded the cache we merge the two** buffers.*/PUBLIC HTStream * HTMIMEPartial (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream){#ifndef NO_CACHE HTParentAnchor * anchor = HTRequest_anchor(request); HTFormat format = HTAnchor_format(anchor); HTStream * pipe = NULL; /* ** The merge stream is a place holder for where we can put data when it ** arrives. We have two feeds: one from the cache and one from the net. ** We call the stream stack already now to get the right output stream. ** We can do this as we already know the content type from when we got the ** first part of the object. */ HTStream * merge = HTMerge(HTStreamStack(format, output_format, output_stream, request, YES), 2); /* ** Now we create the MIME parser stream in partial data mode. We also ** set the target to our merge stream. */ HTStream * me = HTMIMEConvert(request, param, input_format, output_format, output_stream); me->mode |= HT_MIME_PARTIAL; me->target = merge;#if 0 /* JK: this doesn't work because this work is repeated before */ /* ** Create the cache append stream, and a Tee stream */ { HTStream * append = HTStreamStack(WWW_CACHE_APPEND, output_format, output_stream, request, NO); if (append) me->target = HTTee(me->target, append, NULL); }#endif /* ** Create the pipe buffer stream to buffer the data that we read ** from the network */ if ((pipe = HTPipeBuffer(me->target, 0))) me->target = pipe; /* ** Now start the second load from the cache. First we read this data from ** the cache and then we flush the data that we have read from the net. */ { HTRequest * cache_request = HTRequest_new(); /* ** Set the output format to source and the output stream to the ** merge stream. As we have already set up the stream pipe, we just ** load it as source. */ HTRequest_setOutputFormat(cache_request, WWW_SOURCE); HTRequest_setOutputStream(cache_request, merge); /* ** Bind the anchor to the new request and also register a local ** AFTER filter to flush the pipe buffer so that we can get ** rest of the data through. */ HTRequest_setAnchor(cache_request, (HTAnchor *) anchor); HTRequest_addBefore(cache_request, HTCacheLoadFilter, NULL, NULL, HT_FILTER_FIRST, YES); HTRequest_addAfter(cache_request, HTCacheFlushFilter, NULL, pipe, HT_ALL, HT_FILTER_FIRST, YES); HTTRACE(STREAM_TRACE, "Partial..... Starting cache load\n"); HTLoad(cache_request, NO); } return me;#else return NULL;#endif}PUBLIC void HTMIME_setSaveStream (HTConverter * save_stream){ LocalSaveStream = save_stream;}PUBLIC HTConverter * HTMIME_saveStream (void){ return LocalSaveStream;}#ifndef NO_CACHE/* HTMIME_anchor2response * Copies the anchor HTTP headers into a response object by means * of the generic _dispatchParsers function. Written so that we can * copy the HTTP headers stored in the cache to the response object. */PRIVATE void HTMIME_anchor2response (HTRequest * req){ char * token; char * value; HTAssocList * header; HTAssoc * pres; HTResponse * res; HTParentAnchor * anchor; if (!req) return; anchor = HTRequest_anchor (req); header = HTAnchor_header (anchor); if (!anchor || !header) return; while ((pres = (HTAssoc *) HTAssocList_nextObject (header))) { token = HTAssoc_name (pres); value = HTAssoc_value (pres); _dispatchParsers (req, token, value); } /* ** Notify the response object not to delete the lists that we ** have inherited from the anchor object */ res = HTRequest_response (req); HTResponse_isCached (res, YES); }/*** A small AFTER filter that is a frontend to the ** HTMIME_anchor2headers function.*/PUBLIC HTStream * HTCacheCopyHeaders (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream){ HTTRACE(STREAM_TRACE, "Cache Copy Headers.. Copying headers into the response object\n"); HTMIME_anchor2response (request); return HT_OK;}#endif /* NO_CACHE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -