Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RestClient.DownloadStreamAsync() error handler #2128

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 35 additions & 4 deletions src/RestSharp/RestClient.Async.cs
Expand Up @@ -40,19 +40,50 @@ public partial class RestClient {
/// <inheritdoc />
[PublicAPI]
public async Task<Stream?> DownloadStreamAsync(RestRequest request, CancellationToken cancellationToken = default) {
return await DownloadStreamInternalAsync(request, null, cancellationToken);
}

/// <inheritdoc />
[PublicAPI]
public async Task<Stream?> DownloadStreamAsync(RestRequest request, Action<RestResponse> errorHandler, CancellationToken cancellationToken = default) {
return await DownloadStreamInternalAsync(request, errorHandler, cancellationToken);
}

async Task<Stream?> DownloadStreamInternalAsync(RestRequest request, Action<RestResponse>? errorHandler, CancellationToken cancellationToken = default) {
// Make sure we only read the headers so we can stream the content body efficiently
request.CompletionOption = HttpCompletionOption.ResponseHeadersRead;
var response = await ExecuteRequestAsync(request, cancellationToken).ConfigureAwait(false);
var internalResponse = await ExecuteRequestAsync(request, cancellationToken).ConfigureAwait(false);

if (errorHandler != null) {
if (internalResponse.Exception != null) {
var response = GetErrorResponse(request, internalResponse.Exception, internalResponse.TimeoutToken);

errorHandler(response);
}
else if (internalResponse.ResponseMessage!.IsSuccessStatusCode == false) {
var response = await RestResponse.FromHttpResponse(
internalResponse.ResponseMessage!,
request,
Options.Encoding,
internalResponse.CookieContainer?.GetCookies(internalResponse.Url),
Options.CalculateResponseStatus,
cancellationToken
)
.ConfigureAwait(false);

errorHandler(response);
}
}

var exception = response.Exception ?? response.ResponseMessage?.MaybeException();
var exception = internalResponse.Exception ?? internalResponse.ResponseMessage?.MaybeException();

if (exception != null) {
return Options.ThrowOnAnyError ? throw exception : null;
}

if (response.ResponseMessage == null) return null;
if (internalResponse.ResponseMessage == null) return null;

return await response.ResponseMessage.ReadResponseStream(request.ResponseWriter, cancellationToken).ConfigureAwait(false);
return await internalResponse.ResponseMessage.ReadResponseStream(request.ResponseWriter, cancellationToken).ConfigureAwait(false);
}

static RestResponse GetErrorResponse(RestRequest request, Exception exception, CancellationToken timeoutToken) {
Expand Down
49 changes: 49 additions & 0 deletions test/RestSharp.Tests.Integrated/DownloadFileTests.cs
@@ -1,5 +1,6 @@
using System.Net;
using System.Text;
using RestSharp.Extensions;
using RestSharp.Tests.Shared.Fixtures;

namespace RestSharp.Tests.Integrated;
Expand All @@ -9,6 +10,7 @@ public sealed class DownloadFileTests : IDisposable {
_server = HttpServerFixture.StartServer("Assets/Koala.jpg", FileHandler);
var options = new RestClientOptions(_server.Url) { ThrowOnAnyError = true };
_client = new RestClient(options);
_clientNoThrow = new RestClient(_server.Url);
}

public void Dispose() => _server.Dispose();
Expand All @@ -28,6 +30,7 @@ public sealed class DownloadFileTests : IDisposable {

readonly HttpServerFixture _server;
readonly RestClient _client;
readonly RestClient _clientNoThrow;
readonly string _path = AppDomain.CurrentDomain.BaseDirectory;

[Fact]
Expand Down Expand Up @@ -65,6 +68,52 @@ public sealed class DownloadFileTests : IDisposable {
Assert.Equal(expected, response);
}

[Fact]
public async Task Runs_ErrorHandler_On_Download_Request_Failure() {
var client = new RestClient("http://localhost:12345");
var request = new RestRequest("nonexisting");
RestResponse? errorResponse = null;
var stream = await client.DownloadStreamAsync(request, (r) => {
errorResponse = r;
});

Assert.Null(stream);
Assert.NotNull(errorResponse);
Assert.Equal(ResponseStatus.Error, errorResponse.ResponseStatus);
}

[Fact]
public async Task Runs_ErrorHandler_On_Download_Response_StatusCode_Not_Successful() {
var request = new RestRequest("Assets/Koala1.jpg");
RestResponse? errorResponse = null;
var stream = await _clientNoThrow.DownloadStreamAsync(request, (r) => {
errorResponse = r;
});

Assert.Null(stream);
Assert.NotNull(errorResponse);
Assert.Equal(ResponseStatus.Completed, errorResponse.ResponseStatus);
Assert.False(errorResponse.IsSuccessStatusCode);
Assert.Equal(HttpStatusCode.NotFound, errorResponse.StatusCode);
}

[Fact]
public async Task Doesnt_Run_ErrorHandler_On_Download_Success() {
var request = new RestRequest("Assets/Koala.jpg");
RestResponse? errorResponse = null;
var stream = await _clientNoThrow.DownloadStreamAsync(request, (r) => {
errorResponse = r;
});

Assert.NotNull(stream);
Assert.Null(errorResponse);

var expected = await File.ReadAllBytesAsync(Path.Combine(_path, "Assets", "Koala.jpg"));
var bytes = await stream.ReadAsBytes(CancellationToken.None);

Assert.Equal(expected, bytes);
}

[Fact]
public async Task Writes_Response_To_Stream() {
var tempFile = Path.GetTempFileName();
Expand Down