Beispiel #1
0
        private async Task <State> DoExpectConfigAsync()
        {
            Stream stream = networkStream.Stream;
            Frame  frame  = await Frame.ReadAsync(stream, shutdownTokenSource.Token, logger);

            if (frame == null)
            {
                logger.Site().Information("{0} EOS encountered while waiting for config, so disconnecting.", this);
                return(State.Disconnecting);
            }

            var result = EpoxyProtocol.Classify(frame, logger);

            switch (result.Disposition)
            {
            case EpoxyProtocol.FrameDisposition.ProcessConfig:
                // we don't actually use the config yet
                return(connectionType == ConnectionType.Server ? State.ServerSendConfig : State.Connected);

            case EpoxyProtocol.FrameDisposition.HandleProtocolError:
                // we got a protocol error while we expected config
                handshakeError = result.Error;
                return(State.Disconnecting);

            case EpoxyProtocol.FrameDisposition.HangUp:
                return(State.Disconnecting);

            default:
                protocolError = result.ErrorCode ?? ProtocolErrorCode.PROTOCOL_VIOLATED;
                logger.Site().Error("{0} Unsupported FrameDisposition {1} when waiting for config. ErrorCode: {2})",
                                    this, result.Disposition, protocolError);
                return(State.SendProtocolError);
            }
        }
Beispiel #2
0
        private State DoCreated()
        {
            State result;

            if (connectionType == ConnectionType.Server)
            {
                var   args            = new ConnectedEventArgs(this);
                Error disconnectError = parentListener.InvokeOnConnected(args);

                if (disconnectError == null)
                {
                    result = State.ServerExpectConfig;
                }
                else
                {
                    logger.Site().Information("{0} Rejecting connection because {1}:{2}",
                                              this, disconnectError.error_code, disconnectError.message);

                    protocolError = ProtocolErrorCode.CONNECTION_REJECTED;
                    errorDetails  = disconnectError;
                    result        = State.SendProtocolError;
                }
            }
            else
            {
                result = State.ClientSendConfig;
            }

            return(result);
        }
Beispiel #3
0
        private State?DispatchRequest(EpoxyHeaders headers, ArraySegment <byte> payload, ArraySegment <byte> layerData)
        {
            if (headers.error_code != (int)ErrorCode.OK)
            {
                logger.Site().Error("{0} Received request with a non-zero error code. Conversation ID: {1}",
                                    this, headers.conversation_id);
                protocolError = ProtocolErrorCode.PROTOCOL_VIOLATED;
                return(State.SendProtocolError);
            }

            Task.Run(async() =>
            {
                var totalTime      = Stopwatch.StartNew();
                var requestMetrics = Metrics.StartRequestMetrics(ConnectionMetrics);
                var receiveContext = new EpoxyReceiveContext(this, ConnectionMetrics, requestMetrics);

                IMessage request        = Message.FromPayload(Unmarshal.From(payload));
                IBonded bondedLayerData = (layerData.Array == null) ? null : Unmarshal.From(layerData);

                ILayerStack layerStack;
                Error layerError = parentTransport.GetLayerStack(requestMetrics.request_id, out layerStack);

                if (layerError == null)
                {
                    layerError = LayerStackUtils.ProcessOnReceive(
                        layerStack, MessageType.Request, receiveContext, bondedLayerData, logger);
                }

                IMessage result;

                if (layerError == null)
                {
                    result = await serviceHost.DispatchRequest(headers.method_name, receiveContext, request);
                }
                else
                {
                    logger.Site().Error("{0} Receiving request {1}/{2} failed due to layer error (Code: {3}, Message: {4}).",
                                        this, headers.conversation_id, headers.method_name,
                                        layerError.error_code, layerError.message);

                    // Set layer error as result of this Bond method call and do not dispatch to method.
                    // Since this error will be returned to client, cleanse out internal server error details, if any.
                    result = Message.FromError(Errors.CleanseInternalServerError(layerError));
                }

                await SendReplyAsync(headers.conversation_id, result, layerStack, requestMetrics);
                Metrics.FinishRequestMetrics(requestMetrics, totalTime);
                metrics.Emit(requestMetrics);
            });

            // no state change needed
            return(null);
        }
