public void TestIsRateLimit(bool expected, string remainin) { var headers = new HttpTestHeaders { { "X-RateLimit-Remainin", remainin }, }; var github = new Github(io.Object, config.Object, null, transport.Object); Assert.AreEqual(expected, github.IsRateLimit(headers)); }
/// <inheritdoc cref="GetRemoteContent(string, bool)"/> /// <param name="fetchingRepoData">Whether is fetching the main repository data.</param> protected (string Content, HttpHeaders Headers) GetRemoteContent(string uri, bool fetchingRepoData = false) { try { return(base.GetRemoteContent(uri)); } catch (TransportException ex) { var github = new Github(IO, Config, Process, GetTransport()); switch (ex.HttpStatusCode) { case HttpStatusCode.Unauthorized: case HttpStatusCode.NotFound: if (!fetchingRepoData) { throw; } if (github.AuthorizeOAuth(host)) { return(base.GetRemoteContent(uri)); } if (!IO.IsInteractive && AttemptCloneFallback()) { return(null, null); } var scopesIssued = Array.Empty <string>(); var scopesNeeded = Array.Empty <string>(); var headers = ex.GetHeaders(); if (headers != null) { if (headers.TryGetValues("X-OAuth-Scopes", out IEnumerable <string> values)) { var scopes = string.Join(string.Empty, values); scopesIssued = scopes.Split(' '); } if (headers.TryGetValues("X-Accepted-OAuth-Scopes", out values)) { var scopes = string.Join(string.Empty, values); scopesNeeded = scopes.Split(' '); } } var scopesFailed = Arr.Difference(scopesIssued, scopesNeeded); // non-authenticated requests get no scopesNeeded, so ask // for credentials authenticated requests which failed some // scopes should ask for new credentials too if (headers == null || scopesNeeded.Length == 0 || scopesFailed.Length > 0) { github.AuthorizeOAuthInteractively(host, $"Your GitHub credentials are required to fetch private repository metadata (<info>{uri}</info>)"); } return(base.GetRemoteContent(uri)); case HttpStatusCode.Forbidden: if (!IO.HasAuthentication(host) && github.AuthorizeOAuth(host)) { return(base.GetRemoteContent(uri)); } if (!IO.IsInteractive && fetchingRepoData && AttemptCloneFallback()) { return(null, null); } var isRateLimited = github.IsRateLimit(ex.GetHeaders()); if (!IO.HasAuthentication(host)) { if (!IO.IsInteractive) { IO.WriteError($"<error>GitHub API limit exhausted. Failed to get metadata for the {uri} repository, try running in interactive mode so that you can enter your GitHub credentials to increase the API limit</error>"); throw; } github.AuthorizeOAuthInteractively(host, $"API limit exhausted. Enter your GitHub credentials to get a larger API limit (<info>{uri}</info>)"); return(base.GetRemoteContent(uri)); } if (isRateLimited) { var(limit, reset) = github.GetRateLimit(ex.GetHeaders()); IO.WriteError($"<error>GitHub API limit ({limit} calls/hr) is exhausted. You are already authorized so you have to wait until {reset} before doing more requests</error>"); } throw; default: throw; } } }