public async Task OnConnectedAsync(FramingConnection connection) { var inputPipe = connection.Input; var modeDecoder = new ServerModeDecoder(); try { await modeDecoder.ReadModeAsync(inputPipe); } catch (CommunicationException e) { // see if we need to send back a framing fault string framingFault; if (FramingEncodingString.TryGetFaultString(e, out framingFault)) { // TODO: Timeouts await connection.SendFaultAsync(framingFault, Timeout.InfiniteTimeSpan /*GetRemainingTimeout()*/, ConnectionOrientedTransportDefaults.MaxViaSize + ConnectionOrientedTransportDefaults.MaxContentTypeSize); } return; // Completing the returned Task causes the connection to be closed if needed and cleans everything up. } connection.FramingMode = modeDecoder.Mode; await _next(connection); // One .NET Framework, with the way that AspNetCore closes a connection, it sometimes doesn't send the // final bytes if those bytes haven't been sent yet. Delaying completeing the connection to compensate. await Task.Delay(5); // AspNetCore 2.1 doesn't close the connection. 2.2+ does so these lines can eventually be rmoved. connection.RawTransport.Input.Complete(); connection.RawTransport.Output.Complete(); }
public async Task OnConnectedAsync(FramingConnection connection) { using (_appLifetime.ApplicationStopped.Register(() => { connection.RawTransport.Input.Complete(); connection.RawTransport.Output.Complete(); })) { IConnectionReuseHandler reuseHandler = null; do { var inputPipe = connection.Input; var modeDecoder = new ServerModeDecoder(); try { if (!await modeDecoder.ReadModeAsync(inputPipe)) { break; // Input pipe closed } } catch (CommunicationException e) { // see if we need to send back a framing fault string framingFault; if (FramingEncodingString.TryGetFaultString(e, out framingFault)) { // TODO: Timeouts await connection.SendFaultAsync(framingFault, Timeout.InfiniteTimeSpan /*GetRemainingTimeout()*/, ConnectionOrientedTransportDefaults.MaxViaSize + ConnectionOrientedTransportDefaults.MaxContentTypeSize); } return; // Completing the returned Task causes the connection to be closed if needed and cleans everything up. } connection.FramingMode = modeDecoder.Mode; await _next(connection); // Unwrap the connection. // TODO: Investigate calling Dispose on the wrapping stream to improve cleanup. nb: .NET Framework does not call Dispose. connection.Transport = connection.RawTransport; // connection.ServiceDispatcher is null until later middleware layers are executed. if (reuseHandler == null) { reuseHandler = connection.ServiceDispatcher.Binding.GetProperty <IConnectionReuseHandler>(new BindingParameterCollection()); } } while (await reuseHandler.ReuseConnectionAsync(connection, _appLifetime.ApplicationStopping)); // On .NET Framework, with the way that AspNetCore closes a connection, it sometimes doesn't send the // final bytes if those bytes haven't been sent yet. Delaying completeing the connection to compensate. await Task.Delay(5); } // AspNetCore 2.1 doesn't close the connection. 2.2+ does so these lines can eventually be removed. connection.RawTransport.Input.Complete(); connection.RawTransport.Output.Complete(); }
public async Task OnConnectedAsync(FramingConnection connection) { var receiveTimeout = connection.ServiceDispatcher.Binding.ReceiveTimeout; var timeoutHelper = new TimeoutHelper(receiveTimeout); bool success = false; try { var decoder = connection.FramingDecoder as ServerSingletonDecoder; Fx.Assert(decoder != null, "FramingDecoder must be non-null and an instance of ServerSessionDecoder"); // first validate our content type //ValidateContentType(connection, decoder); UpgradeState upgradeState = UpgradeState.None; // next read any potential upgrades and finish consuming the preamble ReadOnlySequence <byte> buffer = ReadOnlySequence <byte> .Empty; while (true) { if (buffer.Length == 0 && CanReadAndDecode(upgradeState)) { var readResult = await connection.Input.ReadAsync(); buffer = readResult.Buffer; if (readResult.IsCompleted) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException()); } } while (true) { if (CanReadAndDecode(upgradeState)) { Fx.Assert(buffer.Length > 0, "There must be something in the buffer to decode"); int bytesDecoded = decoder.Decode(buffer); if (bytesDecoded > 0) { buffer = buffer.Slice(bytesDecoded); if (buffer.Length == 0) { connection.Input.AdvanceTo(buffer.Start); } } } switch (decoder.CurrentState) { case ServerSingletonDecoder.State.UpgradeRequest: switch (upgradeState) { case UpgradeState.None: //change the state so that we don't read/decode until it is safe ChangeUpgradeState(ref upgradeState, UpgradeState.VerifyingUpgradeRequest); break; case UpgradeState.VerifyingUpgradeRequest: if (connection.StreamUpgradeAcceptor == null) { await connection.SendFaultAsync(FramingEncodingString.UpgradeInvalidFault, timeoutHelper.RemainingTime(), TransportDefaults.MaxDrainSize); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ProtocolException(SR.Format(SR.UpgradeRequestToNonupgradableService, decoder.Upgrade))); } if (!connection.StreamUpgradeAcceptor.CanUpgrade(decoder.Upgrade)) { await connection.SendFaultAsync(FramingEncodingString.UpgradeInvalidFault, timeoutHelper.RemainingTime(), TransportDefaults.MaxDrainSize); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.Format(SR.UpgradeProtocolNotSupported, decoder.Upgrade))); } ChangeUpgradeState(ref upgradeState, UpgradeState.WritingUpgradeAck); // accept upgrade await connection.Output.WriteAsync(ServerSingletonEncoder.UpgradeResponseBytes, timeoutHelper.GetCancellationToken()); await connection.Output.FlushAsync(timeoutHelper.GetCancellationToken()); ChangeUpgradeState(ref upgradeState, UpgradeState.UpgradeAckSent); break; case UpgradeState.UpgradeAckSent: // This state was used to capture any extra read bytes into PreReadConnection but we don't need to do that when using pipes. // This extra state transition has been left here to maintain the same state transitions as on .NET Framework to make comparison easier. ChangeUpgradeState(ref upgradeState, UpgradeState.BeginUpgrade); break; case UpgradeState.BeginUpgrade: // Set input pipe so that the next read will return all the unconsumed bytes. // If all bytes have already been consumed so the buffer has 0 length, AdvanceTo would throw // as it's already been called. if (buffer.Length > 0) { connection.Input.AdvanceTo(buffer.Start); } buffer = ReadOnlySequence <byte> .Empty; try { await UpgradeConnectionAsync(connection); ChangeUpgradeState(ref upgradeState, UpgradeState.EndUpgrade); } catch (Exception exception) { if (Fx.IsFatal(exception)) { throw; } throw; } break; case UpgradeState.EndUpgrade: //Must be a different state here than UpgradeComplete so that we don't try to read from the connection ChangeUpgradeState(ref upgradeState, UpgradeState.UpgradeComplete); break; case UpgradeState.UpgradeComplete: //Client is doing more than one upgrade, reset the state ChangeUpgradeState(ref upgradeState, UpgradeState.VerifyingUpgradeRequest); break; } break; case ServerSingletonDecoder.State.Start: SetupSecurityIfNecessary(connection); if (upgradeState == UpgradeState.UpgradeComplete || //We have done at least one upgrade, but we are now done. upgradeState == UpgradeState.None) //no upgrade, just send the preample end bytes { ChangeUpgradeState(ref upgradeState, UpgradeState.WritingPreambleEnd); // we've finished the preamble. Ack and return. await connection.Output.WriteAsync(ServerSessionEncoder.AckResponseBytes); await connection.Output.FlushAsync(); //terminal state ChangeUpgradeState(ref upgradeState, UpgradeState.PreambleEndSent); } // If all bytes have already been consumed so the buffer has 0 length, AdvanceTo would throw // as it's already been called. if (buffer.Length > 0) { connection.Input.AdvanceTo(buffer.Start); } success = true; await _next(connection); return; } if (buffer.Length == 0) { break; } } } } finally { if (!success) { connection.Abort(); } } }
private Message DecodeMessage(FramingConnection connection, ref ReadOnlySequence <byte> buffer) { int maxBufferSize = connection.MaxBufferSize; var decoder = (ServerSessionDecoder)connection.FramingDecoder; while (!connection.EOF && buffer.Length > 0) { int bytesRead = decoder.Decode(buffer); if (bytesRead > 0) { if (!connection.EnvelopeBuffer.IsEmpty) { var remainingEnvelopeBuffer = connection.EnvelopeBuffer.Slice(connection.EnvelopeOffset, connection.EnvelopeSize - connection.EnvelopeOffset); CopyBuffer(buffer, remainingEnvelopeBuffer, bytesRead); connection.EnvelopeOffset += bytesRead; } buffer = buffer.Slice(bytesRead); } switch (decoder.CurrentState) { case ServerSessionDecoder.State.EnvelopeStart: int envelopeSize = decoder.EnvelopeSize; if (envelopeSize > maxBufferSize) { // TODO: Remove synchronous wait. This is needed because the buffer is passed by ref. connection.SendFaultAsync(FramingEncodingString.MaxMessageSizeExceededFault, connection.ServiceDispatcher.Binding.SendTimeout, TransportDefaults.MaxDrainSize).GetAwaiter().GetResult(); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( MaxMessageSizeStream.CreateMaxReceivedMessageSizeExceededException(maxBufferSize)); } connection.EnvelopeBuffer = connection.BufferManager.TakeBuffer(envelopeSize); connection.EnvelopeSize = envelopeSize; connection.EnvelopeOffset = 0; break; case ServerSessionDecoder.State.EnvelopeEnd: if (!connection.EnvelopeBuffer.IsEmpty) { Message message = null; try { message = connection.MessageEncoder.ReadMessage( new ArraySegment <byte>(connection.EnvelopeBuffer.ToArray(), 0, connection.EnvelopeSize), connection.BufferManager, connection.FramingDecoder.ContentType); } catch (XmlException xmlException) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ProtocolException(SR.MessageXmlProtocolError, xmlException)); } connection.EnvelopeBuffer = null; return(message); } break; case ServerSessionDecoder.State.End: connection.EOF = true; break; } } return(null); }