HTTP/2 RFC를 응용한 HTTP/2 Checker의 구현
몇 년 전, 프로젝트에 HTTP/2를 도입할 무렵, KeyCDN에서 만든 HTTP/2-Test 서비스를 보고,
이것을 어떻게 만들었을까 궁금했습니다. 아마도 직접 도메인 주소로 테스트 에이전트가 접속해서
HTTP/2 지원 여부에 대한 요청을 하고, 뭔가 받은 응답 값을 사용했겠지 예상이 되더군요.
원리의 파악..
유사하게 작동하는 HTTP/2 Checker를 구현하기 위해 HTTP/2 RFC(Request for Comments) 내용을 찾아보았습니다.
모든 통신 프로토콜이 그렇듯 HTTP/2의 RFC-7540에서의 정의를 다시 한번 빌리자면,
HTTP/2를 사용하기 위해서는 클라이언트와 서버 양측 모두 HTTP/2를 지원해야 합니다.
대부분의 최근 브라우저 버전들은 TLS 기반의 HTTP/2를 지원하며, 일부는 HTTP/3까지도 지원합니다.
HTTP/2 RFC-7540에 따르면, 클라이언트(웹 브라우저)는 웹 서버와 HTTP/2 통신을 하기 위해
서버에게 HTTP/2를 지원하는지를 질의해야 하고, 그에 대한 응답 여부에 따라 HTTP/2를 사용할지,
HTTP1.1(혹은 이하 버전)으로 통신을 해야 할지 결정합니다.
아마도 이 과정에서 도메인의 HTTP/2 지원 여부 또한 파악이 가능해지는 단계입니다.
그렇다면 핵심 부분은 바로 클라이언트가 어떻게 서버에게 그런 질의를 하는지에 대한 세분화된 절차인데,
관련 내용을 RFC에서 찾아보니, HTTP 통신과 HTTPS 통신 각각 다른 방법이 정의되어 있었습니다.
HTTP(non-secure) 통신에서의 HTTP/2 연결
이 부분은 3.2. Starting HTTP/2 for "http" URIs 섹션에 정의되어 있습니다. 클라이언트가 최초 서버 접속 시,
Upgrade, HTTP-2 Settings 헤더를 첫 요청 헤더에 실어 보냅니다.
HTTP2-Settings 커스텀 헤더는 각종 연결에 필요한 설정 정보를 base64url 인코딩한 값입니다.
1 2 3 4 5
GET / HTTP/1.1 Host: server.example.com Connection: Upgrade, HTTP2-Settings Upgrade: h2c HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>
1 2 3 4
HTTP/1.1 200 OK Content-Length: 243 Content-Type: text/html ...
서버가 HTTP/2를 지원한다면, 101 Switching Protocols 응답을 하며, 프로토콜이 업데이트 가능함을 Connection 헤더로 알려주고, Upgrade 헤더에 HTTP/2 지시자인 h2c로 응답합니다. 그 후, 클라이언트는 이후 연결부터 HTTP/2를 사용합니다.
참고로 101 응답은 HTTP에서 WebSocket으로 전환할 때도 사용되는 응답 코드입니다.
1 2 3 4
HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: h2c [ HTTP/2 connection]
빠른 이해를 위해 지금까지의 내용을 다이어그램으로 표현해보았습니다.
그렇지만 RFC와는 달리, 실제 현존하는 대부분의 브라우저들이나 WebView 컴포넌트 등은 HTTPS에서만 HTTP/2를 지원합니다. 따라서 웹 서버에서 HTTP/2를 지원하려면 먼저 Let's Encrypt와 같은 인증서를 준비하고, 서버에서 HTTPS를 지원할 수 있도록 해야 합니다.
HTTPS(secure) 통신에서의 HTTP/2 연결
이 부분은 3.3. Starting HTTP/2 for "https" URIs 섹션에 정의되어 있습니다.
클라이언트는 TLS 기반의 https URI로 접속하면서 ALPN(Application-Layer Protocol Negotiation)을 사용합니다.
ALPN 이란 단어 뜻 그대로 Layer 7에서 클라이언트와 서버가 상호 협의(negotiation) 하에 사용할 프로토콜을 결정하는 방식입니다.
클라이언트가 후보 프로토콜 집합을 ALPN extension 값으로 전달하면,
서버는 그중에서 자신이 지원하는 버전 하나를 고릅니다. HTTPS를 사용하는 HTTP/2의 ALPN extension 값은 h2입니다.
이제 어느 정도 HTTP/2의 연결 방식에 대한 지식은 충분한 것 같습니다.
RFC의 내용을 좀 더 정확하게 이해하는 방법 중 하나는 프로토타입을 직접 코딩해보는 것이죠.
웹 서버 주소(도메인)을 입력하면, HTTP/2 지원 여부를 HTTP와 HTTPS 두 가지 방법으로 테스트하는 함수를
위에서 설명한 RFC 내용대로 파이썬으로 구현하였습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
import sys import requests import socket import ssl def checkh2(): #Define headers list for HTTP test headers = {'Accept': '*/*', 'user-agent': 'h2-check/1.0.1', 'Connection': 'Upgrade, HTTP2-Settings', 'Upgrade': 'h2c', 'HTTP2-Settings': '<base64url encoding of HTTP/2 SETTINGS payload>'} while True: domain = raw_input("\nInput domain: ") if domain == 'bye': break try: #Send HTTP Request with Upgrade, Connection headers r = requests.get('http://' + domain, headers=headers, allow_redirects=True) #If status code is 101, the domain supports h2c for HTTP/2 if r.status_code == 101: print 'HTTP/2 is supported using h2c indicator.' else: print 'HTTP/2 is not supported using HTTP.' #Initiate an SSL contenxt ctx = ssl.create_default_context() #Add ALPN candidates to the SSL context ctx.set_alpn_protocols(['h2', 'spdy/3', 'http/1.1']) #Open a socket using the SSL context and connect to the domain using 443 ports conn = ctx.wrap_socket(socket.socket(), server_hostname=domain) conn.connect((domain, 443)) #If server choose h2 extension, it supports ALPN supported HTTP/2 if conn.selected_alpn_protocol() == 'h2': print 'HTTP/2 is supported using ALPN extension.' #If no h2 extensoin, Inform which protocol server choose else: print 'HTTP/2 is not supported but ' + str(conn.selected_alpn_protocol()) + ' is supported.' r.close() except: print 'Connection errror. Please check the domain name.' if __name__ == '__main__': checkh2()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
$python h2checker.py Input domain: nghttp2.org HTTP/2 is supported using h2c indicator. HTTP/2 is supported using ALPN extension. .. Input domain: www.google.com HTTP/2 is not supported using HTTP. HTTP/2 is supported using ALPN extension. .. Input domain: facebook.com HTTP/2 is not supported using HTTP. HTTP/2 is supported using ALPN extension. .. Input domain: www.naver.com HTTP/2 is not supported using HTTP. HTTP/2 is supported using ALPN extension. .. Input domain: www.daum.net HTTP/2 is not supported using HTTP. HTTP/2 is not supported but http/1.1 is supported. .. Input domain: bye
국내의 양대 포털 사이트 중 하나만 HTTP/2를 지원하는 것은 좀 의아하긴 하지만,
그 서비스가 대륙별 혹은 국가별로 CDN(Contents Delivery Network)을 사용한다면,
이 결과는 테스트 위치마다 다를 수도 있습니다.
맺으며..
만약, RFC 내용을 스터디하는 것보다, HTTP/2 지원 여부만 조사하는 것이 중요하다면
아래 openssl 명령문 하나로도 웹 서버가 ALPN h2 extension과 HTTP/2를 지원하고 있는지 바로 알 수 있습니다.
1 2 3 4
$openssl s_client -alpn h2 -connect google.com:443 -status | grep ALPN ... ALPN protocol: h2 ...
그렇지만 배운 내용 그대로 코드로 구현해보며, 프로토콜의 RFC 내용을 좀 더 빠르게 이해할 수 있었기 때문에 이 글을 공유합니다.
https://http3check.net/ 서비스와 유사하게 구현한 HTTP/3 Checker에 대한 글도 준비가 되면, 공유할 예정입니다.