Skip to content
Merged

Fixes #1750

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
4 changes: 3 additions & 1 deletion src/RestSharp/Request/RequestContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using System.Runtime.Serialization;
using RestSharp.Extensions;
using static RestSharp.KnownHeaders;
// ReSharper disable InvertIf

// ReSharper disable SuggestBaseTypeForParameter

Expand Down Expand Up @@ -153,9 +154,10 @@ void AddPostParameters(ParametersCollection? postParameters) {
if (Content is MultipartFormDataContent mpContent) {
// we got the multipart form already instantiated, just add parameters to it
foreach (var postParameter in postParameters!) {
var parameterName = postParameter.Name!;
mpContent.Add(
new StringContent(postParameter.Value!.ToString()!, _client.Options.Encoding, postParameter.ContentType),
postParameter.Name!
_request.MultipartFormQuoteParameters ? $"\"{parameterName}\"" : parameterName
);
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/RestSharp/Request/RestRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ public RestRequest(Uri resource, Method method = Method.Get)
/// </summary>
public bool AlwaysMultipartFormData { get; set; }

/// <summary>
/// When set to true, parameters in a multipart form data requests will be enclosed in
/// quotation marks. Default is false. Enable it if the remote endpoint requires parameters
/// to be in quotes (for example, FreshDesk API).
/// </summary>
public bool MultipartFormQuoteParameters { get; set; }

public string? FormBoundary { get; set; }

/// <summary>
Expand Down
19 changes: 11 additions & 8 deletions src/RestSharp/Response/RestResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,12 @@ public static RestResponse<T> FromResponse(RestResponse response)
[DebuggerDisplay("{" + nameof(DebuggerDisplay) + "()}")]
public class RestResponse : RestResponseBase {
internal static async Task<RestResponse> FromHttpResponse(
HttpResponseMessage httpResponse,
RestRequest request,
Encoding encoding,
CookieCollection cookieCollection,
CancellationToken cancellationToken
HttpResponseMessage httpResponse,
RestRequest request,
Encoding encoding,
CookieCollection cookieCollection,
CalculateResponseStatus calculateResponseStatus,
CancellationToken cancellationToken
) {
return request.AdvancedResponseWriter?.Invoke(httpResponse) ?? await GetDefaultResponse().ConfigureAwait(false);

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

var bytes = stream == null ? null : await stream.ReadAsBytes(cancellationToken).ConfigureAwait(false);
var content = bytes == null ? null : httpResponse.GetResponseString(bytes, encoding);
var content = bytes == null ? null : httpResponse.GetResponseString(bytes, encoding);

return new RestResponse {
Content = content,
Expand All @@ -87,7 +88,7 @@ async Task<RestResponse> GetDefaultResponse() {
Version = httpResponse.RequestMessage?.Version,
ContentLength = httpResponse.Content.Headers.ContentLength,
ContentType = httpResponse.Content.Headers.ContentType?.MediaType,
ResponseStatus = httpResponse.IsSuccessStatusCode ? ResponseStatus.Completed : ResponseStatus.Error,
ResponseStatus = calculateResponseStatus(httpResponse),
ErrorException = MaybeException(),
ResponseUri = httpResponse.RequestMessage!.RequestUri,
Server = httpResponse.Headers.Server.ToString(),
Expand Down Expand Up @@ -122,4 +123,6 @@ async Task<RestResponse> GetDefaultResponse() {
}
}
}
}
}

public delegate ResponseStatus CalculateResponseStatus(HttpResponseMessage httpResponse);
3 changes: 1 addition & 2 deletions src/RestSharp/RestClient.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using RestSharp.Extensions;

namespace RestSharp;

public partial class RestClient {
Expand All @@ -33,6 +31,7 @@ public async Task<RestResponse> ExecuteAsync(RestRequest request, CancellationTo
request,
Options.Encoding,
CookieContainer.GetCookies(internalResponse.Url),
CalculateResponseStatus,
cancellationToken
)
.ConfigureAwait(false)
Expand Down
18 changes: 12 additions & 6 deletions src/RestSharp/RestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ public partial class RestClient : IDisposable {
/// </summary>
public string[] AcceptedContentTypes { get; set; } = null!;

/// <summary>
/// Function to calculate the response status. By default, the status will be Completed if it was successful, or NotFound.
/// </summary>
public CalculateResponseStatus CalculateResponseStatus { get; set; } = httpResponse
=> httpResponse.IsSuccessStatusCode || httpResponse.StatusCode == HttpStatusCode.NotFound
? ResponseStatus.Completed
: ResponseStatus.Error;

HttpClient HttpClient { get; }

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

void ConfigureHttpClient(HttpClient httpClient) {
if (Options.Timeout > 0)
httpClient.Timeout = TimeSpan.FromMilliseconds(Options.Timeout);
if (Options.Timeout > 0) httpClient.Timeout = TimeSpan.FromMilliseconds(Options.Timeout);
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(Options.UserAgent);
}

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

if (Options.MaxRedirects.HasValue)
handler.MaxAutomaticRedirections = Options.MaxRedirects.Value;
if (Options.MaxRedirects.HasValue) handler.MaxAutomaticRedirections = Options.MaxRedirects.Value;
}

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

if (!Options.AllowMultipleDefaultParametersWithSameName &&
!MultiParameterTypes.Contains(parameter.Type) &&
!MultiParameterTypes.Contains(parameter.Type) &&
DefaultParameters.Any(x => x.Name == parameter.Name)) {
throw new ArgumentException("A default parameters with the same name has already been added", nameof(parameter));
}
Expand Down Expand Up @@ -212,4 +218,4 @@ public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
23 changes: 20 additions & 3 deletions test/RestSharp.Tests.Integrated/RequestFailureTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,45 @@ public async Task Handles_GET_Request_Errors_With_Response_Type() {
[Fact]
public async Task Throws_on_unsuccessful_call() {
var client = new RestClient(new RestClientOptions(_fixture.Server.Url) { ThrowOnAnyError = true });
var request = new RestRequest("status?code=404");
var request = new RestRequest("status?code=500");

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

[Fact]
public async Task GetAsync_throws_on_unsuccessful_call() {
var request = new RestRequest("status?code=404");
var request = new RestRequest("status?code=500");

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

[Fact]
public async Task GetAsync_generic_throws_on_unsuccessful_call() {
public async Task GetAsync_completes_on_404() {
var request = new RestRequest("status?code=404");

var response = await _client.GetAsync(request);
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
response.ResponseStatus.Should().Be(ResponseStatus.Completed);
}

[Fact]
public async Task GetAsync_generic_throws_on_unsuccessful_call() {
var request = new RestRequest("status?code=500");

var task = () => _client.GetAsync<Response>(request);
await task.Should().ThrowExactlyAsync<HttpRequestException>();
}

[Fact]
public async Task GetAsync_returns_null_on_404() {
var request = new RestRequest("status?code=404");

var response = await _client.GetAsync<Response>(request);
response.Should().BeNull();
}

class Response {
public string Message { get; set; }
}
Expand Down
13 changes: 12 additions & 1 deletion test/RestSharp.Tests/UrlBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -357,4 +357,15 @@ public void Should_update_parameter_if_it_already_exists() {

Assert.Equal(expected, output);
}
}

[Fact]
public void Should_use_ipv6_address() {
var baseUrl = new Uri("https://[fe80::290:e8ff:fe8b:2537%en10]:8443");
var client = new RestClient(baseUrl);
var request = new RestRequest("api/v1/auth");
var actual = client.BuildUri(request);

actual.HostNameType.Should().Be(UriHostNameType.IPv6);
actual.AbsoluteUri.Should().Be("https://[fe80::290:e8ff:fe8b:2537]:8443/api/v1/auth");
}
}