Example #1
0
        protected GitEndPointResponseData SendRequest(
            long requestId,
            Uri requestUri,
            HttpMethod httpMethod,
            string requestContent,
            CancellationToken cancellationToken,
            MediaTypeWithQualityHeaderValue acceptType = null)
        {
            string authString = null;
            string errorMessage;

            if (!this.authentication.IsAnonymous &&
                !this.authentication.TryGetCredentials(this.Tracer, out authString, out errorMessage))
            {
                return(new GitEndPointResponseData(
                           HttpStatusCode.Unauthorized,
                           new GitObjectsHttpException(HttpStatusCode.Unauthorized, errorMessage),
                           shouldRetry: true,
                           message: null,
                           onResponseDisposed: null));
            }

            HttpRequestMessage request = new HttpRequestMessage(httpMethod, requestUri);

            // By default, VSTS auth failures result in redirects to SPS to reauthenticate.
            // To provide more consistent behavior when using the GCM, have them send us 401s instead
            request.Headers.Add("X-TFS-FedAuthRedirect", "Suppress");

            request.Headers.UserAgent.Add(this.userAgentHeader);

            if (!string.IsNullOrEmpty(authString))
            {
                request.Headers.Authorization = new AuthenticationHeaderValue("Basic", authString);
            }

            if (acceptType != null)
            {
                request.Headers.Accept.Add(acceptType);
            }

            if (requestContent != null)
            {
                request.Content = new StringContent(requestContent, Encoding.UTF8, "application/json");
            }

            EventMetadata responseMetadata = new EventMetadata();

            responseMetadata.Add("RequestId", requestId);
            responseMetadata.Add("availableConnections", availableConnections.CurrentCount);

            Stopwatch requestStopwatch = Stopwatch.StartNew();

            availableConnections.Wait(cancellationToken);
            TimeSpan connectionWaitTime = requestStopwatch.Elapsed;

            TimeSpan responseWaitTime = default(TimeSpan);
            GitEndPointResponseData gitEndPointResponseData = null;
            HttpResponseMessage     response = null;

            try
            {
                requestStopwatch.Restart();

                try
                {
                    response = this.client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).GetAwaiter().GetResult();
                }
                finally
                {
                    responseWaitTime = requestStopwatch.Elapsed;
                }

                responseMetadata.Add("CacheName", GetSingleHeaderOrEmpty(response.Headers, "X-Cache-Name"));
                responseMetadata.Add("StatusCode", response.StatusCode);

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    string contentType = GetSingleHeaderOrEmpty(response.Content.Headers, "Content-Type");
                    responseMetadata.Add("ContentType", contentType);

                    this.authentication.ApproveCredentials(this.Tracer, authString);
                    Stream responseStream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult();

                    gitEndPointResponseData = new GitEndPointResponseData(
                        response.StatusCode,
                        contentType,
                        responseStream,
                        message: response,
                        onResponseDisposed: () => availableConnections.Release());
                }
                else
                {
                    errorMessage = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
                    int statusInt = (int)response.StatusCode;

                    bool shouldRetry = ShouldRetry(response.StatusCode);

                    if (response.StatusCode == HttpStatusCode.Unauthorized &&
                        this.authentication.IsAnonymous)
                    {
                        shouldRetry  = false;
                        errorMessage = "Anonymous request was rejected with a 401";
                    }
                    else if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.BadRequest || response.StatusCode == HttpStatusCode.Redirect)
                    {
                        this.authentication.RejectCredentials(this.Tracer, authString);
                        if (!this.authentication.IsBackingOff)
                        {
                            errorMessage = string.Format("Server returned error code {0} ({1}). Your PAT may be expired and we are asking for a new one. Original error message from server: {2}", statusInt, response.StatusCode, errorMessage);
                        }
                        else
                        {
                            errorMessage = string.Format("Server returned error code {0} ({1}) after successfully renewing your PAT. You may not have access to this repo. Original error message from server: {2}", statusInt, response.StatusCode, errorMessage);
                        }
                    }
                    else
                    {
                        errorMessage = string.Format("Server returned error code {0} ({1}). Original error message from server: {2}", statusInt, response.StatusCode, errorMessage);
                    }

                    gitEndPointResponseData = new GitEndPointResponseData(
                        response.StatusCode,
                        new GitObjectsHttpException(response.StatusCode, errorMessage),
                        shouldRetry,
                        message: response,
                        onResponseDisposed: () => availableConnections.Release());
                }
            }
            catch (TaskCanceledException)
            {
                cancellationToken.ThrowIfCancellationRequested();

                errorMessage = string.Format("Request to {0} timed out", requestUri);

                gitEndPointResponseData = new GitEndPointResponseData(
                    HttpStatusCode.RequestTimeout,
                    new GitObjectsHttpException(HttpStatusCode.RequestTimeout, errorMessage),
                    shouldRetry: true,
                    message: response,
                    onResponseDisposed: () => availableConnections.Release());
            }
            catch (HttpRequestException httpRequestException) when(httpRequestException.InnerException is System.Security.Authentication.AuthenticationException)
            {
                // This exception is thrown on OSX, when user declines to give permission to access certificate
                gitEndPointResponseData = new GitEndPointResponseData(
                    HttpStatusCode.Unauthorized,
                    httpRequestException.InnerException,
                    shouldRetry: false,
                    message: response,
                    onResponseDisposed: () => availableConnections.Release());
            }
            catch (WebException ex)
            {
                gitEndPointResponseData = new GitEndPointResponseData(
                    HttpStatusCode.InternalServerError,
                    ex,
                    shouldRetry: true,
                    message: response,
                    onResponseDisposed: () => availableConnections.Release());
            }
            finally
            {
                responseMetadata.Add("connectionWaitTimeMS", $"{connectionWaitTime.TotalMilliseconds:F4}");
                responseMetadata.Add("responseWaitTimeMS", $"{responseWaitTime.TotalMilliseconds:F4}");

                this.Tracer.RelatedEvent(EventLevel.Informational, "NetworkResponse", responseMetadata);

                if (gitEndPointResponseData == null)
                {
                    // If gitEndPointResponseData is null there was an unhandled exception
                    if (response != null)
                    {
                        response.Dispose();
                    }

                    availableConnections.Release();
                }
            }

            return(gitEndPointResponseData);
        }
