예제 #1
0
        /// <summary>
        /// Converts <see cref="HttpResponseMessage"/> to <see cref="RelayResponse"/>.
        /// </summary>
        /// <param name="httpResponseMessage">Response message.</param>
        /// <returns>Relay response.</returns>
        private async Task <RelayResponse> ToRelayResponseAsync(HttpResponseMessage httpResponseMessage)
        {
            RelayResponse relayResponse = new RelayResponse
            {
                HttpStatusCode     = httpResponseMessage.StatusCode,
                StatusDescription  = httpResponseMessage.ReasonPhrase,
                Headers            = new WebHeaderCollection(),
                RequestEndDateTime = DateTimeOffset.Now,
            };

            // Copy the response headers.
            foreach (KeyValuePair <string, IEnumerable <string> > responseHeader in httpResponseMessage.Headers)
            {
                relayResponse.Headers.Add(responseHeader.Key, string.Join(",", responseHeader.Value));
            }

            if (httpResponseMessage.Content != null)
            {
                foreach (KeyValuePair <string, IEnumerable <string> > responseHeader in httpResponseMessage.Content.Headers)
                {
                    relayResponse.Headers.Add(responseHeader.Key, string.Join(",", responseHeader.Value));
                }

                relayResponse.OutputStream = await httpResponseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);
            }

            return(relayResponse);
        }
예제 #2
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);
            }
        }
예제 #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);
        }