Пример #1
0
        internal async Task ReturnResponseMessageAsync()
        {
            // Check if the response is already returning because the TrySetResult below could happen a bit late
            // (as it happens on a different thread) by which point the CompleteResponseAsync could run and calls this
            // method again.
            if (!_returningResponse)
            {
                _returningResponse = true;

                try
                {
                    await _responseFeature.FireOnSendingHeadersAsync();
                }
                catch (Exception ex)
                {
                    Abort(ex);
                    return;
                }

                // Copy the feature collection so we're not multi-threading on the same collection.
                var newFeatures = new FeatureCollection();
                foreach (var pair in _httpContext.Features)
                {
                    newFeatures[pair.Key] = pair.Value;
                }
                var serverResponseFeature = _httpContext.Features.Get <IHttpResponseFeature>() !;
                // The client gets a deep copy of this so they can interact with the body stream independently of the server.
                var clientResponseFeature = new HttpResponseFeature()
                {
                    StatusCode   = serverResponseFeature.StatusCode,
                    ReasonPhrase = serverResponseFeature.ReasonPhrase,
                    Headers      = serverResponseFeature.Headers,
                    Body         = _responseReaderStream
                };
                newFeatures.Set <IHttpResponseFeature>(clientResponseFeature);
                newFeatures.Set <IHttpResponseBodyFeature>(new StreamResponseBodyFeature(_responseReaderStream));
                _responseTcs.TrySetResult(new DefaultHttpContext(newFeatures));
            }
        }
Пример #2
0
        public async Task CompareRequestResponse()
        {
            var requestFeature = new HttpRequestFeature {
                Path = "/api/batch"
            };

            requestFeature.Headers.Add(HeaderNames.ContentType,
                                       "multipart/mixed; boundary=\"batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e\"");
            requestFeature.Body = TestUtilities.GetNormalizedContentStream("MultipartRequest.txt");
            var responseFeature = new HttpResponseFeature();
            var mockedEvents    = new MockedBatchEventHandler();

            using (responseFeature.Body = new MemoryStream())
            {
                await AssertExecution(requestFeature, responseFeature, mockedEvents,
                                      CreateFirstResponse(),
                                      CreateSecondResponse(),
                                      CreateThirdResponse(),
                                      CreateFourthResponse()).ConfigureAwait(false);

                Assert.Equal(StatusCodes.Status200OK, responseFeature.StatusCode);
                var refText = await TestUtilities.GetNormalizedContentStream("MultipartResponse.txt")
                              .ReadAsStringAsync().ConfigureAwait(false);

                responseFeature.Body.Position = 0;
                var outputText = await responseFeature.Body.ReadAsStringAsync().ConfigureAwait(false);

                var boundary = Regex.Match(outputText, "--(.+?)--").Groups[1].Value;
                refText = refText.Replace("61cfbe41-7ea6-4771-b1c5-b43564208ee5",
                                          boundary); // replace with current boundary;
                Assert.Equal(refText, outputText);
                Assert.Equal(1, mockedEvents.BatchEndCount);
                Assert.Equal(1, mockedEvents.BatchStartCount);
                Assert.Equal(4, mockedEvents.BatchRequestPreparationCount);
                Assert.Equal(4, mockedEvents.BatchRequestExecutingCount);
                Assert.Equal(4, mockedEvents.BatchRequestExecutedCount);
            }
        }
Пример #3
0
        public async Task NoBoundary()
        {
            var requestFeature = new HttpRequestFeature {
                Path = "/api/batch"
            };

            requestFeature.Headers.Add(HeaderNames.ContentType, "multipart/mixed");
            requestFeature.Body = Stream.Null;
            var responseFeature = new HttpResponseFeature();
            var mockedEvents    = new MockedBatchEventHandler();

            using (responseFeature.Body = new MemoryStream())
            {
                await AssertExecution(requestFeature, responseFeature, mockedEvents,
                                      CreateFirstResponse(),
                                      CreateSecondResponse(),
                                      CreateThirdResponse(),
                                      CreateFourthResponse()).ConfigureAwait(false);

                Assert.Equal(StatusCodes.Status400BadRequest, responseFeature.StatusCode);
                Assert.Equal(0, mockedEvents.BatchStartCount);
            }
        }
