예제 #1
0
        /// <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");
                }
            }
예제 #2
0
        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.
                    }
                }
            }
예제 #3
0
파일: OutgoingAsync.cs 프로젝트: wandec/ice
 public override List <ArraySegment <byte> > GetRequestData(int requestId) =>
 Ice1Definitions.GetRequestData(RequestFrame, requestId);
예제 #4
0
        /// <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;
        }