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); } }
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); }
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); }
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); }
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); }
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); }
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); }
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; } }
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(); }
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; }
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; } }
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; } }
public ProtocolException(string message, ProtocolErrorCode code, Exception innerException) : base(message, innerException) { Code = code; }
public ErrorResponse(ProtocolErrorCode trailer, byte[] body = default) { Init(body, trailer); }
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(); }
public override void Init(byte[] body, ProtocolErrorCode trailer) { Body = body; Trailer = trailer; }
public static ProtocolError WithCode(ProtocolErrorCode code) { return(new ProtocolError(code)); }
private ProtocolError(ProtocolErrorCode code) { ErrorCode = code; }
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); }
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; }
private IRawConvertible CreateError(ProtocolErrorCode code) { return(new ErrorResponse(code)); }
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; }
public ProtocolException(string message, ProtocolErrorCode code) : base(message) { Code = code; }
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; }