Example #2
0
        public virtual List <GitObjectSize> QueryForFileSizes(IEnumerable <string> objectIds, CancellationToken cancellationToken)
        {
            long requestId = HttpRequestor.GetNewRequestId();

            string objectIdsJson       = ToJsonList(objectIds);
            Uri    cacheServerEndpoint = new Uri(this.CacheServer.SizesEndpointUrl);
            Uri    originEndpoint      = new Uri(this.enlistment.RepoUrl + GVFSConstants.Endpoints.GVFSSizes);

            EventMetadata metadata = new EventMetadata();

            metadata.Add("RequestId", requestId);
            int objectIdCount = objectIds.Count();

            if (objectIdCount > 10)
            {
                metadata.Add("ObjectIdCount", objectIdCount);
            }
            else
            {
                metadata.Add("ObjectIdJson", objectIdsJson);
            }

            this.Tracer.RelatedEvent(EventLevel.Informational, "QueryFileSizes", metadata, Keywords.Network);

            RetryWrapper <List <GitObjectSize> > retrier = new RetryWrapper <List <GitObjectSize> >(this.RetryConfig.MaxAttempts, cancellationToken);

            retrier.OnFailure += RetryWrapper <List <GitObjectSize> > .StandardErrorHandler(this.Tracer, requestId, "QueryFileSizes");

            RetryWrapper <List <GitObjectSize> > .InvocationResult requestTask = retrier.Invoke(
                tryCount =>
            {
                Uri gvfsEndpoint;
                if (nextCacheServerAttemptTime < DateTime.Now)
                {
                    gvfsEndpoint = cacheServerEndpoint;
                }
                else
                {
                    gvfsEndpoint = originEndpoint;
                }

                using (GitEndPointResponseData response = this.SendRequest(requestId, gvfsEndpoint, HttpMethod.Post, objectIdsJson, cancellationToken))
                {
                    if (response.StatusCode == HttpStatusCode.NotFound)
                    {
                        nextCacheServerAttemptTime = nextCacheServerAttemptTime.AddMinutes(15);
                        return(new RetryWrapper <List <GitObjectSize> > .CallbackResult(response.Error, true));
                    }

                    if (response.HasErrors)
                    {
                        return(new RetryWrapper <List <GitObjectSize> > .CallbackResult(response.Error, response.ShouldRetry));
                    }

                    string objectSizesString         = response.RetryableReadToEnd();
                    List <GitObjectSize> objectSizes = JsonConvert.DeserializeObject <List <GitObjectSize> >(objectSizesString);
                    return(new RetryWrapper <List <GitObjectSize> > .CallbackResult(objectSizes));
                }
            });

            return(requestTask.Result ?? new List <GitObjectSize>(0));
        }
