Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling 100-continue when redirecting a POST request from a Web API controller

I have an ApiController which responds to a POST request by redirecting it via HTTP's status code 307. It does so using only information from the header, so the body of the request is not needed by this action. This action is equivalent to:

public HttpResponseMessage Post() {
    var url;
    // Some logic to construct the URL
    var response = new HttpResponseMessage(HttpStatusCode.TemporaryRedirect);
    response.Headers.Location = new System.Uri(url);
    return response;
}

This is straightforward enough, but there is one improvement I would like to make. The request body could potentially contain a large amount of data, so I would like to leverage the HTTP status code 100 to make this request more efficient. With the controller as it is now, a conversation might look like this:

> POST /api/test HTTP/1.1
> Expect: 100-continue
> ...

< HTTP/1.1 100 Continue

> (request body is sent)

< HTTP/1.1 307 Temporary Redirect
< Location: (the URL)
< ...

Since the request body is not needed by the redirection action, I would like to be able to shorten the conversation to:

> POST /api/controller HTTP/1.1
> Expect: 100-continue
> ...

< HTTP/1.1 307 Temporary Redirect
< Location: (the URL)
< ...

I have spent the better part of a day researching how to accomplish this, and I have not been able to come up with a solution. In my research, I have learned:

  • When the ApiController's action executes, the 100 Continue has already been sent.
  • When the ApiController is constructed, the 100 Continue has already been sent.
  • When the HttpApplication's PreRequestHandlerExecute event is triggered, the 100 Continue response has not been sent.
  • When a DelegatingHandler executes, the 100 Continue has already been sent.

Based on this, the best solution I have come up with so far is to create an HttpModule which uses the RouteData on the RequestContext to override the response when the ApiController in question is the recipient of the request. This is far from an ideal solution, however, for several reasons (code separation, not taking advantage of Web API's parameter binding, and bypassing additional logic in an AuthorizeAttribute on the ApiController).

It seems as if there must be a better solution to this, but I have found very little information on how to properly handle the Expect: 100-continue header in a Web API application. What would be the simplest way to implement this ApiController to properly handle the Expect: 100-continue header?

like image 695
Jordan Higgins Avatar asked Nov 11 '22 00:11

Jordan Higgins


1 Answers

...Are you sure that you need this optimization?

If you're using IIS 6, you're looking at kicking down into IIS 5 Compatibility Mode and writing a ReadRawData/SendRawData ISAPI Filter. An ISAPI Extension is out of the question for reasons given further down in my response. (If you're using IIS 5 or below, may God have mercy on your soul)

If you're using IIS 7+, you might be able to get away with writing an IIS Native Module.

You are correct in your thinking that by the time the controller becomes involved, the response has already been sent because Web API lives inside ASP.NET; this response is being handled by the IIS Core.

Some light reading material

HTTP.SYS IIS and the 100 Continue

David Wang

A "100 continue", like a "400 Bad Request" or a Kernel Response Cache Hit, is special in that HTTP.SYS transparently handles it in kernel mode without notifying user mode of anything.In addition, ISAPI Extensions cannot interact with any response output - they can only generate response output, not see results of response output. Thus, an ISAPI Extension will never be able to interact with requests that generate "100 continue" nor "100 continue" responses themselves to suppress them.

On IIS6, the only way to inject user mode processing into these transparent request handlings of HTTP.SYS is to run in IIS5 Compatibility Mode and use an ReadRawData/SendRawData ISAPI Filter. ReadRawData forces HTTP.SYS to hand the raw data off the network into user mode for filtering PRIOR to parsing that user mode output into HTTP requests to place into queues.

Of course, this method completely defeats the purpose of running IIS6 with Application Pools and process isolation (a single failure in this filtering user mode process halts the entire server)... but such is the server-side compromise when the client is buggy...

FYI: This approach will not work on Vista Server/IIS7. HTTP.SYS will no longer hand raw data off the network into user mode for filtering prior to parsing, so it will be impossible for user mode code to know that a request which triggers the automatic "100 continue" happened.


Edit

Haacked Http Web Request Expect 100 Continue

like image 163
K. Alan Bates Avatar answered Nov 14 '22 21:11

K. Alan Bates