6

I'm implementing a proxy action method that forwards the incoming web request and forwards it to another web page, adding a few headers. The action method works file for GET requests, but I'm still struggling with forwarding the incoming POST request.

The problem is that I don't know how to properly write the request body to the outgoing HTTP request stream.

Here's a shortened version of what I've got so far:

//the incoming request stream
var requestStream=HttpContext.Current.Request.InputStream;
//the outgoing web request
var webRequest = (HttpWebRequest)WebRequest.Create(url);
...

//copy incoming request body to outgoing request
if (requestStream != null && requestStream.Length>0)
            {
                long length = requestStream.Length;
                webRequest.ContentLength = length;
                requestStream.CopyTo(webRequest.GetRequestStream())                    
            }

//THE NEXT LINE THROWS A ProtocolViolationException
 using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
                {
                    ...
                }

As soon as I call GetResponse on the outgoing http request, I get the following exception:

ProtocolViolationException: You must write ContentLength bytes to the request stream before calling [Begin]GetResponse.

I don't understand why this is happening, since requestStream.CopyTo should have taken care of writing the right amount of bytes.

Any suggestions would be greatly appreciated.

Thanks,

Adrian

Adrian Grigore
  • 31,759
  • 32
  • 127
  • 205
  • 1
    related question - http://stackoverflow.com/questions/226784/how-to-create-a-simple-proxy-in-c – James Manning Aug 10 '10 at 09:55
  • @James Manning: Thanks for the link, but I am way past that. My proxy works fine for all kinds of GET requests. It's just the POST request body that's still giving me problems. – Adrian Grigore Aug 10 '10 at 10:19
  • Have you tried calling Stream.Flush() on the stream returned by webRequest.GetRequestStream() before proceeding to call webRequest.GetResponse()? – Mattias S Aug 10 '10 at 11:41
  • @Mattias S: I just did, but it does not seem to make any difference. – Adrian Grigore Aug 10 '10 at 14:48
  • for the purpose of debugging, i'd probably change it to write the stream to an intermediary byte array (memorystream, then toarray), check its contents and length, then have the byte array written. Also, IMHO you should do the webRequest.GetRequestStream() assigned to a local var with a using so you're closing the request stream before writing, so something like using (var rs = webRequest.GetRequestStream()) { requestStream.CopyTo(rs); } (or the byte array if you go that route). I've had lots of bugs go away once I actually dispose of streams when I should (and the flushing/closing happens) – James Manning Aug 11 '10 at 04:29

3 Answers3

13

Yes, .Net is very finicky about this. The way to solve the problem is to both flush and close the stream. In other words:

Stream webStream = null;

try
{
    //copy incoming request body to outgoing request
    if (requestStream != null && requestStream.Length>0)
    {
        long length = requestStream.Length;
        webRequest.ContentLength = length;
        webStream = webRequest.GetRequestStream();
        requestStream.CopyTo(webStream);
    }
}
finally
{
    if (null != webStream)
    {
        webStream.Flush();
        webStream.Close();    // might need additional exception handling here
    }
}

// No more ProtocolViolationException!
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
{
    ...
}
Brian
  • 3,279
  • 4
  • 28
  • 41
  • Great Post Brian. Works like a charm. However, It seems that .CopyTo() is only supported in .NET 4.0+ – Sage Oct 26 '11 at 14:25
  • Thanks. This fixed my problem. One question: Is there a reason you used Flush and Close in a finally block, rather than wrapping webStream in a using block? Are there side-effects with regard to webRequest? – David Jul 06 '12 at 03:40
  • I'm pretty sure flushing doesn't change anything... GetRequestStream returns an instance of ConnectStream, and this class overrides Flush with an empty method, so calling Flush on it has no effect at all. – Thomas Levesque Jan 24 '14 at 11:13
2

The answer @brian works, however, I found that once requestStream.CopyTo(stream) was called, it would fire off my HttpWebResponse. This was an issue since I wasn't quite ready to send the request. So if anyone is having an issue with not all of the request headers or other data being sent, it is because CopyTo is firing your request.

Seth
  • 609
  • 1
  • 5
  • 21
1

try modifying the block inside if statement

long length = requestStream.Length;
webRequest.ContentLength = length;
requestStream.CopyTo(webRequest.GetRequestStream())

with

webRequest.Method = "POST";
webRequest.ContentLength = requestStream.Length;
webRequest.ContentType = "application/x-www-form-urlencoded";
Stream stream = webRequest.GetRequestStream();
requestStream.CopyTo(stream);
stream.Close();
ajay_whiz
  • 16,085
  • 3
  • 33
  • 44
  • I already got that (it's in the ... part). As I said, this is just the shortened version. The other stuff works fine though, so I do not think it is relevant to this problem. – Adrian Grigore Aug 10 '10 at 10:18