How can one access raw content of the request stream?

3,506 views
Skip to first unread message

Lucas Nodine

unread,
Feb 10, 2012, 11:59:23 PM2/10/12
to servic...@googlegroups.com
I would very much like to have access to the underlying raw inputstream.  I have found a couple posts that seem to deal with the topic: (1) https://groups.google.com/d/topic/servicestack/dSy2A2nQjTI/discussion and (2) https://groups.google.com/d/topic/servicestack/7rOguYt2Sr4/discussion.  However, the first seems rather complex and the second one just fades out.

Does anyone know of a simple way to access that elusive underlying InputStream?  Consider a binary stream representing a movie or something very large coming in through a rest interface.  How would that fit into the DTO?

Thanks!

Demis Bellot

unread,
Feb 11, 2012, 12:05:23 AM2/11/12
to servic...@googlegroups.com
You can get the underlying request stream with:

base.RequestContrext.Get<IHttpRequest>().InputStream

And the original ASP.NET Request object with 

base.Request.Get<IHttpRequest>().OriginalRequest 

Someone was going to improve this somewhat by allowing you to mark your Request DTOs with a ISkipSerialization in which it would not attempt to deserialize the request and instead inject the IHttpRequest into the Request DTO.

Cheers,
--
- Demis


Lucas Nodine

unread,
Feb 11, 2012, 12:15:04 AM2/11/12
to servic...@googlegroups.com
Very awesome, works wonderfully.  Very nice products here Demis.  Thanks for the dev support too ;)

Lucas Nodine

unread,
Feb 11, 2012, 10:13:17 PM2/11/12
to servic...@googlegroups.com
Any reason this would not work for a Put?  I'm seeing a positive value for the content-length (header) but the input stream is of length 0.

Demis Bellot

unread,
Feb 11, 2012, 10:41:28 PM2/11/12
to servic...@googlegroups.com
Is this in a browser (i.e via Ajax)? As it is disabled in some browsers.

If this is the case you can emulate a "Soft PUT" using the X-Http-Method-Override HTTP Header with a normal Ajax POST.

Another way to enable Cross-Origin Resource Sharing (CORS) which will allow browsers to send PUT requests is for website to signal it's ok via CORS headers.

Here's an example of sending Global HTTP Headers on every request by adding this to your WebHost:

public override void Configure(Container container)
{
    //Permit modern browsers (e.g. Firefox) to allow sending of any REST HTTP Method
    base.SetConfig(new EndpointHostConfig
    {
        GlobalResponseHeaders = {
            { "Access-Control-Allow-Origin", "*" },
            { "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" },
            { "Access-Control-Allow-Headers", "Content-Type" },
        },
    });
}

Alternatively you can add it on a service-by-services basis:

public class HelloService : IService<Hello> {
    public object Execute(Hello request)
    {
        var dto = new HelloResponse { Result = "Hello, " + request.Name };
        return new HttpResult(dto) {
            Headers = {
              { "Access-Control-Allow-Origin", "*" },
              { "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" },
              { "Access-Control-Allow-Headers", "Content-Type" }, }
        };
    }
}


Cheers,


On Sat, Feb 11, 2012 at 10:13 PM, Lucas Nodine <lucas...@gmail.com> wrote:
Any reason this would not work for a Put?  I'm seeing a positive value for the content-length (header) but the input stream is of length 0.



Lucas Nodine

unread,
Feb 11, 2012, 11:09:16 PM2/11/12
to servic...@googlegroups.com
It is a PUT coming from Firefox.

Let me break down the situation.  Firefox does a put

PUT http://localhost:49927/Services/Security/SecurityArea/3

Request Headers
Acceptapplication/json,application/javascript
Accept-Encodinggzip, deflate
Accept-Languageen-us,en;q=0.5
Connectionkeep-alive
Content-Length255
Content-Typeapplication/json; charset=UTF-8
CookieASP.NET_SessionId=f3hek53gi4awnx54ypaz434g; AuthToken=02339850-2843-4fb1-82b4-9b888596fad8
Hostlocalhost:49927
Refererhttp://localhost:49927/PartTest.aspx
Transactioncommit
User-AgentMozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0) Gecko/20100101 Firefox/10.0
X-Requested-WithXMLHttpRequest

Sending the following in the Request body:

{"UtcCreated":"/Date(1327877500038-0600)/","UtcModified":"/Date(1327877500038-0600)/","UtcDisabled":null,"CreatedBy":null,"ModifiedBy":null,"DisabledBy":null,"Id":3,"Name":"Test Security Area1","Description":null,"IsBaseLevel":false,"Parent":{"$ref":"4"}}

Now, that request fires the onPut method of my service.  However, RequestContext.Get<IHttpRequest>().InputStream's length is 0.

This came about as I'm trying to access the #4 value of $ref, but was having issues figuring out how to make my DTO to grab it.  But, now this has become a mission.

I just confirmed this same problem using Curl (saw the same behavior):

c:\>curl -H "Content-Type: application/json" -vX PUT http://localhost:49927/Serv
ices/Security/SecurityArea/3 -d "{\"UtcCreated\":\"/Date(1327877500038-0600)/\",
\"UtcModified\":\"/Date(1327877500038-0600)/\",\"UtcDisabled\":null,\"CreatedBy\
":null,\"ModifiedBy\":null,\"DisabledBy\":null,\"Id\":3,\"Name\":\"Test Security
 Area1\",\"Description\":null,\"IsBaseLevel\":false,\"Parent\":{\"$ref\":\"4\"}}
