LoginSignup
9
4

More than 3 years have passed since last update.

JavaScriptで学ぶHTTP通信〜HTTPStreaming編〜

Last updated at Posted at 2020-10-04

はじめに

本記事は、WebSocketを使わないPUSH通信の一方式である、Chunked transfer encodingによるHTTPStreamingのサンプルプログラムを示す

  • サーバ側はRubyで書いたCGIプログラム
  • クライアント側はバニラJavaScriptによるfetchAPIを使用したプログラムである
  • HTTPStreamingにより、サーバ側で時間のかかる処理をしていても、実行終了を待たずに処理結果をその都度、クライアントに通知可能になる

Chunked transfer encodingによるHTTPStreaming

WebSocketを使わないPUSH通信の一方式。百聞は一見に如かずということで、HTTP Streaming パターン テストというデモサイトを紹介。このサイトの場合は、PHP/jQueryでの実装である。

サーバからのレスポンス通信をどうすればいいのか

  • レスポンスヘッダとしてTransfer-encoding: chunkedを送る
  • サーバからPUSH通知する塊(chunk)毎に、chunkのサイズとchunkの中身を送る
  • 具体的には次の通り。
Content-Type: text/plain
Transfer-Encoding: chunked

1
a
2
aa
3
aaa
4
aaaa
5
aaaaa
6
aaaaaa
7
aaaaaaa
8
aaaaaaaa
9
aaa
aaaaaa
a
aaaaaaaaaa
b
aaaaaaaaaaa
c
aaaaaaaaaaaa
d
aaaaaaaaaaaaa
e
aaaaaaaaaaaaaa
0
[CRLF]
  • chunkのサイズは16進数で送る。
  • 通信の終わりは0と空行を送る。
  • chunkのサイズとchunkが一致しない場合、クライアントはエラーになる
  • このChunked transfer encodingはHTTP/1.1の4.1節 で定義されている。次の通りである。
chunked-body   = *chunk
                 last-chunk
                 trailer-part
                 CRLF
chunk          = chunk-size [ chunk-ext ] CRLF
                 chunk-data CRLF
chunk-size     = 1*HEXDIG
last-chunk     = 1*("0") [ chunk-ext ] CRLF
chunk-data     = 1*OCTET ; a sequence of chunk-size octets

クライアントではどう受け取ればいいのか

  • サーバ側ではRubyによるCGIプログラム
  • クライアントはJavaScriptでfetchAPIを使用する実装を示す

  • 一秒おきに、chunkを送るプログラム

streaming.cgi
#!/usr/bin/ruby                                                                                                         

puts "Content-Type: text/plain"
puts "Transfer-encoding: chunked"
puts "\n"

15.times{|n|
  if(n > 0) then
    puts n.to_s(16) # 16進数
    puts 'a' * n
    STDOUT.flush
  end
  sleep 1
}

puts "0"
puts               # 空行
  • クライアントではレスポンス通信の結果をReadStreamから得る
  • 毎秒送られてくるchunkを、送られた都度で画面上に出力する
index.js
function startStream(){
    const url = "./streaming.cgi"
    fetch(url)
        .then((res)=>{
            if(res.ok){
                const reader = res.body.getReader();
                let received = 0;
                const txt = new TextDecoder();

                reader.read().then(function processText({done, value}){
                    if(done){
                        console.log("Stream Complete")
                        return;
                    }
                    received += value.length;
                    const chunk = txt.decode(value);

                    let listItem = document.createElement('li');
                    listItem.textContent = 'Read ' + received + ' bytes. Current chunk = ' + chunk;
                    document.getElementById("result").appendChild(listItem);

                    return reader.read().then(processText);
                })

            }else{
                console.log(res);
            }
        })
        .catch((err)=>{
            console.log(err);
        })
}
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
        <title>HTTP streaming Test</title>
        <script src="./index.js"></script>
        <style></style>
    </head>
    <body>
        <h1>HTTP streaming Test</h1>

        <button onclick="startStream()">Streaming開始</button>
        <ul id="result"></ul>
    </body>
</html>

実行結果

chunkedTransferEncoding.gif


参考情報まとめ

9
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4