Skip to content

Commit e85c1a4

Browse files
author
Devin Rader
committed
Updated RestClient to deserialize objects except in cases of non-protocol errors
1 parent d2776a5 commit e85c1a4

File tree

7 files changed

+167
-29
lines changed

7 files changed

+167
-29
lines changed
Lines changed: 107 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,27 @@ 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-
}
41-
4243
[Fact]
43-
public void Handles_Different_Root_Element_On_Error()
44+
public void Handles_Different_Root_Element_On_Http_Error()
4445
{
4546
const string baseUrl = "http://localhost:8080/";
4647
using(SimpleServer.Create(baseUrl, Handlers.Generic<ResponseHandler>()))
@@ -59,7 +60,7 @@ public void Handles_Different_Root_Element_On_Error()
5960
var response = client.Execute<Response>(request);
6061

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

@@ -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/Http.Async.cs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -287,14 +287,21 @@ private static void GetRawResponseAsync(IAsyncResult result, Action<HttpWebRespo
287287
{
288288
throw ex;
289289
}
290-
if (ex.Response is HttpWebResponse)
291-
{
292-
raw = ex.Response as HttpWebResponse;
293-
}
294-
else
295-
{
296-
throw ex;
297-
}
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+
}
298305
}
299306

300307
callback(raw);

RestSharp/Http.Sync.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,12 @@ private static HttpWebResponse GetRawResponse(HttpWebRequest request)
169169
}
170170
catch (WebException ex)
171171
{
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+
172178
if (ex.Response is HttpWebResponse)
173179
{
174180
return ex.Response as HttpWebResponse;

RestSharp/IRestResponse.cs

Lines changed: 2 additions & 1 deletion
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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -475,11 +475,11 @@ private IRestResponse<T> Deserialize<T>(IRestRequest request, IRestResponse raw)
475475
response = raw.toAsyncResponse<T>();
476476
response.Request = request;
477477

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-
{
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) {
483483
IDeserializer handler = GetHandler(raw.ContentType);
484484
handler.RootElement = request.RootElement;
485485
handler.DateFormat = request.DateFormat;

0 commit comments

Comments
 (0)