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