コード例 #1
0
        private async Task ProcessNegotiate(HttpContext context, HttpConnectionDispatcherOptions options, ConnectionLogScope logScope)
        {
            context.Response.ContentType = "application/json";
            string error = null;
            int    clientProtocolVersion = 0;

            if (context.Request.Query.TryGetValue("NegotiateVersion", out var queryStringVersion))
            {
                // Set the negotiate response to the protocol we use.
                var queryStringVersionValue = queryStringVersion.ToString();
                if (!int.TryParse(queryStringVersionValue, out clientProtocolVersion))
                {
                    error = $"The client requested an invalid protocol version '{queryStringVersionValue}'";
                    Log.InvalidNegotiateProtocolVersion(_logger, queryStringVersionValue);
                }
                else if (clientProtocolVersion < options.MinimumProtocolVersion)
                {
                    error = $"The client requested version '{clientProtocolVersion}', but the server does not support this version.";
                    Log.NegotiateProtocolVersionMismatch(_logger, clientProtocolVersion);
                }
                else if (clientProtocolVersion > _protocolVersion)
                {
                    clientProtocolVersion = _protocolVersion;
                }
            }
            else if (options.MinimumProtocolVersion > 0)
            {
                // NegotiateVersion wasn't parsed meaning the client requests version 0.
                error = $"The client requested version '0', but the server does not support this version.";
                Log.NegotiateProtocolVersionMismatch(_logger, 0);
            }

            // Establish the connection
            HttpConnectionContext connection = null;

            if (error == null)
            {
                connection = CreateConnection(options, clientProtocolVersion);
            }

            // Set the Connection ID on the logging scope so that logs from now on will have the
            // Connection ID metadata set.
            logScope.ConnectionId = connection?.ConnectionId;

            // Don't use thread static instance here because writer is used with async
            var writer = new MemoryBufferWriter();

            try
            {
                // Get the bytes for the connection id
                WriteNegotiatePayload(writer, connection?.ConnectionId, connection?.ConnectionToken, context, options, clientProtocolVersion, error);

                Log.NegotiationRequest(_logger);

                // Write it out to the response with the right content length
                context.Response.ContentLength = writer.Length;
                await writer.CopyToAsync(context.Response.Body);
            }
            finally
            {
                writer.Reset();
            }
        }
コード例 #2
0
        private async Task <bool> EnsureConnectionStateAsync(HttpConnectionContext connection, HttpContext context, HttpTransportType transportType, HttpTransportType supportedTransports, ConnectionLogScope logScope, HttpConnectionDispatcherOptions options)
        {
            if ((supportedTransports & transportType) == 0)
            {
                context.Response.ContentType = "text/plain";
                context.Response.StatusCode  = StatusCodes.Status404NotFound;
                Log.TransportNotSupported(_logger, transportType);
                await context.Response.WriteAsync($"{transportType} transport not supported by this end point type");

                return(false);
            }

            // Set the IHttpConnectionFeature now that we can access it.
            connection.Features.Set(context.Features.Get <IHttpConnectionFeature>());

            if (connection.TransportType == HttpTransportType.None)
            {
                connection.TransportType = transportType;
            }
            else if (connection.TransportType != transportType)
            {
                context.Response.ContentType = "text/plain";
                context.Response.StatusCode  = StatusCodes.Status400BadRequest;
                Log.CannotChangeTransport(_logger, connection.TransportType, transportType);
                await context.Response.WriteAsync("Cannot change transports mid-connection");

                return(false);
            }

            // Configure transport-specific features.
            if (transportType == HttpTransportType.LongPolling)
            {
                connection.HasInherentKeepAlive = true;

                // For long polling, the requests come and go but the connection is still alive.
                // To make the IHttpContextFeature work well, we make a copy of the relevant properties
                // to a new HttpContext. This means that it's impossible to affect the context
                // with subsequent requests.
                var existing = connection.HttpContext;
                if (existing == null)
                {
                    var httpContext = CloneHttpContext(context);
                    connection.HttpContext = httpContext;
                }
                else
                {
                    // Set the request trace identifier to the current http request handling the poll
                    existing.TraceIdentifier = context.TraceIdentifier;

                    // Don't copy the identity if it's a windows identity
                    // We specifically clone the identity on first poll if it's a windows identity
                    // If we swapped the new User here we'd have to dispose the old identities which could race with the application
                    // trying to access the identity.
                    if (!(context.User.Identity is WindowsIdentity))
                    {
                        existing.User = context.User;
                    }
                }
            }
            else
            {
                connection.HttpContext = context;
            }

            // Setup the connection state from the http context
            connection.User = connection.HttpContext.User;

            // Set the Connection ID on the logging scope so that logs from now on will have the
            // Connection ID metadata set.
            logScope.ConnectionId = connection.ConnectionId;

            return(true);
        }
コード例 #3
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.CreateScope();
            newHttpContext.RequestServices = connection.ServiceScope.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;
        }