Beispiel #4
0
        private async Task <State> DoSendProtocolErrorAsync()
        {
            ProtocolErrorCode errorCode = protocolError;
            Error             details   = errorDetails;

            var frame = MakeProtocolErrorFrame(errorCode, details);

            Log.Debug("{0}.{1}: Sending protocol error with code {2} and details {3}.",
                      this, nameof(DoSendProtocolErrorAsync), errorCode, details == null ? "<null>" : details.error_code + details.message);

            bool wasSent = await SendFrameAsync(frame);

            Log.Debug(
                "{0}.{1}: Sending protocol error with code {2} {3}.",
                this, nameof(DoSendProtocolErrorAsync), errorCode, wasSent ? "succeded" : "failed");

            return(State.Disconnecting);
        }
Beispiel #5
0
        private async Task <State> DoSendProtocolErrorAsync()
        {
            ProtocolErrorCode errorCode = protocolError;
            Error             details   = errorDetails;

            var frame = MakeProtocolErrorFrame(errorCode, details, logger);

            logger.Site().Debug("{0} Sending protocol error with code {1} and details {2}.",
                                this, errorCode, details == null ? "<null>" : details.error_code + details.message);

            bool wasSent = await SendFrameAsync(frame);

            logger.Site().Debug(
                "{0} Sending protocol error with code {1} {2}.",
                this, errorCode, wasSent ? "succeeded" : "failed");

            return(State.Disconnecting);
        }
Beispiel #6
0
        internal static Frame MakeProtocolErrorFrame(ProtocolErrorCode errorCode, Error details, Logger logger)
        {
            var protocolError = new ProtocolError
            {
                error_code = errorCode,
                details    = (details == null ? null : new Bonded <Error>(details))
            };

            var outputBuffer = new OutputBuffer(16);
            var fastWriter   = new FastBinaryWriter <OutputBuffer>(outputBuffer);

            Serialize.To(fastWriter, protocolError);

            var frame = new Frame(1, logger);

            frame.Add(new Framelet(FrameletType.ProtocolError, outputBuffer.Data));
            return(frame);
        }
Beispiel #7
0
        private State?DispatchRequest(EpoxyHeaders headers, ArraySegment <byte> payload, ArraySegment <byte> layerData)
        {
            if (headers.error_code != (int)ErrorCode.OK)
            {
                Log.Error("{0}.{1}: Received request with a non-zero error code. Conversation ID: {2}",
                          this, nameof(DispatchRequest), headers.conversation_id);
                protocolError = ProtocolErrorCode.PROTOCOL_VIOLATED;
                return(State.SendProtocolError);
            }

            IMessage request = Message.FromPayload(Unmarshal.From(payload));

            var receiveContext = new EpoxyReceiveContext(this);

            IBonded bondedLayerData = (layerData.Array == null) ? null : Unmarshal.From(layerData);

            Error layerError = LayerStackUtils.ProcessOnReceive(parentTransport.LayerStack, MessageType.Request, receiveContext, bondedLayerData);

            Task.Run(async() =>
            {
                IMessage result;

                if (layerError == null)
                {
                    result = await serviceHost.DispatchRequest(headers.method_name, receiveContext, request,
                                                               connectionMetrics);
                }
                else
                {
                    Log.Error("{0}.{1}: Receiving request {2}/{3} failed due to layer error (Code: {4}, Message: {5}).",
                              this, nameof(DispatchRequest), headers.conversation_id, headers.method_name,
                              layerError.error_code, layerError.message);
                    result = Message.FromError(layerError);
                }

                await SendReplyAsync(headers.conversation_id, result);
            });

            // no state change needed
            return(null);
        }
