ServiceStack - using gzip / deflate compression using JSONP requests

I have a ServiceStack service that compresses the response using RequestContext.ToOptimizedResult() , for example:

 [Route("/numbers/search")] public class FindNumbers { } public object Get(FindNumbers query) { var data = new List<string> { "One", "Two", "Three" }; return RequestContext.ToOptimizedResult(data); } 

This works fine when issuing a request, for example:

 GET http://myhost:13487/numbers/search.json 

And it compresses, as expected, with the Accept-Encoding request header:

 Accept-Encoding: gzip,deflate,sdch 

I can also issue a JSONP request:

 GET http://myhost:13487/numbers/search?callback=func 

which correctly returns the application/javascript callback (uncompressed).

PROBLEM

When I add the Accept-Encoding request header to the JSONP request, the response is compressed JSON data according to the original JSON request, not a compressed application/javascript callback.

Are there obvious reasons why I am missing this behavior, or is it just a bug in ServiceStack? My expectation would be to get a compressed JSONP response in the response, but I'm pretty green with JSONP, and there might be a good reason for backing up.

Notice that I work through the ServiceStack source, but I decided I got it there because more brains are better than one ...

Thanks in advance

EDIT

So, I traced the problem using the following source

https://github.com/ServiceStack/ServiceStack/blob/5d09d439cd1a13712411552e2b3ede5a71af2ee5/src/ServiceStack/Host/Handlers/GenericHandler.cs#L79

and

https://github.com/ServiceStack/ServiceStack/blob/5d09d439cd1a13712411552e2b3ede5a71af2ee5/src/ServiceStack/Host/RestHandler.cs#L107

 if (doJsonp && !(response is CompressedResult)) return httpRes.WriteToResponse(httpReq, response, (callback + "(").ToUtf8Bytes(),")".ToUtf8Bytes()); return httpRes.WriteToResponse(httpReq, response); 

So, if the answer is a compressed result, then regardless of the requirement for JSONP via ?callback=func answer will simply contain compressed json (in the case of the example above), which corresponds to true with my conclusions above. Thus, it seems that the jsonp callback wrapper should be applied earlier in the stop lot.

+7
c # jsonp gzip servicestack deflate
source share
1 answer

For those who are interested, I solved this by writing a compression plugin that intercepts the response and handles the compression outside of the service method, and I believe that this should be done. He also addresses the JSONP issue described above.

In my opinion, compression is an orthogonal aspect of the logic of the service method and moves it outside the service method as a response filter, which allows servicing service calls with strong typing instead of ugly signatures public object MyServiceMethod(DtoType request) { } for allowing arbitrary compressed / uncompressed the answers. I assumed that if the client specifies a valid Accept-Encoding header, then the response will be compressed regardless of what, in my opinion, is an honest call.

At the moment, I have refused the transfer request to ServiceStack, because I see it as a serious change in the approach to how the infrastructure handles compression and will require significant preliminary discussion with the owners. This code is for demo purposes only, but I use it and it works very well.

the code:

 public class CompressionFeature : IPlugin { public void Register(IAppHost appHost) { appHost.ResponseFilters.Add((request, response, dto) => { if (dto == null || dto is AuthResponse || dto is CompressedResult || dto is Exception) return; using (var serializationContext = new HttpRequestContext(request, response, dto)) { if (!serializationContext.RequestAttributes.AcceptsDeflate && !serializationContext.RequestAttributes.AcceptsGzip) return; var serializedDto = EndpointHost.ContentTypeFilter.SerializeToString(serializationContext, dto); var callback = request.GetJsonpCallback(); var isJsonpRequest = EndpointHost.Config.AllowJsonpRequests && !string.IsNullOrEmpty(callback); if (isJsonpRequest) { serializedDto = (callback + "(") + serializedDto + ")"; serializationContext.ResponseContentType = ContentType.JavaScript; } var compressedBytes = serializedDto.Compress(serializationContext.CompressionType); var compressedResult = new CompressedResult(compressedBytes, serializationContext.CompressionType, serializationContext.ResponseContentType); response.WriteToResponse(compressedResult, serializationContext.ResponseContentType); } }); } } 

Register the plugin in your AppHost:

 appHost.Plugins.Add(new CompressionFeature()); 
+9
source share

All Articles