Example #3
0
        protected GitEndPointResponseData SendRequest(
            long requestId,
            Uri requestUri,
            HttpMethod httpMethod,
            string requestContent,
            CancellationToken cancellationToken,
            MediaTypeWithQualityHeaderValue acceptType = null)
        {
            string authString;
            string errorMessage;

            if (!this.authentication.TryGetCredentials(this.Tracer, out authString, out errorMessage))
            {
                return(new GitEndPointResponseData(
                           HttpStatusCode.Unauthorized,
                           new GitObjectsHttpException(HttpStatusCode.Unauthorized, errorMessage),
                           shouldRetry: true,
                           message: null,
                           onResponseDisposed: null));
            }

            HttpRequestMessage request = new HttpRequestMessage(httpMethod, requestUri);

            request.Headers.UserAgent.Add(this.userAgentHeader);

            if (!string.IsNullOrEmpty(authString))
            {
                request.Headers.Authorization = new AuthenticationHeaderValue("Basic", authString);
            }

            if (acceptType != null)
            {
                request.Headers.Accept.Add(acceptType);
            }

            if (requestContent != null)
            {
                request.Content = new StringContent(requestContent, Encoding.UTF8, "application/json");
            }

            EventMetadata responseMetadata = new EventMetadata();

            responseMetadata.Add("RequestId", requestId);
            responseMetadata.Add("availableConnections", availableConnections.CurrentCount);

            Stopwatch requestStopwatch = Stopwatch.StartNew();

            availableConnections.Wait(cancellationToken);
            TimeSpan connectionWaitTime = requestStopwatch.Elapsed;

            TimeSpan responseWaitTime = default(TimeSpan);
            GitEndPointResponseData gitEndPointResponseData = null;
            HttpResponseMessage     response = null;

            try
            {
                requestStopwatch.Restart();

                try
                {
                    response = this.client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).GetAwaiter().GetResult();
                }
                finally
                {
                    responseWaitTime = requestStopwatch.Elapsed;
                }

                responseMetadata.Add("CacheName", GetSingleHeaderOrEmpty(response.Headers, "X-Cache-Name"));
                responseMetadata.Add("StatusCode", response.StatusCode);

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    string contentType = GetSingleHeaderOrEmpty(response.Content.Headers, "Content-Type");
                    responseMetadata.Add("ContentType", contentType);

                    this.authentication.ConfirmCredentialsWorked(authString);
                    Stream responseStream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult();

                    gitEndPointResponseData = new GitEndPointResponseData(
                        response.StatusCode,
                        contentType,
                        responseStream,
                        message: response,
                        onResponseDisposed: () => availableConnections.Release());
                }
                else
                {
                    errorMessage = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
                    int statusInt = (int)response.StatusCode;

                    if (string.IsNullOrWhiteSpace(errorMessage))
                    {
                        if (response.StatusCode == HttpStatusCode.Unauthorized)
                        {
                            this.authentication.Revoke(authString);
                            if (!this.authentication.IsBackingOff)
                            {
                                errorMessage = "Server returned error code 401 (Unauthorized). Your PAT may be expired and we are asking for a new one.";
                            }
                            else
                            {
                                errorMessage = "Server returned error code 401 (Unauthorized) after successfully renewing your PAT. You may not have access to this repo";
                            }
                        }
                        else
                        {
                            errorMessage = string.Format("Server returned error code {0} ({1})", statusInt, response.StatusCode);
                        }
                    }

                    gitEndPointResponseData = new GitEndPointResponseData(
                        response.StatusCode,
                        new GitObjectsHttpException(response.StatusCode, errorMessage),
                        ShouldRetry(response.StatusCode),
                        message: response,
                        onResponseDisposed: () => availableConnections.Release());
                }
            }
            catch (TaskCanceledException)
            {
                cancellationToken.ThrowIfCancellationRequested();

                errorMessage = string.Format("Request to {0} timed out", requestUri);

                gitEndPointResponseData = new GitEndPointResponseData(
                    HttpStatusCode.RequestTimeout,
                    new GitObjectsHttpException(HttpStatusCode.RequestTimeout, errorMessage),
                    shouldRetry: true,
                    message: response,
                    onResponseDisposed: () => availableConnections.Release());
            }
            catch (WebException ex)
            {
                gitEndPointResponseData = new GitEndPointResponseData(
                    HttpStatusCode.InternalServerError,
                    ex,
                    shouldRetry: true,
                    message: response,
                    onResponseDisposed: () => availableConnections.Release());
            }
            finally
            {
                responseMetadata.Add("connectionWaitTimeMS", $"{connectionWaitTime.TotalMilliseconds:F4}");
                responseMetadata.Add("responseWaitTimeMS", $"{responseWaitTime.TotalMilliseconds:F4}");

                this.Tracer.RelatedEvent(EventLevel.Informational, "NetworkResponse", responseMetadata);

                if (gitEndPointResponseData == null)
                {
                    // If gitEndPointResponseData is null there was an unhandled exception
                    if (response != null)
                    {
                        response.Dispose();
                    }

                    availableConnections.Release();
                }
            }

            return(gitEndPointResponseData);
        }