Пример #4
0
        private static void CloneHttpContext(HttpContext context, HttpConnectionContext connection)
        {
            // The reason we're copying the base features instead of the HttpContext properties is
            // so that we can get all of the logic built into DefaultHttpContext to extract higher level
            // structure from the low level properties
            var existingRequestFeature = context.Features.Get <IHttpRequestFeature>() !;

            var requestFeature = new HttpRequestFeature();

            requestFeature.Protocol    = existingRequestFeature.Protocol;
            requestFeature.Method      = existingRequestFeature.Method;
            requestFeature.Scheme      = existingRequestFeature.Scheme;
            requestFeature.Path        = existingRequestFeature.Path;
            requestFeature.PathBase    = existingRequestFeature.PathBase;
            requestFeature.QueryString = existingRequestFeature.QueryString;
            requestFeature.RawTarget   = existingRequestFeature.RawTarget;
            var requestHeaders = new Dictionary <string, StringValues>(existingRequestFeature.Headers.Count, StringComparer.OrdinalIgnoreCase);

            foreach (var header in existingRequestFeature.Headers)
            {
                requestHeaders[header.Key] = header.Value;
            }
            requestFeature.Headers = new HeaderDictionary(requestHeaders);

            var existingConnectionFeature = context.Features.Get <IHttpConnectionFeature>();
            var connectionFeature         = new HttpConnectionFeature();

            if (existingConnectionFeature != null)
            {
                connectionFeature.ConnectionId    = existingConnectionFeature.ConnectionId;
                connectionFeature.LocalIpAddress  = existingConnectionFeature.LocalIpAddress;
                connectionFeature.LocalPort       = existingConnectionFeature.LocalPort;
                connectionFeature.RemoteIpAddress = existingConnectionFeature.RemoteIpAddress;
                connectionFeature.RemotePort      = existingConnectionFeature.RemotePort;
            }

            // The response is a dud, you can't do anything with it anyways
            var responseFeature = new HttpResponseFeature();

            var features = new FeatureCollection();

            features.Set <IHttpRequestFeature>(requestFeature);
            features.Set <IHttpResponseFeature>(responseFeature);
            features.Set <IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null));
            features.Set <IHttpConnectionFeature>(connectionFeature);

            // REVIEW: We could strategically look at adding other features but it might be better
            // if we expose a callback that would allow the user to preserve HttpContext properties.

            var newHttpContext = new DefaultHttpContext(features);

            newHttpContext.TraceIdentifier = context.TraceIdentifier;

            var endpointFeature = context.Features.Get <IEndpointFeature>();

            newHttpContext.SetEndpoint(endpointFeature?.Endpoint);

            CloneUser(newHttpContext, context);

            connection.ServiceScope        = context.RequestServices.CreateAsyncScope();
            newHttpContext.RequestServices = connection.ServiceScope.Value.ServiceProvider;

            // REVIEW: This extends the lifetime of anything that got put into HttpContext.Items
            newHttpContext.Items = new Dictionary <object, object?>(context.Items);

            connection.HttpContext = newHttpContext;
        }
 public override void OnStarting(Func <object, Task> callback, object state)
 {
     HttpResponseFeature.OnStarting(callback, state);
 }
 public override void OnCompleted(Func <object, Task> callback, object state)
 {
     HttpResponseFeature.OnCompleted(callback, state);
 }
