/// <summary>Constructs an incoming request frame.</summary> /// <param name="protocol">The Ice protocol.</param> /// <param name="data">The frame data as an array segment.</param> /// <param name="maxSize">The maximum payload size, checked during decompression.</param> /// <param name="socketStream">The optional socket stream. The stream is non-null if there's still data to /// read on the stream after the reading the request frame.</param> internal IncomingRequestFrame( Protocol protocol, ArraySegment <byte> data, int maxSize, SocketStream?socketStream) : base(protocol, maxSize) { SocketStream = socketStream; var istr = new InputStream(data, Protocol.GetEncoding()); if (Protocol == Protocol.Ice1) { var requestHeader = new Ice1RequestHeader(istr); Identity = requestHeader.Identity; Facet = Ice1Definitions.GetFacet(requestHeader.FacetPath); Location = Array.Empty <string>(); Operation = requestHeader.Operation; IsIdempotent = requestHeader.OperationMode != OperationMode.Normal; Context = requestHeader.Context; Priority = default; Deadline = DateTime.MaxValue; } else { int headerSize = istr.ReadSize(); int startPos = istr.Pos; // We use the generated code for the header body and read the rest of the header "by hand". var requestHeaderBody = new Ice2RequestHeaderBody(istr); Identity = requestHeaderBody.Identity; Facet = requestHeaderBody.Facet ?? ""; Location = requestHeaderBody.Location ?? Array.Empty <string>(); Operation = requestHeaderBody.Operation; IsIdempotent = requestHeaderBody.Idempotent ?? false; Priority = requestHeaderBody.Priority ?? default; if (requestHeaderBody.Deadline < -1 || requestHeaderBody.Deadline == 0) { throw new InvalidDataException($"received invalid deadline value {requestHeaderBody.Deadline}"); } // The infinite deadline is encoded as -1 and converted to DateTime.MaxValue Deadline = requestHeaderBody.Deadline == -1 ? DateTime.MaxValue : DateTime.UnixEpoch + TimeSpan.FromMilliseconds(requestHeaderBody.Deadline); Context = requestHeaderBody.Context ?? new SortedDictionary <string, string>(); BinaryContext = istr.ReadBinaryContext(); if (istr.Pos - startPos != headerSize) { throw new InvalidDataException( @$ "received invalid request header: expected {headerSize} bytes but read {istr.Pos - startPos } bytes"); } if (Location.Any(segment => segment.Length == 0)) { throw new InvalidDataException("received request with an empty location segment"); } }
public override async ValueTask <SocketStream> AcceptStreamAsync(CancellationToken cancel) { while (true) { // Receive the Ice1 frame header. ArraySegment <byte> buffer; if (Endpoint.IsDatagram) { buffer = await _socket.ReceiveDatagramAsync(cancel).ConfigureAwait(false); if (buffer.Count < Ice1Definitions.HeaderSize) { ReceivedInvalidData($"received datagram with {buffer.Count} bytes"); continue; } Received(buffer.Count); } else { buffer = new ArraySegment <byte>(new byte[256], 0, Ice1Definitions.HeaderSize); await ReceiveAsync(buffer, cancel).ConfigureAwait(false); } // Check the header Ice1Definitions.CheckHeader(buffer.AsReadOnlySpan(0, Ice1Definitions.HeaderSize)); int size = buffer.AsReadOnlySpan(10, 4).ReadInt(); if (size < Ice1Definitions.HeaderSize) { ReceivedInvalidData($"received ice1 frame with only {size} bytes"); continue; } if (size > IncomingFrameMaxSize) { ReceivedInvalidData($"frame with {size} bytes exceeds Ice.IncomingFrameMaxSize value"); continue; } // Read the remainder of the frame if needed. if (size > buffer.Count) { if (Endpoint.IsDatagram) { ReceivedInvalidData($"maximum datagram size of {buffer.Count} exceeded"); continue; } if (size > buffer.Array !.Length) { // Allocate a new array and copy the header over. var tmpBuffer = new ArraySegment <byte>(new byte[size], 0, size); buffer.AsSpan().CopyTo(tmpBuffer.AsSpan(0, Ice1Definitions.HeaderSize)); buffer = tmpBuffer; } else { buffer = new ArraySegment <byte>(buffer.Array !, 0, size); } Debug.Assert(size == buffer.Count); await ReceiveAsync(buffer.Slice(Ice1Definitions.HeaderSize), cancel).ConfigureAwait(false); } // Make sure the socket is marked as validated. This flag is necessary because incoming // connection initialization doesn't wait for connection validation message. So the connection // is considered validated on the server side only once the first frame is received. This is // only useful for connection warnings, to prevent a warning from showing up if the server side // connection is closed before the first message is received (which can occur with SSL for // example if the certification validation fails on the client side). IsValidated = true; // Parse the received frame and translate it into a stream ID, frame type and frame data. The returned // stream ID can be negative if the Ice1 frame is no longer supported (batch requests). (long streamId, Ice1FrameType frameType, ArraySegment <byte> frame) = ParseFrame(buffer); if (streamId >= 0) { if (TryGetStream(streamId, out Ice1NetworkSocketStream? stream)) { // If this is a known stream, pass the data to the stream. if (frameType == Ice1FrameType.ValidateConnection) { // Except for the validate connection frame, subsequent validate connection messages are // heartbeats sent by the peer. We just handle it here and don't pass it over the control // stream which only expect the close frame at this point. Debug.Assert(stream.IsControl); continue; } try { stream.ReceivedFrame(frameType, frame); } catch { // Ignore, the stream has been aborted } } else if (frameType == Ice1FrameType.Request) { // Create a new input stream for the request. If serialization is enabled, ensure we acquire // the semaphore first to serialize the dispatching. try { stream = new Ice1NetworkSocketStream(this, streamId); AsyncSemaphore?semaphore = stream.IsBidirectional ? _bidirectionalSerializeSemaphore : _unidirectionalSerializeSemaphore; if (semaphore != null) { await semaphore.WaitAsync(cancel).ConfigureAwait(false); } stream.ReceivedFrame(frameType, frame); return(stream); } catch { // Ignore, if the connection is being closed or the stream has been aborted. stream?.Dispose(); } } else if (frameType == Ice1FrameType.ValidateConnection) { // If we received a connection validation frame and the stream is not known, it's the first // received connection validation message, create the control stream and return it. stream = new Ice1NetworkSocketStream(this, streamId); Debug.Assert(stream.IsControl); stream.ReceivedFrame(frameType, frame); return(stream); } else { // The stream has been disposed, ignore the data. } } }
public override List <ArraySegment <byte> > GetRequestData(int requestId) => Ice1Definitions.GetRequestData(RequestFrame, requestId);
/// <summary>Constructs an incoming request frame.</summary> /// <param name="protocol">The Ice protocol.</param> /// <param name="data">The frame data as an array segment.</param> /// <param name="sizeMax">The maximum payload size, checked during decompression.</param> public IncomingRequestFrame(Protocol protocol, ArraySegment <byte> data, int sizeMax) : base(data, protocol, sizeMax) { var istr = new InputStream(Data, Protocol.GetEncoding()); if (Protocol == Protocol.Ice1) { var requestHeaderBody = new Ice1RequestHeaderBody(istr); Identity = requestHeaderBody.Identity; Facet = Ice1Definitions.GetFacet(requestHeaderBody.FacetPath); Location = Array.Empty <string>(); Operation = requestHeaderBody.Operation; IsIdempotent = requestHeaderBody.OperationMode != OperationMode.Normal; Context = requestHeaderBody.Context; Priority = default; Deadline = DateTime.MaxValue; } else { var requestHeaderBody = new Ice2RequestHeaderBody(istr); Identity = requestHeaderBody.Identity; Facet = requestHeaderBody.Facet ?? ""; Location = requestHeaderBody.Location ?? Array.Empty <string>(); Operation = requestHeaderBody.Operation; IsIdempotent = requestHeaderBody.Idempotent ?? false; Priority = requestHeaderBody.Priority ?? default; if (requestHeaderBody.Deadline < -1 || requestHeaderBody.Deadline == 0) { throw new InvalidDataException($"received invalid deadline value {requestHeaderBody.Deadline}"); } // The infinite deadline is encoded as -1 and converted to DateTime.MaxValue Deadline = requestHeaderBody.Deadline == -1 ? DateTime.MaxValue : DateTime.UnixEpoch + TimeSpan.FromMilliseconds(requestHeaderBody.Deadline); Context = null !; // initialized below if (Location.Any(segment => segment.Length == 0)) { throw new InvalidDataException("received request with empty location segment"); } } if (Identity.Name.Length == 0) { throw new InvalidDataException("received request with null identity"); } if (Operation.Length == 0) { throw new InvalidDataException("received request with empty operation name"); } (int size, int sizeLength, Encoding encoding) = Data.Slice(istr.Pos).AsReadOnlySpan().ReadEncapsulationHeader(Protocol.GetEncoding()); Payload = Data.Slice(istr.Pos, size + sizeLength); // the payload is the encapsulation if (Protocol == Protocol.Ice2) { // BinaryContext is a computed property that depends on Payload. if (BinaryContext.TryGetValue(0, out ReadOnlyMemory <byte> value)) { Context = value.Read(istr => istr.ReadDictionary(minKeySize: 1, minValueSize: 1, InputStream.IceReaderIntoString, InputStream.IceReaderIntoString)); } else { Context = new Dictionary <string, string>(); } } if (protocol == Protocol.Ice1 && size + 4 + istr.Pos != data.Count) { // The payload holds an encapsulation and the encapsulation must use up the full buffer with ice1. // "4" corresponds to fixed-length size with the 1.1 encoding. throw new InvalidDataException($"invalid request encapsulation size: {size}"); } Encoding = encoding; HasCompressedPayload = Encoding == Encoding.V20 && Payload[sizeLength + 2] != 0; }