Example #1
0
                         Protocol Protocol) Parse(
            string uriString,
            bool oaEndpoints,
            Communicator communicator)
        {
            Debug.Assert(IsUri(uriString));

            try
            {
                bool iceScheme = uriString.StartsWith("ice:");
                if (iceScheme && oaEndpoints)
                {
                    throw new FormatException("an object adapter endpoint supports only ice+transport URIs");
                }

                var generalOptions = new Dictionary <string, string>();
                Dictionary <string, string>?endpointOptions = iceScheme ? null : new Dictionary <string, string>();

                Uri uri = InitialParse(uriString, generalOptions, endpointOptions);

                Protocol protocol = Protocol.Ice2;
                if (generalOptions.TryGetValue("protocol", out string?protocolValue))
                {
                    protocol = ProtocolExtensions.Parse(protocolValue);
                }

                Encoding encoding = protocol.IsSupported() ? protocol.GetEncoding() : Encoding.V2_0;
                if (generalOptions.TryGetValue("encoding", out string?encodingValue))
                {
                    encoding = Encoding.Parse(encodingValue);
                }

                InvocationMode invocationMode = InvocationMode.Twoway;
                if (generalOptions.TryGetValue("invocation-mode", out string?invocationModeValue))
                {
                    if (protocol != Protocol.Ice1)
                    {
                        throw new FormatException("option `invocation-mode' requires the ice1 protocol");
                    }
                    if (oaEndpoints)
                    {
                        throw new FormatException(
                                  "option `invocation-mode' is not applicable to object adapter endpoints");
                    }
                    if (int.TryParse(invocationModeValue, out int _))
                    {
                        throw new FormatException($"invalid value `{invocationModeValue}' for invocation-mode");
                    }
                    invocationMode = Enum.Parse <InvocationMode>(invocationModeValue, ignoreCase: true);
                }

                string        facet = uri.Fragment.Length >= 2 ? Uri.UnescapeDataString(uri.Fragment.TrimStart('#')) : "";
                List <string> path  =
                    uri.AbsolutePath.TrimStart('/').Split('/').Select(s => Uri.UnescapeDataString(s)).ToList();

                List <Endpoint>?endpoints = null;

                if (endpointOptions != null) // i.e. not ice scheme
                {
                    endpoints = new List <Endpoint>
                    {
                        CreateEndpoint(communicator,
                                       oaEndpoints,
                                       endpointOptions,
                                       protocol,
                                       uri,
                                       uriString)
                    };

                    if (generalOptions.TryGetValue("alt-endpoint", out string?altEndpointValue))
                    {
                        foreach (string endpointStr in altEndpointValue.Split(','))
                        {
                            if (endpointStr.StartsWith("ice:"))
                            {
                                throw new FormatException(
                                          $"invalid URI scheme for endpoint `{endpointStr}': must be empty or ice+transport");
                            }

                            string altUriString = endpointStr;
                            if (!altUriString.StartsWith("ice+"))
                            {
                                altUriString = $"{uri.Scheme}://{altUriString}";
                            }

                            // The separator for endpoint options in alt-endpoint is $, and we replace these $ by &
                            // before sending the string the main parser (InitialParse), which uses & as separator.
                            altUriString = altUriString.Replace('$', '&');

                            // No need to clear endpointOptions before reusing it since CreateEndpoint consumes all the
                            // endpoint options
                            Debug.Assert(endpointOptions.Count == 0);
                            uri = InitialParse(altUriString, generalOptions: null, endpointOptions);

                            Debug.Assert(uri.AbsolutePath[0] == '/'); // there is always a first segment
                            if (uri.AbsolutePath.Length > 1 || uri.Fragment.Length > 0)
                            {
                                throw new FormatException(
                                          $"endpoint `{endpointStr}' must not specify a path or fragment");
                            }
                            endpoints.Add(CreateEndpoint(communicator,
                                                         oaEndpoints,
                                                         endpointOptions,
                                                         protocol,
                                                         uri,
                                                         endpointStr));
                        }
                    }
                }

                return(encoding,
                       (IReadOnlyList <Endpoint>?)endpoints ?? ImmutableArray <Endpoint> .Empty,
                       facet,
                       invocationMode,
                       path,
                       protocol);
            }
            catch (Exception ex)
            {
                // Give context to the exception.
                throw new FormatException($"failed to parse URI `{uriString}'", ex);
            }
        }
