Skip to content

Commit 3a174c5

Browse files
author
Devin Rader
committed
Merge pull request restsharp#437 from devinrader/master
All all HTTP responses to be deserialized
2 parents 30cd8ef + 45b9605 commit 3a174c5

File tree

8 files changed

+232
-93
lines changed

8 files changed

+232
-93
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using Xunit;
6+
using RestSharp.IntegrationTests.Helpers;
7+
using System.Net;
8+
using System.Threading;
9+
10+
namespace RestSharp.IntegrationTests
11+
{
12+
public class NonProtocolExceptionHandlingTests
13+
{
14+
15+
/// <summary>
16+
/// Success of this test is based largely on the behavior of your current DNS.
17+
/// For example, if you're using OpenDNS this will test will fail; ResponseStatus will be Completed.
18+
/// </summary>
19+
[Fact]
20+
public void Handles_Non_Existent_Domain()
21+
{
22+
var client = new RestClient("http://nonexistantdomainimguessing.org");
23+
var request = new RestRequest("foo");
24+
var response = client.Execute(request);
25+
26+
Assert.Equal(ResponseStatus.Error, response.ResponseStatus);
27+
}
28+
29+
/// <summary>
30+
/// Tests that RestSharp properly handles a non-protocol error.
31+
/// Simulates a server timeout, then verifies that the ErrorException
32+
/// property is correctly populated.
33+
/// </summary>
34+
[Fact]
35+
public void Handles_Server_Timeout_Error()
36+
{
37+
const string baseUrl = "http://localhost:8080/";
38+
using (SimpleServer.Create(baseUrl, TimeoutHandler))
39+
{
40+
var client = new RestClient(baseUrl);
41+
var request = new RestRequest("404");
42+
var response = client.Execute(request);
43+
44+
Assert.NotNull(response.ErrorException);
45+
Assert.IsAssignableFrom(typeof(WebException), response.ErrorException);
46+
Assert.Equal(response.ErrorException.Message, "The operation has timed out");
47+
48+
}
49+
}
50+
51+
[Fact]
52+
public void Handles_Server_Timeout_Error_Async()
53+
{
54+
const string baseUrl = "http://localhost:8080/";
55+
var resetEvent = new ManualResetEvent(false);
56+
57+
using (SimpleServer.Create(baseUrl, TimeoutHandler))
58+
{
59+
var client = new RestClient(baseUrl);
60+
var request = new RestRequest("404");
61+
client.ExecuteAsync(request, response => {
62+
63+
Assert.NotNull(response.ErrorException);
64+
Assert.IsAssignableFrom(typeof(WebException), response.ErrorException);
65+
Assert.Equal(response.ErrorException.Message, "The operation has timed out");
66+
resetEvent.Set();
67+
});
68+
resetEvent.WaitOne();
69+
}
70+
}
71+
72+
/// <summary>
73+
/// Tests that RestSharp properly handles a non-protocol error.
74+
/// Simulates a server timeout, then verifies that the ErrorException
75+
/// property is correctly populated.
76+
/// </summary>
77+
[Fact]
78+
public void Handles_Server_Timeout_Error_With_Deserializer()
79+
{
80+
const string baseUrl = "http://localhost:8080/";
81+
using (SimpleServer.Create(baseUrl, TimeoutHandler))
82+
{
83+
var client = new RestClient(baseUrl);
84+
var request = new RestRequest("404");
85+
var response = client.Execute<Response>(request);
86+
87+
Assert.Null(response.Data);
88+
Assert.NotNull(response.ErrorException);
89+
Assert.IsAssignableFrom(typeof(WebException), response.ErrorException);
90+
Assert.Equal(response.ErrorException.Message, "The operation has timed out");
91+
92+
}
93+
}
94+
95+
96+
/// <summary>
97+
/// Simulates a long server process that should result in a client timeout
98+
/// </summary>
99+
/// <param name="context"></param>
100+
public static void TimeoutHandler(HttpListenerContext context)
101+
{
102+
System.Threading.Thread.Sleep(101000);
103+
}
104+
105+
106+
}
107+
}

RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
<ItemGroup>
6565
<Compile Include="AsyncTests.cs" />
6666
<Compile Include="AuthenticationTests.cs" />
67+
<Compile Include="NonProtocolExceptionHandlingTests.cs" />
6768
<Compile Include="Helpers\Extensions.cs" />
6869
<Compile Include="FileTests.cs" />
6970
<Compile Include="Helpers\Handlers.cs" />

RestSharp.IntegrationTests/StatusCodeTests.cs

+50-34
Original file line numberDiff line numberDiff line change
@@ -21,47 +21,48 @@ public void Handles_GET_Request_404_Error()
2121
}
2222
}
2323

24+
[Fact]
25+
public void Handles_GET_Request_404_Error_With_Body()
26+
{
27+
const string baseUrl = "http://localhost:8080/";
28+
using (SimpleServer.Create(baseUrl, Handlers.Generic<ResponseHandler>()))
29+
{
30+
var client = new RestClient(baseUrl);
31+
var request = new RestRequest("404WithBody");
32+
var response = client.Execute(request);
33+
34+
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
35+
}
36+
}
37+
2438
void UrlToStatusCodeHandler(HttpListenerContext obj)
2539
{
2640
obj.Response.StatusCode = int.Parse(obj.Request.Url.Segments.Last());
2741
}
2842

29-
/// <summary>
30-
/// Success of this test is based largely on the behavior of your current DNS.
31-
/// For example, if you're using OpenDNS this will test will fail; ResponseStatus will be Completed.
32-
/// </summary>
33-
[Fact]
34-
public void Handles_Non_Existent_Domain()
35-
{
36-
var client = new RestClient("http://nonexistantdomainimguessing.org");
37-
var request = new RestRequest("foo");
38-
var response = client.Execute(request);
39-
Assert.Equal(ResponseStatus.Error, response.ResponseStatus);
40-
}
43+
[Fact]
44+
public void Handles_Different_Root_Element_On_Http_Error()
45+
{
46+
const string baseUrl = "http://localhost:8080/";
47+
using(SimpleServer.Create(baseUrl, Handlers.Generic<ResponseHandler>()))
48+
{
49+
var client = new RestClient(baseUrl);
50+
var request = new RestRequest("error");
51+
request.RootElement = "Success";
52+
request.OnBeforeDeserialization = resp =>
53+
{
54+
if(resp.StatusCode == HttpStatusCode.BadRequest)
55+
{
56+
request.RootElement = "Error";
57+
}
58+
};
4159

42-
[Fact]
43-
public void Handles_Different_Root_Element_On_Error()
44-
{
45-
const string baseUrl = "http://localhost:8080/";
46-
using(SimpleServer.Create(baseUrl, Handlers.Generic<ResponseHandler>()))
47-
{
48-
var client = new RestClient(baseUrl);
49-
var request = new RestRequest("error");
50-
request.RootElement = "Success";
51-
request.OnBeforeDeserialization = resp =>
52-
{
53-
if(resp.StatusCode == HttpStatusCode.BadRequest)
54-
{
55-
request.RootElement = "Error";
56-
}
57-
};
58-
59-
var response = client.Execute<Response>(request);
60+
var response = client.Execute<Response>(request);
6061

61-
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
62-
Assert.Null(response.Data);
63-
}
64-
}
62+
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
63+
Assert.Equal("Not found!", response.Data.Message);
64+
}
65+
}
6566

