[インデックス 18767] ファイルの概要
このコミットは、Go言語の net/http
パッケージにおける Response.TLS
フィールドに関するマイナーな修正と最適化を目的としています。具体的には、TLS接続の状態情報 (tls.ConnectionState
) の取り扱いを改善し、効率性を高め、関連するドキュメントを更新しています。
コミット
commit 6433bff205f24c0f527f87284b8c09c6476e5812
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Wed Mar 5 12:40:13 2014 -0800
net/http: minor fixes and optimization for Response.TLS
Also add it to doc/go1.3.txt.
Update #7289
LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/71740043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6433bff205f24c0f527f87284b8c09c6476e5812
元コミット内容
net/http
: Response.TLS
のマイナーな修正と最適化。
doc/go1.3.txt
にも追加。
Issue #7289 を更新。
変更の背景
このコミットの主な背景には、net/http
パッケージがTLS (Transport Layer Security) を使用して安全なHTTP通信を行う際に、レスポンスオブジェクト (http.Response
) に含まれるTLS接続の状態情報 (Response.TLS
) の取り扱いに関する改善の必要性がありました。
以前の実装では、TLS接続の状態がレスポンスごとに新しくコピーされて Response.TLS
フィールドに割り当てられていました。これは、特に多数のHTTPリクエストを処理するようなシナリオにおいて、不要なメモリ割り当てとコピーのオーバーヘッドを引き起こす可能性がありました。このコミットは、このオーバーヘッドを削減し、パフォーマンスを向上させることを目的としています。
また、Response.TLS
フィールドのドキュメントが不明瞭であったり、そのポインタが共有される性質が明記されていなかったりしたため、開発者が誤解する可能性がありました。このコミットでは、コメントを更新し、このフィールドが暗号化されていないレスポンスでは nil
になること、およびポインタが共有されるため変更すべきではないことを明確にしています。
Update #7289
という記述から、この変更が特定のバグ報告や機能改善要求に対応するものであることが示唆されます。具体的なIssueの内容は不明ですが、上記のパフォーマンスとドキュメントの改善がその要求の一部であったと考えられます。
前提知識の解説
このコミットを理解するためには、以下の知識が役立ちます。
- Go言語の
net/http
パッケージ: Go言語でHTTPクライアントおよびサーバーを構築するための標準ライブラリです。HTTPリクエストの送信、レスポンスの受信、ルーティング、ミドルウェアの適用など、HTTP通信の基本的な機能を提供します。 - TLS (Transport Layer Security): インターネット上で安全な通信を行うための暗号化プロトコルです。HTTP通信をTLSで保護するとHTTPSとなります。TLSは、通信の盗聴、改ざん、なりすましを防ぐための暗号化、認証、データ整合性を提供します。
- TLSハンドシェイク: クライアントとサーバーがTLS接続を確立する際に実行される一連のプロトコルです。このプロセス中に、暗号スイートのネゴシエーション、証明書の交換と検証、セッションキーの生成などが行われます。
tls.ConnectionState
: Go言語のcrypto/tls
パッケージで定義されている構造体で、確立されたTLS接続に関する詳細な情報(使用されたTLSバージョン、暗号スイート、ピア証明書、セッション再開情報など)を保持します。http.Response
構造体:net/http
パッケージで定義されており、HTTPレスポンスを表します。この構造体には、ステータスコード、ヘッダー、ボディなどの情報が含まれます。Response.TLS
フィールドは、このレスポンスがTLS接続を介して受信された場合に、そのTLS接続の状態情報を提供します。http.Transport
構造体:net/http
パッケージのHTTPクライアントが実際にネットワーク接続を確立し、リクエストを送信し、レスポンスを受信するメカニズムを実装する構造体です。接続の再利用(Keep-Alive)、プロキシ、TLS設定などを管理します。persistConn
構造体:http.Transport
の内部で使用される構造体で、永続的なネットワーク接続(Keep-Alive接続など)を管理します。これにより、複数のHTTPリクエストで同じTCP/TLS接続を再利用し、パフォーマンスを向上させることができます。
技術的詳細
このコミットの技術的な詳細は、主に net/http
パッケージ内の Response
、Transport
、および persistConn
構造体における tls.ConnectionState
の取り扱い方法の変更に集約されます。
-
http.Response
構造体のTLS
フィールドのコメント更新:- 変更前:
Response.TLS
フィールドのコメントは、Transport
がTLS対応接続に対してこのフィールドを設定し、それ以外の場合はnil
のままにすることを説明していました。 - 変更後: コメントがより明確になり、
TLS
フィールドが暗号化されていないレスポンスではnil
になること、そしてポインタがレスポンス間で共有されるため、変更すべきではないことが明記されました。これは、後述の最適化と密接に関連しています。
- 変更前:
-
http.Transport
およびpersistConn
におけるtls.ConnectionState
の管理:- 変更前:
Transport
のreadLoop
メソッド内で、TLS接続 (*tls.Conn
) からConnectionState()
を取得し、その値を新しいtls.ConnectionState
構造体にコピーしてresp.TLS
に割り当てていました。 - 変更後:
persistConn
構造体に新しいフィールドtlsState *tls.ConnectionState
が追加されました。これは、永続的な接続が確立された際に、そのTLS接続の状態を保持するためのものです。Transport
のdialConn
メソッド内で、TLS接続が確立された直後にtlsConn.ConnectionState()
を取得し、そのポインタをpconn.tlsState
に保存するようになりました。persistConn
のreadLoop
メソッド内で、レスポンスが生成される際に、resp.TLS
にpc.tlsState
を直接割り当てるようになりました。これにより、tls.ConnectionState
の不要なコピーが回避され、代わりに既存のポインタが共有される形になります。
- 変更前:
この変更により、TLS接続の状態情報が persistConn
オブジェクトに一度だけ保存され、その後の同じ永続接続を介したすべてのレスポンスでそのポインタが再利用されるため、メモリ割り当てとコピーのオーバーヘッドが削減されます。これは、特に多数のHTTPリクエストが同じTLS接続を介して行われる場合に、顕著なパフォーマンス向上をもたらします。
-
テストコードの改善 (
src/pkg/net/http/client_test.go
):- テスト
TestResponseSetsTLSConnectionState
において、res.Body.Close()
がdefer
ステートメントとして追加され、レスポンスボディが確実にクローズされるようになりました。 - エラーメッセージのフォーマットが改善され、
t.Errorf
でgot
とwant
の値がより明確に表示されるようになりました。
- テスト
-
ドキュメントの更新 (
doc/go1.3.txt
):- Go 1.3 の変更点リストに「
net/http
:Request.TLS
を追加 (CL 52660047)」という項目が追加されました。ただし、このコミットの変更内容はResponse.TLS
に焦点を当てているため、この記述は少し混乱を招く可能性があります。おそらく、関連する別の変更(Request.TLS
の追加)が同じリリースサイクルで計画されていたか、または記述ミスである可能性があります。このコミット自体はResponse.TLS
の最適化と修正です。
- Go 1.3 の変更点リストに「
コアとなるコードの変更箇所
src/pkg/net/http/response.go
--- a/src/pkg/net/http/response.go
+++ b/src/pkg/net/http/response.go
@@ -76,10 +76,10 @@ type Response struct {
// This is only populated for Client requests.
Request *Request
- // TLS allows information about the TLS connection on which the
- // response was received. The Transport in this package sets the field
- // for TLS-enabled connections before returning the Response otherwise
- // it leaves the field nil.
+ // TLS contains information about the TLS connection on which the
+ // response was received. It is nil for unencrypted responses.
+ // The pointer is shared between responses and should not be
+ // modified.
TLS *tls.ConnectionState
}
src/pkg/net/http/transport.go
--- a/src/pkg/net/http/transport.go
+++ b/src/pkg/net/http/transport.go
@@ -583,6 +583,8 @@ func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) {
return nil, err
}
}
+ cs := tlsConn.ConnectionState()
+ pconn.tlsState = &cs
pconn.conn = tlsConn
}
@@ -718,6 +720,7 @@ type persistConn struct {
t *Transport
cacheKey connectMethodKey
conn net.Conn
+ tlsState *tls.ConnectionState
closed bool // whether conn has been closed
br *bufio.Reader // from conn
bw *bufio.Writer // to conn
@@ -792,9 +795,8 @@ func (pc *persistConn) readLoop() {
}
}
- if tlsConn, ok := pc.conn.(*tls.Conn); resp != nil && ok {
- resp.TLS = new(tls.ConnectionState)
- *resp.TLS = tlsConn.ConnectionState()
+ if resp != nil {
+ resp.TLS = pc.tlsState
}
hasBody := resp != nil && rc.req.Method != "HEAD" && resp.ContentLength != 0
コアとなるコードの解説
src/pkg/net/http/response.go
の変更
Response.TLS
フィールドのコメント更新:- 変更前は、
Response.TLS
がTransport
によって設定されることのみを述べていました。 - 変更後は、「暗号化されていないレスポンスでは
nil
になる」という条件と、「ポインタがレスポンス間で共有されるため、変更すべきではない」という重要な注意点が追加されました。これは、このコミットで行われた最適化(tls.ConnectionState
のコピーを避け、ポインタを共有する)と整合性を保つためのドキュメント上の修正です。開発者がこのフィールドを安全かつ正しく利用するためのガイドラインを提供します。
- 変更前は、
src/pkg/net/http/transport.go
の変更
-
persistConn
構造体へのtlsState
フィールドの追加:type persistConn struct { ... tlsState *tls.ConnectionState ... }
persistConn
は永続的なネットワーク接続(TCP/TLS接続)を管理する内部構造体です。この新しいフィールドは、確立されたTLS接続のtls.ConnectionState
をポインタとして保持するために導入されました。これにより、接続が確立された時点で一度だけConnectionState
を取得し、それを接続のライフサイクル全体で再利用できるようになります。
-
dialConn
メソッドでのtlsState
の保存:cs := tlsConn.ConnectionState()
pconn.tlsState = &cs
dialConn
は、新しいネットワーク接続(TLS接続を含む)を確立する役割を担います。TLS接続 (tlsConn
) が正常に確立された後、そのConnectionState()
を取得し、そのアドレスをpersistConn
オブジェクト (pconn
) の新しく追加されたtlsState
フィールドに保存します。これにより、TLS接続の状態情報がpersistConn
に紐付けられ、後で簡単に参照できるようになります。
-
readLoop
メソッドでのresp.TLS
へのtlsState
の直接割り当て:- 変更前:
if tlsConn, ok := pc.conn.(*tls.Conn); resp != nil && ok { resp.TLS = new(tls.ConnectionState); *resp.TLS = tlsConn.ConnectionState() }
- 以前は、レスポンスが読み取られるたびに、
tls.Conn
からConnectionState()
を取得し、新しいtls.ConnectionState
オブジェクトを割り当てて、その中に値をコピーしていました。これは、レスポンスごとにメモリ割り当てとデータコピーが発生することを意味します。
- 以前は、レスポンスが読み取られるたびに、
- 変更後:
if resp != nil { resp.TLS = pc.tlsState }
readLoop
は、永続接続を介して受信したレスポンスを処理します。この変更により、レスポンスのTLS
フィールドに、persistConn
に保存されているtlsState
ポインタが直接割り当てられるようになりました。- この変更がこのコミットの「最適化」の核心です。
tls.ConnectionState
の不要なコピーが排除され、代わりに既存のポインタが共有されるため、特に多数のレスポンスが同じTLS接続を介して処理される場合のパフォーマンスが向上します。メモリ割り当ての削減は、ガベージコレクションの負荷軽減にも寄与します。
- 変更前:
これらの変更は、Goの net/http
クライアントがTLS接続をより効率的に管理し、Response.TLS
フィールドの利用方法をより明確にするための重要な改善です。
関連リンク
- Go CL (Code Review) リンク: https://golang.org/cl/71740043
参考にした情報源リンク
- Go言語の公式ドキュメント (
net/http
パッケージ,crypto/tls
パッケージ) - TLS (Transport Layer Security) の一般的な概念に関する情報
- Go言語のコミット履歴と差分情報