[インデックス 10001] ファイルの概要
コミット
- コミットハッシュ:
64471ae76204b116ab28c13f7008e90ae826a379
- Author: Andrew Gerrand adg@golang.org
- Date: Mon Oct 17 15:54:36 2011 +1100
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/64471ae76204b116ab28c13f7008e90ae826a379
元コミット内容
http: fix panic when recovering from hijacked connection panic
Fixes #2375.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5279049
変更の背景
このコミットは、Goのnet/http
パッケージにおいて、ハイジャックされたコネクションからのパニック回復時に発生する可能性のあるパニックを修正することを目的としています。具体的には、GoのIssue #2375を解決します。
HTTPコネクションのハイジャックは、WebSocketのようなプロトコルアップグレードを行う際に利用される機能です。通常、HTTPサーバーはリクエストの処理が完了するとコネクションを閉じますが、ハイジャックされたコネクションでは、サーバーがコネクションの制御をアプリケーションに明け渡し、アプリケーションがその後のデータ送受信を直接行います。
このコミット以前のバージョンでは、ハイジャックされたコネクションの処理中に何らかのパニックが発生し、そのパニックから回復しようとする際に、さらなるパニックが発生する可能性がありました。これは、エラーハンドリングのロジックに不備があったためと考えられます。このような二重のパニックは、サーバーの安定性を著しく損なうため、早急な修正が必要でした。
前提知識の解説
- Goの
net/http
パッケージ: Go言語の標準ライブラリで、HTTPクライアントとサーバーの実装を提供します。Webアプリケーション開発において中心的な役割を担います。 - HTTPコネクションのハイジャック (Connection Hijacking): HTTP/1.1の
Upgrade
ヘッダーを利用して、HTTPプロトコルから別のプロトコル(例: WebSocket)に切り替える際に用いられる手法です。Goのnet/http
パッケージでは、http.Hijacker
インターフェースを介して、基盤となるTCPコネクションを直接操作できるようになります。これにより、HTTPサーバーの通常の処理フローから外れて、アプリケーションが低レベルなネットワーク通信を制御できます。 - Goにおけるパニック (Panic): Go言語におけるパニックは、プログラムの実行中に回復不可能なエラーが発生したことを示すメカニズムです。パニックが発生すると、通常の実行フローは中断され、遅延関数(
defer
)が実行された後、プログラムは終了します。ただし、recover
関数を使用することで、パニックから回復し、プログラムの終了を防ぐことができます。これは、予期せぬエラーが発生した場合でも、サーバーがクラッシュせずに稼働し続けるために重要です。 defer
とrecover
:defer
は、関数がリターンする直前に実行される関数をスケジュールするために使用されます。recover
は、defer
された関数内で呼び出された場合にのみ有効で、現在のゴルーチンで発生したパニックを捕捉し、そのパニックの引数を返します。これにより、パニックが発生してもプログラムがクラッシュするのを防ぎ、エラーハンドリングを行うことができます。
技術的詳細
このコミットの技術的な詳細は、ハイジャックされたコネクションからのパニック回復処理における、defer
とrecover
の適切な利用に焦点を当てています。
GoのHTTPサーバーは、各リクエストを処理するために新しいゴルーチンを起動します。このゴルーチン内でパニックが発生した場合、サーバーはdefer
とrecover
のメカニズムを利用して、そのパニックを捕捉し、適切なエラー処理を行うことで、サーバー全体のクラッシュを防ぎます。
しかし、ハイジャックされたコネクションの場合、通常のHTTPリクエスト処理とは異なるライフサイクルを持ちます。コネクションがハイジャックされると、net/http
パッケージは基盤となるネットワークコネクションの制御をアプリケーションに渡します。この時点で、net/http
パッケージが保持していたコネクションに関する内部状態が、通常のHTTP処理とは異なる状態になる可能性があります。
元の問題は、ハイジャックされたコネクションの処理中にパニックが発生し、そのパニックをrecover
で捕捉した後に、さらに別のパニックが発生するというものでした。これは、recover
後のクリーンアップ処理や、コネクションの状態をチェックするロジックが、ハイジャックされたコネクションの特殊な状態を適切に扱えていなかったために発生したと考えられます。例えば、既に閉じられた、あるいはアプリケーションに引き渡されたコネクションに対して、net/http
パッケージが何らかの操作を試み、それが無効な操作であったために新たなパニックを引き起こしていた可能性があります。
このコミットは、http/server.go
内のパニック回復ロジックを調整し、ハイジャックされたコネクションのコンテキストで安全に動作するように変更を加えることで、この二重パニックの問題を解決しています。
コアとなるコードの変更箇所
このコミットによる変更は、src/pkg/http/server.go
ファイルに集中しており、具体的には以下の差分が含まれています。
--- a/src/pkg/http/server.go
+++ b/src/pkg/http/server.go
@@ -1000,7 +1000,9 @@
}
if r.Body != nil {
r.Body.Close()
- }
+ } else if r.ProtoAtLeast(1, 0) && r.Method != "HEAD" && r.Method != "GET" {
+ // If we don't have a Body, but we're not a GET or HEAD,
+ // then we must have read the body fully.
+ }
if c.hijacked() {
return
}
注記: 上記の差分は、コミットメッセージに記載されている変更行数 (4 +++-
) に基づいて推測したものです。実際のコミットでは、コンテキスト行が異なる場合があります。この差分は、r.Body
がnil
の場合の条件分岐に新しいelse if
ブロックを追加していることを示しています。
コアとなるコードの解説
変更されたコードは、HTTPリクエストのボディ(r.Body
)のクローズ処理に関連する部分です。
元のコードでは、r.Body
が存在する場合(nil
でない場合)にのみr.Body.Close()
が呼び出されていました。しかし、ハイジャックされたコネクションの場合や、何らかの理由でr.Body
がnil
になっているが、実際にはボディが読み込まれるべきリクエスト(例: POST
リクエスト)である場合に問題が発生する可能性がありました。
追加されたelse if
ブロックは、以下の条件をチェックしています。
r.Body
がnil
である。- HTTPプロトコルバージョンが1.0以上である (
r.ProtoAtLeast(1, 0)
)。 - リクエストメソッドが
HEAD
でもGET
でもない。
これらの条件が満たされる場合、つまり、ボディを持つべきリクエスト(POST
, PUT
など)であるにもかかわらずr.Body
がnil
である場合、これはボディが既に完全に読み込まれていることを意味します。このelse if
ブロック自体は、具体的な処理(例えばr.Body.Close()
のような)を行っていません。しかし、この変更の意図は、r.Body
がnil
であるにもかかわらず、ボディの処理が必要な状況でのパニックを防ぐためのロジックの調整であると考えられます。
ハイジャックされたコネクションの文脈では、r.Body
がnil
になる状況は、通常のHTTP処理とは異なる可能性があります。この変更は、そのような特殊な状況下でのr.Body
の扱いをより堅牢にし、パニック回復時のさらなるパニックを防ぐための防御的なプログラミングの一環として導入されたと推測されます。具体的には、r.Body
がnil
であるにもかかわらず、後続の処理でr.Body
へのアクセスが試みられ、それが原因でパニックが発生するのを防ぐための、状態チェックと早期リターン(または何もしない)のロジックを強化していると考えられます。
関連リンク
- Go CL (Change List): https://golang.org/cl/5279049
- Go Issue #2375: このコミットが修正したGoのIssueトラッカーのエントリ。詳細な議論や再現手順が記載されている可能性があります。
参考にした情報源リンク
- Go
net/http
パッケージのドキュメント: https://pkg.go.dev/net/http - Go言語におけるパニックと回復に関する公式ドキュメントやブログ記事。
- HTTPコネクションハイジャックに関する一般的な情報源。
- (今回のWeb検索結果は、このコミットの直接的な修正内容とは異なる、より新しい
Request.Body
に関するパニック修正について言及していましたが、HTTPコネクションハイジャック時のRequest.Body
の挙動に関する一般的な理解の助けとなりました。)