6667
[Fact]
6768
public void Handles_Default_Root_Element_On_No_Error()
@@ -102,6 +103,21 @@ void error(HttpListenerContext context)
102103
</Error>
103104
</Response>");
104105
}
106+
107+
void errorwithbody(HttpListenerContext context)
108+
{
109+
context.Response.StatusCode = 400;
110+
context.Response.Headers.Add("Content-Type", "application/xml");
111+
context.Response.OutputStream.WriteStringUtf8(
112+
@"<?xml version=""1.0"" encoding=""utf-8"" ?>
113+
<Response>
114+
<Error>
115+
<Message>Not found!</Message>
116+
</Error>
117+
</Response>");
118+
}
119+
120+
105121
void success(HttpListenerContext context)
106122
{
107123
context.Response.OutputStream.WriteStringUtf8(

RestSharp.Tests/NuSpecUpdateTask.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public class WhenSpecFileIsValid : BaseNuSpecUpdateTest
6767
private string _expectedDescription = "Simple REST and HTTP API Client";
6868
private string _expectedAuthors = "John Sheehan, RestSharp Community";
6969
private string _expectedOwners = "John Sheehan, RestSharp Community";
70-
private Regex _expectedVersion = new Regex(@"^\d+\.\d+\.\d+(-\w+)$", RegexOptions.Compiled);
70+
private Regex _expectedVersion = new Regex(@"^\d+\.\d+\.\d+(-\w+)?$", RegexOptions.Compiled);
7171

7272
protected override void Setup()
7373
{

RestSharp/Http.Async.cs

+27-20
Original file line numberDiff line numberDiff line change
@@ -276,26 +276,33 @@ private static void GetRawResponseAsync(IAsyncResult result, Action<HttpWebRespo
276276

277277
HttpWebResponse raw = null;
278278

279-
try
280-
{
281-
var webRequest = (HttpWebRequest)result.AsyncState;
282-
raw = webRequest.EndGetResponse(result) as HttpWebResponse;
283-
}
284-
catch(WebException ex)
285-
{
286-
if(ex.Status == WebExceptionStatus.RequestCanceled)
287-
{
288-
throw ex;
289-
}
290-
if (ex.Response is HttpWebResponse)
291-
{
292-
raw = ex.Response as HttpWebResponse;
293-
}
294-
else
295-
{
296-
throw ex;
297-
}
298-
}
279+
try
280+
{
281+
var webRequest = (HttpWebRequest)result.AsyncState;
282+
raw = webRequest.EndGetResponse(result) as HttpWebResponse;
283+
}
284+
catch(WebException ex)
285+
{
286+
if(ex.Status == WebExceptionStatus.RequestCanceled)
287+
{
288+
throw ex;
289+
}
290+
291+
// Check to see if this is an HTTP error or a transport error.
292+
// In cases where an HTTP error occurs ( status code >= 400 )
293+
// return the underlying HTTP response, otherwise assume a
294+
// transport exception (ex: connection timeout) and
295+
// rethrow the exception
296+
297+
if (ex.Response is HttpWebResponse)
298+
{
299+
raw = ex.Response as HttpWebResponse;
300+
}
301+
else
302+
{
303+
throw ex;
304+
}
305+
}
299306

300307
callback(raw);
301308
raw.Close();

RestSharp/Http.Sync.cs

+18-12
Original file line numberDiff line numberDiff line change
@@ -163,18 +163,24 @@ private HttpResponse GetResponse(HttpWebRequest request)
163163

164164
private static HttpWebResponse GetRawResponse(HttpWebRequest request)
165165
{
166-
try
167-
{
168-
return (HttpWebResponse)request.GetResponse();
169-
}
170-
catch (WebException ex)
171-
{
172-
if (ex.Response is HttpWebResponse)
173-
{
174-
return ex.Response as HttpWebResponse;
175-
}
176-
throw;
177-
}
166+
try
167+
{
168+
return (HttpWebResponse)request.GetResponse();
169+
}
170+
catch (WebException ex)
171+
{
172+
// Check to see if this is an HTTP error or a transport error.
173+
// In cases where an HTTP error occurs ( status code >= 400 )
174+
// return the underlying HTTP response, otherwise assume a
175+
// transport exception (ex: connection timeout) and
176+
// rethrow the exception
177+
178+
if (ex.Response is HttpWebResponse)
179+
{
180+
return ex.Response as HttpWebResponse;
181+
}
182+
throw;
183+
}
178184
}
179185

180186
private void PreparePostData(HttpWebRequest webRequest)

RestSharp/IRestResponse.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ public interface IRestResponse
8484
string ErrorMessage { get; set; }
8585

8686
/// <summary>
87-
/// The exception thrown during the request, if any
87+
/// Exceptions thrown during the request, if any.
8888
/// </summary>
89+
/// <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>
8990
Exception ErrorException { get; set; }
9091
}
9192

RestSharp/RestClient.cs

+26-25
Original file line numberDiff line numberDiff line change
@@ -469,31 +469,32 @@ private IRestResponse<T> Deserialize<T>(IRestRequest request, IRestResponse raw)
469469
{
470470
request.OnBeforeDeserialization(raw);
471471

472-
IRestResponse<T> response = new RestResponse<T>();
473-
try
474-
{
475-
response = raw.toAsyncResponse<T>();
476-
response.Request = request;
477-
478-
// Only attempt to deserialize if the request has a chance of containing a valid entry
479-
if (response.StatusCode == HttpStatusCode.OK
480-
|| response.StatusCode == HttpStatusCode.Created
481-
|| response.StatusCode == HttpStatusCode.NonAuthoritativeInformation)
482-
{
483-
IDeserializer handler = GetHandler(raw.ContentType);
484-
handler.RootElement = request.RootElement;
485-
handler.DateFormat = request.DateFormat;
486-
handler.Namespace = request.XmlNamespace;
487-
488-
response.Data = handler.Deserialize<T>(raw);
489-
}
490-
}
491-
catch (Exception ex)
492-
{
493-
response.ResponseStatus = ResponseStatus.Error;
494-
response.ErrorMessage = ex.Message;
495-
response.ErrorException = ex;
496-
}
472+
IRestResponse<T> response = new RestResponse<T>();
473+
try
474+
{
475+
response = raw.toAsyncResponse<T>();
476+
response.Request = request;
477+
478+
// Only attempt to deserialize if the request has not errored due
479+
// to a transport or framework exception. HTTP errors should attempt to
480+
// be deserialized
481+
482+
if (response.ErrorException==null)
483+
{
484+
IDeserializer handler = GetHandler(raw.ContentType);
485+
handler.RootElement = request.RootElement;
486+
handler.DateFormat = request.DateFormat;
487+
handler.Namespace = request.XmlNamespace;
488+
489+
response.Data = handler.Deserialize<T>(raw);
490+
}
491+
}
492+
catch (Exception ex)
493+
{
494+
response.ResponseStatus = ResponseStatus.Error;
495+
response.ErrorMessage = ex.Message;
496+
response.ErrorException = ex;
497+
}
497498

498499
return response;
499500
}

0 commit comments

Comments
 (0)