Skip to content

Instantly share code, notes, and snippets.

@hkwi
Last active February 20, 2017 04:55
Show Gist options
  • Save hkwi/de5f862e2f245ae8e98d757c072ae1a0 to your computer and use it in GitHub Desktop.
Save hkwi/de5f862e2f245ae8e98d757c072ae1a0 to your computer and use it in GitHub Desktop.

h2, h2c という方法がある。

http を使う h2c は破壊されやすい(firewallとか)ので、使用上注意が必要。

openssl genrsa -out server.key
openssl req -new -x509 -sha256 -key server.key -out server.crt
SSL_CERT_FILE=server.crt hyper --debug GET https://localhost:8080/a

https での http/2 は TLS でのネゴシエーション機構として導入された ALPN という仕掛けを使う。h2 という ALPN を使う。openssl 1.0.2 以降で次のようにして TLS レベルでのネゴシエーションを確認できる。

SSL_CERT_FILE=server.crt openssl s_client -host localhost -port 443 -alpn h2

ちなみに普通の HTTP を https にする場合で ALPN を使う場合は http/1.1 を使えばよいらしい。この他にも spdy/3,spdy/2 などがある。

SSL_CERT_FILE=server.crt openssl s_client -host localhost -port 443 -alpn http/1.1

http での http/2 は h2c という Upgrade を使う。原理的には ALPN 無しで Upgrade: h2c というパターンもあり得るといえばあり得る。

golang net/http では Hijaker を使えば実装できる。github.com/hkwi/h2c で実装した。同様の upgrade を行う golang.org/x/net/websocket もそうしている。

golang での h2c で、謎の RST_FRAME が出て何かと調べたら half-closed の状態に遷移できない、どういうことなの。 https://github.com/golang/net/blob/master/http2/server.go#L1006 https://github.com/golang/go/blob/master/src/net/http/h2_bundle.go#L3793 対向が END_STREAM を送っていないときに、local が half-closed のまま開きっぱなしになってしまうので、対向の half-closed に文句を言うために CANCELを送るらしい。そして実際には CANCEL ではなく、処理完了している。

[  0.000] Connected
[  0.000] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  0.001] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
          (dep_stream_id=0, weight=201, exclusive=0)
[  0.002] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
          (dep_stream_id=0, weight=101, exclusive=0)
[  0.002] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
          (dep_stream_id=0, weight=1, exclusive=0)
[  0.003] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
          (dep_stream_id=7, weight=1, exclusive=0)
[  0.003] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
          (dep_stream_id=3, weight=1, exclusive=0)
[  0.003] send HEADERS frame <length=38, flags=0x25, stream_id=13>
          ; END_STREAM | END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
          :method: GET
          :path: /
          :scheme: http
          :authority: localhost:8080
          accept: */*
          accept-encoding: gzip, deflate
          user-agent: nghttp2/1.7.1
[  0.005] recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
          (niv=3)
          [SETTINGS_MAX_FRAME_SIZE(0x05):1048576]
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):250]
          [SETTINGS_MAX_HEADER_LIST_SIZE(0x06):1048896]
[  0.005] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.005] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
golang > time.Sleep(time.Second)
golang > fmt.Fprintf(w, "1")
golang > time.Sleep(time.Second)
golang > if p,ok := w.(http.Pusher); ok {
golang >         p.Push("/t", nil)
golang > }
[  2.007] recv (stream_id=13) :method: GET
[  2.007] recv (stream_id=13) :scheme: http
[  2.007] recv (stream_id=13) :authority: localhost:8080
[  2.007] recv (stream_id=13) :path: /t
[  2.007] recv PUSH_PROMISE frame <length=22, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0, promised_stream_id=2)
golang > time.Sleep(time.Second)
golang > fmt.Fprintf(w, "2")
[  3.008] recv (stream_id=2) :status: 200
[  3.008] recv (stream_id=2) content-type: text/plain; charset=utf-8
[  3.008] recv (stream_id=2) content-length: 1
[  3.008] recv (stream_id=2) date: Mon, 20 Feb 2017 04:27:46 GMT
[  3.008] recv HEADERS frame <length=48, flags=0x04, stream_id=2>
          ; END_HEADERS
          (padlen=0)
          ; First push response header
t[  3.008] recv DATA frame <length=1, flags=0x01, stream_id=2>
          ; END_STREAM
golang > time.Sleep(time.Second)
golang > if p,ok := w.(http.Pusher); ok {
golang >         p.Push("/s", nil)
golang > }
[  4.008] recv (stream_id=13) :method: GET
[  4.008] recv (stream_id=13) :scheme: http
[  4.008] recv (stream_id=13) :authority: localhost:8080
[  4.008] recv (stream_id=13) :path: /s
[  4.008] recv PUSH_PROMISE frame <length=11, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0, promised_stream_id=4)
golang > time.Sleep(time.Second)
golang > fmt.Fprintf(w, "3")
[  5.008] recv (stream_id=4) :status: 200
[  5.008] recv (stream_id=4) content-type: text/plain; charset=utf-8
[  5.008] recv (stream_id=4) content-length: 1
[  5.008] recv (stream_id=4) date: Mon, 20 Feb 2017 04:27:48 GMT
[  5.008] recv HEADERS frame <length=27, flags=0x04, stream_id=4>
          ; END_HEADERS
          (padlen=0)
          ; First push response header
s[  5.008] recv DATA frame <length=1, flags=0x01, stream_id=4>
          ; END_STREAM
golang > time.Sleep(time.Second)
golang > // end Handler call
[  6.009] recv (stream_id=13) :status: 200
[  6.009] recv (stream_id=13) content-type: text/plain; charset=utf-8
[  6.009] recv (stream_id=13) content-length: 3
[  6.009] recv (stream_id=13) date: Mon, 20 Feb 2017 04:27:49 GMT
[  6.009] recv HEADERS frame <length=29, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0)
          ; First response header
123[  6.009] recv DATA frame <length=3, flags=0x01, stream_id=13>
          ; END_STREAM
[  6.009] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
          (last_stream_id=4, error_code=NO_ERROR(0x00), opaque_data(0)=[])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment