Skip to content

Instantly share code, notes, and snippets.

@JunTaoLuo
Last active February 20, 2019 21:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save JunTaoLuo/6127169c8a643a36122981f232ad849d to your computer and use it in GitHub Desktop.
Save JunTaoLuo/6127169c8a643a36122981f232ad849d to your computer and use it in GitHub Desktop.
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Net.Http;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Grpc.Core;
namespace Sample.Clients
{
public class HttpClientCallInvoker : CallInvoker
{
private string _requestUriRoot;
private HttpClient _client;
public HttpClientCallInvoker(string requestUriRoot, X509Certificate2 certificate)
{
var handler = new HttpClientHandler();
EnsureHttp2Feature(handler);
handler.ClientCertificates.Add(certificate);
handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true;
_client = new HttpClient(handler);
_requestUriRoot = requestUriRoot;
}
public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options)
{
throw new NotImplementedException();
}
public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options)
{
throw new NotImplementedException();
}
public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
{
throw new NotImplementedException();
}
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
{
var callUri = "https://" + _requestUriRoot + method.FullName;
var requestBytes = method.RequestMarshaller.Serializer(request);
var payload = new Memory<byte>(new byte[requestBytes.Length + 1 + 4]);
payload.Span[0] = 0;
BinaryPrimitives.WriteUInt32BigEndian(payload.Span.Slice(1), (uint)requestBytes.Length);
requestBytes.CopyTo(payload.Slice(1 + 4));
var content = new ReadOnlyMemoryContent(payload);
var message = new HttpRequestMessage(HttpMethod.Post, callUri);
message.Content = content;
message.Version = new Version(2, 0);
var response = _client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead).GetAwaiter().GetResult();
var responseStream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult();
int byteRead;
while ((byteRead = responseStream.ReadByte()) != -1)
{
Console.WriteLine(byteRead);
}
return null;
}
public override TResponse BlockingUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
{
return AsyncUnaryCall(method, host, options, request)?.GetAwaiter().GetResult();
}
public static void EnsureHttp2Feature(HttpClientHandler handler)
{
// All .NET Core implementations of HttpClientHandler have HTTP/2 enabled by default except when using
// SocketsHttpHandler. Right now, the HTTP/2 feature is disabled on SocketsHttpHandler unless certain
// AppContext switches or environment variables are set. To help with testing, we can enable the HTTP/2
// feature for a specific handler instance by using reflection.
FieldInfo field_socketsHttpHandler = typeof(HttpClientHandler).GetField(
"_socketsHttpHandler",
BindingFlags.NonPublic | BindingFlags.Instance);
if (field_socketsHttpHandler == null)
{
// Not using .NET Core implementation, i.e. could be .NET Framework or UAP.
return;
}
object _socketsHttpHandler = field_socketsHttpHandler.GetValue(handler);
if (_socketsHttpHandler == null)
{
// Not using SocketsHttpHandler, i.e. using WinHttpHandler or CurlHandler.
return;
}
// Get HttpConnectionSettings object from SocketsHttpHandler.
Type type_SocketsHttpHandler = typeof(HttpClientHandler).Assembly.GetType("System.Net.Http.SocketsHttpHandler");
FieldInfo field_settings = type_SocketsHttpHandler.GetField(
"_settings",
BindingFlags.NonPublic | BindingFlags.Instance);
object _settings = field_settings.GetValue(_socketsHttpHandler);
// Set _maxHttpVersion field to HTTP/2.0.
Type type_HttpConnectionSettings = typeof(HttpClientHandler).Assembly.GetType("System.Net.Http.HttpConnectionSettings");
FieldInfo field_maxHttpVersion = type_HttpConnectionSettings.GetField(
"_maxHttpVersion",
BindingFlags.NonPublic | BindingFlags.Instance);
field_maxHttpVersion.SetValue(_settings, new Version(2, 0));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment