Ejemplo n.º 1
0
        /// <summary>
        /// Handles the HTTP request received over the hybrid connection.
        /// </summary>
        /// <param name="context">Relayed request context.</param>
        /// <returns>Task tracking operation.</returns>
        private async Task HandleProxyRequestAsync(RelayedHttpListenerContext context)
        {
            RelayRequest relayRequest = new RelayRequest
            {
                Headers              = context.Request.Headers,
                HttpMethod           = new HttpMethod(context.Request.HttpMethod),
                InputStream          = context.Request.InputStream,
                RequestPathAndQuery  = context.Request.Url.AbsoluteUri.Substring(this.relayUrl.Length),
                RequestStartDateTime = DateTimeOffset.Now,
            };

            try
            {
                RelayResponse relayResponse = await this.relayManager.HandleRelayRequestAsync(relayRequest).ConfigureAwait(false);

                context.Response.StatusCode        = relayResponse.HttpStatusCode;
                context.Response.StatusDescription = relayResponse.StatusDescription;

                // Copy over outgoing headers.
                foreach (string headerName in relayResponse.Headers.Keys)
                {
                    // Not copying over the Transfer-Encoding header as the communication between server and Relay, and Relay and client are
                    // different connections.
                    if (headerName.Equals("Transfer-Encoding", StringComparison.OrdinalIgnoreCase))
                    {
                        continue;
                    }

                    context.Response.Headers[headerName] = relayResponse.Headers[headerName];
                }

                if (relayResponse.OutputStream != null)
                {
                    await relayResponse.OutputStream.CopyToAsync(context.Response.OutputStream).ConfigureAwait(false);
                }

                await context.Response.CloseAsync().ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                context.Response.StatusCode        = HttpStatusCode.InternalServerError;
                context.Response.StatusDescription = "Exception occurred at Server";

                byte[] errorMessage = Encoding.UTF8.GetBytes(ex.Message);
                await context.Response.OutputStream.WriteAsync(errorMessage, 0, errorMessage.Length).ConfigureAwait(false);
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Converts <see cref="RelayRequest"/> to <see cref="HttpRequestMessage"/>.
        /// </summary>
        /// <param name="relayRequest">Incoming relay request.</param>
        /// <param name="requestId">Request Id.</param>
        /// <returns>Http request message.</returns>
        private async Task <HttpRequestMessage> ToHttpRequestMessageAsync(RelayRequest relayRequest, string requestId)
        {
            Uri internalRequestUrl = new Uri(this.internalServiceUrl + "/" + relayRequest.RequestPathAndQuery.TrimStart('/'));

            HttpRequestMessage httpRequestMessage = new HttpRequestMessage(relayRequest.HttpMethod, internalRequestUrl);

            // Prepare request content.
            // Caveat here! .NetFX uses WebRequest behind HttpClient so it does not allow body content in GET requests
            // but NetCore uses an entirely different stack and thus allows body in GET.
            // Once ADAL Library starts allowing UI based auth in NetCore we can get rid of NetFX altogether and allow users
            // to pass request body in GET too.
            if (relayRequest.InputStream != null && relayRequest.HttpMethod != HttpMethod.Get && relayRequest.HttpMethod != HttpMethod.Head)
            {
                MemoryStream memoryStream = new MemoryStream((int)relayRequest.InputStream.Length);
                await relayRequest.InputStream.CopyToAsync(memoryStream).ConfigureAwait(false);

                httpRequestMessage.Content = new StreamContent(memoryStream);
                memoryStream.Seek(0, SeekOrigin.Begin);

                httpRequestMessage.Content.Headers.ContentLength = memoryStream.Length;
                RelayRequestManager.CopyContentHeader(httpRequestMessage.Content, relayRequest.Headers);

                if (httpRequestMessage.Content.Headers.ContentLength != memoryStream.Length)
                {
                    this.logger.LogWarning(
                        "Content-Length header mismatch for request Id '{0}'. Value from request '{1}'. Value from actual content stream '{2}'",
                        requestId,
                        httpRequestMessage.Content.Headers.ContentLength.GetValueOrDefault(),
                        memoryStream.Length);
                }
            }

            // Try to blindly add the headers. Content headers will get filtered out here.
            foreach (string headerName in relayRequest.Headers.Keys)
            {
                httpRequestMessage.Headers.TryAddWithoutValidation(headerName, relayRequest.Headers[headerName]);
            }

            // Set the correct host header.
            httpRequestMessage.Headers.Host = internalRequestUrl.Host;

            return(httpRequestMessage);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Handles the incoming request, passes it down to the internal service and returns the response.
        /// </summary>
        /// <param name="relayRequest">Incoming relay request.</param>
        /// <returns>Response from the internal service.</returns>
        public async Task <RelayResponse> HandleRelayRequestAsync(RelayRequest relayRequest)
        {
            if (relayRequest is null)
            {
                throw new ArgumentNullException(nameof(relayRequest));
            }

            string requestId = Guid.NewGuid().ToString();

            this.logger.LogTrace("Received request with Id '{0}'", requestId);

            // Inform listener that a request has been received.
            if (this.relayRequestEventListener != null)
            {
                RelayRequest clonedRequest = relayRequest.Clone() as RelayRequest;
#pragma warning disable CS4014 // We want eventing pipeline to run in parallel and not block the actual request execution.
                Task.Run(async() =>
                {
                    try
                    {
                        await this.relayRequestEventListener.RequestReceivedAsync(requestId, clonedRequest).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        // Ignoring exceptions from listeners.
                        this.logger.LogWarning("Relay request event listener failed for request Id '{0}' during 'RequestReceivedAsync' phase with exception '{1}'. Ignoring.", requestId, ex);
                    }
                });
#pragma warning restore CS4014 // We want eventing pipeline to run in parallel and not block the actual request execution.
            }

            HttpRequestMessage httpRequestMessage = null;

            try
            {
                httpRequestMessage = await this.ToHttpRequestMessageAsync(relayRequest, requestId).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                this.logger.LogError("CRITICAL ERROR!!! Failed to convert Relay request into outgoing request. Error - '{0}'. Request Id - '{1}'", ex, requestId);
                throw;
            }

            // Pass the request through all the plugins.
            foreach (ITunnelRelayPlugin tunnelRelayPlugin in this.tunnelRelayPlugins)
            {
                try
                {
                    httpRequestMessage = await tunnelRelayPlugin.PreProcessRequestToServiceAsync(httpRequestMessage).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    this.logger.LogError("CRITICAL ERROR!!! Plugin '{0}' failed with error - '{1}' for request Id '{2}' during 'PreProcessRequestToServiceAsync' phase", tunnelRelayPlugin.PluginName, ex, requestId);
                    throw;
                }
            }

            HttpResponseMessage httpResponseMessage;

            try
            {
                this.logger.LogTrace("Making external request to server for request Id '{0}'", requestId);
                httpResponseMessage = await this.httpClient.SendAsync(httpRequestMessage).ConfigureAwait(false);

                this.logger.LogTrace("Received response from server for request Id '{0}'", requestId);
            }
            catch (HttpRequestException httpException)
            {
                this.logger.LogError("Hit exception while sending request to server for request Id '{0}'. Error '{1}'", requestId, httpException);
                httpResponseMessage = new HttpResponseMessage(HttpStatusCode.BadGateway)
                {
                    Content = new StringContent(httpException.ToString()),
                };
            }
            catch (Exception ex)
            {
                this.logger.LogError("Hit critical exception while processing request '{0}'. Error '{1}'", requestId, ex);
                throw;
            }

            // Pass the response through all the plugins.
            foreach (ITunnelRelayPlugin tunnelRelayPlugin in this.tunnelRelayPlugins)
            {
                try
                {
                    httpResponseMessage = await tunnelRelayPlugin.PostProcessResponseFromServiceAsync(httpResponseMessage).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    this.logger.LogError("CRITICAL ERROR!!! Plugin '{0}' failed with error - '{1}' for request Id '{2}' during 'PostProcessResponseFromServiceAsync' phase", tunnelRelayPlugin.PluginName, ex, requestId);
                    throw;
                }
            }

            RelayResponse relayResponse = null;

            try
            {
                relayResponse = await this.ToRelayResponseAsync(httpResponseMessage).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                this.logger.LogError("Failed to convert outgoing response into relay response for request '{0}'. Error '{1}'", requestId, ex);
            }

            if (this.relayRequestEventListener != null)
            {
                RelayResponse clonedResponse = relayResponse.Clone() as RelayResponse;
#pragma warning disable CS4014 // We want eventing pipeline to run in parallel and not slowdown the actual request execution.
                Task.Run(async() =>
                {
                    try
                    {
                        await this.relayRequestEventListener.ResponseSentAsync(requestId, clonedResponse).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        // Ignoring exceptions from listeners.
                        this.logger.LogWarning("Relay request event listener failed for request Id '{0}' during 'ResponseSentAsync' phase with exception '{1}'. Ignoring.", requestId, ex);
                    }
                });
#pragma warning restore CS4014 // We want eventing pipeline to run in parallel and not slowdown the actual request execution.
            }

            return(relayResponse);
        }