diff --git a/RestSharp.IntegrationTests/NonProtocolExceptionHandlingTests.cs b/RestSharp.IntegrationTests/NonProtocolExceptionHandlingTests.cs
new file mode 100644
index 000000000..6a1410a8f
--- /dev/null
+++ b/RestSharp.IntegrationTests/NonProtocolExceptionHandlingTests.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Xunit;
+using RestSharp.IntegrationTests.Helpers;
+using System.Net;
+using System.Threading;
+
+namespace RestSharp.IntegrationTests
+{
+ public class NonProtocolExceptionHandlingTests
+ {
+
+ ///
+ /// Success of this test is based largely on the behavior of your current DNS.
+ /// For example, if you're using OpenDNS this will test will fail; ResponseStatus will be Completed.
+ ///
+ [Fact]
+ public void Handles_Non_Existent_Domain()
+ {
+ var client = new RestClient("http://nonexistantdomainimguessing.org");
+ var request = new RestRequest("foo");
+ var response = client.Execute(request);
+
+ Assert.Equal(ResponseStatus.Error, response.ResponseStatus);
+ }
+
+ ///
+ /// Tests that RestSharp properly handles a non-protocol error.
+ /// Simulates a server timeout, then verifies that the ErrorException
+ /// property is correctly populated.
+ ///
+ [Fact]
+ public void Handles_Server_Timeout_Error()
+ {
+ const string baseUrl = "http://localhost:8080/";
+ using (SimpleServer.Create(baseUrl, TimeoutHandler))
+ {
+ var client = new RestClient(baseUrl);
+ var request = new RestRequest("404");
+ var response = client.Execute(request);
+
+ Assert.NotNull(response.ErrorException);
+ Assert.IsAssignableFrom(typeof(WebException), response.ErrorException);
+ Assert.Equal(response.ErrorException.Message, "The operation has timed out");
+
+ }
+ }
+
+ [Fact]
+ public void Handles_Server_Timeout_Error_Async()
+ {
+ const string baseUrl = "http://localhost:8080/";
+ var resetEvent = new ManualResetEvent(false);
+
+ using (SimpleServer.Create(baseUrl, TimeoutHandler))
+ {
+ var client = new RestClient(baseUrl);
+ var request = new RestRequest("404");
+ client.ExecuteAsync(request, response => {
+
+ Assert.NotNull(response.ErrorException);
+ Assert.IsAssignableFrom(typeof(WebException), response.ErrorException);
+ Assert.Equal(response.ErrorException.Message, "The operation has timed out");
+ resetEvent.Set();
+ });
+ resetEvent.WaitOne();
+ }
+ }
+
+ ///
+ /// Tests that RestSharp properly handles a non-protocol error.
+ /// Simulates a server timeout, then verifies that the ErrorException
+ /// property is correctly populated.
+ ///
+ [Fact]
+ public void Handles_Server_Timeout_Error_With_Deserializer()
+ {
+ const string baseUrl = "http://localhost:8080/";
+ using (SimpleServer.Create(baseUrl, TimeoutHandler))
+ {
+ var client = new RestClient(baseUrl);
+ var request = new RestRequest("404");
+ var response = client.Execute(request);
+
+ Assert.Null(response.Data);
+ Assert.NotNull(response.ErrorException);
+ Assert.IsAssignableFrom(typeof(WebException), response.ErrorException);
+ Assert.Equal(response.ErrorException.Message, "The operation has timed out");
+
+ }
+ }
+
+
+ ///
+ /// Simulates a long server process that should result in a client timeout
+ ///
+ ///
+ public static void TimeoutHandler(HttpListenerContext context)
+ {
+ System.Threading.Thread.Sleep(101000);
+ }
+
+
+ }
+}
diff --git a/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj b/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj
index 62479f791..3872ba5bb 100644
--- a/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj
+++ b/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj
@@ -64,6 +64,7 @@
+
diff --git a/RestSharp.IntegrationTests/StatusCodeTests.cs b/RestSharp.IntegrationTests/StatusCodeTests.cs
index 33bfe529c..ad7e61762 100644
--- a/RestSharp.IntegrationTests/StatusCodeTests.cs
+++ b/RestSharp.IntegrationTests/StatusCodeTests.cs
@@ -21,47 +21,48 @@ public void Handles_GET_Request_404_Error()
}
}
+ [Fact]
+ public void Handles_GET_Request_404_Error_With_Body()
+ {
+ const string baseUrl = "http://localhost:8080/";
+ using (SimpleServer.Create(baseUrl, Handlers.Generic()))
+ {
+ var client = new RestClient(baseUrl);
+ var request = new RestRequest("404WithBody");
+ var response = client.Execute(request);
+
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+ }
+
void UrlToStatusCodeHandler(HttpListenerContext obj)
{
obj.Response.StatusCode = int.Parse(obj.Request.Url.Segments.Last());
}
- ///
- /// Success of this test is based largely on the behavior of your current DNS.
- /// For example, if you're using OpenDNS this will test will fail; ResponseStatus will be Completed.
- ///
- [Fact]
- public void Handles_Non_Existent_Domain()
- {
- var client = new RestClient("http://nonexistantdomainimguessing.org");
- var request = new RestRequest("foo");
- var response = client.Execute(request);
- Assert.Equal(ResponseStatus.Error, response.ResponseStatus);
- }
+ [Fact]
+ public void Handles_Different_Root_Element_On_Http_Error()
+ {
+ const string baseUrl = "http://localhost:8080/";
+ using(SimpleServer.Create(baseUrl, Handlers.Generic()))
+ {
+ var client = new RestClient(baseUrl);
+ var request = new RestRequest("error");
+ request.RootElement = "Success";
+ request.OnBeforeDeserialization = resp =>
+ {
+ if(resp.StatusCode == HttpStatusCode.BadRequest)
+ {
+ request.RootElement = "Error";
+ }
+ };
- [Fact]
- public void Handles_Different_Root_Element_On_Error()
- {
- const string baseUrl = "http://localhost:8080/";
- using(SimpleServer.Create(baseUrl, Handlers.Generic()))
- {
- var client = new RestClient(baseUrl);
- var request = new RestRequest("error");
- request.RootElement = "Success";
- request.OnBeforeDeserialization = resp =>
- {
- if(resp.StatusCode == HttpStatusCode.BadRequest)
- {
- request.RootElement = "Error";
- }
- };
-
- var response = client.Execute(request);
+ var response = client.Execute(request);
- Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
- Assert.Null(response.Data);
- }
- }
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ Assert.Equal("Not found!", response.Data.Message);
+ }
+ }
[Fact]
public void Handles_Default_Root_Element_On_No_Error()
@@ -102,6 +103,21 @@ void error(HttpListenerContext context)
");
}
+
+ void errorwithbody(HttpListenerContext context)
+ {
+ context.Response.StatusCode = 400;
+ context.Response.Headers.Add("Content-Type", "application/xml");
+ context.Response.OutputStream.WriteStringUtf8(
+@"
+
+
+ Not found!
+
+");
+ }
+
+
void success(HttpListenerContext context)
{
context.Response.OutputStream.WriteStringUtf8(
diff --git a/RestSharp.Tests/NuSpecUpdateTask.cs b/RestSharp.Tests/NuSpecUpdateTask.cs
index fa9db80e3..33b3ba1bd 100644
--- a/RestSharp.Tests/NuSpecUpdateTask.cs
+++ b/RestSharp.Tests/NuSpecUpdateTask.cs
@@ -67,7 +67,7 @@ public class WhenSpecFileIsValid : BaseNuSpecUpdateTest
private string _expectedDescription = "Simple REST and HTTP API Client";
private string _expectedAuthors = "John Sheehan, RestSharp Community";
private string _expectedOwners = "John Sheehan, RestSharp Community";
- private Regex _expectedVersion = new Regex(@"^\d+\.\d+\.\d+(-\w+)$", RegexOptions.Compiled);
+ private Regex _expectedVersion = new Regex(@"^\d+\.\d+\.\d+(-\w+)?$", RegexOptions.Compiled);
protected override void Setup()
{
diff --git a/RestSharp/Http.Async.cs b/RestSharp/Http.Async.cs
index 62e90df58..69f4a6a33 100644
--- a/RestSharp/Http.Async.cs
+++ b/RestSharp/Http.Async.cs
@@ -276,26 +276,33 @@ private static void GetRawResponseAsync(IAsyncResult result, Action= 400 )
+ // return the underlying HTTP response, otherwise assume a
+ // transport exception (ex: connection timeout) and
+ // rethrow the exception
+
+ if (ex.Response is HttpWebResponse)
+ {
+ raw = ex.Response as HttpWebResponse;
+ }
+ else
+ {
+ throw ex;
+ }
+ }
callback(raw);
raw.Close();
diff --git a/RestSharp/Http.Sync.cs b/RestSharp/Http.Sync.cs
index 6ed9c65bc..3adc1e317 100644
--- a/RestSharp/Http.Sync.cs
+++ b/RestSharp/Http.Sync.cs
@@ -163,18 +163,24 @@ private HttpResponse GetResponse(HttpWebRequest request)
private static HttpWebResponse GetRawResponse(HttpWebRequest request)
{
- try
- {
- return (HttpWebResponse)request.GetResponse();
- }
- catch (WebException ex)
- {
- if (ex.Response is HttpWebResponse)
- {
- return ex.Response as HttpWebResponse;
- }
- throw;
- }
+ try
+ {
+ return (HttpWebResponse)request.GetResponse();
+ }
+ catch (WebException ex)
+ {
+ // Check to see if this is an HTTP error or a transport error.
+ // In cases where an HTTP error occurs ( status code >= 400 )
+ // return the underlying HTTP response, otherwise assume a
+ // transport exception (ex: connection timeout) and
+ // rethrow the exception
+
+ if (ex.Response is HttpWebResponse)
+ {
+ return ex.Response as HttpWebResponse;
+ }
+ throw;
+ }
}
private void PreparePostData(HttpWebRequest webRequest)
diff --git a/RestSharp/IRestResponse.cs b/RestSharp/IRestResponse.cs
index 2f452d545..8573401c5 100644
--- a/RestSharp/IRestResponse.cs
+++ b/RestSharp/IRestResponse.cs
@@ -84,8 +84,9 @@ public interface IRestResponse
string ErrorMessage { get; set; }
///
- /// The exception thrown during the request, if any
+ /// Exceptions thrown during the request, if any.
///
+ /// Will contain only network transport or framework exceptions thrown during the request. HTTP protocol errors are handled by RestSharp and will not appear here.
Exception ErrorException { get; set; }
}
diff --git a/RestSharp/RestClient.cs b/RestSharp/RestClient.cs
index 6ba871526..5e00ebb10 100644
--- a/RestSharp/RestClient.cs
+++ b/RestSharp/RestClient.cs
@@ -469,31 +469,32 @@ private IRestResponse Deserialize(IRestRequest request, IRestResponse raw)
{
request.OnBeforeDeserialization(raw);
- IRestResponse response = new RestResponse();
- try
- {
- response = raw.toAsyncResponse();
- response.Request = request;
-
- // Only attempt to deserialize if the request has a chance of containing a valid entry
- if (response.StatusCode == HttpStatusCode.OK
- || response.StatusCode == HttpStatusCode.Created
- || response.StatusCode == HttpStatusCode.NonAuthoritativeInformation)
- {
- IDeserializer handler = GetHandler(raw.ContentType);
- handler.RootElement = request.RootElement;
- handler.DateFormat = request.DateFormat;
- handler.Namespace = request.XmlNamespace;
-
- response.Data = handler.Deserialize(raw);
- }
- }
- catch (Exception ex)
- {
- response.ResponseStatus = ResponseStatus.Error;
- response.ErrorMessage = ex.Message;
- response.ErrorException = ex;
- }
+ IRestResponse response = new RestResponse();
+ try
+ {
+ response = raw.toAsyncResponse();
+ response.Request = request;
+
+ // Only attempt to deserialize if the request has not errored due
+ // to a transport or framework exception. HTTP errors should attempt to
+ // be deserialized
+
+ if (response.ErrorException==null)
+ {
+ IDeserializer handler = GetHandler(raw.ContentType);
+ handler.RootElement = request.RootElement;
+ handler.DateFormat = request.DateFormat;
+ handler.Namespace = request.XmlNamespace;
+
+ response.Data = handler.Deserialize(raw);
+ }
+ }
+ catch (Exception ex)
+ {
+ response.ResponseStatus = ResponseStatus.Error;
+ response.ErrorMessage = ex.Message;
+ response.ErrorException = ex;
+ }
return response;
}