private HttpResponseMessage Get(Uri uri, string accept, int retries) { //work out the basic scope + action we'd need to perform this GET string scope = null; if (_TokenSource.TryParseScope(uri, out var scopePath)) { var action = scopePath == "registry:catalog" ? "*" : "pull"; scope = $"{scopePath}:{action}"; } using (var client = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false })) { client.Timeout = Timeout; HttpResponseMessage result; var message = new HttpRequestMessage { RequestUri = uri }; message.Headers.Authorization = _TokenSource.GetAuthorization(scope); if (!string.IsNullOrEmpty(accept)) { message.Headers.Add("Accept", accept); } try { result = client.SendAsync(message).Result; } catch { if (retries > 0) { result = Get(uri, accept, retries - 1); } else { throw; } } if (result.IsSuccessStatusCode) { return(result); } else if (retries > 0) { if (result.StatusCode == HttpStatusCode.Unauthorized && !string.IsNullOrEmpty(scope)) { var authRequest = _TokenSource.ParseWwwAuthenticate(result.Headers.WwwAuthenticate.First()); if (authRequest.scope != scope) { throw new ArgumentException($"The scope requested by the server ({authRequest.scope}) does not match that expected by the auth engine ({scope})"); } // skip service check for dockerhub, since it returns inconsistent values //if (!IsDockerHub && authRequest.service != Host) { throw new ArgumentException($"The service indicated by the server ({authRequest.service}), does not match that expected by the auth engine ({Host})."); } if (_TokenSource.UpdateAuthorization(authRequest.scope)) { return(Get(uri, accept, retries - 1)); } else { throw new Exception($"Access was denied to the remote resource, and authorization could not be obtained."); } } else if (result.StatusCode == HttpStatusCode.Redirect || result.StatusCode == HttpStatusCode.RedirectKeepVerb) { //decriment retries even though nothing failed, to ensure we don't get caught in a redirect loop return(Get(result.Headers.Location, accept, retries - 1)); } else if (result.StatusCode == HttpStatusCode.NotFound) { throw new NotFoundException(); } else { System.Threading.Thread.Sleep(1000); return(Get(uri, accept, retries - 1)); } } else { throw new Exception($"The remote request failed with status {result.StatusCode}"); } } }