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件) を見る