Пример #7
0
        private static HttpContext CloneHttpContext(HttpContext context)
        {
            // The reason we're copying the base features instead of the HttpContext properties is
            // so that we can get all of the logic built into DefaultHttpContext to extract higher level
            // structure from the low level properties
            var existingRequestFeature = context.Features.Get <IHttpRequestFeature>();

            var requestFeature = new HttpRequestFeature();

            requestFeature.Protocol    = existingRequestFeature.Protocol;
            requestFeature.Method      = existingRequestFeature.Method;
            requestFeature.Scheme      = existingRequestFeature.Scheme;
            requestFeature.Path        = existingRequestFeature.Path;
            requestFeature.PathBase    = existingRequestFeature.PathBase;
            requestFeature.QueryString = existingRequestFeature.QueryString;
            requestFeature.RawTarget   = existingRequestFeature.RawTarget;
            var requestHeaders = new Dictionary <string, StringValues>(existingRequestFeature.Headers.Count, StringComparer.Ordinal);

            foreach (var header in existingRequestFeature.Headers)
            {
                requestHeaders[header.Key] = header.Value;
            }
            requestFeature.Headers = new HeaderDictionary(requestHeaders);

            var existingConnectionFeature = context.Features.Get <IHttpConnectionFeature>();
            var connectionFeature         = new HttpConnectionFeature();

            if (existingConnectionFeature != null)
            {
                connectionFeature.ConnectionId    = existingConnectionFeature.ConnectionId;
                connectionFeature.LocalIpAddress  = existingConnectionFeature.LocalIpAddress;
                connectionFeature.LocalPort       = existingConnectionFeature.LocalPort;
                connectionFeature.RemoteIpAddress = existingConnectionFeature.RemoteIpAddress;
                connectionFeature.RemotePort      = existingConnectionFeature.RemotePort;
            }

            // The response is a dud, you can't do anything with it anyways
            var responseFeature = new HttpResponseFeature();

            var features = new FeatureCollection();

            features.Set <IHttpRequestFeature>(requestFeature);
            features.Set <IHttpResponseFeature>(responseFeature);
            features.Set <IHttpConnectionFeature>(connectionFeature);

            // REVIEW: We could strategically look at adding other features but it might be better
            // if we expose a callback that would allow the user to preserve HttpContext properties.

            var newHttpContext = new DefaultHttpContext(features);

            newHttpContext.TraceIdentifier = context.TraceIdentifier;

            CloneUser(newHttpContext, context);

            // Making request services function property could be tricky and expensive as it would require
            // DI scope per connection. It would also mean that services resolved in middleware leading up to here
            // wouldn't be the same instance (but maybe that's fine). For now, we just return an empty service provider
            newHttpContext.RequestServices = EmptyServiceProvider.Instance;

            // REVIEW: This extends the lifetime of anything that got put into HttpContext.Items
            newHttpContext.Items = new Dictionary <object, object>(context.Items);
            return(newHttpContext);
        }
Пример #8
0
        private static FeatureCollection BuildFeatureCollection(HttpRequestFeature requestFeature, HttpResponseFeature responseFeature)
        {
            var features = new FeatureCollection();

            features.Set <IHttpRequestFeature>(requestFeature);
            features.Set <IHttpResponseFeature>(responseFeature);
            return(features);
        }
Пример #9
0
        public void TestNegotiateHandlerWithMultipleEndpointsAndCustomRouter()
        {
            var config          = new ConfigurationBuilder().Build();
            var router          = new TestCustomRouter();
            var serviceProvider = new ServiceCollection().AddSignalR()
                                  .AddAzureSignalR(
                o => o.Endpoints = new ServiceEndpoint[]
            {
                new ServiceEndpoint(ConnectionString2),
                new ServiceEndpoint(ConnectionString3, name: "chosen"),
                new ServiceEndpoint(ConnectionString4),
            })
                                  .Services
                                  .AddLogging()
                                  .AddSingleton <IEndpointRouter>(router)
                                  .AddSingleton <IConfiguration>(config)
                                  .BuildServiceProvider();

            var requestFeature = new HttpRequestFeature
            {
                Path        = "/user/path/negotiate/",
                QueryString = "?endpoint=chosen"
            };
            var features = new FeatureCollection();

            features.Set <IHttpRequestFeature>(requestFeature);
            var httpContext = new DefaultHttpContext(features);

            var handler           = serviceProvider.GetRequiredService <NegotiateHandler>();
            var negotiateResponse = handler.Process(httpContext, "chat");

            Assert.NotNull(negotiateResponse);
            Assert.Equal($"http://localhost3/client/?hub=chat&asrs.op=%2Fuser%2Fpath&endpoint=chosen", negotiateResponse.Url);

            // With no query string should return 400
            requestFeature = new HttpRequestFeature
            {
                Path = "/user/path/negotiate/",
            };

            var responseFeature = new HttpResponseFeature();

            features.Set <IHttpRequestFeature>(requestFeature);
            features.Set <IHttpResponseFeature>(responseFeature);
            httpContext = new DefaultHttpContext(features);

            handler           = serviceProvider.GetRequiredService <NegotiateHandler>();
            negotiateResponse = handler.Process(httpContext, "chat");

            Assert.Null(negotiateResponse);

            Assert.Equal(400, responseFeature.StatusCode);

            // With no query string should return 400
            requestFeature = new HttpRequestFeature
            {
                Path        = "/user/path/negotiate/",
                QueryString = "?endpoint=notexists"
            };

            responseFeature = new HttpResponseFeature();
            features.Set <IHttpRequestFeature>(requestFeature);
            features.Set <IHttpResponseFeature>(responseFeature);
            httpContext = new DefaultHttpContext(features);

            handler = serviceProvider.GetRequiredService <NegotiateHandler>();
            Assert.Throws <InvalidOperationException>(() => handler.Process(httpContext, "chat"));
        }
