brunch

You can make anything
by writing

C.S.Lewis

by 테크유람 Oct 04. 2016

HTTP/2 Checker 구현하기

RFC 7540에 기반한  HTTP/2 통신을  Python 코드로 구현

HTTP/2 프로토콜을 사용하기 위해서는 클라이언트와 서버 모두 HTTP/2를 지원해야 한다.


즉 어느 한쪽만 HTTP/2를 지원한다면 해당 통신은 HTTP1.1 등, 구버전의 프로토콜로 fall-back 될 수밖에 없다. 어느 브라우져가 현재 HTTP/2를 지원하고 있는지는 http://caniuse.com/#search=http%2F2 에서 확인할 수 있다.


서버 쪽 지원 사항을 포함하여 확인할 수 있는 곳은 https://github.com/http2/http2-spec/wiki/Implementations 이다. 두 개의 페이지에서 알 수 있듯이 상위 버전의 클라이언트(주로 웹 브라우져)와 최신 웹 서버 (apache, Nginx 등)등은 HTTP/2를 지원하고 있다.


특정 웹 서비스가 HTTP/2를 지원하는지, HTTP1.1만을 지원하는지 해당 서비스를 사용하려는 클라이언트에서 빠르게 판단이 되어야 Client-Server간 통신 프로토콜을 어떤 버전으로 할 지 정할 수 있다. 이 부분에 대한 기술적 스펙은 HTTP/2 RFC의 3장에 나와있다. - https://tools.ietf.org/html/rfc7540#section-3


RFC 내용상에서, HTTP/2 지원여부를 클라이언트-서버가 판단하는 방식을 요약하면 다음과 같다.


지원 여부의 확인을 위해 HTTP/2에서는  'h2c' 와 'h2' 라는 indicator를 사용한다. h2c는 HTTP 통신, h2는 HTTPS 통신을 각각 의미한다.


HTTP 기반 통신


첫 GET 요청은 HTTP1.1 버전으로 시작하되 Connection, Upgrade, HTTP2-Settings 요청 헤더를 사용하여, 클라이언트는 HTTP/2를 지원하고 있으므로 서버또한 HTTP/2 프로토콜를 사용할 수 있도록 요청한다.


GET / HTTP/1.1

Host: server.example.com

① Connection: Upgrade, HTTP2-Settings

② Upgrade: h2c -> HTTP용 indicator 값을 사용

③ HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>


①, ② 값은 고정이며, ③ 값은 SETTINGS frame 설정값에 따라 달라질 수 있으며 base64url 방식으로 인코딩된 값이 1개만 설정 되어야 한다.


만약, 서버가 HTTP/2를 지원한다면 다음과 같은 101 응답을 내려준다.


HTTP/1.1 101 Switching Protocols

Connection: Upgrade

Upgrade: h2c

[ HTTP/2 connection ... -> 이후 부터는 HTTP/2 통신


101 응답이후에는 HTTP/2 방식으로 통신이 시작되며 GET 요청한 주소의 200 OK 응답을 다시 받게 된다. 서버가 HTTP/2 를 지원하지 않는다면, Connection 및 Upgrade 요청 헤더는 무시되며, HTTP1.1 기반으로 GET 요청한 주소의 200 혹은 302/301/404 응답을 받게 될 것이고 클라이언트는 서버가 HTTP/2를 지원하지 않는다는 정보를 취득하고 이하 통신 부터는 모두 HTTP1.1 기반의 통신을 진행한다.


Connection, Upgrade 헤더를 통해 프로토콜 교체를 요청하는 클라이언트 방식은 HTTP/2 이전에도 HTTP1.1 방식의 연결에서 WebSocket 등으로 사용중인 프로토콜을 변경하고 싶을때 사용하던 방식과 동일하다.


HTTPS(TLS) 기반 통신


HTTP/2 환경에서의 HTTPS 통신은 TLS기반의 ALPN(Application-Layer Protocol Negotiation)을 사용한다. ALPN은 SNI와 같은 TLS Extension 중에 하나이며, ALPN에 대한 자세한 내용은 별도의 포스트에서 소개하겠지만 요약하자면 OSI 7 레이어중, 최상단인 Application Layer상에서 사용할 프로토콜을 클라이언트-서버가 협상(negotiation)하고 결정하는 방식이다. - http://www.rfc-editor.org/info/rfc7301 , 여기서의 Application Layer는 결국 HTTP(S)이고, 'h2' 라는 indicator를 클라이언트가 TLS 통신으로 서버에 보내어 서버가 accept하는 방식을 사용하는 것이다.


위의 RFC 내용을 기반으로 특정 도메인을 매개변수로 하여 HTTP/2 지원 여부를 확인하는 코드를 Python으로 개발한 코드는 아래와 같다.


위의 코드를 개발할때 사용한 test case는 HTTP와 HTTPS 통신을 모두 HTTP/2 지원하는 경우, HTTPS만 HTTP/2를 지원하는 경우, 두 가지 모두 HTTP1.1만 지원하는 경우 등으로 나누어 진행하였으며, 잘 알려진 도메인으로 테스트 한 결과는 다음과 같다.


<HTTP와 HTTPS 통신을 모두 HTTP/2 지원>

$ python h2check.py nghttp2.org

This domain supports HTTP/2 with h2c - HTTP

This domain supports HTTP/2 with h2 - HTTPS

<HTTPS만 HTTP/2를 지원하는 경우>

$ python h2check.py www.facebook.com

This domain does not support HTTP/2 with h2c - HTTP

This domain supports HTTP/2 with h2 - HTTPS

<두 가지 모두 HTTP1.1만 지원하는 경우>

$ python h2check.py www.nike.com

This domain does not support HTTP/2 with h2c - HTTP

This domain does not support HTTP/2 with h2 - HTTPS but http/1.1


개발 참고 자료:

https://tools.ietf.org/html/rfc7301#section-3.2

https://docs.python.org/3/library/ssl.html

https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids


소스 코드:


작가의 이전글 HTTP Persistent Connection
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari