Web

HTTP の Range ヘッダフィールドを使い、ページの一部分だけを取得する

投稿日:2019年3月4日 更新日:

HTTP/1.1 の Range ヘッダフィールドを使って、ページの一部分のみを取得する実験です。

1. Range ヘッダフィールドとは?

Range ヘッダフィールドは、HTTP/1.1 プロトコルのリクエストヘッダに指定することができるヘッダフィールドの1つで、取得したいコンテンツの範囲を指定することができます。

仕様書: Hypertext Transfer Protocol (HTTP/1.1): Range Requests

2. 環境

Ubuntu 18.04 LTS (WSL)

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.1 LTS
Release:        18.04
Codename:       bionic

3. 使うツール

curl コマンドを使用します。

バージョンは以下です。

$ curl -V
curl 7.47.0 (x86_64-pc-linux-gnu) libcurl/7.47.0 GnuTLS/3.5.18 zlib/1.2.11 libidn/1.33 librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP UnixSockets

-r というオプションで、Range ヘッダフィールドが使えるようです。

つまり、範囲を指定してコンテンツを取得することができます。

$ man curl
(省略)
-r, --range <range>
    (HTTP/FTP/SFTP/FILE)  Retrieve a byte range (i.e a partial document) from a HTTP/1.1,
    FTP or SFTP server or a local FILE. Ranges can be specified in a number of ways.
    0-499     specifies the first 500 bytes
    500-999   specifies the second 500 bytes
    -500      specifies the last 500 bytes
    9500-     specifies the bytes from offset 9500 and forward
    0-0,-1    specifies the first and last byte only(*)(HTTP)
    100-199,500-599
              specifies two separate 100-byte ranges(*) (HTTP)
    (*) = NOTE that this will cause the server to reply with a multipart response!
    Only digit characters (0-9) are valid in the 'start' and 'stop' fields of the 'start-
    stop'  range  syntax.  If  a  non-digit character is given in the range, the server's
    response will be unspecified, depending on the server's configuration.
    You should also be aware that many HTTP/1.1 servers do not have this feature enabled,
    so that when you attempt to get a range, you'll instead get the whole document.
    FTP  and SFTP range downloads only support the simple 'start-stop' syntax (optionally
    with one of the numbers omitted). FTP use depends on the extended FTP command SIZE.
    If this option is used several times, the last one will be used.
(省略)

4. 対象ページ

Webサーバ側には以下の HTML ファイルを配置します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>HTTP Range Header Test</title>
</head>
<body>
  <h1>HTTP Range Header Test</h1>
Hello,
World!
</body>
</html>

この中の、以下の部分だけ取得することを目標とします。

Hello,
World!

以下の URL でこのページにアクセスできるようにしておきます。

5. 実験

まず、-I オプションを指定してアクセスし、レスポンスヘッダのみを表示します。

$ curl -I https://misc.laboradian.com/test/003/
HTTP/1.1 200 OK
Server: nginx
Date: Sun, 03 Mar 2019 07:50:48 GMT
Content-Type: text/html
Content-Length: 311
Connection: keep-alive
Last-Modified: Sun, 03 Mar 2019 07:43:11 GMT
ETag: "137-5832bcab3a233"
Vary: Accept-Encoding
Accept-Ranges: bytes

