/* 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 }
/* ** Tries really hard to get rid of the data. ** Returns: ** -1 Error ** 0 Buffered the data ** 1 Got rid of the data */ PUBLIC int HTMuxSession_disposeData (HTMuxSession * me, const char * buf, int len) { HTTRACE(MUX_TRACE, "Mux Channel. Writing %d bytes to session %p\n" _ len _ me); /* ** There are two situations that can occur: Either we have an accepted session ** with a Net object or we have an unaccepted session with no Net object. In ** the former case we try to get rid of the data by pushing it directly to the ** read stream of the Net object. In the latter case we buffer as much as we ** can. */ if (me) { HTNet * net; HTStream * sink = NULL; int status; if ((net = me->net) && (sink = HTNet_readStream(net))) { /* ** Look first to see if we have old data that we can dispose down ** the sink. We keep the buffer stream so that we can reuse it later. */ if (me->buffer && me->buffering) { if ((*me->buffer->isa->flush)(me->buffer) == HT_OK) { HTTRACE(MUX_TRACE, "Mux Channel. Flushed buffered data\n"); me->buffering = NO; } else if ((*me->buffer->isa->put_block)(me->buffer, buf, len) >= 0) { HTTRACE(MUX_TRACE, "Mux Channel. Buffer accepted data\n"); return 0; } HTTRACE(MUX_TRACE, "Mux Channel. Can't buffer data\n"); return (-1); } /* ** See if we can get rid of the new data. If not then try to buffer it. ** If this also fails then we reset the channel. A positive return code ** from the stream means that we got rid of the data successfully. */ if ((status = (*sink->isa->put_block)(sink, buf, len)) >= 0) { HTTRACE(MUX_TRACE, "Mux Channel. Stream returned %d\n" _ status); /* ** If we get back a HT_LOADED then we have all the data we need ** and we can terminate the request */ if (status == HT_LOADED) { HTNet_execute (net, HTEvent_END); return 0; } /* ** Decide whether we should send a credit message ** MORE TO COME */ me->read += len; if (me->read >= DEFAULT_CREDIT / 2) { me->read = 0; return 1; } return 0; } } /* ** The stream is not ready and we try to buffer the data in ** the meantime. */ if (!me->buffer) { me->buffer = HTPipeBuffer(sink, DEFAULT_CREDIT); me->buffering = YES; } status = (*me->buffer->isa->put_block)(me->buffer, buf, len); if (status >= 0) { HTTRACE(MUX_TRACE, "Mux Channel. Buffer accepted data\n"); return 0; } HTTRACE(MUX_TRACE, "Mux Channel. Buffer returned %d\n" _ status); } return (-1); }