Skip to content

Commit fc27aef

Browse files
Fixes (#1750)
* * Quoting form parameters (optional) * Completed on 404 (can override) * Test for IPv6
1 parent 21feeb0 commit fc27aef

File tree

7 files changed

+66
-21
lines changed

7 files changed

+66
-21
lines changed

src/RestSharp/Request/RequestContent.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using System.Runtime.Serialization;
1717
using RestSharp.Extensions;
1818
using static RestSharp.KnownHeaders;
19+
// ReSharper disable InvertIf
1920

2021
// ReSharper disable SuggestBaseTypeForParameter
2122

@@ -149,9 +150,10 @@ void AddPostParameters(ParametersCollection? postParameters) {
149150
if (Content is MultipartFormDataContent mpContent) {
150151
// we got the multipart form already instantiated, just add parameters to it
151152
foreach (var postParameter in postParameters!) {
153+
var parameterName = postParameter.Name!;
152154
mpContent.Add(
153155
new StringContent(postParameter.Value!.ToString()!, _client.Options.Encoding, postParameter.ContentType),
154-
postParameter.Name!
156+
_request.MultipartFormQuoteParameters ? $"\"{parameterName}\"" : parameterName
155157
);
156158
}
157159
}

src/RestSharp/Request/RestRequest.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ public RestRequest(Uri resource, Method method = Method.Get)
6868
/// </summary>
6969
public bool AlwaysMultipartFormData { get; set; }
7070

71+
/// <summary>
72+
/// When set to true, parameters in a multipart form data requests will be enclosed in
73+
/// quotation marks. Default is false. Enable it if the remote endpoint requires parameters
74+
/// to be in quotes (for example, FreshDesk API).
75+
/// </summary>
76+
public bool MultipartFormQuoteParameters { get; set; }
77+
7178
public string? FormBoundary { get; set; }
7279

7380
/// <summary>

src/RestSharp/Response/RestResponse.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,12 @@ public static RestResponse<T> FromResponse(RestResponse response)
6161
[DebuggerDisplay("{" + nameof(DebuggerDisplay) + "()}")]
6262
public class RestResponse : RestResponseBase {
6363
internal static async Task<RestResponse> FromHttpResponse(
64-
HttpResponseMessage httpResponse,
65-
RestRequest request,
66-
Encoding encoding,
67-
CookieCollection cookieCollection,
68-
CancellationToken cancellationToken
64+
HttpResponseMessage httpResponse,
65+
RestRequest request,
66+
Encoding encoding,
67+
CookieCollection cookieCollection,
68+
CalculateResponseStatus calculateResponseStatus,
69+
CancellationToken cancellationToken
6970
) {
7071
return request.AdvancedResponseWriter?.Invoke(httpResponse) ?? await GetDefaultResponse().ConfigureAwait(false);
7172

@@ -78,7 +79,7 @@ async Task<RestResponse> GetDefaultResponse() {
7879
#endif
7980

8081
var bytes = stream == null ? null : await stream.ReadAsBytes(cancellationToken).ConfigureAwait(false);
81-
var content = bytes == null ? null : httpResponse.GetResponseString(bytes, encoding);
82+
var content = bytes == null ? null : httpResponse.GetResponseString(bytes, encoding);
8283

8384
return new RestResponse {
8485
Content = content,
@@ -87,7 +88,7 @@ async Task<RestResponse> GetDefaultResponse() {
8788
Version = httpResponse.RequestMessage?.Version,
8889
ContentLength = httpResponse.Content.Headers.ContentLength,
8990
ContentType = httpResponse.Content.Headers.ContentType?.MediaType,
90-
ResponseStatus = httpResponse.IsSuccessStatusCode ? ResponseStatus.Completed : ResponseStatus.Error,
91+
ResponseStatus = calculateResponseStatus(httpResponse),
9192
ErrorException = MaybeException(),
9293
ResponseUri = httpResponse.RequestMessage!.RequestUri,
9394
Server = httpResponse.Headers.Server.ToString(),
@@ -122,4 +123,6 @@ async Task<RestResponse> GetDefaultResponse() {
122123
}
123124
}
124125
}
125-
}
126+
}
127+
128+
public delegate ResponseStatus CalculateResponseStatus(HttpResponseMessage httpResponse);

src/RestSharp/RestClient.Async.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
using RestSharp.Extensions;
16-
1715
namespace RestSharp;
1816

1917
public partial class RestClient {
@@ -33,6 +31,7 @@ public async Task<RestResponse> ExecuteAsync(RestRequest request, CancellationTo
3331
request,
3432
Options.Encoding,
3533
CookieContainer.GetCookies(internalResponse.Url),
34+
CalculateResponseStatus,
3635
cancellationToken
3736
)
3837
.ConfigureAwait(false)

src/RestSharp/RestClient.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ public partial class RestClient : IDisposable {
3535
/// </summary>
3636
public string[] AcceptedContentTypes { get; set; } = null!;
3737

38+
/// <summary>
39+
/// Function to calculate the response status. By default, the status will be Completed if it was successful, or NotFound.
40+
/// </summary>
41+
public CalculateResponseStatus CalculateResponseStatus { get; set; } = httpResponse
42+
=> httpResponse.IsSuccessStatusCode || httpResponse.StatusCode == HttpStatusCode.NotFound
43+
? ResponseStatus.Completed
44+
: ResponseStatus.Error;
45+
3846
HttpClient HttpClient { get; }
3947

4048
internal RestClientOptions Options { get; }
@@ -103,8 +111,7 @@ public RestClient(HttpClient httpClient, RestClientOptions? options = null, bool
103111
public RestClient(HttpMessageHandler handler, bool disposeHandler = true) : this(new HttpClient(handler, disposeHandler), null, true) { }
104112

105113
void ConfigureHttpClient(HttpClient httpClient) {
106-
if (Options.Timeout > 0)
107-
httpClient.Timeout = TimeSpan.FromMilliseconds(Options.Timeout);
114+
if (Options.Timeout > 0) httpClient.Timeout = TimeSpan.FromMilliseconds(Options.Timeout);
108115
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(Options.UserAgent);
109116
}
110117

@@ -126,8 +133,7 @@ void ConfigureHttpMessageHandler(HttpClientHandler handler) {
126133
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
127134
}
128135

129-
if (Options.MaxRedirects.HasValue)
130-
handler.MaxAutomaticRedirections = Options.MaxRedirects.Value;
136+
if (Options.MaxRedirects.HasValue) handler.MaxAutomaticRedirections = Options.MaxRedirects.Value;
131137
}
132138

133139
internal Func<string, string> Encode { get; set; } = s => s.UrlEncode();
@@ -153,7 +159,7 @@ public RestClient AddDefaultParameter(Parameter parameter) {
153159
);
154160

155161
if (!Options.AllowMultipleDefaultParametersWithSameName &&
156-
!MultiParameterTypes.Contains(parameter.Type) &&
162+
!MultiParameterTypes.Contains(parameter.Type) &&
157163
DefaultParameters.Any(x => x.Name == parameter.Name)) {
158164
throw new ArgumentException("A default parameters with the same name has already been added", nameof(parameter));
159165
}
@@ -212,4 +218,4 @@ public void Dispose() {
212218
Dispose(true);
213219
GC.SuppressFinalize(this);
214220
}
215-
}
221+
}

test/RestSharp.Tests.Integrated/RequestFailureTests.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,28 +34,45 @@ public async Task Handles_GET_Request_Errors_With_Response_Type() {
3434
[Fact]
3535
public async Task Throws_on_unsuccessful_call() {
3636
var client = new RestClient(new RestClientOptions(_fixture.Server.Url) { ThrowOnAnyError = true });
37-
var request = new RestRequest("status?code=404");
37+
var request = new RestRequest("status?code=500");
3838

3939
var task = () => client.ExecuteAsync<Response>(request);
4040
await task.Should().ThrowExactlyAsync<HttpRequestException>();
4141
}
4242

4343
[Fact]
4444
public async Task GetAsync_throws_on_unsuccessful_call() {
45-
var request = new RestRequest("status?code=404");
45+
var request = new RestRequest("status?code=500");
4646

4747
var task = () => _client.GetAsync(request);
4848
await task.Should().ThrowExactlyAsync<HttpRequestException>();
4949
}
5050

5151
[Fact]
52-
public async Task GetAsync_generic_throws_on_unsuccessful_call() {
52+
public async Task GetAsync_completes_on_404() {
5353
var request = new RestRequest("status?code=404");
5454

55+
var response = await _client.GetAsync(request);
56+
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
57+
response.ResponseStatus.Should().Be(ResponseStatus.Completed);
58+
}
59+
60+
[Fact]
61+
public async Task GetAsync_generic_throws_on_unsuccessful_call() {
62+
var request = new RestRequest("status?code=500");
63+
5564
var task = () => _client.GetAsync<Response>(request);
5665
await task.Should().ThrowExactlyAsync<HttpRequestException>();
5766
}
5867

68+
[Fact]
69+
public async Task GetAsync_returns_null_on_404() {
70+
var request = new RestRequest("status?code=404");
71+
72+
var response = await _client.GetAsync<Response>(request);
73+
response.Should().BeNull();
74+
}
75+
5976
class Response {
6077
public string Message { get; set; }
6178
}

test/RestSharp.Tests/UrlBuilderTests.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,4 +357,15 @@ public void Should_update_parameter_if_it_already_exists() {
357357

358358
Assert.Equal(expected, output);
359359
}
360-
}
360+
361+
[Fact]
362+
public void Should_use_ipv6_address() {
363+
var baseUrl = new Uri("https://[fe80::290:e8ff:fe8b:2537%en10]:8443");
364+
var client = new RestClient(baseUrl);
365+
var request = new RestRequest("api/v1/auth");
366+
var actual = client.BuildUri(request);
367+
368+
actual.HostNameType.Should().Be(UriHostNameType.IPv6);
369+
actual.AbsoluteUri.Should().Be("https://[fe80::290:e8ff:fe8b:2537]:8443/api/v1/auth");
370+
}
371+
}

0 commit comments

Comments
 (0)