internal static RestServiceCollection Create(IEnumerable <FileDescriptor> fileDescriptors)
        {
            var services      = fileDescriptors.SelectMany(file => file.Services);
            var typeRegistry  = TypeRegistry.FromFiles(fileDescriptors.ToArray());
            var parser        = new JsonParser(JsonParser.Settings.Default.WithIgnoreUnknownFields(true).WithTypeRegistry(typeRegistry));
            var methodsByName = services.SelectMany(service => service.Methods)
                                .Where(x => true) // TODO: filter out streaming methods.
                                .Select(method => RestMethod.Create(method, parser))
                                .ToDictionary(restMethod => restMethod.FullName);

            return(new RestServiceCollection(new ReadOnlyDictionary <string, RestMethod>(methodsByName)));
        }
Example #2
0
        private async Task AddAuthHeadersAsync(HttpRequestMessage request, RestMethod method)
        {
            Uri    hostUri            = request.RequestUri.IsAbsoluteUri ? request.RequestUri : _httpClient.BaseAddress;
            string schemeAndAuthority = hostUri.GetLeftPart(UriPartial.Authority);

            var metadata = new Metadata();
            var context  = new AuthInterceptorContext(schemeAndAuthority, method.FullName);

            await _channelAuthInterceptor(context, metadata).ConfigureAwait(false);

            foreach (var entry in metadata)
            {
                request.Headers.Add(entry.Key, entry.Value);
            }
        }
Example #3
0
        private async Task <ReadHttpResponseMessage> SendAsync <TRequest>(RestMethod restMethod, string host, CallOptions options, TRequest request, CancellationToken deadlineToken)
        {
            // Ideally, add the header in the client builder instead of in the ServiceSettingsBase...
            var httpRequest = restMethod.CreateRequest((IMessage)request, host);

            foreach (var headerKeyValue in options.Headers
                     .Where(mh => !mh.IsBinary)
                     .Where(mh => mh.Key != VersionHeaderBuilder.HeaderName))
            {
                httpRequest.Headers.Add(headerKeyValue.Key, headerKeyValue.Value);
            }
            httpRequest.Headers.Add(VersionHeaderBuilder.HeaderName, RestVersion);

            HttpResponseMessage httpResponseMessage;

            using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(options.CancellationToken, deadlineToken))
            {
                try
                {
                    await AddAuthHeadersAsync(httpRequest, restMethod, linkedCts.Token).ConfigureAwait(false);

                    httpResponseMessage = await _httpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseContentRead, linkedCts.Token).ConfigureAwait(false);
                }
                catch (TaskCanceledException ex) when(deadlineToken.IsCancellationRequested)
                {
                    throw new RpcException(new Status(StatusCode.DeadlineExceeded, $"The timeout was reached when calling a method `{restMethod.FullName}`", ex));
                }
            }

            try
            {
                string content = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);

                return(new ReadHttpResponseMessage(httpResponseMessage, content));
            }
            catch (Exception ex)
            {
                // Let's defer the throwing of this exception to when it's actually needed,
                // so that we can at least read headers and other metadata.
                var exInfo = ExceptionDispatchInfo.Capture(ex);
                return(new ReadHttpResponseMessage(httpResponseMessage, exInfo));
            }
        }
Example #4
0
        private async Task <HttpResponseMessage> SendAsync <TRequest>(RestMethod restMethod, string host, CallOptions options, TRequest request, CancellationToken cancellationToken)
        {
            // Ideally, add the header in the client builder instead of in the ServiceSettingsBase...
            // TODO: Use options. How do we set the timeout for an individual HTTP request? We probably need to create a linked cancellation token.
            var httpRequest = restMethod.CreateRequest((IMessage)request, host);

            foreach (var headerKeyValue in options.Headers
                     .Where(mh => !mh.IsBinary)
                     .Where(mh => mh.Key != VersionHeaderBuilder.HeaderName))
            {
                httpRequest.Headers.Add(headerKeyValue.Key, headerKeyValue.Value);
            }
            httpRequest.Headers.Add(VersionHeaderBuilder.HeaderName, RestVersion);

            // TODO: How do we cancel this?
            await AddAuthHeadersAsync(httpRequest, restMethod).ConfigureAwait(false);

            var task = _httpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseContentRead, cancellationToken);

            return(await task.ConfigureAwait(false));
        }
Example #5
0
        private async Task AddAuthHeadersAsync(HttpRequestMessage request, RestMethod restMethod, CancellationToken combinedCancellationToken)
        {
            Uri    hostUri            = request.RequestUri.IsAbsoluteUri ? request.RequestUri : _httpClient.BaseAddress;
            string schemeAndAuthority = hostUri.GetLeftPart(UriPartial.Authority);

            var metadata = new Metadata();
            var context  = new AuthInterceptorContext(schemeAndAuthority, restMethod.FullName);

            var combinedCancellationTask = Task.Delay(-1, combinedCancellationToken);
            var channelTask = _channelAuthInterceptor(context, metadata);
            var resultTask  = await Task.WhenAny(channelTask, combinedCancellationTask).ConfigureAwait(false);

            // If the combinedCancellationTask "wins" `Task.WhenAny` by being cancelled, the following await will throw TaskCancelledException.
            // If the channelTask "wins" by being faulted, the await will rethrow its exception.
            // Finally, if the channelTask completes, the await does nothing.
            await resultTask.ConfigureAwait(false);

            // If we're here, the channelTask has completed successfully.

            foreach (var entry in metadata)
            {
                request.Headers.Add(entry.Key, entry.Value);
            }
        }