Пример #10
0
        private static HttpContext CreateHttpContext(HttpContext originalContext)
        {
            // Clone the features so that a new set is used for each context.
            // The features themselves will be reused but not the collection. We
            // store the request container as a feature of the request and we don't want
            // the features added to one context/request to be visible on another.
            //
            // Note that just about everything inm the HttpContext and HttpRequest is
            // backed by one of these features. So reusing the features means the HttContext
            // and HttpRequests are the same without needing to copy properties. To make them
            // different, we need to avoid copying certain features to that the objects don't
            // share the same storage/
            IFeatureCollection features = new FeatureCollection();
            string             pathBase = "";

            foreach (KeyValuePair <Type, object> kvp in originalContext.Features)
            {
                // Don't include the OData features. They may already
                // be present. This will get re-created later.
                //
                // Also, clear out the items feature, which is used
                // to store a few object, the one that is an issue here is the Url
                // helper, which has an affinity to the context. If we leave it,
                // the context of the helper no longer matches the new context and
                // the resulting url helper doesn't have access to the OData feature
                // because it's looking in the wrong context.
                //
                // Because we need a different request and response, leave those features
                // out as well.
                if (kvp.Key == typeof(IHttpRequestFeature))
                {
                    pathBase = ((IHttpRequestFeature)kvp.Value).PathBase;
                }

                if (kvp.Key == typeof(IODataBatchFeature) ||
                    kvp.Key == typeof(IODataFeature) ||
                    kvp.Key == typeof(IItemsFeature) ||
                    kvp.Key == typeof(IHttpRequestFeature) ||
                    kvp.Key == typeof(IHttpResponseFeature))
                {
                    continue;
                }

                features[kvp.Key] = kvp.Value;
            }

            // Add in an items, request and response feature.
            features[typeof(IItemsFeature)]       = new ItemsFeature();
            features[typeof(IHttpRequestFeature)] = new HttpRequestFeature
            {
                PathBase = pathBase
            };
            features[typeof(IHttpResponseFeature)] = new HttpResponseFeature();

            // Create a context from the factory or use the default context.
            HttpContext         context            = null;
            IHttpContextFactory httpContextFactory = originalContext.RequestServices.GetRequiredService <IHttpContextFactory>();

            if (httpContextFactory != null)
            {
                context = httpContextFactory.Create(features);
            }
            else
            {
                context = new DefaultHttpContext(features);
            }

            // Clone parts of the request. All other parts of the request will be
            // populated during batch processing.
            context.Request.Cookies = originalContext.Request.Cookies;
            foreach (KeyValuePair <string, StringValues> header in originalContext.Request.Headers)
            {
                context.Request.Headers.Add(header);
            }

            // Create a response body as the default response feature does not
            // have a valid stream.
            context.Response.Body = new MemoryStream();

            return(context);
        }
 public override void OnSendingHeaders(Action <object> callback, object state)
 {
     HttpResponseFeature.OnSendingHeaders(callback, state);
 }
Пример #12
0
        private static async Task WritePartToResponse(Kooboo.IndexedDB.FilePart part, HttpResponseFeature Res)
        {
            long offset      = part.BlockPosition + part.RelativePosition;
            long totalToSend = part.Length;

            Res.Headers["Content-Length"] = totalToSend.ToString();


            var stream = Kooboo.IndexedDB.StreamManager.OpenReadStream(part.FullFileName);

            stream.Position = offset;

            try
            {
                await stream.ChunkCopyAsync(Res.Body, totalToSend);
            }
            catch (IndexOutOfRangeException)
            {
            }
            finally
            {
                await Res.Body.FlushAsync();
            }
        }