"
* About to connect() to localhost port 49927 (#0)
* connected
* Connected to localhost () port 49927 (#0)
> PUT /Services/Security/SecurityArea/3 HTTP/1.1
> User-Agent: curl/7.21.0 (amd64-pc-win32) libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.
2.3
> Host: localhost:49927
> Accept: */*
> Content-Type: application/json
> Content-Length: 255
>
< HTTP/1.1 200 OK
< Server: ASP.NET Development Server/10.0.0.0
< Date: Sun, 12 Feb 2012 04:03:47 GMT
< X-AspNet-Version: 4.0.30319
< Cache-Control: private
< Content-Length: 0
< Connection: Close
<
* Closing connection #0

Demis Bellot

unread,
Feb 11, 2012, 11:31:07 PM2/11/12
to servic...@googlegroups.com
PUT requests aren't treated any differently than POST's in ServiceStack so different behaviour is likely due to the underlying ASP.NET Host.

Here's potentially the same issue with ASP.NET PUT on StackOverflow:
Can you try resetting the InputStream.Position to see if it works?

Request.InputStream.Position = 0;


Lucas Nodine

unread,
Feb 11, 2012, 11:34:02 PM2/11/12
to servic...@googlegroups.com
Already tried that.  The length property of the InputStream is 0.

Lucas Nodine

unread,
Feb 12, 2012, 12:40:47 AM2/12/12
to servic...@googlegroups.com
Looking at

ServiceStack.WebHost.Endpoints.Support.EndpointHandlerBase.ProcessRequest(HttpContext context) - context.Request.InputStream.Length = 255

ServiceStack.WebHost.Endpoints.RestHandler.ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, string operationName) - httpReq.InputStream.Length = 255

ServiceStack.WebHost.Endpoints.RestHandler.GetRequest(IHttpRequest httpReq, IRestPath restPath) - httpReq.InputStream.Length = 255

Now, line 105 of RestHandler.cs sends the httpReq.InputStream to the deserializer and it comes back with a length of 0.



Lucas Nodine

unread,
Feb 12, 2012, 1:13:29 AM2/12/12
to servic...@googlegroups.com
Found out why.

In ServiceStack.Text.JsonSerializer.DeserializeFromStream(Type type, Stream stream)

The code reads...

using (var reader = new StreamReader(stream, UTF8EncodingWithoutBom))
{
  return DeserializeFromString(reader.ReadToEnd(), type);
}

I believe that when the code execution leaves the using, .net calls dispose on the StreamReader, thereby closing the underlying stream.

Demis Bellot

unread,
Feb 12, 2012, 1:15:24 AM2/12/12
to servic...@googlegroups.com
It's also possible you can't re-read from the InputStream twice.

I think the ISkipSerialization marker on the DTOs would be the best solution - as it would tell servicestack not to read from the RequestStream and just inject the IHttpRequest / Or RequestStream.

Unfortunately I'm still working on some failing tests as the result of yesterdays pull requests that were merged in.

Once I see all green again, I'll try add that feature myself - and then cut a new build.

Will keep this thread updated.

Lucas Nodine

unread,
Feb 12, 2012, 1:57:01 AM2/12/12
to servic...@googlegroups.com
You think?  Get does not close the stream, seems counter-intuitive that put would.

Perhaps consider inspecting for JSON, reading to end of JSON then just stopping.  This would accommodate situations where a JSON header leads a binary data object or similar situations.

Then if the dev wanted, they could reposition the stream and reaccess the JSON or whatever.

Just a thought, it will work for me however you decide to handle it.

Demis Bellot

unread,
Feb 12, 2012, 2:20:43 AM2/12/12
to servic...@googlegroups.com
GET's doesn't have a request body, hence there's no Request InputStream to close.

I prefer not to have dangling open streams, and am definitely not a fan of "is JSON feature detection" / magic behaviour.

The other alternative for the time being is to use a raw IHttpHandler for certain requests. You can register them with something like:

SetEndpoingHostConfig( new EndpoingHostConfig {
    RawHttpHandlers = { httpReq => return httpReq.PathInfo == "..." ? new MyCustomHttpHandler() : null }
});

Basically it allows you to intercept all ServiceStack requests and by-pass ServiceStack by returning your own IHttpHandler for the matching request.



Demis Bellot

unread,
Feb 12, 2012, 5:19:53 AM2/12/12
to servic...@googlegroups.com
I've just checked in an new interface IRequiresRequestStream that you can have your DTOs implement which will skip trying to deserialize the Request DTO for that request and instead inject the Request InputStream.

It includes tests for both ASP.NET / HttpListener hosts showing POST + PUT requests:

https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Tests/RawRequestTests.cs


Cheers,

Lucas Nodine

unread,
Feb 12, 2012, 8:56:45 AM2/12/12
to servic...@googlegroups.com
Appears to work wonderfully from my quick checks.  Thanks a lot for doing that so fast too!
Reply all
Reply to author
Forward
0 new messages