Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpRequestException -- Is this a client or server issue?

Tags:

Awhile ago I implemented some code to consume a REST Api using the HttpClient class.

using (var client = new HttpClient() { BaseAddress = new Uri(@"https://thirdparty.com") }) {     client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(...);      var uri = new Uri(@"rest/api/foo", UriKind.Relative);     var content = new StringContent(json.ToString());      using (var response = await client.PostAsync(uri, content))     {         // etc ...     } } 

This code seemed to work perfectly fine against both the test and production environments (each of which access a test/production uri). Recently, we started to get an HttpRequestException in the production environment only: System.Net.Http.HttpRequestException: Error while copying content to a stream.

This seemed a bit strange, so I used Postman to send the same message and it worked just fine. I wasn't sure why our code was failing and Postman was working. I changed a parameter in the json data (the state from "NY" to "NV") and our .NET code worked fine -- of course we can't just send the wrong json data, so this isn't a solution; this was more of an observation that the exact same code worked fine with different content.


What's interesting is that there are two code changes we could make that will resolve this. Firstly, Postman is able to generate working C# code using the RestSharp package. Alternatively, I found an answer from another question pointing to using HttpVersion 1.0:

using (var request = new HttpRequestMessage(HttpMethod.Post, uri)) {     request.Version = HttpVersion.Version10;     request.Content = new StringContent(json.ToString());      using (var response = await client.SendAsync(request))     {         // etc ...     } } 

The confusing part is that Postman uses the HTTP/1.1 version. So, in summary:

  • If we change the json data (US State from "NY" to "NV"), the code works.
  • The same exact json and code works against the test uri.
  • Changing the code to use the RestSharp package works.
  • Changing the code to use HTTP/1.0 instead of HTTP/1.1 works.

Why on earth is Postman able to work using HTTP/1.1 but HttpClient fails? Is this a problem with our client (the code works for other US States)? Is this a bug in the .NET Framework? Is there something wrong with the implementation/hosting of the REST Api from the third party?


Postman headers:

POST rest/api/foo HTTP/1.1 Host: thirdparty.com Content-Type: application/json Authorization: Basic SOME_ENCRYPTED_USER_PASS Cache-Control: no-cache Postman-Token: 2fa5b5a0-b5d3-bd4c-40f0-d2b55b60316b 

Sample Json:

{     "stateCode": "NY",     "packageID": "58330",     "name": "58330-PRI-1",     "documents": [         {             "type": "SPECIAL",             "name": "Sample Document",             "documentID": "3569976"         }     ],     "descriptions": [         {             "city": "New York",             "state": "NY"         }     ] } 

Stacktrace:

AggregateException: One or more errors occured. HttpRequestException: Error while copying content to a stream. IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. SocketException: An existing connection was forcibly closed by the remote host. 
like image 262
myermian Avatar asked Jul 27 '16 14:07

myermian


1 Answers

Due to the fact that you can change the data slightly and have it succeed, I would say your issues has nothing to do with any of your code. What if their server has cached a bad value and will continue sending you that value until their cache clears?

Try implicitly telling the server not to use cached values...

using (var client = new HttpClient() { BaseAddress = new Uri(@"https://thirdparty.com") }) {     client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(...);      var uri = new Uri(@"rest/api/foo", UriKind.Relative);     var content = new StringContent(json.ToString());      client.DefaultRequestHeaders.CacheControl = CacheControlHeaderValue.Parse("no-cache");      using (var response = await client.PostAsync(uri, content))     {         // etc ...     } } 

and see if you get a valid response stream.

like image 134
Larry Dukek Avatar answered Oct 13 '22 22:10

Larry Dukek