Пример #13
0
        public async Task <AckResult> HandleMessageTask(IHandledMessage message)
        {
            if (_cancellationToken.IsCancellationRequested)
            {
                return(AckResult.NackRequeue);
            }
            try
            {
                var bytes = message.Body;

                //note: This may not be the correct way to implement the FeatureCollection.  In most of the available samples,
                //features are static and the HttpContext is customized so it can be manipulated directly within this method.
                //For the time being, this approach is very easy and provides better decoupling.  When things get more complicated
                //we may revert to the other approach.

                //Typical webserver is a lot more complicated because it needs to handle the http protocol.HttpProtocol.ProcessRequests
                //and can handle multiple requests via the same connection.  MsgNThen is entirely reliant on RabbitMQ client to handle all
                //such issues.
                //The Protocol can't directly access the HttpContext itself (which is really strange at first glance). Instead it implements
                //"IFeature"s that the HttpContext uses to implement its properties.

                var requestFeatures       = new FeatureCollection();
                var messageRequestFeature = new HttpRequestFeature
                {
                    Path = $"/{message.RoutingKey}",
                    //fix: use MemoryStream.WriteAsync(ReadOnlyMemory<Byte>)
                    Body    = new MemoryStream(bytes.ToArray()),
                    Method  = "GET",
                    Headers = new HeaderDictionary(),
                };
                if (message.Properties.Headers != null)
                {
                    foreach (var property in message.Properties.Headers)
                    {
                        messageRequestFeature.Headers[property.Key] = property.Value?.ToString();
                    }
                }

                var groupInfo = GetMessageGroupInfo(message);


                messageRequestFeature.Headers[HeaderConstants.MessageId]     = message.Properties.MessageId;
                messageRequestFeature.Headers[HeaderConstants.AppId]         = message.Properties.AppId;
                messageRequestFeature.Headers[HeaderConstants.ReplyTo]       = message.Properties.ReplyTo;
                messageRequestFeature.Headers[HeaderConstants.CorrelationId] = message.Properties.CorrelationId;
                //note: https://www.rabbitmq.com/validated-user-id.html
                messageRequestFeature.Headers[HeaderConstants.UserId]  = message.Properties.UserId;
                messageRequestFeature.Headers.ContentLength            = message.Body.Length;
                messageRequestFeature.Headers[HeaderNames.ContentType] = message.Properties.ContentType;

                var responseStream      = new MemoryStream();
                var responseBody        = new StreamResponseBodyFeature(responseStream);
                var httpResponseFeature = new HttpResponseFeature()
                {
                    Body = responseStream
                };
                requestFeatures.Set <IHttpResponseBodyFeature>(responseBody);
                requestFeatures.Set <IHttpRequestFeature>(messageRequestFeature);
                requestFeatures.Set <IHttpResponseFeature>(httpResponseFeature);
                var context = _application.CreateContext(requestFeatures);
                await _application.ProcessRequestAsync(context);

                if (groupInfo != null)
                {
                    _messageGroupHandler.MessageHandled(groupInfo.Value.messageGroupId, groupInfo.Value.messageId);
                }

                if (responseStream.Length > 0)
                {
                    if (groupInfo != null)
                    {
                        //when the application populated the response we could either
                        // - store this information so that the andThen processor can pick it up later or
                        // - stream this request as data to the ReplyTo address for the same reason.
                        //use Redis and S3 as result stores so that the final andThen can use the data collected from
                        //those requests.
                    }
                    else
                    {
                        //since this isn't an andThen request, we should send this to the ReplyTo address
                        //todo: XXXX implement result forwarding (using something like IAndThenMessageDeliverer)
                    }
                    //any/all of the above scenarios are handled by the following interface by using the
                    //replyHandler to interpret the response and the message.Properties.ReplyTo address.
                }

                return(AckResult.Ack);
            }
            catch (Exception e)
            {
                _logger.LogError(e, "HandleMessageTask Failed");
                return(AckResult.NackQuit);
            }
        }
