9

I'm trying to debug a specific issue with my ASP.NET application. The client runs the following code:

void uploadFile( string serverUrl, string filePath )
{
    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.
        Create( serverUrl );
    CredentialCache cache = new CredentialCache();
    cache.Add( new Uri( serverUrl ), "Basic", new NetworkCredential( "User", "pass" ) );
    request.Credentials = cache;
    request.Method = "POST";
    request.ContentType = "application/octet-stream";
    request.Timeout = 60000;
    request.KeepAlive = true;

    using( BinaryReader reader = new BinaryReader( 
        File.OpenRead( filePath ) ) ) {

        request.ContentLength = reader.BaseStream.Length;
        using( Stream stream = request.GetRequestStream() ) {
            byte[] buffer = new byte[1024];
            while( true ) {
                int bytesRead = reader.Read( buffer, 0, buffer.Length );
                if( bytesRead == 0 ) {
                    break;
                }
                stream.Write( buffer, 0, bytesRead );
            }
        }
    }

     HttpWebResponse result = (HttpWebResponse)request.GetResponse();
     //handle result - not relevant
 }

and Write() throws an exception with "Unable to write data to the transport connection: An established connection was aborted by the software in your host machine." text. I used System.Net tracing and found that something goes wrong when I send the request with Content-Length set.

Specifically if I omit everything that is inside using statement in the code above the server promptly replies with WWW-Authenticate and then the client reposts the request with WWW-Authenticate and everything goes fine except the file in not uploaded and the request fails much later.

I'd like to do the following: send an request without data, wait for WWW-Authenticate, then repeat it with WWW-Authenticate and data. So I tried to modify the code above: first set all the parameters, then call GetResponse(), then do sending, but when I try to set ContentLength property an exception is thrown with "This property cannot be set after writing has started" text.

So HttpWebRequest seems to be non-reusable.

How do I reuse it for resending the request without closing the connection?

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • I have also the same problem. If you got the solution then please provide or display the modified new code , that will help me. – Herin Jun 24 '13 at 13:13
  • @Herin: No solution if you use authentication with nonce. If you use something like basic authentication you can just set "Authorization" headers so that there's no `WWW-Authenticate` phase. – sharptooth Jun 24 '13 at 13:23
  • But i ma getting error at line 'stream.Write( buffer, 0, bytesRead )' then what is the solution for that. Can you please provide your modified code , so that i can understand better. Thanks – Herin Jun 24 '13 at 13:30
  • @Herin: That was so long ago, I barely remember the details. I guess your best bet is to ask a separate question with all the details of your situation. – sharptooth Jun 24 '13 at 13:55

2 Answers2

16

You don't reuse a request - as the name suggests, it's one request. However, if you issue multiple requests for the same host, .NET will reuse the underlying network connection by default.

Note that you do need to dispose of the WebResponse returned by request.GetResponse - otherwise the underlying infrastructure won't know that you're actually done with it, and won't be able to reuse the connection.

(As an aside, why are you using BinaryReader? Just use the stream returned by File.OpenRead directly.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I can vouch for the WebResponse disposal. I didn't dispose of the WebResponse in one of my applications and it crated a nasty memory leak because I was instantiating several HttpWebRequests. – Jeff LaFay Feb 08 '11 at 13:28
  • Fair, but what other mechanism could I use to solve my problem - specifically that I need to get authenticated and then reuse the same `WWW-Authenticate`? – sharptooth Feb 08 '11 at 13:34
  • @sharptooth: It's not clear why you can't just grab the header from the response and use it in the next request. Why do you think you have to reuse the same object? – Jon Skeet Feb 08 '11 at 13:48
  • Well, I suppose that the server will be paranoid if it uses something like Digest-MD5 with nonce - the nonce will only be valid for the very same connection so that no third party can listen to it and impersonate himself as the real client. This is why I assume that I need to keep the TCP connection open. – sharptooth Feb 08 '11 at 13:58
  • @sharptooth: I wouldn't expect that to be the case. HTTP isn't designed for that - it's designed as a request/response protocol, and reusing the same connection should be an implementation detail. (As I say, it probably *will* use the same connection anyway, but you shouldn't really be thinking about that.) – Jon Skeet Feb 08 '11 at 14:00
2

In addition to the Jon Skeet's answer, you don't need to manually set the ContentLength property. HttpWebRequest will auto calculate and populate that property.

Genady Sergeev
  • 1,650
  • 9
  • 11