From 4e6319ca278a9e0f4c8cc67b3dc2d685944be047 Mon Sep 17 00:00:00 2001 From: Chris Simpson Date: Tue, 5 Jul 2022 07:25:25 +0100 Subject: [PATCH 1/2] Adding in handling for secondary rate limit exceptions --- Octokit.Tests/Http/ConnectionTests.cs | 24 +++++++++++++++++++++++- Octokit/Http/Connection.cs | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Octokit.Tests/Http/ConnectionTests.cs b/Octokit.Tests/Http/ConnectionTests.cs index c38aee0759..353d9dd550 100644 --- a/Octokit.Tests/Http/ConnectionTests.cs +++ b/Octokit.Tests/Http/ConnectionTests.cs @@ -180,7 +180,7 @@ public async Task ThrowsApiValidationExceptionFor422Response() } [Fact] - public async Task ThrowsRateLimitExceededExceptionForForbidderResponse() + public async Task ThrowsRateLimitExceededExceptionForForbiddenResponse() { var httpClient = Substitute.For(); var response = CreateResponse( @@ -202,6 +202,28 @@ public async Task ThrowsRateLimitExceededExceptionForForbidderResponse() exception.Message); } + [Fact] + public async Task ThrowsSecondaryRateLimitExceededExceptionForForbiddenResponse() + { + var httpClient = Substitute.For(); + var response = CreateResponse( + HttpStatusCode.Forbidden, + "{\"message\":\"You have exceeded a secondary rate limit. Please wait a few minutes before you try again.\"}"); + + httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response)); + var connection = new Connection(new ProductHeaderValue("OctokitTests"), + _exampleUri, + Substitute.For(), + httpClient, + Substitute.For()); + + var exception = await Assert.ThrowsAsync( + () => connection.GetResponse(new Uri("endpoint", UriKind.Relative))); + + Assert.Equal("You have exceeded a secondary rate limit. Please wait a few minutes before you try again.", + exception.Message); + } + [Fact] public async Task ThrowsLoginAttemptsExceededExceptionForForbiddenResponse() { diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs index 81759fdc2a..054763310c 100644 --- a/Octokit/Http/Connection.cs +++ b/Octokit/Http/Connection.cs @@ -719,7 +719,7 @@ static Exception GetExceptionForForbidden(IResponse response) { string body = response.Body as string ?? ""; - if (body.Contains("rate limit exceeded")) + if (body.Contains("rate limit exceeded") || body.Contains("secondary rate limit")) { return new RateLimitExceededException(response); } From 16572601a691e6155873ca409aee17c393a74cb6 Mon Sep 17 00:00:00 2001 From: Chris Simpson Date: Wed, 6 Jul 2022 21:00:17 +0100 Subject: [PATCH 2/2] Changing SecondaryRateLimit to be it's own custom exception --- Octokit.Tests/Http/ConnectionTests.cs | 2 +- .../SecondaryRateLimitExceededException.cs | 67 +++++++++++++++++++ Octokit/Http/Connection.cs | 7 +- 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 Octokit/Exceptions/SecondaryRateLimitExceededException.cs diff --git a/Octokit.Tests/Http/ConnectionTests.cs b/Octokit.Tests/Http/ConnectionTests.cs index 353d9dd550..0dc817c246 100644 --- a/Octokit.Tests/Http/ConnectionTests.cs +++ b/Octokit.Tests/Http/ConnectionTests.cs @@ -217,7 +217,7 @@ public async Task ThrowsSecondaryRateLimitExceededExceptionForForbiddenResponse( httpClient, Substitute.For()); - var exception = await Assert.ThrowsAsync( + var exception = await Assert.ThrowsAsync( () => connection.GetResponse(new Uri("endpoint", UriKind.Relative))); Assert.Equal("You have exceeded a secondary rate limit. Please wait a few minutes before you try again.", diff --git a/Octokit/Exceptions/SecondaryRateLimitExceededException.cs b/Octokit/Exceptions/SecondaryRateLimitExceededException.cs new file mode 100644 index 0000000000..48d264a751 --- /dev/null +++ b/Octokit/Exceptions/SecondaryRateLimitExceededException.cs @@ -0,0 +1,67 @@ +using System; +using System.Diagnostics.CodeAnalysis; +#if !NO_SERIALIZABLE +using System.Runtime.Serialization; +#endif + +namespace Octokit +{ + /// + /// Exception thrown when Secondary GitHub API Rate limits are exceeded. + /// + /// + /// + /// This occurs when GitHub perceives misuse of the API. You may get this if + /// you are polling heavily, creating content rapidly or making concurrent requests. + /// + /// See https://docs.github.com/en/rest/overview/resources-in-the-rest-api#secondary-rate-limits for more details. + /// +#if !NO_SERIALIZABLE + [Serializable] +#endif + [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", + Justification = "These exceptions are specific to the GitHub API and not general purpose exceptions")] + public class SecondaryRateLimitExceededException : ForbiddenException + { + /// + /// Constructs an instance of the class. + /// + /// The HTTP payload from the server + public SecondaryRateLimitExceededException(IResponse response) : this(response, null) + { + } + + /// + /// Constructs an instance of the class. + /// + /// The HTTP payload from the server + /// The inner exception + public SecondaryRateLimitExceededException(IResponse response, Exception innerException) : base(response, innerException) + { + Ensure.ArgumentNotNull(response, nameof(response)); + } + + public override string Message + { + get { return ApiErrorMessageSafe ?? "Secondary API Rate Limit exceeded"; } + } + +#if !NO_SERIALIZABLE + /// + /// Constructs an instance of . + /// + /// + /// The that holds the + /// serialized object data about the exception being thrown. + /// + /// + /// The that contains + /// contextual information about the source or destination. + /// + protected SecondaryRateLimitExceededException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +} diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs index 054763310c..46bb710f12 100644 --- a/Octokit/Http/Connection.cs +++ b/Octokit/Http/Connection.cs @@ -719,11 +719,16 @@ static Exception GetExceptionForForbidden(IResponse response) { string body = response.Body as string ?? ""; - if (body.Contains("rate limit exceeded") || body.Contains("secondary rate limit")) + if (body.Contains("rate limit exceeded")) { return new RateLimitExceededException(response); } + if (body.Contains("secondary rate limit")) + { + return new SecondaryRateLimitExceededException(response); + } + if (body.Contains("number of login attempts exceeded")) { return new LoginAttemptsExceededException(response);