Пример #14
0
        private static async Task WritePartToResponse(Kooboo.IndexedDB.FilePart part, HttpResponseFeature Res)
        {
            long offset = part.BlockPosition + part.RelativePosition;

            byte[] buffer      = new byte[8096];
            long   totalToSend = part.Length;
            int    count       = 0;

            Res.Headers["Content-Length"] = totalToSend.ToString();

            long bytesRemaining = totalToSend;

            var stream = Kooboo.IndexedDB.StreamManager.OpenReadStream(part.FullFileName);

            stream.Position = offset;

            while (bytesRemaining > 0)
            {
                try
                {
                    if (bytesRemaining <= buffer.Length)
                    {
                        count = await stream.ReadAsync(buffer, 0, (int)bytesRemaining);
                    }
                    else
                    {
                        count = await stream.ReadAsync(buffer, 0, buffer.Length);
                    }

                    if (count == 0)
                    {
                        return;
                    }

                    await Res.Body.WriteAsync(buffer, 0, count);

                    bytesRemaining -= count;
                }
                catch (IndexOutOfRangeException)
                {
                    await Res.Body.FlushAsync();

                    return;
                }
                finally
                {
                    await Res.Body.FlushAsync();
                }
            }
        }
        /// <summary>
        /// Invokes batch request.
        /// </summary>
        /// <param name="httpContext">Source batch request context.</param>
        /// <param name="requests">Array of requests.</param>
        private async Task InvokeAsyncBatchAsync(HttpContext httpContext, JArray requests)
        {
            JArray responses = new JArray();
            int    i         = 1;

            foreach (JObject requestObj in requests)
            {
                var contextFeatures = new FeatureCollection(httpContext.Features);

                StringBuilder requestStringBuilder = new StringBuilder();
                await requestObj.WriteToAsync(new JsonTextWriter(new StringWriter(requestStringBuilder)));

                var requestFeature = new HttpRequestFeature()
                {
                    Body        = new MemoryStream(Encoding.UTF8.GetBytes(requestStringBuilder.ToString())),
                    Headers     = httpContext.Request.Headers,
                    Method      = httpContext.Request.Method,
                    Protocol    = httpContext.Request.Protocol,
                    Scheme      = httpContext.Request.Scheme,
                    QueryString = httpContext.Request.QueryString.Value
                };
                contextFeatures.Set <IHttpRequestFeature>(requestFeature);

                var responseMemoryStream = new MemoryStream();
                var responseFeature      = new HttpResponseFeature()
                {
                    Body = responseMemoryStream
                };
                contextFeatures.Set <IHttpResponseFeature>(responseFeature);

                contextFeatures.Set <IHttpRequestLifetimeFeature>(new HttpRequestLifetimeFeature());

                var context = this.httpContextFactory.Create(contextFeatures);

                try
                {
                    await this.next.Invoke(context).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    await this.HandleRpcInvokeExceptionAsync(context, ex);
                }

                responseMemoryStream.Position = 0;

                var response = (responseMemoryStream.Length == 0) ? CreateError(RPCErrorCode.RPC_METHOD_NOT_FOUND, "Method not found") : await JObject.LoadAsync(new JsonTextReader(new StreamReader(responseMemoryStream)));

                if (requestObj.ContainsKey("id"))
                {
                    response["id"] = requestObj["id"];
                }
                else
                {
                    response.Remove("id");
                }

                responses.Add(response);
                i++;
            }

            httpContext.Response.ContentType = "application/json; charset=utf-8";

            // Update the response with the array of responses.
            using (StreamWriter streamWriter = new StreamWriter(httpContext.Response.Body))
                using (JsonTextWriter textWriter = new JsonTextWriter(streamWriter))
                {
                    await responses.WriteToAsync(textWriter);
                }
        }
