diff --git a/Octokit.Reactive/Clients/IObservableReleasesClient.cs b/Octokit.Reactive/Clients/IObservableReleasesClient.cs
index b4a2b2a200..7320f5addd 100644
--- a/Octokit.Reactive/Clients/IObservableReleasesClient.cs
+++ b/Octokit.Reactive/Clients/IObservableReleasesClient.cs
@@ -1,6 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reactive;
+using System.Threading;
namespace Octokit.Reactive
{
@@ -250,8 +251,9 @@ public interface IObservableReleasesClient
///
/// The to attach the uploaded asset to
/// Description of the asset with its data
+ /// An optional token to monitor for cancellation requests
/// Thrown when a general API error occurs.
- IObservable UploadAsset(Release release, ReleaseAssetUpload data);
+ IObservable UploadAsset(Release release, ReleaseAssetUpload data, CancellationToken cancellationToken = default);
///
/// Gets the specified for the specified release of the specified repository.
diff --git a/Octokit.Reactive/Clients/ObservableReleasesClient.cs b/Octokit.Reactive/Clients/ObservableReleasesClient.cs
index b9568e56dc..644269643b 100644
--- a/Octokit.Reactive/Clients/ObservableReleasesClient.cs
+++ b/Octokit.Reactive/Clients/ObservableReleasesClient.cs
@@ -1,6 +1,7 @@
using System;
using System.Reactive;
using System.Reactive.Threading.Tasks;
+using System.Threading;
using Octokit.Reactive.Internal;
namespace Octokit.Reactive
@@ -400,13 +401,14 @@ public IObservable GetAsset(long repositoryId, int assetId)
///
/// The to attach the uploaded asset to
/// Description of the asset with its data
+ /// An optional token to monitor for cancellation requests
/// Thrown when a general API error occurs.
- public IObservable UploadAsset(Release release, ReleaseAssetUpload data)
+ public IObservable UploadAsset(Release release, ReleaseAssetUpload data, CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNull(release, nameof(release));
Ensure.ArgumentNotNull(data, nameof(data));
- return _client.UploadAsset(release, data).ToObservable();
+ return _client.UploadAsset(release, data, cancellationToken).ToObservable();
}
///
diff --git a/Octokit.Tests/Clients/ReleasesClientTests.cs b/Octokit.Tests/Clients/ReleasesClientTests.cs
index db25679e88..742c8e8912 100644
--- a/Octokit.Tests/Clients/ReleasesClientTests.cs
+++ b/Octokit.Tests/Clients/ReleasesClientTests.cs
@@ -1,7 +1,9 @@
using System;
using System.IO;
+using System.Threading;
using System.Threading.Tasks;
using NSubstitute;
+using Octokit.Internal;
using Xunit;
namespace Octokit.Tests.Clients
@@ -484,6 +486,42 @@ public async Task OverrideDefaultTimeout()
apiConnection.Received().Post(Arg.Any(), uploadData.RawData, Arg.Any(), uploadData.ContentType, newTimeout);
}
+
+ [Fact]
+ public async Task CanBeCancelled()
+ {
+ var httpClient = new CancellationTestHttpClient();
+ var connection = new Connection(new ProductHeaderValue("TEST"), httpClient);
+ var apiConnection = new ApiConnection(connection);
+
+ var fixture = new ReleasesClient(apiConnection);
+
+ var release = new Release("https://uploads.github.com/anything");
+ var uploadData = new ReleaseAssetUpload("good", "good/good", Stream.Null, null);
+
+ using (var cts = new CancellationTokenSource())
+ {
+ var uploadTask = fixture.UploadAsset(release, uploadData, cts.Token);
+
+ cts.Cancel();
+
+ await Assert.ThrowsAsync(() => uploadTask);
+ }
+ }
+
+ private class CancellationTestHttpClient : IHttpClient
+ {
+ public async Task Send(IRequest request, CancellationToken cancellationToken)
+ {
+ await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
+
+ throw new Exception("HTTP operation was not cancelled");
+ }
+
+ public void Dispose() { }
+
+ public void SetRequestTimeout(TimeSpan timeout) { }
+ }
}
public class TheGetAssetMethod
diff --git a/Octokit/Clients/IReleasesClient.cs b/Octokit/Clients/IReleasesClient.cs
index 56a700a23c..d8ef30bb33 100644
--- a/Octokit/Clients/IReleasesClient.cs
+++ b/Octokit/Clients/IReleasesClient.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Threading;
using System.Threading.Tasks;
namespace Octokit
@@ -250,8 +251,9 @@ public interface IReleasesClient
///
/// The to attach the uploaded asset to
/// Description of the asset with its data
+ /// An optional token to monitor for cancellation requests
/// Thrown when a general API error occurs.
- Task UploadAsset(Release release, ReleaseAssetUpload data);
+ Task UploadAsset(Release release, ReleaseAssetUpload data, CancellationToken cancellationToken = default);
///
/// Gets the specified for the specified release of the specified repository.
diff --git a/Octokit/Clients/ReleasesClient.cs b/Octokit/Clients/ReleasesClient.cs
index d1e6e0d3c0..dd1b360eb6 100644
--- a/Octokit/Clients/ReleasesClient.cs
+++ b/Octokit/Clients/ReleasesClient.cs
@@ -1,5 +1,6 @@
using System.Threading.Tasks;
using System.Collections.Generic;
+using System.Threading;
namespace Octokit
{
@@ -398,9 +399,10 @@ public Task> GetAllAssets(long repositoryId, int id,
///
/// The to attach the uploaded asset to
/// Description of the asset with its data
+ /// An optional token to monitor for cancellation requests
/// Thrown when a general API error occurs.
[ManualRoute("POST", "{server}/repos/{owner}/{repo}/releases/{release_id}/assets")]
- public Task UploadAsset(Release release, ReleaseAssetUpload data)
+ public Task UploadAsset(Release release, ReleaseAssetUpload data, CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNull(release, nameof(release));
Ensure.ArgumentNotNull(data, nameof(data));
@@ -414,14 +416,16 @@ public Task UploadAsset(Release release, ReleaseAssetUpload data)
data.RawData,
AcceptHeaders.StableVersion,
data.ContentType,
- data.Timeout.GetValueOrDefault());
+ data.Timeout.GetValueOrDefault(),
+ cancellationToken);
}
return ApiConnection.Post(
endpoint,
data.RawData,
AcceptHeaders.StableVersion,
- data.ContentType);
+ data.ContentType,
+ cancellationToken);
}
///
diff --git a/Octokit/Http/ApiConnection.cs b/Octokit/Http/ApiConnection.cs
index 5a477db6e0..1a5694271c 100644
--- a/Octokit/Http/ApiConnection.cs
+++ b/Octokit/Http/ApiConnection.cs
@@ -216,13 +216,14 @@ public Task> GetAll(Uri uri, IDictionary par
/// Creates a new API resource in the list at the specified URI.
///
/// URI endpoint to send request to
+ /// An optional token to monitor for cancellation requests
/// Representing the received HTTP response
/// Thrown when an API error occurs.
- public Task Post(Uri uri)
+ public Task Post(Uri uri, CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNull(uri, nameof(uri));
- return Connection.Post(uri);
+ return Connection.Post(uri, cancellationToken);
}
///
@@ -230,13 +231,14 @@ public Task Post(Uri uri)
///
/// The API resource's type.
/// URI of the API resource to get
+ /// An optional token to monitor for cancellation requests
/// The created API resource.
/// Thrown when an API error occurs.
- public async Task Post(Uri uri)
+ public async Task Post(Uri uri, CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNull(uri, nameof(uri));
- var response = await Connection.Post(uri).ConfigureAwait(false);
+ var response = await Connection.Post(uri, cancellationToken).ConfigureAwait(false);
return response.Body;
}
@@ -246,14 +248,15 @@ public async Task Post(Uri uri)
/// The API resource's type.
/// URI of the API resource to get
/// Object that describes the new API resource; this will be serialized and used as the request's body
+ /// An optional token to monitor for cancellation requests
/// The created API resource.
/// Thrown when an API error occurs.
- public Task Post(Uri uri, object data)
+ public Task Post(Uri uri, object data, CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNull(uri, nameof(uri));
Ensure.ArgumentNotNull(data, nameof(data));
- return Post(uri, data, null, null);
+ return Post(uri, data, null, null, cancellationToken);
}
///
@@ -263,11 +266,12 @@ public Task Post(Uri uri, object data)
/// URI of the API resource to get
/// Object that describes the new API resource; this will be serialized and used as the request's body
/// Accept header to use for the API request
+ /// An optional token to monitor for cancellation requests
/// The created API resource.
/// Thrown when an API error occurs.
- public Task Post(Uri uri, object data, string accepts)
+ public Task Post(Uri uri, object data, string accepts, CancellationToken cancellationToken = default)
{
- return Post(uri, data, accepts, null);
+ return Post(uri, data, accepts, null, cancellationToken);
}
///
@@ -278,14 +282,15 @@ public Task Post(Uri uri, object data, string accepts)
/// Object that describes the new API resource; this will be serialized and used as the request's body
/// Accept header to use for the API request
/// Content type of the API request
+ /// An optional token to monitor for cancellation requests
/// The created API resource.
/// Thrown when an API error occurs.
- public async Task Post(Uri uri, object data, string accepts, string contentType)
+ public async Task Post(Uri uri, object data, string accepts, string contentType, CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNull(uri, nameof(uri));
Ensure.ArgumentNotNull(data, nameof(data));
- var response = await Connection.Post(uri, data, accepts, contentType).ConfigureAwait(false);
+ var response = await Connection.Post(uri, data, accepts, contentType, cancellationToken: cancellationToken).ConfigureAwait(false);
return response.Body;
}
@@ -298,25 +303,26 @@ public async Task Post(Uri uri, object data, string accepts, string conten
/// Accept header to use for the API request
/// Content type of the API request
/// Two Factor Authentication Code
+ /// An optional token to monitor for cancellation requests
/// The created API resource.
/// Thrown when an API error occurs.
- public async Task Post(Uri uri, object data, string accepts, string contentType, string twoFactorAuthenticationCode)
+ public async Task Post(Uri uri, object data, string accepts, string contentType, string twoFactorAuthenticationCode, CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNull(uri, nameof(uri));
Ensure.ArgumentNotNull(data, nameof(data));
Ensure.ArgumentNotNull(twoFactorAuthenticationCode, nameof(twoFactorAuthenticationCode));
- var response = await Connection.Post(uri, data, accepts, contentType, twoFactorAuthenticationCode).ConfigureAwait(false);
+ var response = await Connection.Post(uri, data, accepts, contentType, twoFactorAuthenticationCode, cancellationToken).ConfigureAwait(false);
return response.Body;
}
- public async Task Post(Uri uri, object data, string accepts, string contentType, TimeSpan timeout)
+ public async Task Post(Uri uri, object data, string accepts, string contentType, TimeSpan timeout, CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNull(uri, nameof(uri));
Ensure.ArgumentNotNull(data, nameof(data));
- var response = await Connection.Post(uri, data, accepts, contentType, timeout).ConfigureAwait(false);
+ var response = await Connection.Post(uri, data, accepts, contentType, timeout, cancellationToken).ConfigureAwait(false);
return response.Body;
}
diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs
index 6c2c7560cd..81759fdc2a 100644
--- a/Octokit/Http/Connection.cs
+++ b/Octokit/Http/Connection.cs
@@ -251,44 +251,51 @@ public Task> Patch(Uri uri, object body, string accepts)
/// Performs an asynchronous HTTP POST request.
///
/// URI endpoint to send request to
+ /// An optional token to monitor for cancellation requests
/// representing the received HTTP response
- public async Task Post(Uri uri)
+ public async Task Post(Uri uri, CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNull(uri, nameof(uri));
- var response = await SendData