GolangでHTTP/1.1のチャンク受信をやってみた
HTTP/1.1では、データをチャンクと呼ばれる小さな塊に分割し送受信するルールを定めている。こうする事で、時間のかかるデータの転送を少しずつ行うことができる。チャンク方式を使えば、検索やライブ配信などで効率的な転送ができる。
まとめ
Goの標準パッケージが充実しているので、やることはそこまで多くない。
TCPソケットを開き、リクエストをサーバーに投げる
TCPソケットオープン
dialer := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}
conn, err := dialer.Dial("tcp", "localhost:8080")
if err != nil {
panic(err)
}
defer conn.Close()
コネクションを通じてリクエストの送信
request, err := http.NewRequest("GET", "http://localhost:8080/chunked", nil)
err = request.Write(conn)
if err != nil {
panic(err)
}
コネクションからbufio.Readerを作成
io.Reader作成。コネクションはio.Readerを実装しているので、bufio.Readerの作成が可能。
reader := bufio.NewReader(conn)
※レスポンスヘッダに「Transfer-Encoding: chunked」が含まれているかを確認
resp, err := http.ReadResponse(reader, request)
if err != nil {
panic(err)
}
if resp.TransferEncoding[0] != "chunked" {
panic("wront transfer encoding")
}
チャンクのサイズを逐次取得し、サイズ分読み込む
チャンク方式では、チャンクデータのサイズとデータ本体が逐次送信されてくる。これら二つはCRLFで区切られている。
チャンクのサイズをパースする時に、サイズを表すバイト配列の後ろ2個を読み込んでいない。これは、HTTPが区切り文字としてCRLFを使用しているため。
for {
// サイズを取得
sizeStr, err := reader.ReadBytes('\n')
if err == io.EOF {
break
}
// 16進数のサイズをパース。サイズが0ならクローズ
size, err := strconv.ParseInt(string(sizeStr[:len(sizeStr)-2]), 16, 64)
if size == 0 {
break
}
if err != nil {
panic(err)
}
// サイズ数分バッファを確保して読み込み
line := make([]byte, int(size))
reader.Read(line)
reader.Discard(2)
log.Println(" ", string(line))
}
全部
参考

Real World HTTP ―歴史とコードに学ぶインターネットとウェブ技術
- 作者: 渋川よしき
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/06/14
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る