Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions RestSharp.IntegrationTests/NonProtocolExceptionHandlingTests.cs
Original file line number Diff line number Diff line change
@@ -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
{

/// <summary>
/// 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.
/// </summary>
[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);
}

/// <summary>
/// Tests that RestSharp properly handles a non-protocol error.
/// Simulates a server timeout, then verifies that the ErrorException
/// property is correctly populated.
/// </summary>
[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();
}
}

/// <summary>
/// Tests that RestSharp properly handles a non-protocol error.
/// Simulates a server timeout, then verifies that the ErrorException
/// property is correctly populated.
/// </summary>
[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<Response>(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");

}
}


/// <summary>
/// Simulates a long server process that should result in a client timeout
/// </summary>
/// <param name="context"></param>
public static void TimeoutHandler(HttpListenerContext context)
{
System.Threading.Thread.Sleep(101000);
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<ItemGroup>
<Compile Include="AsyncTests.cs" />
<Compile Include="AuthenticationTests.cs" />
<Compile Include="NonProtocolExceptionHandlingTests.cs" />
<Compile Include="Helpers\Extensions.cs" />
<Compile Include="FileTests.cs" />
<Compile Include="Helpers\Handlers.cs" />
Expand Down
84 changes: 50 additions & 34 deletions RestSharp.IntegrationTests/StatusCodeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ResponseHandler>()))
{
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());
}

/// <summary>
/// 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.
/// </summary>
[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<ResponseHandler>()))
{
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<ResponseHandler>()))
{
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<Response>(request);
var response = client.Execute<Response>(request);

Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.Null(response.Data);
}
}
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.Equal("Not found!", response.Data.Message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation is weird.

}
}

[Fact]
public void Handles_Default_Root_Element_On_No_Error()
Expand Down Expand Up @@ -102,6 +103,21 @@ void error(HttpListenerContext context)
</Error>
</Response>");
}

void errorwithbody(HttpListenerContext context)
{
context.Response.StatusCode = 400;
context.Response.Headers.Add("Content-Type", "application/xml");
context.Response.OutputStream.WriteStringUtf8(
@"<?xml version=""1.0"" encoding=""utf-8"" ?>
<Response>
<Error>
<Message>Not found!</Message>
</Error>
</Response>");
}


void success(HttpListenerContext context)
{
context.Response.OutputStream.WriteStringUtf8(
Expand Down
2 changes: 1 addition & 1 deletion RestSharp.Tests/NuSpecUpdateTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
47 changes: 27 additions & 20 deletions RestSharp/Http.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,26 +276,33 @@ private static void GetRawResponseAsync(IAsyncResult result, Action<HttpWebRespo

HttpWebResponse raw = null;

try
{
var webRequest = (HttpWebRequest)result.AsyncState;
raw = webRequest.EndGetResponse(result) as HttpWebResponse;
}
catch(WebException ex)
{
if(ex.Status == WebExceptionStatus.RequestCanceled)
{
throw ex;
}
if (ex.Response is HttpWebResponse)
{
raw = ex.Response as HttpWebResponse;
}
else
{
throw ex;
}
}
try
{
var webRequest = (HttpWebRequest)result.AsyncState;
raw = webRequest.EndGetResponse(result) as HttpWebResponse;
}
catch(WebException ex)
{
if(ex.Status == WebExceptionStatus.RequestCanceled)
{
throw 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)
{
raw = ex.Response as HttpWebResponse;
}
else
{
throw ex;
}
}

callback(raw);
raw.Close();
Expand Down
30 changes: 18 additions & 12 deletions RestSharp/Http.Sync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion RestSharp/IRestResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ public interface IRestResponse
string ErrorMessage { get; set; }

/// <summary>
/// The exception thrown during the request, if any
/// Exceptions thrown during the request, if any.
/// </summary>
/// <remarks>Will contain only network transport or framework exceptions thrown during the request. HTTP protocol errors are handled by RestSharp and will not appear here.</remarks>
Exception ErrorException { get; set; }
}

Expand Down
51 changes: 26 additions & 25 deletions RestSharp/RestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -469,31 +469,32 @@ private IRestResponse<T> Deserialize<T>(IRestRequest request, IRestResponse raw)
{
request.OnBeforeDeserialization(raw);

IRestResponse<T> response = new RestResponse<T>();
try
{
response = raw.toAsyncResponse<T>();
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<T>(raw);
}
}
catch (Exception ex)
{
response.ResponseStatus = ResponseStatus.Error;
response.ErrorMessage = ex.Message;
response.ErrorException = ex;
}
IRestResponse<T> response = new RestResponse<T>();
try
{
response = raw.toAsyncResponse<T>();
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<T>(raw);
}
}
catch (Exception ex)
{
response.ResponseStatus = ResponseStatus.Error;
response.ErrorMessage = ex.Message;
response.ErrorException = ex;
}

return response;
}
Expand Down