Beispiel #8
0
        internal static ClassifyState TransitionExpectFirstFramelet(
            ClassifyState state, Frame frame, ref ProtocolErrorCode? errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectFirstFramelet);
            Debug.Assert(frame != null);

            if (frame.Framelets.Count == 0)
            {
                logger.Site().Error("Frame was empty.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            switch (frame.Framelets[0].Type)
            {
                case FrameletType.EpoxyHeaders:
                    return ClassifyState.ExpectEpoxyHeaders;

                case FrameletType.EpoxyConfig:
                    return ClassifyState.ExpectConfig;

                case FrameletType.ProtocolError:
                    return ClassifyState.ExpectProtocolError;

                default:
                    logger.Site().Error("Frame began with invalid FrameletType {0}.", frame.Framelets[0].Type);
                    errorCode = ProtocolErrorCode.MALFORMED_DATA;
                    return ClassifyState.MalformedFrame;
            }
        }
Beispiel #9
0
        private async Task ConnectionLoop()
        {
            while (true)
            {
                State nextState;

                try
                {
                    if (state == State.Disconnected)
                    {
                        break; // while loop
                    }

                    switch (state)
                    {
                    case State.Created:
                        nextState = DoCreated();
                        break;

                    case State.ClientSendConfig:
                    case State.ServerSendConfig:
                        nextState = await DoSendConfigAsync();

                        break;

                    case State.ClientExpectConfig:
                    case State.ServerExpectConfig:
                        nextState = await DoExpectConfigAsync();

                        break;

                    case State.Connected:
                        // signal after state change to prevent races with
                        // EnsureCorrectState
                        startTask.SetResult(true);
                        nextState = await DoConnectedAsync();

                        break;

                    case State.SendProtocolError:
                        nextState = await DoSendProtocolErrorAsync();

                        break;

                    case State.Disconnecting:
                        nextState = DoDisconnect();
                        break;

                    case State.Disconnected:     // we should never enter this switch in the Disconnected state
                    default:
                        logger.Site().Error("{0} Unexpected connection state: {1}", this, state);
                        protocolError = ProtocolErrorCode.INTERNAL_ERROR;
                        nextState     = State.SendProtocolError;
                        break;
                    }
                }
                catch (Exception ex) when(state != State.Disconnecting && state != State.Disconnected)
                {
                    logger.Site().Error(ex, "{0} Unhandled exception. Current state: {1}", this, state);

                    // we're in a state where we can attempt to disconnect
                    protocolError = ProtocolErrorCode.INTERNAL_ERROR;
                    nextState     = State.Disconnecting;
                }
                catch (Exception ex)
                {
                    logger.Site().Error(ex, "{0} Unhandled exception during shutdown. Abandoning connection. Current state: {1}",
                                        this, state);
                    break; // the while loop
                }

                state = nextState;
            } // while (true)

            if (state != State.Disconnected)
            {
                logger.Site().Information("{0} Abandoning connection. Current state: {1}", this, state);
            }

            DoDisconnected();
        }
Beispiel #10
0
        internal static ClassifyState TransitionExpectConfig(
            ClassifyState state, Frame frame, ref ProtocolErrorCode? errorCode, ref FrameDisposition disposition,
            Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectConfig);
            Debug.Assert(frame != null);

            if (frame.Count == 0 || frame.Framelets[0].Type != FrameletType.EpoxyConfig)
            {
                return ClassifyState.InternalStateError;
            }

            if (frame.Count != 1)
            {
                logger.Site().Error("Config frame had trailing framelets.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            var framelet = frame.Framelets[0];

            var inputBuffer = new InputBuffer(framelet.Contents);
            var fastBinaryReader = new FastBinaryReader<InputBuffer>(inputBuffer, version: 1);

            // We don't currently do anything with the config aside from try to deserialize it.
            EpoxyConfig config;
            switch (configDeserializer.TryDeserialize(fastBinaryReader, out config))
            {
                case Deserialize.Result.Success:
                    break;

                default:
                    logger.Site().Error("Didn't get a valid {0}.", nameof(EpoxyConfig));
                    errorCode = ProtocolErrorCode.MALFORMED_DATA;
                    return ClassifyState.MalformedFrame;
            }

            disposition = FrameDisposition.ProcessConfig;
            return ClassifyState.ClassifiedValidFrame;
        }
Beispiel #11
0
        internal static ClassifyState TransitionFrameComplete(
            ClassifyState state, EpoxyHeaders headers, ref ProtocolErrorCode? errorCode, Logger logger)
        {
            if (state != ClassifyState.FrameComplete || headers == null)
            {
                return ClassifyState.InternalStateError;
            }

            switch (headers.message_type)
            {
                case EpoxyMessageType.REQUEST:
                case EpoxyMessageType.RESPONSE:
                case EpoxyMessageType.EVENT:
                    return ClassifyState.ValidFrame;

                default:
                    logger.Site().Warning("Received unrecognized message type {0}.", headers.message_type);
                    errorCode = ProtocolErrorCode.NOT_SUPPORTED;
                    return ClassifyState.MalformedFrame;
            }
        }
Beispiel #12
0
        internal static ClassifyState TransitionExpectEndOfFrame(
            ClassifyState state, Frame frame, ArraySegment<byte> layerData, ref ProtocolErrorCode? errorCode,
            Logger logger)
        {
            // FIXME: Change all of these to asserts.
            if (state != ClassifyState.ExpectEndOfFrame || frame == null)
            {
                return ClassifyState.InternalStateError;
            }

            var validFrameSize = (layerData.Array == null ? 2 : 3);
            if (frame.Count == validFrameSize)
            {
                return ClassifyState.FrameComplete;
            }
            else
            {
                logger.Site().Error("Frame had trailing framelets.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }
        }
Beispiel #13
0
 public ProtocolException(string message, ProtocolErrorCode code, Exception innerException)
     : base(message, innerException)
 {
     Code = code;
 }
Beispiel #14
0
 public ErrorResponse(ProtocolErrorCode trailer, byte[] body = default)
 {
     Init(body, trailer);
 }
Beispiel #15
0
 public abstract void Init(byte[] data, ProtocolErrorCode trailer);
 /// <summary>
 /// Parse the ProtocolError structure.
 /// </summary>
 /// <param name="s">An stream containing ProtocolError structure.</param>
 public override void Parse(Stream s)
 {
     base.Parse(s);
     this.ErrorProtocol = new bit32StreamObjectHeaderStart();
     this.ErrorProtocol.Parse(s);
     this.ErrorCode = (ProtocolErrorCode)ReadUint();
 }
Beispiel #17
0
 public override void Init(byte[] body, ProtocolErrorCode trailer)
 {
     Body    = body;
     Trailer = trailer;
 }
Beispiel #18
0
 public static ProtocolError WithCode(ProtocolErrorCode code)
 {
     return(new ProtocolError(code));
 }
Beispiel #19
0
 private ProtocolError(ProtocolErrorCode code)
 {
     ErrorCode = code;
 }
Beispiel #20
0
        private async Task <State> DoConnectedAsync()
        {
            while (!shutdownTokenSource.IsCancellationRequested)
            {
                Frame frame;

                try
                {
                    Stream stream = networkStream.Stream;
                    frame = await Frame.ReadAsync(stream, shutdownTokenSource.Token, logger);

                    if (frame == null)
                    {
                        logger.Site().Information("{0} EOS encountered, so disconnecting.", this);
                        return(State.Disconnecting);
                    }
                }
                catch (EpoxyProtocolErrorException pex)
                {
                    logger.Site().Error(pex, "{0} Protocol error encountered.", this);
                    protocolError = ProtocolErrorCode.PROTOCOL_VIOLATED;
                    return(State.SendProtocolError);
                }
                catch (Exception ex) when(ex is IOException || ex is ObjectDisposedException || ex is SocketException)
                {
                    logger.Site().Error(ex, "{0} IO error encountered.", this);
                    return(State.Disconnecting);
                }

                var result = EpoxyProtocol.Classify(frame, logger);
                switch (result.Disposition)
                {
                case EpoxyProtocol.FrameDisposition.DeliverRequestToService:
                {
                    State?nextState = DispatchRequest(result.Headers, result.MessageData, result.LayerData);
                    if (nextState.HasValue)
                    {
                        return(nextState.Value);
                    }
                    else
                    {
                        // continue the read loop
                        break;
                    }
                }

                case EpoxyProtocol.FrameDisposition.DeliverResponseToProxy:
                    DispatchResponse(result.Headers, result.MessageData, result.LayerData);
                    break;

                case EpoxyProtocol.FrameDisposition.DeliverEventToService:
                    DispatchEvent(result.Headers, result.MessageData, result.LayerData);
                    break;

                case EpoxyProtocol.FrameDisposition.SendProtocolError:
                    protocolError = result.ErrorCode ?? ProtocolErrorCode.INTERNAL_ERROR;
                    return(State.SendProtocolError);

                case EpoxyProtocol.FrameDisposition.HandleProtocolError:
                case EpoxyProtocol.FrameDisposition.HangUp:
                    return(State.Disconnecting);

                default:
                    logger.Site().Error("{0} Unsupported FrameDisposition {1}", this, result.Disposition);
                    protocolError = ProtocolErrorCode.INTERNAL_ERROR;
                    return(State.SendProtocolError);
                }
            }

            // shutdown requested between reading frames
            return(State.Disconnecting);
        }
Beispiel #21
0
        internal static ClassifyState TransitionExpectEpoxyHeaders(
            ClassifyState state, Frame frame, ref EpoxyHeaders headers, ref ProtocolErrorCode? errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectEpoxyHeaders);
            Debug.Assert(frame != null);

            if (frame.Count == 0 || frame.Framelets[0].Type != FrameletType.EpoxyHeaders)
            {
                return ClassifyState.InternalStateError;
            }

            var framelet = frame.Framelets[0];
            var inputBuffer = new InputBuffer(framelet.Contents);
            var fastBinaryReader = new FastBinaryReader<InputBuffer>(inputBuffer, version: 1);
            switch (headersDeserializer.TryDeserialize(fastBinaryReader, out headers))
            {
                case Deserialize.Result.Success:
                    break;

                default:
                    logger.Site().Error("Didn't get a valid {0}.", nameof(EpoxyHeaders));
                    errorCode = ProtocolErrorCode.MALFORMED_DATA;
                    return ClassifyState.MalformedFrame;
            }

            logger.Site().Debug("Deserialized {0} with conversation ID {1} and message type {2}.",
                nameof(EpoxyHeaders), headers.conversation_id, headers.message_type);
            return ClassifyState.ExpectOptionalLayerData;
        }
Beispiel #22
0
 private IRawConvertible CreateError(ProtocolErrorCode code)
 {
     return(new ErrorResponse(code));
 }
Beispiel #23
0
        internal static ClassifyState TransitionExpectOptionalLayerData(
            ClassifyState state, Frame frame, EpoxyHeaders headers, ref ArraySegment<byte> layerData,
            ref ProtocolErrorCode? errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectOptionalLayerData);
            Debug.Assert(frame != null);

            if (headers == null)
            {
                return ClassifyState.InternalStateError;
            }

            if (frame.Count < 2)
            {
                logger.Site().Error("Frame had headers but no message data.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            var framelet = frame.Framelets[1];
            if (framelet.Type == FrameletType.LayerData)
            {
                layerData = framelet.Contents;
                logger.Site().Debug("Extracted {0}-byte layer data in conversation ID {1}.",
                    layerData.Count, headers.conversation_id);
            }

            return ClassifyState.ExpectMessageData;
        }
Beispiel #24
0
 public ProtocolException(string message, ProtocolErrorCode code) : base(message)
 {
     Code = code;
 }
Beispiel #25
0
        internal static ClassifyState TransitionExpectMessageData(
            ClassifyState state, Frame frame, EpoxyHeaders headers, ArraySegment<byte> layerData,
            ref MessageData messageData, ref ProtocolErrorCode? errorCode, Logger logger)
        {
            Debug.Assert(state == ClassifyState.ExpectMessageData);
            Debug.Assert(frame != null);

            if (headers == null)
            {
                return ClassifyState.InternalStateError;
            }

            int messageDataIndex = (layerData.Array == null ? 1 : 2);
            if (messageDataIndex >= frame.Count)
            {
                logger.Site().Error("Frame had headers but no message data.");
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            var framelet = frame.Framelets[messageDataIndex];
            if (framelet.Type != FrameletType.PayloadData && framelet.Type != FrameletType.ErrorData)
            {
                logger.Site().Error("Frame had headers but no message data. Unexpected framelet type {0}", (int)framelet.Type);
                errorCode = ProtocolErrorCode.MALFORMED_DATA;
                return ClassifyState.MalformedFrame;
            }

            messageData = new MessageData(
                isError: framelet.Type == FrameletType.ErrorData,
                data: framelet.Contents);

            logger.Site().Debug("Extracted {0}-byte message in conversation ID {1}.",
                messageData.Data.Count, headers.conversation_id);
            return ClassifyState.ExpectEndOfFrame;
        }