예제 #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;
                    }
                }
            }
        }