Accept-Ranges: bytes というヘッダフィールドが返ってきているので、このウェブサーバーは Range に対応していることが分かります。バイト(bytes)単位で範囲を指定できるようです。
(参考: RFC 7233 – Hypertext Transfer Protocol (HTTP/1.1): Range Requests

オプションを指定しなければ、コンテンツの部分のみを取得することができます。

$ curl https://misc.laboradian.com/test/003/
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>HTTP Range Header Test</title>
</head>
<body>
  <h1>HTTP Range Header Test</h1>
Hello,
World!
</body>
</html>

curl コマンドによって取得したコンテンツを od コマンドに渡して、このコンテンツをダンプします(16進数で表示します)。

$ curl -s https://misc.laboradian.com/test/003/ | od -t x1z -A d
0000000 3c 21 44 4f 43 54 59 50 45 20 68 74 6d 6c 3e 0a  ><!DOCTYPE html>.<
0000016 3c 68 74 6d 6c 20 6c 61 6e 67 3d 22 6a 61 22 3e  ><html lang="ja"><
0000032 0a 3c 68 65 61 64 3e 0a 20 20 3c 6d 65 74 61 20  >.<head>.  <meta <
0000048 63 68 61 72 73 65 74 3d 22 55 54 46 2d 38 22 3e  >charset="UTF-8"><
0000064 0a 20 20 3c 6d 65 74 61 20 68 74 74 70 2d 65 71  >.  <meta http-eq<
0000080 75 69 76 3d 22 58 2d 55 41 2d 43 6f 6d 70 61 74  >uiv="X-UA-Compat<
0000096 69 62 6c 65 22 20 63 6f 6e 74 65 6e 74 3d 22 49  >ible" content="I<
0000112 45 3d 65 64 67 65 22 3e 0a 20 20 3c 6d 65 74 61  >E=edge">.  <meta<
0000128 20 6e 61 6d 65 3d 22 76 69 65 77 70 6f 72 74 22  > name="viewport"<
0000144 20 63 6f 6e 74 65 6e 74 3d 22 77 69 64 74 68 3d  > content="width=<
0000160 64 65 76 69 63 65 2d 77 69 64 74 68 2c 20 69 6e  >device-width, in<
0000176 69 74 69 61 6c 2d 73 63 61 6c 65 3d 31 22 3e 0a  >itial-scale=1">.<
0000192 20 20 3c 74 69 74 6c 65 3e 48 54 54 50 20 52 61  >  <title>HTTP Ra<
0000208 6e 67 65 20 48 65 61 64 65 72 20 54 65 73 74 3c  >nge Header Test<<
0000224 2f 74 69 74 6c 65 3e 0a 3c 2f 68 65 61 64 3e 0a  >/title>.</head>.<
0000240 3c 62 6f 64 79 3e 0a 20 20 3c 68 31 3e 48 54 54  ><body>.  <h1>HTT<
0000256 50 20 52 61 6e 67 65 20 48 65 61 64 65 72 20 54  >P Range Header T<
0000272 65 73 74 3c 2f 68 31 3e 0a 48 65 6c 6c 6f 2c 0a  >est</h1>.Hello,.<
0000288 57 6f 72 6c 64 21 0a 3c 2f 62 6f 64 79 3e 0a 3c  >World!.</body>.<<
0000304 2f 68 74 6d 6c 3e 0a                             >/html>.<
0000311
  • 各行の後ろに、その部分のコンテンツが表示可能文字によって表示されています。ここの各行の始まりと終わりの文字が分かりやすいように、頭には「>」最後には「<」が付けられています。また、\r\n といった改行コードは「.」と表示されています。
  • ASCII 文字コードの16進数表現は、ASCII – Wikipedia などを参照してください。

一番左の列が、その行の開始位置を表してますので、それを頼りにすると、「Hello, World!」の部分(赤色を付けた部分)は 281バイト目から294バイト目であることが分かります。

HTTP のレスポンスメッセージ

詳細は省きますが、HTTP のレスポンスメッセージは以下の構造になっています。

レスポンスメッセージ

Range ヘッダフィールドは、あくまで メッセージボディ が対象です。ですので、メッセージボディ内における位置で範囲指定します。

では、-r オプションを使って、281から294バイトのみを取得します。まずは、-vオプションも指定し、リクエストヘッダ・レスポンスヘッダも合わせて表示します。

$ curl -v -r 281-294 https://misc.laboradian.com/test/003/
*   Trying XXX.XXX.XXX.XXX...
* Connected to misc.laboradian.com (XXX.XXX.XXX.XXX) port 80 (#0)
> GET /test/003/ HTTP/1.1
> Host: misc.laboradian.com
> Range: bytes=281-294
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 206 Partial Content
< Server: nginx
< Date: Sun, 03 Mar 2019 14:47:04 GMT
< Content-Type: text/html
< Content-Length: 14
< Connection: keep-alive
< Last-Modified: Sun, 03 Mar 2019 07:43:11 GMT
< ETag: "137-5832bcab3a233"
< Vary: Accept-Encoding
< Content-Range: bytes 281-294/311
<
Hello,
World!
* Connection #0 to host misc.laboradian.com left intact
  • 先頭が > の行: リクエストヘッダ
  • 先頭が < の行: レスポンスヘッダ
  • 先頭が * の行: 追加情報

リクエストヘッダの部分に「Range: bytes=281-294」とあるように、ちゃんと Range ヘッダフィールドが送信されていることが分かります。

それに対し、「Content-Range: bytes 281-294/311」というレスポンスヘッダが返され、指定した範囲のコンテンツが返されていることも分かります。

Connection #0 to host (nil) left intact” と表示されているのは、「まだコネクションがオープンのままになっている」ということを意味します。

参照: c – Meaning of libcurl messages and execution process – Stack Overflow

あらためて、-r オプションのみを指定してコンテンツを取得すると、目的としていた部分のみが表示されます。

$ curl -r 281-294 https://misc.laboradian.com/test/003/
Hello,
World!
  • 2行目最後部分の改行文字分も取得しているため、ちゃんと最後は改行して表示されます。

6. (おまけ)HTTP/2 の場合

HTTP/2 を使う場合も、やり方・結果ともにほとんど同じです。

--https オプションを指定して、https でアクセスします。

$ curl --http2 -v -r 281-294 https://misc.laboradian.com/test/003/
*   Trying XXX.XXX.XXX.XXX...
* TCP_NODELAY set
* Connected to misc.laboradian.com (XXX.XXX.XXX.XXX) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=misc.laboradian.com
*  start date: Feb  2 02:11:07 2019 GMT
*  expire date: May  3 02:11:07 2019 GMT
*  subjectAltName: host "misc.laboradian.com" matched cert's "misc.laboradian.com"
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x1c96b40)
> GET /test/003/ HTTP/2
> Host: misc.laboradian.com
> Range: bytes=281-294
> User-Agent: curl/7.62.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 206
< server: nginx
< date: Sun, 03 Mar 2019 14:47:14 GMT
< content-type: text/html
< content-length: 14
< last-modified: Sun, 03 Mar 2019 07:43:11 GMT
< etag: "137-5832bcab3a233"
< vary: Accept-Encoding
< content-range: bytes 281-294/311
<
Hello,
World!
* Connection #0 to host misc.laboradian.com left intact

📂-Web

執筆者:labo


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

Web Programming

Webプログラミングのための Web入門

この記事では、Webプログラミングで必要になる Webの知識をご紹介します。 目次1. インターネット2. インターネットを土台として存在するプロトコル3. Web とは?4. URL5. HTML2 …

Web

ウェブブラウザがページを取得して表示するまでの流れ

目次1. はじめに2. Chrome デベロッパーツールの [Network]パネル3. リソース毎の処理の流れ4. その後の流れ5. DOMContentLoaded と load イベント6. グ …

Web Vitals

Web Vitals patterns の Responsive Images を試してみました

Web Vitals patterns に載っている「Responsive Images」を実際に試してみました。

Web

【HTML】picture 要素について

目次1. <picture> 要素とは?2. 仕様3. <source> 要素4. 利用例5. 実際に使用された画像がどれかを JavaScript で取得する6. 注意点7. …

Chrome

Chrome 78 の新機能

ome 78 の新機能について簡単に説明します。