Пример #16
0
        private static HttpContext CreateHttpContext(HttpContext originalContext)
        {
            // Clone the features so that a new set is used for each context.
            // The features themselves will be reused but not the collection. We
            // store the request container as a feature of the request and we don't want
            // the features added to one context/request to be visible on another.
            //
            // Note that just about everything in the HttpContext and HttpRequest is
            // backed by one of these features. So reusing the features means the HttContext
            // and HttpRequests are the same without needing to copy properties. To make them
            // different, we need to avoid copying certain features to that the objects don't
            // share the same storage/
            IFeatureCollection features = new FeatureCollection();
            string             pathBase = "";

            foreach (KeyValuePair <Type, object> kvp in originalContext.Features)
            {
                // Don't include the OData features. They may already
                // be present. This will get re-created later.
                //
                // Also, clear out the items feature, which is used
                // to store a few object, the one that is an issue here is the Url
                // helper, which has an affinity to the context. If we leave it,
                // the context of the helper no longer matches the new context and
                // the resulting url helper doesn't have access to the OData feature
                // because it's looking in the wrong context.
                //
                // Because we need a different request and response, leave those features
                // out as well.
                if (kvp.Key == typeof(IHttpRequestFeature))
                {
                    pathBase = ((IHttpRequestFeature)kvp.Value).PathBase;
                }

                if (kvp.Key == typeof(IODataBatchFeature) ||
                    kvp.Key == typeof(IODataFeature) ||
                    kvp.Key == typeof(IItemsFeature) ||
                    kvp.Key == typeof(IHttpRequestFeature) ||
                    kvp.Key == typeof(IHttpResponseFeature) ||
                    kvp.Key == typeof(IQueryFeature)) // Noted: we should not pass the QueryFeature from Main request to the sub request
                {
                    continue;
                }

                if (kvp.Key == typeof(IEndpointFeature))
                {
                    continue;
                }

                features[kvp.Key] = kvp.Value;
            }

            // Add in an items, request and response feature.
            features[typeof(IItemsFeature)]       = new ItemsFeature();
            features[typeof(IHttpRequestFeature)] = new HttpRequestFeature
            {
                PathBase = pathBase
            };

            features[typeof(IHttpResponseFeature)] = new HttpResponseFeature();

            // Create a context from the factory or use the default context.
            HttpContext         context            = null;
            IHttpContextFactory httpContextFactory = originalContext.RequestServices.GetService <IHttpContextFactory>();

            if (httpContextFactory != null)
            {
                context = httpContextFactory.Create(features);
            }
            else
            {
                context = new DefaultHttpContext(features);
            }

            // Clone parts of the request. All other parts of the request will be
            // populated during batch processing.
            context.Request.Cookies = originalContext.Request.Cookies;
            foreach (KeyValuePair <string, StringValues> header in originalContext.Request.Headers)
            {
                string headerKey = header.Key.ToLowerInvariant();
                // do not copy over headers that should not be inherited from batch to individual requests
                if (!nonInheritableHeaders.Contains(headerKey))
                {
                    // some preferences may be inherited, others discarded
                    if (headerKey == "prefer")
                    {
                        string preferencesToInherit = GetPreferencesToInheritFromBatch(header.Value);
                        if (!string.IsNullOrEmpty(preferencesToInherit))
                        {
                            context.Request.Headers.Add(header.Key, preferencesToInherit);
                        }
                    }
                    // do not copy already existing headers, such as Cookie
                    else if (!context.Request.Headers.ContainsKey(header.Key))
                    {
                        context.Request.Headers.Add(header);
                    }
                }
            }

            // Create a response body as the default response feature does not
            // have a valid stream.
            // Use a special batch stream that remains open after the writer is disposed.
            context.Response.Body = new ODataBatchStream();

            return(context);
        }
Пример #17
0
        /// <summary>
        /// Helper method to get the odata path for an arbitrary odata uri.
        /// </summary>
        /// <param name="request">The request instance in current context</param>
        /// <param name="uri">OData uri</param>
        /// <returns>The parsed odata path</returns>
#if NETCORE
        public static ODataPath CreateODataPath(this HttpRequest request, Uri uri)
        {
            if (uri == null)
            {
                throw new ArgumentNullException("uri");
            }

            // Clone the features so that a new set is used for each context.
            // The features themselves will be reused but not the collection. We
            // store the request container as a feature of the request and we don't want
            // the features added to one context/request to be visible on another.
            //
            // Note that just about everything in the HttpContext and HttpRequest is
            // backed by one of these features. So reusing the features means the HttContext
            // and HttpRequests are the same without needing to copy properties. To make them
            // different, we need to avoid copying certain features to that the objects don't
            // share the same storage/
            IFeatureCollection features = new FeatureCollection();

            foreach (KeyValuePair <Type, object> kvp in request.HttpContext.Features)
            {
                // Don't include the OData features. They may already
                // be present. This will get re-created later.
                //
                // Also, clear out the items feature, which is used
                // to store a few object, the one that is an issue here is the Url
                // helper, which has an affinity to the context. If we leave it,
                // the context of the helper no longer matches the new context and
                // the resulting url helper doesn't have access to the OData feature
                // because it's looking in the wrong context.
                //
                // Because we need a different request and response, leave those features
                // out as well.
                if (kvp.Key == typeof(IODataBatchFeature) ||
                    kvp.Key == typeof(IODataFeature) ||
                    kvp.Key == typeof(IItemsFeature) ||
                    kvp.Key == typeof(IHttpRequestFeature) ||
                    kvp.Key == typeof(IHttpResponseFeature))
                {
                    continue;
                }

                features[kvp.Key] = kvp.Value;
            }

            // Add in an items, request and response feature.
            features[typeof(IItemsFeature)]        = new ItemsFeature();
            features[typeof(IHttpRequestFeature)]  = new HttpRequestFeature();
            features[typeof(IHttpResponseFeature)] = new HttpResponseFeature();

            // Create a context from the factory or use the default context.
            HttpContext context = new DefaultHttpContext(features);

            // Clone parts of the request. All other parts of the request will be
            // populated during batch processing.
            context.Request.Cookies = request.HttpContext.Request.Cookies;
            foreach (KeyValuePair <string, StringValues> header in request.HttpContext.Request.Headers)
            {
                context.Request.Headers.Add(header);
            }

            // Copy the Uri.
            context.Request.Scheme = uri.Scheme;
            context.Request.Host   = uri.IsDefaultPort ?
                                     new HostString(uri.Host) :
                                     new HostString(uri.Host, uri.Port);
            context.Request.QueryString = new QueryString(uri.Query);
            context.Request.Path        = new PathString(uri.AbsolutePath);

            // Get the existing OData route
            IRoutingFeature routingFeature = context.Features[typeof(IRoutingFeature)] as IRoutingFeature;
            ODataRoute      route          = routingFeature.RouteData.Routers.OfType <ODataRoute>().FirstOrDefault();

            // Attempt to route the new request and extract the path.
            RouteContext routeContext = new RouteContext(context);

            route.RouteAsync(routeContext).Wait();
            return(context.Request.ODataFeature().Path);
        }
