.NET Core HttpClient의 HTTP/2 지원
지난 글까지 HTTP/2의 서버 측 지원을 알아봤는데,
IIS의 HTTP/2 지원 여부 - h2, h2c
; https://www.sysnet.pe.kr/2/0/12495
ASP.NET Core(Kestrel)의 HTTP/2 지원 여부
; https://www.sysnet.pe.kr/2/0/12500
그렇다면 클라이언트 측도 마저 다뤄야겠지요. ^^ 쉬운 테스트를 위해 서버는 nghttp2.org로 정했고, HttpClient는 기본적인 구성부터 시작해 .NET Core 3.1 환경으로 테스트를 해보겠습니다.
// .NET Core 3.1
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
private static readonly HttpClient _client;
static Program()
{
_client = new HttpClient();
}
static async Task Main()
{
try
{
{
using var response = await _client.GetAsync("http://nghttp2.org");
Console.WriteLine(response.Version);
}
{
using var response = await _client.GetAsync("https://nghttp2.org");
Console.WriteLine(response.Version);
}
}
catch (HttpRequestException ex)
{
Console.WriteLine(ex.ToString());
}
}
}
/* 출력 결과
1.1
1.1
*/
http와 https 모두 1.1 응답을 받고 있는데요, 왜냐하면 HttpClient의 기본 버전은 1.1이고 TLS 협상 시 h2 식별자를 사용하지 않기 때문입니다. 따라서, 명시적으로 HTTP/2 통신을 하려면 버전을 지정해야 합니다.
static Program()
{
_client = new HttpClient()
{
// curl의 --http2-prior-knowledge 옵션과 유사한 역할
DefaultRequestVersion = new Version(2, 0),
};
}
/* 출력 결과
1.1
2.0
*/
그래도 https의 경우에만 2.0으로 통신이 되었고 http의 경우에는 여전히 1.1로 됩니다. 이유는 애당초 HttpClient는 h2c 모드를 지원하지 않기 때문입니다. 다행히 마이크로소프트는 이를 위해 .NET Core 3.0에서 "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport" 전역 설정을 추가했는데요,
static Program()
{
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
SocketsHttpHandler handler = new SocketsHttpHandler();
_client = new HttpClient(handler)
{
DefaultRequestVersion = new Version(2, 0),
};
}
/* 출력 결과
2.0
2.0
*/
이제야 비로소 http와 https에 대해 모두 HTTP/2 통신을 할 수 있게 됩니다.
그런데, .NET 5부터 이런 정책이 바뀌었습니다. 위의 예제 코드를 실행하면, 다시 "1.1"과 "2.0" 출력을 확인할 수 있는데요, 결국 "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport" 옵션 설정이 아무런 효력을 내지 못하고 있는 것입니다. (달리 말해, 하위 호환성이 깨지므로, HTTP/2 통신을 하는 기존 코드는 .NET 5로 마이그레이션 시 반드시 코드 변경을 해야 합니다.)
이에 대해서는 아래의 문서에서 언급하고 있으며,
HTTP/2 - Version Selection
; https://devblogs.microsoft.com/dotnet/net-5-new-networking-improvements/#version-selection
따라서 .NET 5부터는 VersionPolicy 속성을 통해 이를 제어해야 합니다. 가령, 기존의 .NET Core 3.x 코드를 .NET 5로 마이그레이션한다면 다음과 같이 HttpVersionPolicy.RequestVersionExact 옵션을 설정하면 동일하게 동작할 수 있습니다.
static Program()
{
SocketsHttpHandler handler = new SocketsHttpHandler();
_client = new HttpClient(handler)
{
// curl의 --http2-prior-knowledge 옵션과 유사한 역할
DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact,
DefaultRequestVersion = new Version(2, 0),
};
}
버전 선택 정책에 대한 좀 더 자세한 동작 방식은 다음의 글에 별도로 정리했으니 참고하시고,
.NET 5부터 HTTP/1.1, 2.0 선택을 위한 HttpVersionPolicy 동작 방식
; https://www.sysnet.pe.kr/2/0/12501
한 가지 유의할 것은, HttpClient의 경우 h2c 협상에 준하는 동작은 없다는 점입니다. 즉, 처음부터 HTTP/2 또는 HTTP/1.1로 통신을 게시하는 방식입니다.
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]