Example #4
0
        public bool TryQueryGVFSConfig(out GVFSConfig gvfsConfig)
        {
            gvfsConfig = null;

            Uri    gvfsConfigEndpoint;
            string gvfsConfigEndpointString = this.repoUrl + GVFSConstants.Endpoints.GVFSConfig;

            try
            {
                gvfsConfigEndpoint = new Uri(gvfsConfigEndpointString);
            }
            catch (UriFormatException e)
            {
                EventMetadata metadata = new EventMetadata();
                metadata.Add("Method", nameof(this.TryQueryGVFSConfig));
                metadata.Add("Exception", e.ToString());
                metadata.Add("Url", gvfsConfigEndpointString);
                this.Tracer.RelatedError(metadata, "UriFormatException when constructing Uri", Keywords.Network);

                return(false);
            }

            CancellationToken neverCanceledToken = new CancellationToken(canceled: false);
            long requestId = HttpRequestor.GetNewRequestId();
            RetryWrapper <GVFSConfig> retrier = new RetryWrapper <GVFSConfig>(this.RetryConfig.MaxAttempts, neverCanceledToken);

            retrier.OnFailure += RetryWrapper <GVFSConfig> .StandardErrorHandler(this.Tracer, requestId, "QueryGvfsConfig");

            RetryWrapper <GVFSConfig> .InvocationResult output = retrier.Invoke(
                tryCount =>
            {
                GitEndPointResponseData response = this.SendRequest(
                    requestId,
                    gvfsConfigEndpoint,
                    HttpMethod.Get,
                    requestContent: null,
                    cancellationToken: neverCanceledToken);

                if (response.HasErrors)
                {
                    return(new RetryWrapper <GVFSConfig> .CallbackResult(response.Error, response.ShouldRetry));
                }

                try
                {
                    using (StreamReader reader = new StreamReader(response.Stream))
                    {
                        string configString = reader.RetryableReadToEnd();
                        GVFSConfig config   = JsonConvert.DeserializeObject <GVFSConfig>(configString);
                        return(new RetryWrapper <GVFSConfig> .CallbackResult(config));
                    }
                }
                catch (JsonReaderException e)
                {
                    return(new RetryWrapper <GVFSConfig> .CallbackResult(e, false));
                }
            });

            if (output.Succeeded)
            {
                gvfsConfig = output.Result;
                return(true);
            }

            return(false);
        }
        public bool TryQueryGVFSConfig(bool logErrors, out ServerGVFSConfig serverGVFSConfig, out HttpStatusCode?httpStatus, out string errorMessage)
        {
            serverGVFSConfig = null;
            httpStatus       = null;
            errorMessage     = null;

            Uri    gvfsConfigEndpoint;
            string gvfsConfigEndpointString = this.repoUrl + GVFSConstants.Endpoints.GVFSConfig;

            try
            {
                gvfsConfigEndpoint = new Uri(gvfsConfigEndpointString);
            }
            catch (UriFormatException e)
            {
                EventMetadata metadata = new EventMetadata();
                metadata.Add("Method", nameof(this.TryQueryGVFSConfig));
                metadata.Add("Exception", e.ToString());
                metadata.Add("Url", gvfsConfigEndpointString);
                this.Tracer.RelatedError(metadata, "UriFormatException when constructing Uri", Keywords.Network);

                return(false);
            }

            long requestId = HttpRequestor.GetNewRequestId();
            RetryWrapper <ServerGVFSConfig> retrier = new RetryWrapper <ServerGVFSConfig>(this.RetryConfig.MaxAttempts, CancellationToken.None);

            if (logErrors)
            {
                retrier.OnFailure += RetryWrapper <ServerGVFSConfig> .StandardErrorHandler(this.Tracer, requestId, "QueryGvfsConfig");
            }

            RetryWrapper <ServerGVFSConfig> .InvocationResult output = retrier.Invoke(
                tryCount =>
            {
                using (GitEndPointResponseData response = this.SendRequest(
                           requestId,
                           gvfsConfigEndpoint,
                           HttpMethod.Get,
                           requestContent: null,
                           cancellationToken: CancellationToken.None))
                {
                    if (response.HasErrors)
                    {
                        return(new RetryWrapper <ServerGVFSConfig> .CallbackResult(response.Error, response.ShouldRetry));
                    }

                    try
                    {
                        string configString     = response.RetryableReadToEnd();
                        ServerGVFSConfig config = JsonConvert.DeserializeObject <ServerGVFSConfig>(configString);
                        return(new RetryWrapper <ServerGVFSConfig> .CallbackResult(config));
                    }
                    catch (JsonReaderException e)
                    {
                        return(new RetryWrapper <ServerGVFSConfig> .CallbackResult(e, shouldRetry: false));
                    }
                }
            });

            if (output.Succeeded)
            {
                serverGVFSConfig = output.Result;
                httpStatus       = HttpStatusCode.OK;
                return(true);
            }

            GitObjectsHttpException httpException = output.Error as GitObjectsHttpException;

            if (httpException != null)
            {
                httpStatus   = httpException.StatusCode;
                errorMessage = httpException.Message;
            }

            if (logErrors)
            {
                this.Tracer.RelatedError(
                    new EventMetadata
                {
                    { "Exception", output.Error.ToString() }
                },
                    $"{nameof(this.TryQueryGVFSConfig)} failed");
            }

            return(false);
        }