Hey Demis,
Another design question/challenge.
We are designing a HTTP Client Cache component that can be used by a JsonServiceClient that implements HTTP Expiration (with ETag) and HTTP Validation (Cache-Control: no-cache). The idea is that we can just wire in this client cache to a JsonServiceClient, and it would honor HTTP caching headers, just like most browsers do. It is critical for enhancing the performance of our services. Also, we were thinking of contributing this client to SS community for general use, as it is missing from the framework right now for anyone who wants to do the same.
We are having some challenges finding a way to hook this client cache into a JsonServiceClient.
Now, we are aware of the ResultsFilter and ResultsFilterResponse that are described on the wiki page: https://github.com/ServiceStack/ServiceStack/wiki/C%23-client#custom-client-caching-strategy, and the sample gives a good sense of how to go about hooking into the client.
However, in practice, we are finding that this hook is just slightly insufficient when it comes to implementing HTTP Validation (using ETags).
Here is the scenario:
We have a client cache and it is wired into the ResultsFilter and the ResultsFilterResponse of a JSC instance, in the same way as the wiki page demonstrates.
When a GET request by the JSC is made, the ResultsFilter hook fires, and our client cache either responds with null (nothing found in the cache yet) or it responds with the cached response (that it had previously saved with some expiry period). Implementing HTTP Expiration is a cinch, but there are times when HTTP Validation requires us to make a quick validation call to the [same] service with the the same request, and include the If-None-Match header with the the ETag.
As it goes with HTTP Validation, the server can either respond (by throwing in a MS client) a 304 - Not Modified, or return a 2XX fresh response with a new ETag and new Cache-Control headers. The client cache is then supposed to update its expiration and ETag values, and (if 2XX then cache the new response), and then serve its cached version of the response.
So, the issue is that in order for the client cache to make the addtional validation call with the âIf-None-Match: ETagâ it needs a another instance of the JSC to do it. Since the current JSC is blocked in the process of handling a ResultsFilter hook from the previous GET.
Now I havenât tested this yet in a running environment, but I suspect that we canât issue a second 'If-None-Matched validation call from the same JSC that is calling us in the ResultsFilter hook? That would be nice, but I doubt itâs accomadated?
If not, we will need another instance of a JSC to do that for us, and in practice, for that to work, we will need the second JSC instance to include the original request object, to the original request URI and with all the original headers of the original request that was hooked by the ResultsFilter hook. Those original request headers are not available to us in the hook [at present].
I am certain that there is a sound reason why the headers are not in the call from ResultsFilter hook, but without those request headers we cannot complete the âĂf-None-Matchâ validation call. Since to make the âIf-None-Matchâ validation call, we must have the same request headers and request object and same request URI as the hooked call. Because for us, those original headers included things like oAuth Authorization headers and others that we have setup in the RequestFilter of our JSC for each call.
From cursory testing, it also seems that the RequestFilter of our JSC is not processed until after the ResultsFilter hook returns, so we cannot get the current headers in our hooked call either.
Are you aware of this problem? Any ideas on a reasonable workaround? (Perhaps another hook, we can use where the headers are all set in the request by the RequestFilter)? Can you think of anything we can do here? Ideally, we would not want to create another instance of the JSC, but we certainly can and will do at a pinch.
Regards