- From: Henrik Frystyk Nielsen <frystyk@w3.org>
- Date: Fri, 19 Jan 1996 10:40:38 -0500
- To: Maciej Puzio <puzio@zodiac1.mimuw.edu.pl>
- Cc: "'Henrik Frystyk Nielsen'" <frystyk@w3.org>, "'WWW Library Mailing List'" <www-lib@w3.org>
Maciej Puzio writes:
> > > But the upstream module can then check for both codes and then decide what to
> > > do... The important thing is that both codes (or in general all codes in case
> > > of multiple T streams) get propagated upstream.
> > >
> > > The resolving function could also be a direct part of the
> > > upstream module which will be activated each time the T stream returns. As I
> > > see it, both ways will do, but maybe a callback function is a more flexibel
> > > solution.
> >
> > I've got a point of view on it, but I need to think a little.
> > I'll write to you soon.
>
> I've just thought a little. I've also found another problem which is interesting by
> itself, but is also a good argument in our discussion about HTTee.
>
> BTW: I'm not sure whether the HTTee problem deserves such a long discussion.
> It's getting more and more interesting for me, but if you are bored or don't have time,
> please let me know. This will save work and time for both of us. :-)
Actually I think that your solution about having a call back function that can
solve any return code conflict is a good idea - patches are welcome ;-)
> The problem:
>
> In my simple WWW browser all HTML documents were handled by the simple
> stream stack, which led from the network throught the MIME parser to the HTML
> module presenting the document to the user. This stream had only one Tee,
> before the MIME parser, which pushed the copy of the data to the cache. One day
> however I decided to implement the "Save as HTML" menu command in my
> browser. For that I needed a copy of the unparsed (but without headers) source
> data. I didn't want to rely on the data from the cache, because the user of my
> browser can disable it.
>
> Below I give the solution to the problem. I'm not sure whether there is no simpler
> solution. Perhaps this can be done in some really trivial way?
Well, if you want multiple outputs at the same time then the T stream is the
only solution.
> The solution:
>
> What I needed was a Tee after the MIME parser. But how to insert it to the stream
> stack which is created automatically according to the set of converters assigned to
> the request? To do that I created the "converter tee".
>
> HTStream* HTMLPresentAndSave (
> HTRequest* request,
> void* param,
> HTFormat input_format,
> HTFormat output_format,
> HTStream* output_stream)
> {
> return HTTee
> (HTMLPresent (request, param, input_format, output_format, output_stream),
> HTSaveAndCallBack (request, param, input_format, output_format,
> output_stream));
> }
>
> Then I used this new converter instead of HTMLPresent:
>
> HTConversion_add(c,"text/html", "www/present", HTMLPresentAndSave,
> 1.0, 0.0, 0.0);
>
> I did the similar trick for the HTPlainPresent converter.
>
> BTW: I used here HTSaveAndCallBack to save the copy of the source data to
> the file. I know it has been removed from the library, but I reintroduced it in my copy.
> This converter is really useful. I know that using streams everywhere is better than
> passing data through the file, but the operating system doesn't know this and
> in some case requires the file name. HTSaveAndCallBack is also useful as a
> temporary file writer (in this role I used it above, HTFWriter is not good since it's
> not a converter).
I know - it was a bit out of lack of time that I temporaryly disabled it when
I isolated the cache mechanism. I thought it more important to get the cache
out of the core than to preserve it at the moment, but I will put it back in
(in a cache independent implementation).
> Instead of writing to a file I could have used the HTXParse converter, which would
> put the data in the memory buffer.
>
> This solution works perfectly, but has some drawbacks:
>
> 1. I'm not able to write a generic "converter tee". That is, I has to define very
> similar converter tee functions for every pair of converters I want to be tee'ed.
> This is beacuse I can only give a function pointer in HTConversion_add and
> I can't pass the converter any parameters.
This is a design limitation of the streams. Passing a parameter would be very
useful to destinguish slightly different streams. I have thought of that for
some time but didn't get it done.
> 2. I don't have any access to the results of the conveter's work. For example, the
> only way I can get the file name used for saving the data by HTSaveAndCallBack
> is the callback function I assign to the request. This causes that I can
> have only one callback function per request. In my application I use only one
> HTSaveAndCallBack per request, so this doesn't matter, but it's possible to
> have several HTSaveAndCallBacks used for different purposes in one request.
> What's then? It's even worse in the HTXParse case. To return the pointer to the
> memory buffer it uses only one callback function defined in the HTEPtoCl module
> (so we can only define one callback function in the whole application).
>
> How to improve this?
>
> My proposition:
>
> Converters are now functions returning streams. I propose to make them objects.
>
> Below I used the C++ code to make my ideas more readable. Of course, it has
> to be converted to plain C before using it in the library. Please consider the following
> as the pseudocode.
>
> class HTConverter
> {
> public:
> virtual HTStream* CreateStream (
> HTRequest* request,
> void* param,
> HTFormat input_format,
> HTFormat output_format,
> HTStream* output_stream) = 0;
> };
plus of course the extra parameter ;-)
> Some examples of derived classes:
>
> class HTSaveAndCallBack : public HTConverter
> {
> public:
> HTSaveAndCallBack() { ...initialize... }
>
> HTStream* CreateStream ( ...parameters as above... )
> { ...do what HTSaveAndCallBack function does now... }
>
> //callback for the stream, we can also make it a parameter to the constructor
> virtual void OnStreamClose (HTRequest* request, char* filename);
>
> };
>
> class HTConverterTee : public HTConverter
> {
> HTConverter* converter1;
> HTConverter* converter2;
> public:
> HTConverterTee (HTConverter* conv1, HTConverter* conv2)
> { ...initialize... }
>
> HTStream* CreateStream ( ...parameters as above... )
> { return HTTee (
> conv1->CreateStream ( ...arguments... ),
> conv2->CreateStream ( ...arguments... )) }
>
> //callback to resolve Tee result codes, we can also make it a parameter
> //to the constructor
> int ResolveResults (int result1, int result2)
> { ...default implementation... }
> };
>
> These converter objects would be created before assigning to the request and
> would be destroyed together with the request.
>
> Drawback: this looks beautiful in C++, but in C? I'd rather not to think. :-)
Very nice idea - I'll have to think a bit more about how to "translate" it to
C. Another way is of course to swap to C++. I would very much like some input
on how that would be received.
> Now let me go back to the HTTee result codes problem.
> The "converter tee" is an example of the situation when the code pumping data
> to the stream doesn't know whether it's pumping it to a Tee or to something else.
> That's why it can't resolve which result is more important and what to do if it gets
> success and failure together. That's the argument for passing the resolving function
> to a tee creation function as a parameter (callback). It also would be passed to the
> converter tee (as a callback). In the example above it is a virtual member function
> (to override), but in C it will be difficult to program, I think.
>
> Thanks a lot if you've managed to get to this line! :-)
Thanks for the many good suggestions - it is really useful!
--
Henrik Frystyk Nielsen, <frystyk@w3.org>
World-Wide Web Consortium, MIT/LCS NE43-356
545 Technology Square, Cambridge MA 02139, USA
Received on Friday, 19 January 1996 10:41:12 UTC