Webクライアント
サーバーに何かを求めるもの、すべて。
ChromeもWebクライアントですし、curl一行もWebクライアントです。この章ではHTTPリクエストを手で書き、それをシェルスクリプトに包み、curlをきちんと覚え、最後にChromeの開発者ツールを開いて同じ文章が飛び交うのを目で確かめます。
先に尋ねるほうがクライアントです
クライアントとサーバーは機械ではなく役割です。先に話しかけて何かを求めるほうがクライアント、待って答えるほうがサーバーです。ブラウザーもクライアント、curlもクライアント、スマホアプリも、APIを呼ぶ別のサーバーもクライアントです。
だからWebクライアントは一行で書けます。窓も、描画エンジンも、マウスも要りません — つなぎ、リクエストを送り、返ってきたものを読めばよいのです。
| 問い | クライアント | サーバー |
|---|---|---|
| どちらが先に話すか | 接続を開いてリクエストを送ります。 | ポートで待ちます。誰かがつなぐまで黙っています。 |
| どちらに住所が要るか | サーバーの居場所を知る必要があります — ドメインかIP、そしてポート。 | 変わらない住所が必要です。サーバーがドメイン名を持ち、クライアントがたいてい持たない理由です。 |
| 同時に何件か | ふつうは一度に一つの会話です。ただしブラウザーはページを速く読むためにいくつも同時に開きます。 | 同時に多数です。本物のサーバーソフトが複雑になる理由のほとんどがここにあります。 |
地図を取りに行ったりカードを決済したりするWebサーバーは、そのリクエストではクライアントとして振る舞っています。役割は一瞬で入れ替わるので、両端にどんな機械があるかではなく、今このリクエストがどちら向きに進むのかを問いましょう。
ターミナルでWebクライアントを作る
HTTPが文字にすぎないといちばん早く納得する方法は、リクエストを自分で打つことです。前のテーマのシェルWebサーバーを立てておき、ターミナルをもう一つ開いて手で話しかけてみてください。
ncでリクエストを手書きする
netcatはポートにつなぎ、打った文字をそのまま線へ流します。二行打ってEnterを二度押せばサーバーが答えます — 今あなたはブラウザーの仕事を手でやりました。
# connect, then type the two lines below and press Enter once more
nc localhost 8084
GET / HTTP/1.1
Host: localhost
# or send it all at once (printf's \r\n is HTTP's line break)
printf 'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n' | nc localhost 8084最後の空行が大事です。リクエストが終わったとサーバーに告げる印で、だからEnterをもう一度押します。 HTTPは行末ごとにキャリッジリターンと改行を求めます。手打ちでも通るのは多くのサーバーが寛容だからで、printf版は規格が求めるとおりを送ります。 Hostの行はHTTP/1.1が必ず求めます。本物のサイトへこれなしで送ると、ページではなくエラーが返るのがふつうです。
スクリプトに包む
リクエストがprintf一行になれば、使い回せるクライアントは数行で済みます。下のスクリプトはホスト・ポート・パスを受け取ってリクエストを送り、ヘッダーと本文を分けて見せます。
#!/bin/bash
# usage: bash client.sh localhost 8084 /
HOST=${1:-localhost}
PORT=${2:-8084}
PATH_=${3:-/}
printf 'GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n' "$PATH_" "$HOST" \
| nc "$HOST" "$PORT" \
| tr -d '\r' \
| awk 'BODY { print; next } /^$/ { BODY = 1; next } { print "[header] " $0 }'trはHTTPが行末ごとに残すキャリッジリターンを取り除き、awkは空行に出会うまでをヘッダーとして印を付けます — その後はすべて本文です。ブラウザーが描き始める前にする分け方とまったく同じです。
一つのターミナルにシェルWebサーバー、もう一つにこのクライアントを立てれば、自分で書いた会話の両端を持つことになります。サーバーの返事を書き換えるとクライアントがすぐ気付きます — 間に何もないからです。
curl — 毎日使うことになるクライアント
こちらのスクリプトが大ざっぱにやっていたことを、curlはきちんとやります。リダイレクト、HTTPS、ヘッダー、アップロード、時間切れ、クッキーまで。ほとんどの機械に入っていて、ブラウザーを挟まずにサーバーを確かめる標準の手段です。
# body only
curl http://localhost:8084
# body plus the response headers
curl -i http://localhost:8084
# the whole exchange, request headers included
curl -v http://localhost:8084大事なオプション
| オプション | 働き |
|---|---|
| -i | 本文と一緒にレスポンスヘッダーも見せます。ページが思ったものと違うとき、まず付けるオプションです。 |
| -v | 詳しく見る。送ったリクエストと受けたレスポンスを、接続の様子まで一行ずつ見せます。 |
| -L | リダイレクトを追いかけます。なければ301が来たとき、欲しかったページではなくほぼ空のレスポンスを受け取ります。 |
| -o FILE | レスポンスを画面に出さずファイルへ保存します。curlをダウンロード道具として使う形です。 |
| -H "Name: value" | リクエストヘッダーを足します — 中身の種類、認証トークン、望む言語など。 |
| -X POST -d "data" | 尋ねるだけでなくデータを送ります。データを付けるとPOSTになり、ターミナルからフォームやAPIを試す方法がこれです。 |
| -s | 静かに。進み具合の表示を消します。出力をほかのコマンドやスクリプトへ渡すときは必ず付けます。 |
| -A "name" | 特定のブラウザーのふりをします。サイトによっては尋ねる相手が誰かで答えが変わります。 |
APIを呼ぶ
今どきのアプリが話す相手は、ページではなくJSONを返すAPIであることが多いものです。curlがそれを取り、jqが読みやすく整えます。アプリのコードを一行も書かないうちにエンドポイントを確かめる方法です。
# fetch JSON and pick one field out of it with jq
curl -s https://api.github.com/repos/sveltejs/svelte | jq '.stargazers_count'
# send JSON with POST
curl -X POST https://httpbin.org/post \
-H 'Content-Type: application/json' \
-d '{"name":"PES","level":1}'
# download to a file
curl -L -o page.html https://getpes.com何であれまずcurlで試す癖を付けてください。curlが正しい答えを受け取ればサーバーは無事で、間違いは自分のプログラムにあります。curlも変な答えを受け取るなら、見当違いの場所を半日探す手間を省いたことになります。
Chromeも同じクライアント、大きさが違うだけ
Chromeは、あなたがncで送ったそのリクエストを送ります。そのあと、返ってきた答えを人が見られる画面に変える途方もない仕事を足します — 本当の違いはその「足し」だけです。
その段取りを知っておく価値があります。一つひとつがページの狂う場所だからです。何かが出てこないとき問うべきは、いつでも「どの段階で失敗したのか」です。
アドレス欄から絵まで
- 名前を解く DNSがドメイン名をIPアドレスに変えます。ここで失敗するとサーバーに届きもせず、エラーもページではなく名前を指します。
- つないで安全にする 接続を開き、httpsなら何かを送る前に証明書を確かめます。ここで出る警告はページがないという意味ではなく、暗号を信用できないという意味です。
- リクエストを送る あなたが手で打ったのと同じ数行に、ヘッダーがずっと多く付きます。クッキー、読める形式、好みの言語などです。
- 解釈して残りを取りに行く HTMLを読み、タグから要素の木を組み立て、ページが指すものをすべて要求します — スタイルシート、画像、フォント、スクリプトを、たいてい同時に何本も。
- 飾り、並べ、描く CSSを当て、箱ごとの位置を計算し、結果を描き、そのすべてをまた書き換えられるJavaScriptを走らせます。
ターミナルのクライアントか、ブラウザーか
Chrome・Edge・Brave・OperaはどれもChromiumの上に建っているので、一つで動くページはたいていほかでも動きます。SafariとFirefoxは違うエンジンです。サイトを仕上げたと言う前に少なくとも二種類で開くべき理由がそれです。
開発者ツール — 行き交う文章を目で見る
Chromeには世界でいちばん優れたWebのデバッグ道具が入っていて、もう入っています。ncで手ずから送っていたものが、ここではリクエスト一件ずつ、隣にレスポンスも添えて並びます。
F12、またはmacOSならCommand-Option-I、それ以外はControl-Shift-Iです。ページのどこかを右クリックして[検証]を選べば、その要素が選ばれた状態で開きます — いちばん速い入り口です。
パネル
いますぐ試す三つ
- 自分のリクエストを探す Networkパネルを開いてこのページを再読み込みし、いちばん最初の項目を押してください。そこにあるリクエストとレスポンスのヘッダーが、ncに打ち込んでいたあの行そのものです。
- curlとしてコピーする リクエストを右クリックして[Copy as cURL]を選び、ターミナルに貼り付けてください。ヘッダーもクッキーもそのまま、ブラウザーがした通りを再現したことになります。
- わざと壊してみる Elementsパネルで見出しを消して消えるのを見て、Networkで速度を遅く絞ってから再読み込みしてみてください。どちらも安全で、どちらも読むより多くを教えてくれます。
最初のテーマで作ったHTMLファイルを開いて検証すれば、木の中のタグがすべて見覚えのあるものになっているはずです。書き、差し出し、求め、覗くというこの一周が、Web開発全体の縮図です。
自分で記録する欄です。ここで採点はしません。