CHAPTER5 - HTTP/2 프로토콜-4

2019. 3. 6. 15:16Learning HTTP2

5.7 전송 절차

이제 HTTP/2 요청과 응답을 파헤쳐보자. 다시 한번 말하지만, 여기에서는 보기 쉽도록 평문으로 표기하지만 실제로 h2는 압축된 바이너리 형태로 전송된다.


5.7.1 간단한 GET

GET은 GTTP에서 가장 많이 사용되는 요청이다. GET은 단순히 이름이 의미하는 대로 동작한다. 즉, GET은 서버에서 자원을 가져오는 일을 한다. 예를 들어, [예제 5-1]의 akamai.com으로 보내는 GET요청을 보자(쉬운 이해를 위해 불필요한 줄은 생략함).

예제 5-1 HTTP/2 GET 요청


:authority: www.akamai.com

:method: GET

:path: /

:scheme: https

accept: text/html,application/xhtml+xml,...

accept-language: en-US,en;q=0.8

cookie: sidebar_collapsed=0; _mkto_trk=...

upgrade-inseccure-requests: 1

user-agent: Mozilla/5.0 (Macintosh;...
 

이 요청은 GET 메서드를 사용해 HTTPS로 www.akamai.com의 초기 페이지를 요청한다.

[예제 5-2]는 이에 대한 응답을 보여준다.


NOTE_ ':authority'라는 헤더 이름이 이상해 보일 수도 있다. 왜 :host를 사용하지 않을까? 그 이유는 이 헤더가 HTTP/1.1의 Host 헤더보다는 URI의 Authority 영역과 유사하기 때문이다. Authority 영역은 호스트 정보가 들어 있으며, 선택적으로 포트 번호도 포함하므로, Host 헤더의 역할을 훌륭히 해낸다. URI RFC를 미리 읽어 본 독자들을 위해 알려주지만, Authority의 User Information 부분(즉, username과 password)은 h2에서는 명시적으로 금지되어 있다.
 


예제 5-2 HTTP2 GET 응답(헤더만 표시)


:status: 200

cache-control: max-age=600

content-encoding: gzip

content-type: text/html;charset=UTF-8

date: Tue, 31 May 2016 23:38:47 GMT

etag: "08c024491eb772547850bh157abbb6c430-gzip"

expires: Tue, 31 May 2016 23:38:47 GMT

link: <https://c.go-mpulse.net>;rel=preconnet

set-cookie: ak_bmsc=8DEA673F92AC...

vary: Accept-Encoding, User-Agent

x-akamai-transformed: 9c 237807 - pmb=mRUM,1

x-frame-option: SAMEORIGIN


<이후, DATA 프레임 계속>
 

이 응답에서 서버는 요청이 성공했고(200 상태 코드), 쿠키를 설정했으며(set-cookie 헤더), 콘텐츠가 gzip으로 압축되어 있다(content-encoding 헤더)는 것 외에도 다른 많은 중요한 정보를 알려주고 있따.

이제 이 간단한 GET을 위해 무엇이 전송되는지를 살펴보자. 타츠히로 츠지카와가 만든 훌륭한 도구인 nghttp를 사용하면, h2 내부의 모든 것을 보여주는 상세 출력을 얻을 수 있다.


$ nghttp -v -n --no-dep -w 14 -a -H "Header1: FOO" https://www.akamai.com
 

이 명령어는 윈도우 크기를 16KB로 설정하고, 임의의 헤더를 추가한 후, 페이지에서 몇가지 핵심 정보를 내려받도록 요청한다. 다음은 이 명령어의 출력 결과에 설명을 단 것이다.


[ 0.047] Connected

The negotiated protocol: h2 -1

[ 0.164] send SETTINGS frame <length=12, flags=0x00, stream_id=0> -2

(niv=2)

[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]

[SETTINGS_INITIAL_WINDOW_SIZE(0x04):16383] -3

 

nghttp를 살펴보자.

1. h2 협상이 성공했다.

2. 규격에 따라, SETTINGS 프레임을 전송한다.

3. 명령어에 지정한 대로 윈도우 크기를 16KB로 설정한다.
   연결 수준의 정보(이 출력 결과에서는 연결 전문을 볼 수 없지만, SETTINGD 프레임 이전에 이미 전송되었다)이므로 stream_id를 0으로 사용한     것에 주목하자.



[ 0.164] send HEADERS fram <length=45, flags=0x05, stream_id=1>

; END_STREAM | END_HEADERS -4

(padlen=0)

; Open new stream

:method: GET

:path: /

:scheme: https

:authority: www.akamai.com

accept: */*

accept-encoding: gzip, deflate

user-agent: nghttp2/1.9.2

header1: FOO -5

 


여기에서는 요청의 헤더 블록을 볼 수 있다.

4. 클라이언트(nghttp)는 END_HEADERS와 END_STREAM 플래그를 전송한다. 이는 서버에 더 이상 전송할 헤더가 없으며, 데이터도 전송하지 않을 것임을 알려준다. 만약 이 요청이 POST 방식이라면, END_STREAM 플래그는 이 시점에 전송되지 않을 것이다.

5. 이것은 bghttp 명령어에 임의로 추가한 헤더다.


이하 중략....


5.8 요약

HTTP/2 프로토콜은 수년에 걸쳐 개발되었으며, 그 과정에서 수많은 설계, 결정, 혁신, 타협이 이루어져 왔다. 이번 장에서는 와이어샤크 덤프를 통해 h2에서 어떤 일이 일어나는지를 이해하고, h2 프로토콜을 사용할 때 발생할 수 잇는 잠재적인 문제점(계속 바뀌는 쿠키?)을 찾기 위한 기본적인 방법을 소개했다. 더 많은 정보를 알고 싶다면, RRC 7540 자체가 가장 좋은 자료다. RFC는 구현자, 디버거, 탐구자 모두에게 필요한 세부 사항을 제공할 것이다.