> ## Content Index
> Fetch the complete content index at: https://www.popit.kr/llms.txt
> Use this file to discover other available public pages before exploring further.

# HTTP/2 RFC를 응용한 HTTP/2 Checker의 구현
- URL: https://www.popit.kr/implementing-http2-checker-from-rfc/
- Published: 2020-01-14T18:43:55.000Z
- Updated: 2020-01-15T02:24:23.000Z
- Author: Brandon
- Tags: #소프트웨어, http2, http

### 

몇 년 전, 프로젝트에 HTTP/2를 도입할 무렵, KeyCDN에서 만든 HTTP/2-Test 서비스를 보고, 이것을 어떻게 만들었을까 궁금했습니다. 아마도 직접 도메인 주소로 테스트 에이전트가 접속해서 HTTP/2 지원 여부에 대한 요청을 하고, 뭔가 받은 응답 값을 사용했겠지 예상이 되더군요.

![ ](https://www.popit.kr/content/images/wp/2020/01/h1-600x365.png)

<KeyCDN의 [HTTP/2 테스트 서비스](https://tools.keycdn.com/http2-test?ref=popit.kr)\>

### 원리의 파악..

유사하게 작동하는 HTTP/2 Checker를 구현하기 위해 HTTP/2 RFC(Request for Comments) 내용을 찾아보았습니다. 모든 통신 프로토콜이 그렇듯 HTTP/2의 [RFC-7540](https://tools.ietf.org/html/rfc7540?ref=popit.kr)에서의 정의를 다시 한번 빌리자면, HTTP/2를 사용하기 위해서는 클라이언트와 서버 양측 모두 HTTP/2를 지원해야 합니다. 대부분의 최근 브라우저 버전들은 TLS 기반의 HTTP/2를 지원하며, 일부는 HTTP/3까지도 지원합니다.

![](https://www.popit.kr/content/images/wp/2020/01/h2.png)

<https://caniuse.com/#search=http2>

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 인코딩한 값입니다.

```
GET / HTTP/1.1
Host: server.example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>
```

만약 서버가 HTTP/2를 지원하지 않는다면, 요청에 대한 응답을 HTTP1.1 버전의 200 OK로 응답합니다.

```
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으로 전환할 때도 사용되는 응답 코드입니다.

```
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c

[ HTTP/2 connection]
```

빠른 이해를 위해 지금까지의 내용을 다이어그램으로 표현해보았습니다.

![ ](https://www.popit.kr/content/images/wp/2020/01/h3.png)

<HTTP(non-secure) 환경에서의 HTTP/2 연결 과정>

그렇지만 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://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml?ref=popit.kr#alpn-protocol-ids) 값으로 전달하면, 서버는 그중에서 자신이 지원하는 버전 하나를 고릅니다. HTTPS를 사용하는 HTTP/2의 ALPN extension 값은 h2입니다. 

![](https://www.popit.kr/content/images/wp/2020/01/h4.png)

<HTTPS(secure) 환경에서의 HTTP/2 연결 과정>

이제 어느 정도 HTTP/2의 연결 방식에 대한 지식은 충분한 것 같습니다. RFC의 내용을 좀 더 정확하게 이해하는 방법 중 하나는 프로토타입을 직접 코딩해보는 것이죠. 웹 서버 주소(도메인)을 입력하면, HTTP/2 지원 여부를 HTTP와 HTTPS 두 가지 방법으로 테스트하는 함수를 위에서 설명한 RFC 내용대로 파이썬으로 구현하였습니다.

```python
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()

```

코드를 실행하여, 몇 개의 도메인을 대상으로 테스트해본 결과입니다.

```
$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를 지원하고 있는지 바로 알 수 있습니다.

```
$openssl s_client -alpn h2 -connect google.com:443 -status | grep ALPN
...
ALPN protocol: h2
...
```

그렇지만 배운 내용 그대로 코드로 구현해보며, 프로토콜의 RFC 내용을 좀 더 빠르게 이해할 수 있었기 때문에 이 글을 공유합니다.[ https://http3check.net/](https://http3check.net/?ref=popit.kr) 서비스와 유사하게 구현한 HTTP/3 Checker에 대한 글도 준비가 되면, 공유할 예정입니다.

참고한 자료: [https://tools.ietf.org/html/rfc7540](https://tools.ietf.org/html/rfc7540?ref=popit.kr) (HTTP/2) [https://tools.ietf.org/html/rfc7301](https://tools.ietf.org/html/rfc7301?ref=popit.kr) (ALPN)