皆さんこんにちは、ここは 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 つあります。第一に双方向性です。私たちは宅配便を送ることも受け取ることもできます。第二に、転送プロセスには中継が含まれる場合があります。例えば、宅配便を送ると、宅配便の配達員、宅配会社、物流倉庫などを経由して、最終的に受取人に届きます。同時に、これらの中間者もプロトコルを遵守します。
-
超テキスト。最後に超テキストについてですが、文字や画像、音声、動画形式を超えたテキストです。ここで皆さんに質問したいのですが、超テキストには文字、画像、音声、動画形式の他に、最も重要な形式は何でしょうか?そうです、ハイパーリンクです。ハイパーリンクは、私たちがある「超テキスト」から別の「超テキスト」にジャンプできるようにし、私たちのテキストを以前の線形構造から非線形の網状構造に変えます。
一言でまとめると:「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:コンテンツ配信ネットワークです。前述のように、HTTP の転送プロセスには中継が含まれる場合があります。ここでの 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 つずつの
key:value
キーと値のペアと最後の改行記号で構成されます。ここで注意すべきは、:
の前に余分な空白があってはいけません。信じない場合は、telnet
コマンドを試してみてください(Mac ではbrew install telnet
を使用してtelnet
をインストールできます。また、極客時間が提供する実戦リポジトリchronolaw/http_studyと組み合わせて使用することをお勧めします)。
-
-
レスポンスヘッダー
-
Start line はHTTP バージョン、ステータスコード、ステータスコードに対応する説明、空白区切り記号、最後の改行記号で構成されます。
-
Header の構造はリクエストヘッダーと同じです。
-
2)ボディ:一般的にはビジネスに基づいてボディの具体的内容が定められます。これはあってもなくても構いません。
次に、リクエスト行のリクエストメソッドとステータスコードには具体的にどのようなものがあるのかを見てみましょう~
リクエストメソッド#
HTTP/1.1 では 8 種類のリクエストメソッドが定められており、ここでは一般的なものとあまり使われないものの 2 つのカテゴリに分けられます。さらに、拡張リクエストメソッドのカテゴリもあります。これらのメソッドはすべて大文字で表記する必要があります。
ここでは一般的なリクエストメソッドについて説明します:
-
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
は長接続中に最大で受け取るリクエストの数を示します。
長接続の方式により、クライアントは同時に複数のリクエストを発信でき、最初のリクエストの結果を待たずに次のリクエストを発信できます。これがパイプライン通信です。
ただし、短接続でも長接続でも、ヘッダーブロッキング(HoL blocking)問題が発生します。これは HTTP の「リクエスト - レスポンス」モデルがメッセージを「1 発 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 は少し緩やかで、安全なリクエスト(GET/HEAD など)でこの 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. + データ:img、audio。 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 のブロッキング問題だけですか?具体的には TCP を経た後のブロッキング問題で、この段階のブロッキング問題にはどのようなものがあるのでしょうか🤔?大御所の皆さん、疑問を解決していただければ幸いです~)
HTTPS の発展#
この部分では TLS