[Mono-dev] mono WebResponse caching
mlintner at sinenomine.net
Tue Jun 4 18:02:36 UTC 2013
We have noticed that parts of the Mono Web functionality are not yet implemented. We are hoping to be able to add some of these missing pieces. Caching of
HttpWebResponses is the first one we took a look at. It would appear that Microsoft takes advantage of the internet explorer caching. Since it would appear
that an HttpWebResponse is fully populated when it is returned from the cache and the only content in the urls that are cached are the bytes that come in on
the wire, Microsoft must return things from the cache at a very low level and and let the response populate the normal way. This did not seem possible to
emulate when I looked at mono. I also wanted to cache to a directory so that the urls can be inspected or deleted.
After looking at the code for awhile I realized that I could save additional information from the response, ie: headercollection, cookiecontainer, Status
code etc in the same file as the responsestream. A response can be serialized but not all of it, most importantly the responsestream is not serialized. I
wrote a prototype with some helper functions, the only change to existing mono code would be in HttpWebRequest.GetResponse. Before calling BeginGetResponse
I call a function that tries to retrieve the url from the cache. If it is not there, BeginGetResponse is called as normal, the response is grabbed from
EndGetResponse and passed to a method to insert into the cache. This insert method will open a filename in a directory designated as mono_cache with the name
produced by calling HttpUtility.Encode so it is a legal filename. Also you can only read from the responsestream once, which means that if read from it, save
it to a file, then the responsestream is no longer accessible from the WebResponse when it is returned to the calling code. If sream copy worked it could be
used here. One way to fix the problem is to call GetResponseStream(), read it to the end, which produces a string, then finally convert the string to bytes.
Then you can serialize the bytes to the open file, serialize the headercollection and ultimately individually add any field you want in the response when it
is retrieved from the cache, then close the file. Then create a new WebConnectionData instance and populate it with headers and all the relevent fields from
the old response, finally passing the bytes of data to a memory stream constructor and assign it to the WebConnectionData's stream member. Then I construct a
new HttpWebResponse, passing the WebConnectionData and other arguments which are taken from the existing request and the recently retrieved response. The
response is then returned to getResponse then finally to the calling code, good as new. Next time the url is requested the cached file is deserialized, one
member at a time then a WebConnectionData is setup and finally, a HttpWebResponse is constructed with the info read from the file, the WebConnectionData and
from the members of the new request. I concentrated on prototyping caching of HttpWebResponses. Only a simple cache algorithm, no Ftp, no File etc. No cache
aging etc. There are many other things that would have to be done for this to be usable.
Using the prototype I requested www.google.com<http://www.google.com> and it took .1601419 sec to retrieve and cache it, including all the mechanics described above. The
second time returning it from the cache, building the HttpWebResponse took .0007671. This is a 288 times speedup. That kind of performance increase, from a
prototype that is not yet optimized, is compelling. I must add that I measured the same experiment with google under the .Net 4.5 implementation and the
speedup is 769x. Obviously thats something to work toward. Even falling short of that level of speedup, some implementation of response caching for mono
would greatly benefit the users who need better performance of services, rest etc.
So far this is the only mono code that would change to support caching,
public override WebResponse GetResponse()
WebResponse response = ResponseCache.TryRetrieve (this);
if (response == null)
WebAsyncResult result = (WebAsyncResult) BeginGetResponse (null, null);
response = EndGetResponse (result);
response.IsFromCache = false;
HttpWebResponse response2 = ResponseCache.TryInsert ((HttpWebRequest)this, (HttpWebResponse)response);
The code can stand some improvement. It is a prototype and it shows. I have included the file ResponseCache.cs which the above
code hooks into. We were wondering whether anyone has tried to implement response caching before and whether there is any intent to in the future. What are
your thoughts about the approach described above and shown in the ResponseCache.cs file and the 3 other files I included. Bear in mind it
would be refactored so that it fit into the mono architecture a bit better, it needs to be generalized, different strategies for different concrete response
types need to be plugged in, configuration. and a plethora of things I missed. If you feel this approach has merit I can detail all that seperatly. If you
feel a different approach would work better, please let us know, in the end were looking to make mono more robust in some of the ways that are expected but
not yet implemented.
The main thing I want to show here is how it is possible, to wedge a cache strategy into mono's request pipeline without changing or destabilizing
mono itself. The cache implementation could change also, which is one way that this is code falls short is it totally violates open-closed. Is this a
viable approach? There would of course be much more to do, that I have not even mentioned here. Your comments would be appreciated.
The files can be found here.
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Mono-devel-list