みなさん、こんにちは!ここは Bo2SS です~早いもので、卒業から 1 年が経ち、会社には新しい血が注入されました。部門内で大規模なフロントエンド新人研修があり、勇気を出して申し込み、HTTP に関する知識を共有することにしました。実は、これまで HTTP を体系的に学んだことがなかったので、今回の共有のために 2 ヶ月前から準備をしました。先週の共有後、アンケートのフィードバックでは全員から五星🌟の評価をいただいたので、ここに記録しておきます~
前言#
タイトルの通り、今日はネットワーク通信における HTTP と HTTPS のあれこれについてお話しします。
Q:なぜこのテーマを共有するのか?
-
🫡 日常生活でよく見かける。HTTP はインターネット全体で非常に一般的です。例えば、ドラマを見たり、短い動画を見たり、Google でプログラミングをしたりする際に使われます。開発者として、私たちは深く理解する義務があります。
-
🤔 仕事でよく見かける。私たちの仕事の中で、関連する問題にしばしば直面します。例えば、フロントエンドとバックエンドのインターフェースを調整する際に、予期しない状況に遭遇した場合、まず注目すべきは HTTP リクエスト内の情報です。その構造やいくつかの規範に慣れておくべきです。
-
📖 考え方の参考になる。HTTP は 30 年以上の歴史があり、3 つの大きなバージョンがあります。その多くの設計思想は、私たちの開発において参考にする価値があります。
Q:どの資料を参考にしたのか?
今回の共有のために多くの準備をしましたが、主に参考にしたのは以下の資料です:
-
極客時間の《透視 HTTP 协议》コース。このシリーズはおそらく 10 回ほど聞きました。詳細を深く知りたい方はぜひご覧ください。著者は実践的に学べるリポジトリchronolaw/http_studyも提供しており、これを使えば簡単に Web サーバーを構築し、ブラウザを通じてリソースにアクセスして HTTP を理解できます。
-
小林 Coding。これは私が長い間フォローしている公式アカウントで、コンピュータの基礎に関する多くの図解シリーズの記事があります。現在は小林 Coding のウェブ版もあります。
-
Bo2SS。これは私自身の公式アカウントです、ここにあります👋。以前に HTTPS に関連する 2 つの記事を書きました:
また、文中ではいくつかの第三者の画像も引用しており、出典を列挙することはありませんでした。不適切な点があればご連絡ください。
Q:今回の共有の目標は?
-
🔍 HTTP の問題を迅速に特定する。先ほど述べたように、フロントエンドとバックエンドの調整中に、予期しない結果に直面することがよくあります。まず、ステータスコードを通じて迅速に特定できるようにする必要があります。この問題がバックエンドのものかフロントエンドのものかを判断します。
-
🥣 HTTP メッセージ内の一般的なヘッダーフィールドに慣れる。一般的なヘッダーフィールドに慣れることで、HTTP の基本機能を把握できるだけでなく、多くの HTTP の設計思想も学ぶことができます。このメッセージはどこで見ることができるのでしょうか?各端末には一般的にパケットキャプチャツールがあります。
-
🔐 基本的な暗号知識を理解する。インターネット時代において、ユーザーのプライバシーやビジネスの秘密は非常に重要です。
🏁最終目標:この記事を読み終えたら、HTTP を自分で深く学ぶ能力を持つことができるようになります。例えば、WireShark、Chrome、Telnet ツールを使用したり、RFC 文書を見たりすることができます。そこにはネットワーク関連の重要な資料がほぼすべて含まれています。
➕ いくつかの資料:各ツールの使用ガイド(WireShark、Chrome、Telnet)、RFC 文書のまとめ。
Q:今回共有する内容は?
つまり、今回の共有のアウトラインです:
簡単に言うと、今日はHTTP と HTTPS とは何か、そしてそれらがどのように発展してきたかについてお話しします。
では、話を本題に移しましょう:
HTTP とは?#
HTTP とは何か、そして何ではないのか?#
HTTP の正式名称は Hypertext Transfer Protocol、つまり超テキスト、転送、プロトコルです。後ろから前に説明していきましょう~
-
プロトコル。プロトコルとは何か、私たちは賃貸契約や三者間契約を連想できますが、実際には同じ意味です。プロトコルの「協」は、2 人以上の参加者がいることを示し、「議」は合意や規範を示します。何をすることができ、何をしてはいけないかを約束します。
-
転送。次に転送ですが、私たちは宅配便を連想できます。2 点間を転送するために特化しています。その重要な点は 2 つです。第一は双方向性です。私たちは荷物を送ることも受け取ることもできます。第二は転送プロセスに中継があることです。例えば、荷物を送るとき、配達員、宅配会社、物流倉庫などを経由して、最終的に受取人に届きます。これらの中間者もすべてプロトコルを遵守します。
-
超テキスト。最後に超テキストですが、文字通り普通のテキストを超えたテキストです。ここで皆さんに質問したいのですが、超テキストには文字、画像、音声、動画形式の他に、もう 1 つ最も重要な形式は何でしょうか?そうです、それはハイパーリンクです。ハイパーリンクは、私たちがある「超テキスト」から別の「超テキスト」にジャンプできるようにし、私たちのテキストを以前の線形構造から非線形の網状構造に変えます。
一言でまとめると:「HTTP は、コンピュータの世界で特に 2 点間で文字、画像、音声、動画などの超テキストデータを転送するための合意と規範です」。
この図は、基本的な HTTP 通信プロセスにおける参加者を示しています。この図から、HTTP が何でないかを整理し、HTTP についてより明確な理解を得ることができます。
-
HTTP は実体ではありません。左側の Web ブラウザ(送信者)、右側の Web サーバー(受信者)などです。
-
HTTP はインターネットではありません。HTTP が転送する超テキストリソースは、インターネットリソースの一部です。
-
HTTP はプログラミング言語ではありませんが、HTTP はさまざまなプログラミング言語をサポートしています。
-
HTTP はHTMLではありません。HTTP は HTML を転送できますが、HTML は超テキストの一般的な形式です。
-
HTTP は孤立したプロトコルではありません。通常、HTTP の下にはいくつかの低レイヤープロトコルがサポートされています。例えば TCP、IP、DNS などです。また、HTTP の上には HTTP に依存するプロトコルもあります。例えば WebSocket、HTTPDNS などです。これらのプロトコルは相互に絡み合い、プロトコルのネットワークを構成し、HTTP は中心的な地位にあります。
HTTP の全貌#
HTTP の世界の全貌を再度見てみましょう。主にアプリケーション関連と理論関連に分かれています。HTTP 通信リンクについてよりマクロな認識を持つことで、問題を特定する際に、どの段階で問題が発生しているのかをより明確に理解できます。
HTTP 関連アプリケーション#
右から左へ見ていきましょう:
-
インターネット - WWW:インターネットは情報リソースを保存している場所です。WWW はインターネットのサブセットで、ワールドワイドウェブの略です。HTTP に基づいているため、保存されているのはすべて超テキストリソースで、これらのリソースはインターネット全体の約 90% を占めています。
-
Web ブラウザ:Web ブラウザは、HTTP 通信プロセスにおけるリクエスト側であり、リクエストされたリソースを表示できます。
-
Web サーバー:Web サーバーは、HTTP 通信プロセスにおけるレスポンス側であり、ネットワークリソースを管理します。一般的にはハードウェアとソフトウェアに分けられます。ハードウェアは物理サーバーやクラウドサーバーなどの機械を指し、ソフトウェアは Nginx や Apache などのアプリケーションを指します。
-
CDN:Content Delivery Network、つまりコンテンツ配信ネットワークです。前述のように HTTP の転送プロセスには中継があるため、ここで CDN の役割は中継者として、ネットワークプロキシの役割を果たします。CDN はサーバーのリソースをキャッシュし、ネットワーク応答を加速し、負荷分散やセキュリティ保護などの機能を提供します。
-
クローラー:クローラーとは、Web ブラウザに似ており、ユーザーエージェントの一種と理解できます。一般的には、各種検索エンジンが自動的にデータを取得し、データベースに保存するために使用されます。
-
その他:その他には HTML、Web サービス、WAF があります。Web サービスは Web サーバー上で実行される具体的なサービスやサービス開発規範を理解できます。WAF の正式名称は Web アプリケーションファイアウォールで、実際にはプロキシの一種です。
HTTP 関連理論#
同様に右から左へ見ていきます。右側の HTTP/1.1、HTTPS、HTTP/2、HTTP/3 は、今日私たちが話す主要なプロトコルです。左側:
- TCP/IP:これはプロトコルスタックを表し、多くのネットワーク通信プロトコルが含まれています。
ここでは、TCP/IP に基づく HTTP 通信プロセスを宅配便の輸送と類似させてみましょう:
1)超テキスト => MAC:左図の左側の列では、転送される超テキストがアプリケーション層からリンク層まで進む際に、各層を通過するたびに対応するヘッダーが追加されます。例えば HTTP ヘッダー、TCP ヘッダー、IP ヘッダー、MAC ヘッダーのように、これは宅配便の梱包プロセスのようです。
2)MAC => 超テキスト:左図の右側の列では、転送されるデータが各層を通過するたびに 1 つのヘッダーが削除されます。これは宅配便の開梱プロセスのようです。
- URI - URL:URI(I - Identifier)は統一リソース識別子で、URL と URN の 2 つの形式に分かれますが、後者はインターネットの世界ではあまり一般的ではないため、URI は一般的に URL を指します。
1)URL(L - Locator):私たちのブラウザの上部のアドレスが URL です。
その基本構成は上図のようになります。まず赤枠部分に注目しましょう:
-
scheme:最左側の scheme はプロトコルを表します。例えば http、https、ftp などです。ここでプロトコルの後に続く
://
記号は固定で、必須です。 -
host:中間の host はホスト名、またはドメイン名と呼ばれます。DNS について話すときに詳しく説明します。
-
path:最後の path はリソースパスを表します。
Q:ここで 1 つの質問があります。図の例の URL のドメイン名はwww.creatorseo.com/
ですか?
答えは否です。最後のスラッシュ/
は path に属し、アクセスするホストのルートディレクトリを表します。初期のインターネット上のコンピュータはほとんどが UNIX システムだったため、ここでのパス形式は UNIX のファイルパススタイルを採用しています。
次にもう 1 つの図を見てみましょう。この図は URL の完全な形式です。
上図にはさらに 3 つの構成要素が追加されています:
-
user@:URL にユーザー名とパスワード情報を記入できますが、セキュリティ上の理由から使用が推奨されていません。
-
?query:この部分はリソースに対する追加要求を付加できます。
?
で始まり、複数のキーと値のペアk=v
で構成され、各ペアは&
で接続されます。 -
#fragment:これはフラグメント識別子を表し、リソース内のアンカーとして理解できます。クライアントが使用するもので、サーバーには送信されません。普段、ブログを見ているとき(例えば、原文を読むために私のブログページにジャンプする際)、浮動目次の中の特定のタイトルをクリックしてジャンプするのはこの部分を使用しています。
💡 ここで 2 つの小さなリマインダーがあります~1 つは host の後に:port
でポートを指定できることです。もう 1 つは、一般的に URL について話すとき、escape エスケープとencode エンコードの 2 つの概念についても話されます。これらがなければ、サーバーは URL を正しく処理できない可能性があります。path に?
記号が含まれている場合、サーバーはどのように query の開始位置を解析するのでしょうか?
-
escape - エスケープ:特殊文字に対して、一般的にエスケープを行い、直接 ASCII コードの 16 進数に変換し、
%
プレフィックスを追加します。例えばSPACE
は%20
、?
は%3F
に対応します。 -
encode - エンコード:漢字など他の言語に対して、一般的に UTF-8 エンコードを行い、その後エスケープします。信じられない場合は、含まれる中国語の URL をコピーして WeChat に貼り付けてみてください(例えば、原文を読むために私のブログページにジャンプする際)。
2)URN(N - Name):次に URI の 2 つ目の形式 URN に戻ります。これは名前空間と具体的な識別子の形式でリソースをマークします。例えばurn:<NAMESPACE-IDENTIFIER>:<NAMESPACE-SPECIFIC-STRING>
のように。これは私たちがインターネットを利用する際にはあまり一般的ではありませんが、本を買うときに、各本のバーコードの位置に一連の文字があることに気づくかもしれません。例えばISBN xxx-x-xx-xxxx
、これが URN の一種の使い方です。
- DNS:正式名称は Domain Name System、つまりドメイン名システムです。これはドメイン名解決のためのアプリケーション層プロトコルで、ドメイン名を IP アドレスに変換します。
まず、ドメイン名の構造を見てみましょう。まだこの図を見てください。赤枠部分がドメイン名で、階層構造を持ち、.
で区切られています。右側に行くほど階層が高くなります。右から左に、トップレベルドメイン、セカンドレベルドメイン、サードレベルドメインなどがあります。
次に、DNS の種類と DNS がドメイン名を解決する手順を見てみましょう。以下の図のように:
1)DNS の種類。DNS はルート DNS、トップレベル DNS、権威 DNS、非権威 DNS に分かれます。ルート DNS は 13 グループあり、世界中に分布しています。リクエストされたトップレベルドメインに基づいて、DNS を下の対応するトップレベル DNS に解決します。トップレベル DNS はセカンドレベルドメインに基づいて権威 DNS を指定し、最終的にドメイン名に対応する IP を解決します。一部の大企業は独自の DNS を構築しており、これを非権威 DNS と呼びます。これらはより広範に分布しており、Google の 8.8.8.8、Microsoft の 4.2.2.1、CloudFlare の 1.1.1.1 などが有名です。
2)DNS がドメイン名を解決する手順。実際の解決プロセスは 4 つのステップに分かれています。システムはまず DNS キャッシュを探します。これはブラウザ内のものか、システム内のものかもしれません。見つからない場合は、hosts ファイルを確認します。そこには私たちが定義したドメイン名と IP の対応規則が含まれています。Mac の hosts ファイルのパスは/etc/hosts
です。マッチしない場合は、非権威 DNS に問い合わせます。一般的には、私たちのネットワークプロバイダーが指定したものを使用します。まだ解決できない場合は、ルート DNS の解決プロセスに進む必要があります~
💡 ここでいくつかのドメイン解決に関連する一般的なコマンド(dig、host、nslookup
)があります。興味がある方は、ターミナルで試してみてください~
1. DNSアドレス解決プロセス: dig www.baidu.com +trace @8.8.8.8
2. ドメイン名 <=> IP: host www.baidu.com
3. ドメイン => IP: nslookup www.baidu.com
もし WireShark を使っているなら、filter: port 53
で DNS 解決に関連するパケットをフィルタリングできます。
- Proxy:プロキシです。プロキシは一般的に正向プロキシと逆向プロキシに分かれます。正向プロキシはクライアントに近く、逆向プロキシはサーバーに近いです。先ほど述べた CDN は逆向プロキシに該当し、外部ネットワークにアクセスするための VPN は正向プロキシに該当します。
HTTP メッセージ#
ここまでの準備(実際には価値があります)を経て、ついに HTTP の最も重要な部分に到達しました!
HTTP とは、超テキスト転送プロトコルであり、その中で最も重要な部分は最後のプロトコルです。ここでは HTTP メッセージの形式と使用法が定められています。
基本形式#
まず、HTTP メッセージの基本形式を見てみましょう。これは簡単にヘッダーとボディの 2 つの部分に分けられます:
1)ヘッダー:一般的には開始行部分を含み、ヘッダーは Start line と Header で構成されます。下図はリクエスト中のリクエストヘッダーとレスポンスヘッダーの構造を示しています:
-
リクエストヘッダー
-
Start line はリクエストメソッド、URI、HTTP バージョン、空白区切り記号、最後の改行記号で構成されます。
-
Header は 1 つ 1 つの
key:value
キーと値のペアと最後の改行記号で構成されます。ここで:
の前に余分な空白があってはいけません。信じられない場合は、telnet
コマンドを試してみてください(Mac ではbrew install telnet
を使用してtelnet
をインストールし、極客時間が提供する実践リポジトリchronolaw/http_studyと組み合わせて使用することをお勧めします)。
-
-
レスポンスヘッダー
-
Start line はHTTP バージョン、ステータスコード、ステータスコードに対応する説明、空白区切り記号、最後の改行記号で構成されます。
-
Header の構造はリクエストヘッダーと同じです。
-
2)ボディ:一般的にはビジネスに基づいてボディの具体的な内容が定められます。これはあってもなくても構いません。
次に、リクエスト行のリクエストメソッドとステータスコードには具体的にどのようなものがあるかを見てみましょう~
リクエストメソッド#
HTTP/1.1 では 8 種類のリクエストメソッドが規定されており、ここでは一般的なものとあまり使われないものに分けられています。さらに拡張リクエストメソッドもあります。これらのメソッドはすべて大文字でなければなりません。
ここでは一般的なリクエストメソッドについて紹介します:
-
GET と HEADは、サーバーリソースを取得するために使用されます。両者の違いは、HEAD はヘッダー情報のみを取得し、GET は完全なヘッダーとボディ情報を取得します。したがって、特定のリソースが存在するかどうかを確認したい場合やヘッダー情報のみが必要な場合は、HEAD リクエストを使用して、転送量を減らすことができます。
-
POST と PUTは、リソースをサーバーに送信するために使用されます。両者の違いは、前者はサーバーにリソースを作成するもので、データベースの CREATE 操作に似ており、後者はサーバーのリソースを変更するもので、UPDATE 操作に似ています。両者は似ていますが、実際のアプリケーションでは PUT はあまり使用されません。
💡 リクエストメソッドについて話すと、一般的に安全性と冪等性の 2 つの概念にも言及されます:
-
安全性:サーバーリソースに実質的な変更を加えないことを指します。したがって、上記で述べた GET と HEAD は安全です。
-
冪等性:同じ操作を何度も実行した場合、結果が同じかどうかを指します。したがって、上記で述べた GET、HEAD、PUT はすべて冪等です。
レスポンスステータスコード(5 種類)#
ここで私たちの最初の目標に到達しました:🔍 ステータスコードを通じて HTTP の問題を迅速に特定する。
ステータスコードは一般的に5 つの大カテゴリに分けられます:
1xx:情報提示型。一般的にはリクエストの中間状態で、あまり見かけません。
2xx:成功型。これはリクエストが期待通りであることを示し、私たちが最も望むものです。
3xx:リダイレクト型。リソースが変更され、クライアントは別のドメインにリクエストを再送信する必要があります。
4xx:クライアントエラー。このコードが表示された場合、リクエストメッセージが間違っているかどうかを考える必要があります。
5xx:サーバーエラー。このコードが表示された場合、サーバー側の同僚に問題の原因を確認する必要があります。
一般的な具体的なステータスコードは以下の図を参照してください:
-
301:永久リダイレクト。リクエストの URL を変更できます。
-
302:一時リダイレクト。
-
304:サーバーリソースが変更されていないため、ローカルキャッシュにリダイレクトされました。
-
401:未認証エラー。一般的には認証やログインに関連しています。
-
403:アクセスが拒否されました。機密情報にアクセスした可能性があります。
-
404:リソースが見つかりません。リソースパスが間違っているか、アクセス権がない可能性があります(エラーコードはサーバー側でカスタマイズされたもので、404 は一般的にリソースが見つからないことを示しますが、具体的な理由を隠すこともできます)。
-
405:リクエストメソッドが許可されていません。
-
502:ゲートウェイまたはプロキシからのエラーコード。一般的にはゲートウェイまたはプロキシの背後にあるサーバーにエラーが発生したことを示します。
-
503:サーバーが一時的に利用できません。後で再試行してください。
💡 400 や 500 は比較的一般的なエラーコードで、時には底のエラーコードとして返され、未知のエラーが発生したことを示します。また、サーバーが詳細を過度に公開したくないために返されることもあります。いずれにせよ、サーバーは公共の認識をできるだけ遵守する場合、ステータスコードをカスタマイズすることができます。
一般的なヘッダーフィールド(8 種類)#
すぐに、私たちはこの記事の第二の目標に到達しました:🥣 HTTP メッセージ内の一般的なヘッダーフィールドに慣れる。
まず、ヘッダーフィールドを Request、Response、Universal の 3 つの大カテゴリに分けます。Universal カテゴリには Entity サブカテゴリが含まれます。Request カテゴリのヘッダーフィールドはリクエスト側が使用し、Response カテゴリのヘッダーフィールドはレスポンス側が使用し、Universal カテゴリのヘッダーフィールドはリクエスト側とレスポンス側の両方が使用できます。Entity カテゴリのヘッダーフィールドは一般的にボディ属性を説明するために使用されます。
上図には一般的なヘッダーフィールドが示されています。見た目は混乱するかもしれませんが、焦らずに機能別に説明します。補足:同じ色のヘッダーフィールドは一般的に一緒に使用されるか、関連しています。
以下に、ヘッダーフィールドを機能別に主に 8 種類に分けて説明します。
1)ボディ:ボディ属性に関連し、リクエストメッセージ内のものもあれば、レスポンスメッセージ内のものもあります。
ボディのタイプについて言及する際、まず MIME(Multipurpose Internet Mail Extensions、多目的インターネットメール拡張)タイプが何であるかを理解する必要があります。これは電子メールシステムから生まれたもので、現在はボディのタイプを説明するためにも使用されています。ここにMIME タイプのまとめがあります。リンクをクリックすると、application/json
、text/html
、text/javascript
など、非常に馴染みのあるものが見つかるでしょう。前半部分は大きなカテゴリで、後半部分は具体的な形式です。
-
Accept
はリクエスト側が受け入れ可能なボディタイプを示します。複数ある可能性があります。 -
Content-Type
は実際に転送されるボディタイプを示します。
ボディのサイズを減らすために、一般的に圧縮を行います。一般的な圧縮形式には gzip、deflate、br があります。これらはテキストの圧縮効果が非常に良好です。
-
Accept-Encoding
はリクエスト側がサポートできる圧縮形式を示します。複数ある可能性があります。 -
Content-Encoding
は転送されたボディが実際に使用した圧縮形式を示します。
上記の圧縮方式は一般的にテキストに対して良好な圧縮率を持ちますが、圧縮効果が悪い画像、音声、動画などのマルチメディア形式に対しては、大きなファイルの問題を解決するために分割転送という方法があります。
Transfer-Encoding: chunked
:これはデータが分割転送されることを示します。
動画タイプのボディ、例えば私たちが bilibili で動画を見るとき、この動画は一度にリクエストされることはありません。動画を分割してリクエストすることができます。
-
Accept-Ranges: bytes
:一般的に HEAD リクエストを通じてサーバーに範囲リクエストをサポートしているかどうかを尋ねます。もしサポートされていれば、バイト範囲でリクエストできます。 -
Range: bytes=x-y
:サーバーがサポートしている場合、リクエスト側は x〜y バイトの内容を明確にリクエストできます。 -
Content-Range: bytes x-y/length
:これはサーバーが返したボディが x〜y バイトの内容であり、内容の総長さが length であることを示します。
国際化の観点から、言語に対する要求を設定することもできます。
-
Accept-Language
はリクエスト側が理解できる言語を示します。複数ある可能性があります。 -
Content-Language
は転送されたボディの実際の言語を示します。
ここで具体的な例を挙げます:Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
。注意すべき 2 つの詳細があります:
-
HTTP 規範では、
,
の優先度が;
よりも高いです。これは一般的なプログラミング言語の構文とは逆です。したがって、上記のen;q=0.9
は 1 対です。 -
上記の
q
は何を意味するのでしょうか?これは重みを表し、デフォルトは 1 です。レスポンス側はできるだけ重みが最大の言語を使用して内容を返します。
2)Connection:長接続に関連します。
HTTP/1.1 以前は、クライアントがサーバーと通信するたびに接続を再構築する必要があり、頻繁に通信すると、TCP 接続を繰り返し構築して閉じることになります。左図のように、これは短接続です:
したがって、TCP 接続を少し長く保つことができれば、各接続で両端が何度も通信できるようになります。これは右図のように長接続です。HTTP/1.1 はこれをサポートしています。
-
Connection: keep-alive
:これは長接続を使用することを示します。HTTP/1.1 ではデフォルトで有効になっています。 -
Connection: close
:長接続を積極的に閉じることを示します。一般的にはクライアントから発信されます。
サーバー側も長接続の切断タイミングを設定できます。これは Web サーバー内で設定されます。例えば、Nginx ではkeepalive_timeout
が長接続のタイムアウト時間を示し、長時間データの送受信がない場合は接続を自動的に切断します。keepalive_requests
は長接続中に最大で受け取るリクエストの数を示します。
長接続の方式により、クライアントは同時に複数のリクエストを発信できます。最初のリクエストの結果が戻るのを待たずに 2 番目のリクエストを発信できます。これがパイプライン通信です。
ただし、短接続でも長接続でも、先頭ブロッキング(HoL blocking)問題があります。これは HTTP の「リクエスト - レスポンス」モデルがメッセージが「一発一受」であることを規定しているためです。下図を参照してください:
受信側は赤線リクエストを処理し終わるまで、後に発信されたリクエストを処理できません。たとえ後のリクエストが先に到着しても、つまり「先に発信したものは先に処理しなければならない」ということです。
この問題を緩和するための 1 つの方法は、並行接続です。つまり、1 つのドメインに対して複数の長接続を発信し、各長接続は互いに干渉しません。ただし、長接続を維持するにはサーバーリソースが消費され、悪意のある攻撃を受ける可能性もあるため、一般的に長接続の上限は 6〜8 個です。もしそれでも足りない場合、ドメインシャーディングという巧妙な方法があります。同じサーバーに複数のドメインが対応している場合、上限も倍増します。
3)Redirection:リダイレクトに関連します。
ステータスコードについて話すとき、301(永久)や 302(一時)について言及しました。これらの意味を覚えていますか?このようなステータスコードが返された場合、レスポンスヘッダーには必ずリダイレクト先が示されます。
Location
はリダイレクト先を示します。一般的には絶対パスと相対パスの 2 つの形式があります。絶対パスは URL の基本形式に対応し、相対パスは scheme と hostがなく、元のリクエストの URL に基づいてデフォルトで使用されます。
リダイレクトに関連する 3 つのステータスコードもあります:303 は 302 に似ていますが、リクエストメソッドは GET のみです。307 と 308 はそれぞれ 302 と 301 に似ています(ここでは逆です……)が、リダイレクト後のリクエストに変更を加えることは許可されていません。
4)Cookie:HTTP の無状態の特徴によって生じる問題を解決します。
まず、無状態がクライアントかサーバーのどちらを指すのかを明確にする必要があります。そうです、サーバーです。つまり、サーバーは受信したリクエストが今回のものと前回のものにどのように関連しているのかを知りません。これにより、サーバーが特定のシナリオを処理する際の手法が複雑になります。例えば、ショッピングなどです。
したがって、ここで Cookie がこの問題を解決します。簡単に言うと、サーバーはクライアントに小さなメモを渡し、特定のクライアントのアイデンティティを示します。このクライアントは毎回リクエストを送信する際にこの小さなメモを持参し、自分のアイデンティティを証明できます。
-
Set-Cookie: a=xxx
、Set-Cookie: b=yyy
:これはサーバーが返すもので、Cookie は本質的にキーと値のペアであり、各 Cookie は分かれています。 -
Cookie: a=xxx; b=yyy
:これはクライアントがリクエストを送信する際に持参するもので、以前にサーバーが返した Cookie です。これらは一緒にまとめられています。
注意すべき点は、クライアントがこれらの Cookie を受け取った後、それらをクライアントに保存します。Chrome ブラウザを見れば、それらを確認できます。
あれ?Cookie は Name と Value の他に、どうしてこんなに多くの属性があるのでしょうか?実際、サーバーが返す Cookie は一般的に次のようになります:Set-Cookie: a=xxx; Domain=xx; Path=xx; Max-Age=xx; Expires=xx; HttpOnly; Secure; SameSite=xx...
。
-
Domain、Path:クライアントがリクエストする URL がそれらに一致する場合にのみ、この Cookie が持参されます。
-
Max-Age、Expires:Cookie の有効期限を示します。後者の Cache にも似た属性があります。注意すべきは、Max-Age の優先度が Expires よりも高いことです。
-
HttpOnly:これが真である場合、この Cookie は HTTP (S) プロトコルを介してのみ転送され、他の方法でのアクセスが禁止されます。例えば、JS 内では document.cookie を使用して取得できなくなります。これはスクリプト攻撃を防ぐためです。
-
Secure:これが真である場合、この Cookie は安全な HTTPS リクエストを発信する際にのみ持参されます。
-
SameSite=xxx:SameSite=Strict を設定すると、この Cookie はクロスサイトで送信できないように厳格に制限されます。SameSite=Lax は少し緩やかで、安全なリクエストでこの Cookie を使用することを許可します。
5)Cache:キャッシュに関連します。
キャッシュは本当に至る所にあります。HTTP リクエストでも例外ではありません。ここで言及されるキャッシュはクライアントに保存されるもので、目的はネットワークリクエストや返されるデータのサイズをできるだけ減らし、ネットワーク伝送効率を向上させることです。
-
Cache-Control
-
サーバーが返す属性には、
max-age=10
/no-store
/no-cache
/must-revalidate
があります。-
max-age の単位は秒で、返された瞬間から計算が始まります。
-
no-store はクライアントがキャッシュを許可しないことを示します。
-
no-cache はクライアントがキャッシュを使用する前にサーバーで検証する必要があることを示します。
-
must-revalidate はキャッシュが無効になった後、必ず検証する必要があることを示します。
-
-
クライアントが送信できる属性には、
max-age=0
やno-cache
があります。-
一般的に、cmd + R でページをリフレッシュすると、max-age=0 が付加されます。これは、0 秒間生存したデータを意味し、ローカルキャッシュを使用せず、サーバーから最新のメッセージを取得することを意味します。cmd + shift + R で強制的にページをリフレッシュすると、no-cache が付加され、基本的には前者と同じで、サーバーがどのように処理するかによります。
-
では、いつキャッシュが有効になるのでしょうか?一般的には、ブラウザが前進、後退、またはリダイレクトする際、クライアントが発信するリクエストには上記の 2 つの属性が付加されません。
-
-
さらに、キャッシュ制御の柔軟性を高めるために、いくつかの条件フィールドがあります~
-
サーバーが返すものには:
-
Last-Modified
はファイルの最終変更時間を示します。 -
ETag
は Entity Tag の略で、リソースの一意の識別子を示します。これは変更時間ではファイルの変化を正確に区別できない問題を解決するために存在します。例えば、ファイルが 1 秒内に何度も変更された場合、変更時間の最小単位は秒です。また、ファイルが変更された時間属性を持っていても、内容が変わらない場合があります。Etag には強 Etag と弱 Etag があります:-
前者はリソースがバイトレベルで変わらない条件です。
-
後者はリソースが意味的に変わらない条件で、例えばいくつかの空白が追加された場合などです。さらに、弱 Etag の値には前に
W/
マークが付加されます。
-
-
-
クライアントがリクエストする際には:
-
If-Modified-Since
には、前回のリクエストでサーバーが返した Last-Modified が含まれています。もしサーバーのリソースがこの時間よりも更新されていなければ、サーバーは 304 を返し、クライアントはキャッシュを使用すればよいことを示します。 -
If-None-Match
には、前回のリクエストでサーバーが返した ETag が含まれています。もしサーバーのリソースの Etag が変わらなければ、サーバーも 304 を返します。
-
6)Proxy:プロキシに関連します。
プロキシは二重のアイデンティティを持っています。クライアントの目にはサーバーであり、サーバーの目にはクライアントです。
前述のように、プロキシは一般的に正向プロキシと逆向プロキシに分かれます。逆向プロキシは一般的に負荷分散(タスクを合理的に分配し、どのサーバーがリクエストに応答するかを決定する)やセキュリティ保護、暗号化のオフロード(内部ネットワークで通信する際に暗号化を行わず、暗号化と復号化のコストを減らす)、コンテンツキャッシュ(サーバーの応答を一時保存する)などに使用されます。
上記のプロキシサーバーのシナリオにおいて、関連するヘッダーフィールドは:
Via
:プロキシサーバーはリクエストを送信する際に、自分のホスト名とポート情報をこのフィールドの末尾に追加します。
ただし、サーバーは一般的にクライアントの真の IP 情報を知る必要があります。これはアクセス制御、ユーザー画像、統計分析などに便利です。そのため、HTTP 標準の外で以下のヘッダーフィールドが定められています:
-
X-Forwarded-For
:Via の追加方式に似ていますが、追加される内容はリクエスト側の IP アドレスです。 -
X-Real-IP
:クライアントの IP アドレスのみを記録します。これはより簡潔です。
ただし、上記の方法には大きな欠点があります:パフォーマンスの損失です!なぜなら、各プロキシサーバーは HTTP メッセージヘッダーを解析し、メッセージデータを修正する必要があるからです。また、場合によっては、メッセージが変更されることが許可されていない、または(暗号化されているため)変更できないこともあります。したがって、後に専用のプロキシプロトコルが登場しました。これは標準の外で定められたものです。
このプロトコルに基づいて、プロキシサーバーはHTTP メッセージの前に 1 行のテキストを追加するだけで済みます。例えば:
PROXY TCP4 1.1.1.1 2.2.2.2 55555 80\r\n
GET / HTTP/1.1\r\n
Host: www.xxx.com\r\n
\r\n
-
最初は
PROXY
の 5 つの大文字です。 -
次に、クライアントの IP アドレスのタイプ、例えば
TCP4
またはTCP6
です。 -
次に、リクエスト側と応答側のアドレス、リクエスト側と応答側のポート番号です。
-
最後は改行で終了します。
7)Proxy Cache:プロキシキャッシュに関連します。
クライアントがキャッシュできるように、仲介者のプロキシサーバーもキャッシュできます。しかし、プロキシの二重のアイデンティティ性のため、Cache-Control
はプロキシキャッシュに対していくつかのカスタマイズされた属性を追加しています~
-
サーバーからプロキシサーバーへの:
private
はデータがクライアントにのみ保存され、他の人と共有するためにプロキシにキャッシュされないことを示します。例えばユーザーのプライベートデータです。public
はデータが完全にオープンで、誰でもキャッシュできることを示します。s-maxage
はプロキシサーバー上のキャッシュの生存時間を示します。no-transform
はプロキシサーバーがデータに対して変換操作を行うことを禁止します。なぜなら、一部のプロキシはデータを事前に変換して、後のリクエスト処理を便利にすることがあるからです。
-
クライアントからプロキシサーバーへの:
-
max-stale
はキャッシュが期限切れになっても受け入れることを示します。 -
min-fresh
は上記とは逆で、キャッシュがまだしばらくの間有効である必要があることを示します。 -
only-if-cached
はクライアントがプロキシキャッシュのみを受け入れることを示します。プロキシに条件に合ったキャッシュがない場合、クライアントはプロキシにサーバーに再リクエストすることを望みません。
-
8)その他
この一般的なヘッダーフィールドの図を見て、各ヘッダーフィールドの意味と使用法を理解できたでしょうか~
さて、上記にはいくつかのヘッダーフィールドの説明が漏れていましたので、ここにまとめて補足します:
-
Host
はリクエストするホスト名を示します。これは HTTP/1.1 で必ず出現し、サーバーが具体的にどのホストにリクエストを処理させるかを区別するために使用されます(コンピュータ上に複数の仮想ホストがホストされている場合にこの役割があります。そうでなければ、サーバーは一般的に処理しません)。さらに、一般的なネットワークフレームワークでは、URL からデフォルトの Host 値を解析して補完するため、手動で入力しなくても問題ない場合があります。フレームワークがデフォルトで補完してくれます。 -
User-Agent
はユーザーエージェントであり、リクエスト側のアイデンティティを説明するために使用されます。サーバーはこれに基づいて適切なページレイアウトやデータを返すことができます。しかし、歴史的な理由から、その使用法は混乱しています。例えば、各ブラウザは「Mozilla Chrome Safari」と自称しています。 -
Date
はメッセージが作成された時間を示し、一般的にはレスポンスヘッダーに出現します。 -
Server
は Web サービスを提供するソフトウェアの名前とバージョン番号を示しますが、これによりサーバーの一部の情報が公開されるため、セキュリティ上のリスクがある可能性があります。そのため、時にはこのフィールドが返されないこともありますし、単にあいまいな情報だけが返されることもあります。 -
Content-Length
はメッセージ内のボディの長さを示します。このフィールドがない場合、一般的には別のフィールドTransfer-Encoding: chunked
が存在します。前述の通りです。
これで、HTTP とは何かをすべて説明しました。Chrome の開発者ツールや WireShark を使用して理解を深めてみてください。
HTTPS とは?#
HTTPS は HTTP に S が追加されたもので、この S は SSL/TLS プロトコルを表します。
ここで私たちの第三の目標に到達しました:🔐 基本的な暗号知識を理解する。
このセクションでは、以前に関連する記事を書いたため、できるだけ簡潔にします。以下のリンクをクリックして参考にしてください:
-
情報セキュリティ | インターネット時代、どのように信頼を築くか?:3 つの一般的な暗号アルゴリズム、デジタル署名、デジタル証明書。
-
情報セキュリティ | (追加)インターネット時代、どのように信頼を築くか?:SSL/TLS、SSH、iOS 署名、OpenSSL、WireShark の実践。
補足すべき内容は、ECDHEに基づく TLS の主流のハンドシェイク方式とRSAに基づく TLS の従来のハンドシェイク方式の違いです。
両者の重要な違いは、通信鍵生成プロセスにおける第 3 のランダム数Pre-Master
の生成方法です:
-
前者:両端がまず公私鍵をランダムに生成し、その後公鍵(署名付き)を相手にパラメータとして送信します。両端は双方のパラメータに基づいて、ECDHE アルゴリズムを使用して
Pre-Master
を生成します。 -
後者:クライアントは直接ランダム数
Pre-Master
を生成し、サーバー証明書の公鍵で暗号化してサーバーに送信します。
前者の公私鍵はランダムに生成されているため、ある時点で私鍵が漏洩したり、破られたりしても、その通信プロセスにのみ影響します。一方、後者の公私鍵は固定されているため、私鍵が漏洩したり、破られたりすると、それ以前のすべての通信記録が解読されます。なぜなら、忍耐強いハッカーは長期間にわたりメッセージを収集し、この日を待っているからです(スノーデンのプライズム事件はこの点を利用したと言われています)。
つまり、前者は「一度一密」で、前向きな安全性を持ち、後者は「今日の捕獲、明日の解読」のリスクがあり、前向きな安全性を持たないということです。
詳細については、《透視 HTTP 协议》のTLS1.2 接続プロセス解析の授業を参照するか、自分で WireShark でパケットをキャプチャしてみてください~
両者のパケットキャプチャの違いは主に次の通りです:
-
前者は後者よりも「Server Key Exchange」メッセージが 1 つ多いです。
-
前者はクライアントが接続が完全に確立されるのを待たずに、事前に暗号通信を行うことができます。つまり、クライアントはサーバーから「Finished」の確認を待たずにハンドシェイクを完了させることができます。これを「TLS False Start」と呼びます。
HTTP の発展#
以下の表を通じて HTTP の発展過程を整理してみましょう。今日は HTTP の発展について全体的な認識を持てれば良いです~
時間 | バージョン | 主な変更 | 備考 |
---|---|---|---|
1989 | 3 つの主要技術 | HTML、URI、HTTP | ティム・バーナーズ=リーの論文。 |
1991 | HTTP/0.9 | 1. リクエスト方法:GET。 2. データ:HTML。 | RFC なし。 |
1996 | HTTP/1.0 | 1. + リクエスト方法:HEAD、POST。 2. + データ:画像、音声。 3. + その他:HTTP ヘッダー、ステータスコード、プロトコルバージョン。 | RFC-1945(1996)。 正式な標準ではない。 |
1999 | HTTP/1.1 | 1. + リクエスト方法:PUT、DELETE。 2. + キャッシュ制御。 3. +Keep-Alive。 4. + パイプライン転送(Content-Length)、チャンク転送。 5. +Host ヘッダー(必須)。 | +Google、Sina、Sohu、Netease、Tencent。 RFC-2616(1999)。 +Facebook、Twitter、Taobao、JD。 RFC-7230~7235(2014)。 RFC-9112(2022)。 |
2015 | HTTP/2 | 1. データ転送形式:テキスト → バイナリデータ。 2. + 同時リクエスト(ストリームを使用し、パイプライン転送を放棄)。 3. + ヘッダー圧縮。 4. + サーバーがプッシュを許可。 5. +TLS 1.2 以上と組み合わせ。 | Chrome ブラウザの SPDY に基づく(2009)。 RFC-7540(2015)。 RFC-9113(2022)。 |
2022 | HTTP/3 | 1. トランスポート層プロトコル:TCP → QUIC(UDP に基づき、TLS 1.3 を含む、IP: ポート → 接続 ID)。 2. ヘッダー圧縮:HPACK→QPACK | Chrome ブラウザの QUIC に基づく(2012)。 RFC-9114(2022)。 |
-
HTTP/1.0 以降、HTTP は RFC 文書に書き込まれました(RFC 文書のまとめ)。
-
HTTP/1.1 は HTTP の最初の正式な標準であり、ほとんどの機能は一般的なヘッダーフィールドのセクションで紹介しました。この段階では、Google、Sina、Sohu、Netease、Tencent などの企業が設立され、後に Facebook、Twitter、Taobao、JD などの企業も登場しました。
-
HTTP/1.1 は各方面で比較的完成度が高いですが、性能とセキュリティにはまだ大きな最適化の余地があります。したがって、HTTP/2、HTTP/3 は HTTP の性能面を最適化することを主な目的としています。
- HTTP/2は Chrome の SPDY プロトコルに基づいており、Chrome が推進しています。主な変更点は:
-
データ転送形式がテキストからバイナリに変更され、コンピュータの解析が大幅に容易になりました。
-
仮想ストリームの概念に基づいて、マルチプレクシング機能を実現し、HTTP/1.1 のパイプライン機能を置き換えました。
-
HPACK アルゴリズムを利用してヘッダー圧縮を行い、これまでボディに対してのみ圧縮を行っていました。
-
サーバーが新しい「ストリーム」を作成し、メッセージをプッシュすることを許可しました。例えば、ブラウザが HTML をリクエストしたときに、必要になる可能性のある JS や CSS ファイルを事前にクライアントに送信します。
-
セキュリティ面でも強化が行われ、暗号化された HTTP/2 はその下層の通信プロトコルが TLS1.2 以上である必要があり(以前のバージョンには多くの脆弱性がありました)、前向きな安全性と SNI(Server Name Indication、TLS の拡張プロトコルで、ハンドシェイクプロセスの開始時にクライアントが接続しているサーバーのホスト名を知らせる)をサポートし、数百の弱い暗号スイートを「ブラックリスト」に載せました。
-
PS:HTTP/1.1 の並行接続方式に比べ、仮想ストリームの概念は HTTP の先頭ブロッキング問題をより美しく解決しました。
- HTTP/3は Chrome の QUIC プロトコルに基づいており、これも Chrome が推進しています。
-
まず QUIC を見てみましょう:
-
UDP に基づいて信頼性のある転送を実現し、HTTP/2 のストリームの概念を導入しました。
-
TLS1.3 を内蔵し、接続速度を向上させました。
-
接続は「不透明な」接続 ID を使用して両端をマークし、IP アドレスとポートにバインドされなくなり、ユーザーが気づかない接続の移行をサポートします。
-
-
HTTP/3 に戻ると:
-
最大の変更点は、下層のトランスポート層プロトコルを TCP から QUIC に変更し、TCP の先頭ブロッキング問題を完全に解決しました(注意:これは TCP の問題であり、HTTP の問題ではありません)。弱いネットワーク環境でのパフォーマンスが向上します。QUIC 自体が暗号化、ストリーム、マルチプレクシングなどの機能をサポートしているため、HTTP/3 の作業は大幅に軽減されました。
-
ヘッダー圧縮アルゴリズムは HPACK から QPACK にアップグレードされました。
-
2022 年 6 月 6 日、HTTP/3 は正式に RFC 文書に書き込まれ、HTTP/1.1 と HTTP/2 も RFC 文書が更新されました。
-
-
PS:TCP は信頼性のある転送を保証するために特別な「パケットの再送」メカニズムを持っています。つまり、失われたパケットは再送信確認を待たなければならず、他のパケットがすでに受信されていても、バッファ(kernel)に保存され、上層のアプリケーション(user)は取り出せません。下図を参照してください:赤い四角のリクエストが TCP のブロッキングの鍵です。
(実際、ここでの疑問は、HTTP/3 以前に解決されたのは kernel から user のブロッキング問題だけです