From efdae0f92b4b1549e2cc09fb633283d99d69aeb7 Mon Sep 17 00:00:00 2001 From: wessleym Date: Tue, 15 Feb 2022 17:50:48 -0600 Subject: [PATCH 1/2] Improve handling of Unprocessable Entity (422) HTTP errors The code previously tried to ignore HTTP 422 errors, but the response would not be parsed correctly later on. --- .../Exceptions/UnexpectedException.cs | 2 + .../UnprocessableEntityException.cs | 7 ++ src/Braintree/HttpService.cs | 104 ++++++++++-------- 3 files changed, 68 insertions(+), 45 deletions(-) create mode 100644 src/Braintree/Exceptions/UnprocessableEntityException.cs diff --git a/src/Braintree/Exceptions/UnexpectedException.cs b/src/Braintree/Exceptions/UnexpectedException.cs index 286f6942..9fe6ff33 100644 --- a/src/Braintree/Exceptions/UnexpectedException.cs +++ b/src/Braintree/Exceptions/UnexpectedException.cs @@ -4,5 +4,7 @@ namespace Braintree.Exceptions { public class UnexpectedException : BraintreeException { + public UnexpectedException() : base() { } + public UnexpectedException(string message) : base(message) { } } } diff --git a/src/Braintree/Exceptions/UnprocessableEntityException.cs b/src/Braintree/Exceptions/UnprocessableEntityException.cs new file mode 100644 index 00000000..5165437b --- /dev/null +++ b/src/Braintree/Exceptions/UnprocessableEntityException.cs @@ -0,0 +1,7 @@ +namespace Braintree.Exceptions +{ + public class UnprocessableEntityException : BraintreeException + { + public UnprocessableEntityException(string message) : base(message) { } + } +} diff --git a/src/Braintree/HttpService.cs b/src/Braintree/HttpService.cs index 03918b74..16e8aab2 100644 --- a/src/Braintree/HttpService.cs +++ b/src/Braintree/HttpService.cs @@ -84,36 +84,44 @@ public virtual void SetRequestHeaders(HttpWebRequest request) #endif #if netcore - public HttpRequestMessage GetHttpRequest(string URL, string method) { + public HttpRequestMessage GetHttpRequest(string URL, string method) + { var request = Configuration.HttpRequestMessageFactory(new HttpMethod(method), URL); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(GetAuthorizationSchema(), GetAuthorizationHeader()); SetRequestHeaders(request); return request; } - public string GetHttpResponse(HttpRequestMessage request) { - var response = httpClient.SendAsync(request).GetAwaiter().GetResult(); - if (response.StatusCode != (HttpStatusCode)422) + public string GetHttpResponse(HttpRequestMessage request) + { + using (var response = httpClient.SendAsync(request).GetAwaiter().GetResult()) { - ThrowExceptionIfErrorStatusCode(response.StatusCode, null); + using (var responseStream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult()) + { + string responseContent = ParseResponseStream(responseStream); + ThrowExceptionIfErrorStatusCode(response.StatusCode, responseContent); + return responseContent; + } } - - return ParseResponseStream(response.Content.ReadAsStreamAsync().GetAwaiter().GetResult()); } - public async Task GetHttpResponseAsync(HttpRequestMessage request) { - var response = await httpClient.SendAsync(request).ConfigureAwait(false); - if (response.StatusCode != (HttpStatusCode)422) + public async Task GetHttpResponseAsync(HttpRequestMessage request) + { + using (var response = await httpClient.SendAsync(request).ConfigureAwait(false)) { - ThrowExceptionIfErrorStatusCode(response.StatusCode, null); + using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + { + string responseContent = await ParseResponseStreamAsync(responseStream); + ThrowExceptionIfErrorStatusCode(response.StatusCode, responseContent); + return responseContent; + } } - - return await ParseResponseStreamAsync(await response.Content.ReadAsStreamAsync().ConfigureAwait(false)); } #else - public HttpWebRequest GetHttpRequest(string URL, string method) { + public HttpWebRequest GetHttpRequest(string URL, string method) + { const int SecurityProtocolTypeTls12 = 3072; - ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | ((SecurityProtocolType) SecurityProtocolTypeTls12); + ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | ((SecurityProtocolType)SecurityProtocolTypeTls12); var request = Configuration.HttpWebRequestFactory(URL); SetRequestProxy(request); @@ -126,53 +134,59 @@ public HttpWebRequest GetHttpRequest(string URL, string method) { return request; } - public string GetHttpResponse(HttpWebRequest request) { - try { - using (var response = (HttpWebResponse) request.GetResponse()) + public string GetHttpResponse(HttpWebRequest request) + { + try + { + using (var response = (HttpWebResponse)request.GetResponse()) { - return ParseResponseStream(GetResponseStream(response)); + using (var responseStream = GetResponseStream(response)) + { + return ParseResponseStream(responseStream); + } } } catch (WebException e) { - using (var response = (HttpWebResponse) e.Response) + using (var response = (HttpWebResponse)e.Response) { - if (response == null) throw e; - - if (response.StatusCode == (HttpStatusCode)422) // UnprocessableEntity + if (response == null) throw; + using (var responseStream = GetResponseStream((HttpWebResponse)e.Response)) { - return ParseResponseStream(GetResponseStream((HttpWebResponse)e.Response)); + string responseContent = ParseResponseStream(responseStream); + ThrowExceptionIfErrorStatusCode(response.StatusCode, responseContent); + return responseContent; } - - ThrowExceptionIfErrorStatusCode(response.StatusCode, null); } - - throw e; + throw; } } - public async Task GetHttpResponseAsync(HttpWebRequest request) { - try { - using (var response = (HttpWebResponse) await request.GetResponseAsync().ConfigureAwait(false)) + public async Task GetHttpResponseAsync(HttpWebRequest request) + { + try + { + using (var response = (HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false)) { - return await ParseResponseStreamAsync(GetResponseStream(response)).ConfigureAwait(false); + using (var responseStream = GetResponseStream(response)) + { + return await ParseResponseStreamAsync(responseStream).ConfigureAwait(false); + } } } catch (WebException e) { - using (var response = (HttpWebResponse) e.Response) + using (var response = (HttpWebResponse)e.Response) { - if (response == null) throw e; - - if (response.StatusCode == (HttpStatusCode)422) // UnprocessableEntity + if (response == null) throw; + using (var responseStream = GetResponseStream((HttpWebResponse)e.Response)) { - return await ParseResponseStreamAsync(GetResponseStream((HttpWebResponse) e.Response)).ConfigureAwait(false); + string responseContent = await ParseResponseStreamAsync(responseStream).ConfigureAwait(false); + ThrowExceptionIfErrorStatusCode(response.StatusCode, responseContent); + return responseContent; } - - ThrowExceptionIfErrorStatusCode(response.StatusCode, null); } - - throw e; + throw; } } #endif @@ -283,7 +297,7 @@ public static void ThrowExceptionIfErrorStatusCode(HttpStatusCode httpStatusCode { if (httpStatusCode != HttpStatusCode.OK && httpStatusCode != HttpStatusCode.Created) { - switch ((int) httpStatusCode) + switch ((int)httpStatusCode) { case 401: throw new AuthenticationException(); @@ -293,6 +307,8 @@ public static void ThrowExceptionIfErrorStatusCode(HttpStatusCode httpStatusCode throw new NotFoundException(); case 408: throw new RequestTimeoutException(); + case 422: + throw new UnprocessableEntityException(message); case 426: throw new UpgradeRequiredException(); case 429: @@ -304,9 +320,7 @@ public static void ThrowExceptionIfErrorStatusCode(HttpStatusCode httpStatusCode case 504: throw new GatewayTimeoutException(); default: - var exception = new UnexpectedException(); - exception.Source = "Unexpected HTTP_RESPONSE " + httpStatusCode; - throw exception; + throw new UnexpectedException("Unexpected HTTP_RESPONSE " + httpStatusCode + ". Response: " + message); } } } From ea4f9452c651429452694062cdda1184cfca910e Mon Sep 17 00:00:00 2001 From: wessleym Date: Tue, 15 Feb 2022 18:12:16 -0600 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8bd9fdc..7f415535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 5.11.0 +* Improve handling of Unprocessable Entity (422) HTTP errors + ## 5.10.0 * Add plan create/update/find API endpoint * Add `TransactionReview` webhook notification support