expressモジュールによるHTTPサーバ(21)範囲リクエスト
この記事は「expressモジュールによるHTTPサーバ」という連載記事の21つ目の記事です。
その他の記事に関しては下を参照してください。
記事一覧
また、HTTP自体について知りたい方は下を参照してください。
HTTPとは?
この記事ではexpress
モジュールを使って範囲リクエストを処理するHTTPサーバを作成する方法について説明します。
範囲リクエストとは?
範囲リクエスト
範囲リクエストを処理するにはreq.range
関数を使用します。
var express = require('express');
var app = express();
app.use(function (req, res, next) {
var ranges = req.range(10000, {
combine: true
});
next();
});
第1引数に範囲の最大値を指定します。
第2引数にオプションを指定します。この引数は指定しなくても構いません。
オプションには下のようなものがあります。
combine
・・・範囲リクエストの範囲のリストの複数の範囲が重複したり、隣接したりしている場合に範囲を1つに纏めるかを真偽値として指定します。デフォルトはfalse
です。
返り値として範囲リクエストの範囲のリストがオブジェクトの配列として得られます。そして、このオブジェクトの配列を使用して範囲リクエストの処理を行うようにします。
このオブジェクトは下のようなプロパティを有します。
start
・・・範囲の開始インデックスを表します。end
・・・範囲の終了インデックスを表します。
ただし、範囲リクエストが正しい形式ではない場合には-2
が返り、範囲リクエストが処理できない場合には-1
が返ります。
また、HTTPリクエストが範囲リクエストではない場合にはundefined
が返ります。
範囲リクエストが処理できる場合にはHTTPレスポンスのステータスコードを206
(Partial Content
)とし、処理できない場合には416
(Range Not Satisfiable
)とします。
また、範囲リクエストが処理できる場合で範囲が単一である場合にはHTTPレスポンスボディには単一の範囲のデータをそのまま格納し、Content-Type
ヘッダフィールドやContent-Range
ヘッダフィールドなどを設定します。
Content-Typeヘッダフィールドとは?
範囲が複数である場合にはHTTPレスポンスボディには複数の範囲のデータをmultipart/byteranges
メディアタイプ(MIMEタイプ)形式で格納し、Content-Type
ヘッダフィールドなどを設定します。なお、multipart/byteranges
メディアタイプ形式は一般的なmultipart
トップレベルメディアタイプ形式と同じです。
メディアタイプ(MIMEタイプ)とは?multipartとは?
また、範囲リクエストが処理できない場合にはContent-Range
ヘッダフィールドなどを設定します。
サンプルコード1
範囲リクエストに対応し、クライアントからリクエストを受け取ったらテキストデータを返すHTTPサーバを作成し、起動します。
Enter
キーが押されたらサーバを停止し、プログラムを終了します。
express-server-range.js
var express = require('express');
var http = require('http');
var app = express();
app.set('env', 'development');
app.set('x-powered-by', false);
app.set('case sensitive routing', true);
app.set('strict routing', true);
app.get('/', function (req, res, next) {
var data = 'welcome to pizyumi\'s website. this is home page.\r\n';
var ranges = req.range(data.length, {
combine: true
});
if (ranges === -2) {
res.status(400);
res.type('text/plain; charset=utf-8');
res.send(http.STATUS_CODES[400] + '\r\n');
}
else if (ranges === -1) {
res.status(416);
res.type('text/plain; charset=utf-8');
res.set('Content-Range', 'bytes */' + data.length);
res.send(http.STATUS_CODES[416] + '\r\n');
}
else if (ranges === undefined) {
res.status(200);
res.type('text/plain; charset=utf-8');
res.send(data);
}
else if (ranges.length === 1) {
res.status(206);
res.type('text/plain; charset=utf-8');
res.set('Content-Range', 'bytes ' + ranges[0].start + '-' + ranges[0].end + '/' + data.length);
res.send(data.substring(ranges[0].start, ranges[0].end + 1));
}
else {
var boundary = 'xxx'
var send = '';
for (var i = 0; i < ranges.length; i++) {
send += '--' + boundary + '\r\n';
send += 'Content-Type: text/plain; charset=utf-8\r\n';
send += 'Content-Range: bytes ' + ranges[i].start + '-' + ranges[i].end + '/' + data.length + '\r\n';
send += '\r\n';
send += data.substring(ranges[i].start, ranges[i].end + 1) + '\r\n';
}
send += '--' + boundary + '--';
res.status(206);
res.type('multipart/byteranges; boundary=' + boundary);
res.send(send);
}
});
app.get('*', function (req, res, next) {
res.status(404);
res.send(http.STATUS_CODES[404] + '\r\n');
});
app.post('*', function (req, res, next) {
res.send('request method is post.\r\n');
});
app.all('*', function (req, res, next) {
res.status(501);
res.send(http.STATUS_CODES[501] + '\r\n');
});
var server = app.listen(3000, function () {
console.log('http server is running...press enter key to exit.');
process.stdin.on('data', function (data) {
if (data.indexOf('\n') !== -1) {
server.close(function () {
console.log('http server closed.');
process.exit(0);
});
}
});
});
server.on('error', function (err) {
console.error(err);
process.exit(1);
});
使用パッケージ
- express
npm install express
でインストールします。
実行結果
実行結果の確認のためにcURLを使用しています。
cURLのインストール方法
コードを実行するとHTTPサーバが起動します。
C:\work\node>node express-server-range.js
http server is running...press enter key to exit.
cURLを使ってサーバに対して普通のHTTPリクエストを行います。
C:\work\node>curl -v http://localhost:3000/
* Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Content-Length: 50
< ETag: W/"32-juTlohfTcZy04HpD+lfkLw"
< Date: Thu, 16 Mar 2017 14:44:32 GMT
< Connection: keep-alive
<
welcome to pizyumi's website. this is home page.
* Connection #0 to host localhost left intact
サーバから正しくレスポンスが返っていることが分かります。
cURLを使ってサーバに対して範囲リクエストを行います。
C:\work\node>curl -H Range:bytes=0-9 -v http://localhost:3000/
* Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
> Range:bytes=0-9
>
< HTTP/1.1 206 Partial Content
< Content-Type: text/plain; charset=utf-8
< Content-Range: bytes 0-9/50
< Content-Length: 10
< ETag: W/"a-u/McgAoMzbO7/JJ86SpSgg"
< Date: Thu, 16 Mar 2017 14:45:17 GMT
< Connection: keep-alive
<
welcome to* Connection #0 to host localhost left intact
C:\work\node>curl -H Range:bytes=0-9,20-29 -v http://localhost:3000/
* Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
> Range:bytes=0-9,20-29
>
< HTTP/1.1 206 Partial Content
< Content-Type: multipart/byteranges; boundary=xxx; charset=utf-8
< Content-Length: 191
< ETag: W/"bf-pnoNKe5FyAVaPuKT1jLLmQ"
< Date: Thu, 16 Mar 2017 14:45:40 GMT
< Connection: keep-alive
<
--xxx
Content-Type: text/plain; charset=utf-8
Content-Range: bytes 0-9/50
welcome to
--xxx
Content-Type: text/plain; charset=utf-8
Content-Range: bytes 20-29/50
website.
--xxx--* Connection #0 to host localhost left intact
C:\work\node>curl -H Range:bytes=0-9,20-29,40-49 -v http://localhost:3000/
* Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
> Range:bytes=0-9,20-29,40-49
>
< HTTP/1.1 206 Partial Content
< Content-Type: multipart/byteranges; boundary=xxx; charset=utf-8
< Content-Length: 284
< ETag: W/"11c-xY9Otbc6zBmvOXHL3gMqqw"
< Date: Thu, 16 Mar 2017 14:46:08 GMT
< Connection: keep-alive
<
--xxx
Content-Type: text/plain; charset=utf-8
Content-Range: bytes 0-9/50
welcome to
--xxx
Content-Type: text/plain; charset=utf-8
Content-Range: bytes 20-29/50
website.
--xxx
Content-Type: text/plain; charset=utf-8
Content-Range: bytes 40-49/50
me page.
--xxx--* Connection #0 to host localhost left intact
サーバから正しくレスポンスが返っている(ステータスコードが206
(Partial Content
)のレスポンスが返っている)ことが分かります。
Enter
キーを押すとサーバが停止し、プログラムが終了します。
関連
