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); } }
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()); } }