示例#1
0
        public async Task <IConnection> CompletePreambleAsync(TimeSpan timeout)
        {
            var timeoutHelper = new TimeoutHelper(timeout);
            var parent        = this;

            if (!transportSettings.MessageEncoderFactory.Encoder.IsContentTypeSupported(Decoder.ContentType))
            {
                SendFault(FramingEncodingString.ContentTypeInvalidFault, ref timeoutHelper);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.Format(
                                                                                                    SR.ContentTypeMismatch, Decoder.ContentType, parent.transportSettings.MessageEncoderFactory.Encoder.ContentType)));
            }

            IStreamUpgradeChannelBindingProvider channelBindingProvider = null;
            StreamUpgradeAcceptor upgradeAcceptor = null;

            if (transportSettings.Upgrade != null)
            {
                channelBindingProvider = transportSettings.Upgrade.GetProperty <IStreamUpgradeChannelBindingProvider>();
                upgradeAcceptor        = transportSettings.Upgrade.CreateUpgradeAcceptor();
            }

            var          currentConnection = Connection;
            UpgradeState upgradeState      = UpgradeState.None;

            while (true)
            {
                if (size == 0 && CanReadAndDecode(upgradeState))
                {
                    size = await currentConnection.ReadAsync(0, connectionBuffer.Length, timeoutHelper.RemainingTime());

                    if (size == 0)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Decoder.CreatePrematureEOFException());
                    }
                }

                while (true)
                {
                    if (CanReadAndDecode(upgradeState))
                    {
                        int bytesRead = Decoder.Decode(connectionBuffer, offset, size);
                        if (bytesRead > 0)
                        {
                            offset += bytesRead;
                            size   -= bytesRead;
                        }
                    }

                    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 (upgradeAcceptor == null)
                            {
                                SendFault(FramingEncodingString.UpgradeInvalidFault, ref timeoutHelper);
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                                          new ProtocolException(SR.Format(SR.UpgradeRequestToNonupgradableService, Decoder.Upgrade)));
                            }

                            if (!upgradeAcceptor.CanUpgrade(Decoder.Upgrade))
                            {
                                SendFault(FramingEncodingString.UpgradeInvalidFault, ref timeoutHelper);
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.Format(SR.UpgradeProtocolNotSupported, Decoder.Upgrade)));
                            }

                            ChangeUpgradeState(ref upgradeState, UpgradeState.WritingUpgradeAck);
                            // accept upgrade
                            await currentConnection.WriteAsync(ServerSingletonEncoder.UpgradeResponseBytes, 0, ServerSingletonEncoder.UpgradeResponseBytes.Length,
                                                               true, timeoutHelper.RemainingTime());

                            ChangeUpgradeState(ref upgradeState, UpgradeState.UpgradeAckSent);
                            break;

                        case UpgradeState.UpgradeAckSent:
                            IConnection connectionToUpgrade = currentConnection;
                            if (size > 0)
                            {
                                connectionToUpgrade = new PreReadConnection(connectionToUpgrade, connectionBuffer, offset, size);
                            }
                            ChangeUpgradeState(ref upgradeState, UpgradeState.BeginUpgrade);
                            break;

                        case UpgradeState.BeginUpgrade:
                            try
                            {
                                currentConnection = await InitialServerConnectionReader.UpgradeConnectionAsync(currentConnection, upgradeAcceptor, transportSettings);

                                connectionBuffer = currentConnection.AsyncReadBuffer;
                                if (channelBindingProvider != null &&
                                    channelBindingProvider.IsChannelBindingSupportEnabled &&
                                    channelBindingToken == null)        //first one wins in the case of multiple upgrades.
                                {
                                    channelBindingToken = channelBindingProvider.GetChannelBinding(upgradeAcceptor, ChannelBindingKind.Endpoint);
                                }
                                ChangeUpgradeState(ref upgradeState, UpgradeState.EndUpgrade);
                                ChangeUpgradeState(ref upgradeState, UpgradeState.UpgradeComplete);
                            }
                            catch (Exception exception)
                            {
                                if (Fx.IsFatal(exception))
                                {
                                    throw;
                                }

                                WriteAuditFailure(upgradeAcceptor as StreamSecurityUpgradeAcceptor, exception);
                                throw;
                            }
                            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(upgradeAcceptor);

                        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 currentConnection.WriteAsync(ServerSessionEncoder.AckResponseBytes, 0, ServerSessionEncoder.AckResponseBytes.Length,
                                                               true, timeoutHelper.RemainingTime());

                            //terminal state
                            ChangeUpgradeState(ref upgradeState, UpgradeState.PreambleEndSent);
                        }

                        //we are done, this.currentConnection is the upgraded connection
                        return(currentConnection);
                    }

                    if (size == 0)
                    {
                        break;
                    }
                }
            }
        }
            // perform security handshake and ACK connection
            protected override async Task OnOpenAsync(CancellationToken token)
            {
                bool success = false;

                try
                {
                    // TODO: Sort out the timeout here
                    var timeoutHelper = new TimeoutHelper(TimeSpan.FromSeconds(30));
                    // first validate our content type
                    ValidateContentType(ref timeoutHelper);

                    // next read any potential upgrades and finish consuming the preamble
                    for (;;)
                    {
                        if (size == 0)
                        {
                            offset = 0;
                            size   = await Connection.ReadAsync(0, connectionBuffer.Length, timeoutHelper.RemainingTime());

                            if (size == 0)
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException());
                            }
                        }

                        for (;;)
                        {
                            DecodeBytes();
                            switch (decoder.CurrentState)
                            {
                            case ServerSessionDecoder.State.UpgradeRequest:
                                ProcessUpgradeRequest(ref timeoutHelper);

                                // accept upgrade
                                await Connection.WriteAsync(ServerSessionEncoder.UpgradeResponseBytes, 0, ServerSessionEncoder.UpgradeResponseBytes.Length, true, timeoutHelper.RemainingTime());

                                IConnection connectionToUpgrade = Connection;
                                if (size > 0)
                                {
                                    // TODO: Switch to using PreReadConnection constructor which doesn't take a buffer. This is currently causing an extra buffer allocation.
                                    connectionToUpgrade = new PreReadConnection(connectionToUpgrade, connectionBuffer, offset, size);
                                }

                                try
                                {
                                    Connection = await InitialServerConnectionReader.UpgradeConnectionAsync(connectionToUpgrade, upgradeAcceptor, this);

                                    if (channelBindingProvider != null && channelBindingProvider.IsChannelBindingSupportEnabled)
                                    {
                                        SetChannelBinding(channelBindingProvider.GetChannelBinding(upgradeAcceptor, ChannelBindingKind.Endpoint));
                                    }

                                    connectionBuffer = Connection.AsyncReadBuffer;
                                }
                                catch (Exception exception)
                                {
                                    if (Fx.IsFatal(exception))
                                    {
                                        throw;
                                    }

                                    // Audit Authentication Failure
                                    WriteAuditFailure(upgradeAcceptor as StreamSecurityUpgradeAcceptor, exception);
                                    throw;
                                }
                                break;

                            case ServerSessionDecoder.State.Start:
                                SetupSecurityIfNecessary();

                                // we've finished the preamble. Ack and return.
                                await Connection.WriteAsync(ServerSessionEncoder.AckResponseBytes, 0,
                                                            ServerSessionEncoder.AckResponseBytes.Length, true, timeoutHelper.RemainingTime());

                                SetupSessionReader();
                                success = true;
                                return;
                            }

                            if (size == 0)
                            {
                                break;
                            }
                        }
                    }
                }
                finally
                {
                    if (!success)
                    {
                        Connection.Abort();
                    }
                }
            }