public TestHttp1Connection(HttpConnectionContext context) : base(context) { }
public WebSocketsServerTransport(WebSocketOptions options, IDuplexPipe application, HttpConnectionContext connection, ILoggerFactory loggerFactory) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (application == null) { throw new ArgumentNullException(nameof(application)); } if (loggerFactory == null) { throw new ArgumentNullException(nameof(loggerFactory)); } _options = options; _application = application; _connection = connection; // We create the logger with a string to preserve the logging namespace after the server side transport renames. _logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport"); }
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) { CloneHttpContext(context, connection); } 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; UpdateExpiration(connection, context); // 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); }
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.GetRequiredFeature <IHttpRequestFeature>(); var requestFeature = new HttpRequestFeature { Protocol = existingRequestFeature.Protocol, Method = existingRequestFeature.Method, Scheme = existingRequestFeature.Scheme, Path = existingRequestFeature.Path, PathBase = existingRequestFeature.PathBase, QueryString = existingRequestFeature.QueryString, 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; newHttpContext.SetEndpoint(context.GetEndpoint()); 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 LongPollingServerTransport(CancellationToken timeoutToken, PipeReader application, ILoggerFactory loggerFactory, HttpConnectionContext connection) { _timeoutToken = timeoutToken; _application = application; _connection = connection; // We create the logger with a string to preserve the logging namespace after the server side transport renames. _logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Connections.Internal.Transports.LongPollingTransport"); }
public WebSocketsTransport(WebSocketOptions options, IDuplexPipe application, HttpConnectionContext connection, ILoggerFactory loggerFactory) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (application == null) { throw new ArgumentNullException(nameof(application)); } if (loggerFactory == null) { throw new ArgumentNullException(nameof(loggerFactory)); } _options = options; _application = application; _connection = connection; _logger = loggerFactory.CreateLogger <WebSocketsTransport>(); }
static void AddHttpCompressionIfClientCanAcceptIt(HttpConnectionContext context) { string acceptEncoding = context.Request.Headers["Accept-Encoding"]; if (string.IsNullOrEmpty(acceptEncoding)) return; if ((acceptEncoding.IndexOf("gzip", StringComparison.InvariantCultureIgnoreCase) != -1)) { context.SetResponseFilter(s => new GZipStream(s, CompressionMode.Compress, true)); context.Response.Headers["Content-Encoding"] = "gzip"; } else if (acceptEncoding.IndexOf("deflate", StringComparison.InvariantCultureIgnoreCase) != -1) { context.SetResponseFilter(s => new DeflateStream(s, CompressionMode.Compress, true)); context.Response.Headers["Content-Encoding"] = "deflate"; } }
public Http3ControlStream(IHttpApplication <TContext> application, Http3Connection connection, HttpConnectionContext context) : base(connection, context) { _application = application; }
public async Task CopyToAsyncDoesNotCopyBlocks() { var writeCount = 0; var writeTcs = new TaskCompletionSource <(byte[], int, int)>(TaskCreationOptions.RunContinuationsAsynchronously); var mockDestination = new Mock <Stream> { CallBase = true }; mockDestination .Setup(m => m.WriteAsync(It.IsAny <byte[]>(), It.IsAny <int>(), It.IsAny <int>(), CancellationToken.None)) .Callback((byte[] buffer, int offset, int count, CancellationToken cancellationToken) => { writeTcs.SetResult((buffer, offset, count)); writeCount++; }) .Returns(Task.CompletedTask); using (var memoryPool = KestrelMemoryPool.Create()) { var options = new PipeOptions(pool: memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); var transport = pair.Transport; var http1ConnectionContext = new HttpConnectionContext { ServiceContext = new TestServiceContext(), ConnectionFeatures = new FeatureCollection(), Transport = transport, MemoryPool = memoryPool, TimeoutControl = Mock.Of <ITimeoutControl>() }; var http1Connection = new Http1Connection(http1ConnectionContext) { HasStartedConsumingRequestBody = true }; var headers = new HttpRequestHeaders { HeaderContentLength = "12" }; var body = Http1MessageBody.For(HttpVersion.Http11, headers, http1Connection); var copyToAsyncTask = body.CopyToAsync(mockDestination.Object); var bytes = Encoding.ASCII.GetBytes("Hello "); var buffer = http1Connection.RequestBodyPipe.Writer.GetMemory(2048); Assert.True(MemoryMarshal.TryGetArray(buffer, out ArraySegment <byte> segment)); Buffer.BlockCopy(bytes, 0, segment.Array, segment.Offset, bytes.Length); http1Connection.RequestBodyPipe.Writer.Advance(bytes.Length); await http1Connection.RequestBodyPipe.Writer.FlushAsync(); // Verify the block passed to Stream.WriteAsync() is the same one incoming data was written into. Assert.Equal((segment.Array, segment.Offset, bytes.Length), await writeTcs.Task); // Verify the again when GetMemory returns the tail space of the same block. writeTcs = new TaskCompletionSource <(byte[], int, int)>(TaskCreationOptions.RunContinuationsAsynchronously); bytes = Encoding.ASCII.GetBytes("World!"); buffer = http1Connection.RequestBodyPipe.Writer.GetMemory(2048); Assert.True(MemoryMarshal.TryGetArray(buffer, out segment)); Buffer.BlockCopy(bytes, 0, segment.Array, segment.Offset, bytes.Length); http1Connection.RequestBodyPipe.Writer.Advance(bytes.Length); await http1Connection.RequestBodyPipe.Writer.FlushAsync(); Assert.Equal((segment.Array, segment.Offset, bytes.Length), await writeTcs.Task); http1Connection.RequestBodyPipe.Writer.Complete(); await copyToAsyncTask; Assert.Equal(2, writeCount); // Don't call body.StopAsync() because PumpAsync() was never called. http1Connection.RequestBodyPipe.Reader.Complete(); } }
public ServerSentEventsServerTransport(PipeReader application, string connectionId, HttpConnectionContext connection, ILoggerFactory loggerFactory) { _application = application; _connectionId = connectionId; _connection = connection; // We create the logger with a string to preserve the logging namespace after the server side transport renames. _logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Connections.Internal.Transports.ServerSentEventsTransport"); }
public HttpTransport(HttpConnectionContext connection, string path) { Connection = connection; Path = path; }
public async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> application) { var streamListenerFeature = Context.ConnectionFeatures.Get <IQuicStreamListenerFeature>(); // Start other three unidirectional streams here. var controlTask = CreateControlStream(application); var encoderTask = CreateEncoderStream(application); var decoderTask = CreateDecoderStream(application); try { while (true) { var connectionContext = await streamListenerFeature.AcceptAsync(); if (connectionContext == null || _haveSentGoAway) { break; } var httpConnectionContext = new HttpConnectionContext { ConnectionId = connectionContext.ConnectionId, ConnectionContext = connectionContext, Protocols = Context.Protocols, ServiceContext = Context.ServiceContext, ConnectionFeatures = connectionContext.Features, MemoryPool = Context.MemoryPool, Transport = connectionContext.Transport, TimeoutControl = Context.TimeoutControl, LocalEndPoint = connectionContext.LocalEndPoint as IPEndPoint, RemoteEndPoint = connectionContext.RemoteEndPoint as IPEndPoint }; var streamFeature = httpConnectionContext.ConnectionFeatures.Get <IQuicStreamFeature>(); if (!streamFeature.CanWrite) { // Unidirectional stream var stream = new Http3ControlStream <TContext>(application, this, httpConnectionContext); ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false); } else { // Keep track of highest stream id seen for GOAWAY var streamId = streamFeature.StreamId; HighestStreamId = streamId; var http3Stream = new Http3Stream <TContext>(application, this, httpConnectionContext); var stream = http3Stream; _streams[streamId] = http3Stream; ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false); } } } finally { // Abort all streams as connection has shutdown. foreach (var stream in _streams.Values) { stream.Abort(new ConnectionAbortedException("Connection is shutting down.")); } ControlStream.Abort(new ConnectionAbortedException("Connection is shutting down.")); EncoderStream.Abort(new ConnectionAbortedException("Connection is shutting down.")); DecoderStream.Abort(new ConnectionAbortedException("Connection is shutting down.")); await controlTask; await encoderTask; await decoderTask; } }
public Http3Connection(HttpConnectionContext context) { Context = context; DynamicTable = new DynamicTable(0); }