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();
        }
예제 #3
0
        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);
        }