Example #2
0
        internal override async ValueTask InitializeAsync(Action heartbeatCallback, CancellationToken cancel)
        {
            HeartbeatCallback = heartbeatCallback;

            // Initialize the transport
            await _transceiver.InitializeAsync(cancel).ConfigureAwait(false);

            if (IsIncoming)
            {
                (FrameType type, ArraySegment <byte> data) = await ReceiveFrameAsync(cancel);

                if (type != FrameType.Initialize)
                {
                    throw new InvalidDataException($"unexpected Slic frame with frame type `{type}'");
                }

                // Check that the Slic version is supported (we only support version 1 for now)
                var istr = new InputStream(data, Encoding);
                if (istr.ReadUShort() != 1)
                {
                    // If unsupported Slic version, we stop reading there and reply with a VERSION frame to provide
                    // the client the supported Slic versions.
                    await PrepareAndSendFrameAsync(FrameType.Version, ostr =>
                    {
                        ostr.WriteSequence(new ArraySegment <short>(new short[] { 1 }).AsReadOnlySpan());
                    }, cancel).ConfigureAwait(false);

                    (type, data) = await ReceiveFrameAsync(cancel);

                    if (type != FrameType.Initialize)
                    {
                        throw new InvalidDataException($"unexpected Slic frame with frame type `{type}'");
                    }

                    istr = new InputStream(data, Encoding);
                    ushort version = istr.ReadUShort();
                    if (version != 1)
                    {
                        throw new InvalidDataException($"unsupported Slic version `{version}'");
                    }
                }

                string protocol = istr.ReadString();
                if (ProtocolExtensions.Parse(protocol) != Protocol.Ice2)
                {
                    throw new NotSupportedException($"application protocol `{protocol}' is not supported with Slic");
                }

                // TODO: transport parameters

                // Send back an INITIALIZE_ACK frame.
                await PrepareAndSendFrameAsync(FrameType.InitializeAck, istr =>
                {
                    // TODO: transport parameters
                }, cancel).ConfigureAwait(false);
            }
            else
            {
                // Send the INITIALIZE frame.
                await PrepareAndSendFrameAsync(FrameType.Initialize, ostr =>
                {
                    ostr.WriteUShort(1);                       // Slic V1
                    ostr.WriteString(Protocol.Ice2.GetName()); // Ice protocol name
                    // TODO: transport parameters
                }, cancel).ConfigureAwait(false);

                // Read the INITIALIZE_ACK or VERSION frame from the server
                (FrameType type, ArraySegment <byte> data) = await ReceiveFrameAsync(cancel);

                // If we receive a VERSION frame, there isn't much we can do as we only support V1 so we throw
                // with an appropriate message to abort the connection.
                if (type == FrameType.Version)
                {
                    // Read the version sequence provided by the server.
                    short[] versions = new InputStream(data, Encoding).ReadArray <short>();
                    throw new InvalidDataException(
                              $"unsupported Slic version, server supports Slic `{string.Join(", ", versions)}'");
                }
                else if (type != FrameType.InitializeAck)
                {
                    throw new InvalidDataException($"unexpected Slic frame with frame type `{type}'");
                }

                // TODO: transport parameters
            }

            if (Endpoint.Communicator.TraceLevels.Transport >= 1)
            {
                var s = new StringBuilder();
                s.Append(IsIncoming ? "accepted" : "established");
                s.Append(' ');
                s.Append(Endpoint.TransportName);
                s.Append(" connection\n");
                s.Append(ToString());
                Endpoint.Communicator.Logger.Trace(Endpoint.Communicator.TraceLevels.TransportCategory, s.ToString());
            }
        }