Пример #18
0
        /// <summary>
        /// Invokes single request.
        /// </summary>
        /// <param name="httpContext">Source batch request context.</param>
        /// <param name="requestObj">Single request object.</param>
        /// <returns>Single response objects.</returns>
        private async Task <JObject> InvokeSingleAsync(HttpContext httpContext, JObject requestObj)
        {
            var contextFeatures = new FeatureCollection(httpContext.Features);

            var requestFeature = new HttpRequestFeature()
            {
                Body        = new MemoryStream(Encoding.UTF8.GetBytes(requestObj.ToString())),
                Headers     = httpContext.Request.Headers,
                Method      = httpContext.Request.Method,
                Protocol    = httpContext.Request.Protocol,
                Scheme      = httpContext.Request.Scheme,
                QueryString = httpContext.Request.QueryString.Value
            };

            contextFeatures.Set <IHttpRequestFeature>(requestFeature);

            var responseMemoryStream = new MemoryStream();
            var responseFeature      = new HttpResponseFeature()
            {
                Body = responseMemoryStream
            };

            contextFeatures.Set <IHttpResponseFeature>(responseFeature);

            contextFeatures.Set <IHttpRequestLifetimeFeature>(new HttpRequestLifetimeFeature());

            var     context = this.httpContextFactory.Create(contextFeatures);
            JObject response;

            try
            {
                await this.next.Invoke(context).ConfigureAwait(false);

                if (responseMemoryStream.Length == 0)
                {
                    throw new Exception("Method not found");
                }

                responseMemoryStream.Position = 0;
                using (var streamReader = new StreamReader(responseMemoryStream))
                    using (var textReader = new JsonTextReader(streamReader))
                    {
                        // Ensure floats are parsed as decimals and not as doubles.
                        textReader.FloatParseHandling = FloatParseHandling.Decimal;
                        response = await JObject.LoadAsync(textReader);
                    }
            }
            catch (Exception ex)
            {
                await this.HandleRpcInvokeExceptionAsync(context, ex);

                context.Response.Body.Position = 0;
                using (var streamReader = new StreamReader(context.Response.Body, Encoding.Default, true, 1024, true))
                    using (var textReader = new JsonTextReader(streamReader))
                    {
                        // Ensure floats are parsed as decimals and not as doubles.
                        textReader.FloatParseHandling = FloatParseHandling.Decimal;

                        string val = streamReader.ReadToEnd();
                        context.Response.Body.Position = 0;
                        response = await JObject.LoadAsync(textReader);
                    }
            }

            if (requestObj.ContainsKey("id"))
            {
                response["id"] = requestObj["id"];
            }
            else
            {
                response.Remove("id");
            }

            return(response);
        }
Пример #19
0
        private static ModuleHttpResponse BuildResponseMessage(MemoryStream responseStream, HttpResponseFeature responseFeature)
        {
            var response = new ModuleHttpResponse
            {
                StatusCode   = responseFeature.StatusCode,
                ReasonPhrase = responseFeature.ReasonPhrase,
                Body         = responseStream.ToArray(),
                Headers      = new Dictionary <string, string[]>()
            };

            foreach (var entry in responseFeature.Headers)
            {
                response.Headers.Add(entry.Key, entry.Value.ToArray());
            }

            return(response);
        }