Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 10091] ファイルの概要

このコミットは、Go言語の標準ライブラリ net/http/httputil パッケージ内の ReverseProxy における、HTTP Connection ヘッダーの処理に関する以前の変更(CL 5302057 / dac58d9c9e4a)を元に戻すものです。具体的には、リバースプロキシがバックエンドへのリクエストを転送する際に Connection ヘッダーを削除するロジックと、レスポンスヘッダーをコピーするためのヘルパー関数 copyHeader の導入を取り消しています。この変更は、別の「rune change」のためのクリーンなベースラインを確保するために行われました。

コミット

  • コミットハッシュ: 5abb29d1b8b741dcadb22720ced7e5e8a131633f
  • Author: Andrew Gerrand adg@golang.org
  • Date: Wed Oct 26 14:16:34 2011 +0900

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/5abb29d1b8b741dcadb22720ced7e5e8a131633f

元コミット内容

このコミットが「undo」している元のコミット(CL 5302057 / dac58d9c9e4a)は、以下の目的を持っていました。

  • http: remove Connection header in ReverseProxy: リバースプロキシがバックエンドサーバーにリクエストを転送する際に、クライアントから受け取った Connection ヘッダーを削除すること。これは、リバースプロキシとバックエンド間の接続を永続化させたい場合(Keep-Aliveなど)に、クライアントからの Connection: close ヘッダーがバックエンドに伝播するのを防ぐためです。
  • Fixes #2342: GoのIssue #2342 を修正することを意図していました。このIssueは、リバースプロキシが Connection: close ヘッダーを適切に処理しないことによって発生する問題に関連していると考えられます。
  • copyHeader ヘルパー関数の導入: ヘッダーをコピーするための汎用的なヘルパー関数 copyHeader が導入されました。

変更の背景

このコミットの背景は、コミットメッセージに明確に示されています。

need a clean base from weekly.2011-10-25 for rune change

これは、Go言語の内部で進行中の「rune change」(おそらくUnicode文字の扱いに関する変更)のために、weekly.2011-10-25 という特定の時点からクリーンなコードベースが必要であったことを意味します。以前の Connection ヘッダーに関する変更が、この「rune change」と競合したり、複雑さを増したりする可能性があったため、一時的にその変更を元に戻すことが決定されたと考えられます。これは、大規模なプロジェクトにおいて、特定の機能開発やリファクタリングを進める際に、他の独立した変更が干渉しないように、一時的にコードベースを特定の状態に戻す一般的なプラクティスです。

前提知識の解説

1. HTTP Connection ヘッダー

HTTP Connection ヘッダーは、現在のトランザクションが完了した後に、送信側と受信側の間で接続をどのように制御するかを決定するために使用されます。主な値は以下の通りです。

  • Connection: close: 現在のトランザクションが完了したら、接続を閉じることを示します。
  • Connection: Keep-Alive: 接続を維持し、後続のリクエストにも再利用できることを示します。これにより、TCP接続の確立と終了のオーバーヘッドを削減し、パフォーマンスを向上させることができます。

リバースプロキシの文脈では、クライアントが Connection: close を送ってきたとしても、プロキシとバックエンドサーバー間の接続は永続化させたい場合があります。これは、プロキシがバックエンドへの接続プールを管理し、効率的に再利用するためです。もしクライアントの Connection: close がバックエンドにそのまま転送されると、バックエンドはリクエストごとに接続を閉じ、非効率的になる可能性があります。

2. リバースプロキシ (Goの net/http/httputil.ReverseProxy)

リバースプロキシは、クライアントからのリクエストを受け取り、それを一つ以上のバックエンドサーバーに転送し、バックエンドからのレスポンスをクライアントに返すサーバーです。主な役割は以下の通りです。

  • 負荷分散: 複数のバックエンドサーバーにリクエストを分散させ、負荷を均等にする。
  • セキュリティ: バックエンドサーバーを直接インターネットに公開せず、セキュリティ層を提供する。
  • SSLオフロード: SSL/TLS終端をプロキシで行い、バックエンドサーバーの負荷を軽減する。
  • キャッシング: 静的コンテンツをキャッシュし、レスポンスタイムを短縮する。
  • ヘッダー操作: リクエストやレスポンスのヘッダーを変更する。

Go言語の net/http/httputil.ReverseProxy は、このようなリバースプロキシを簡単に構築するための機能を提供します。ReverseProxyhttp.Handler インターフェースを実装しており、ServeHTTP メソッドを通じてリクエストの転送とレスポンスの処理を行います。

3. GoのCL (Change List)

Goコミュニティでは、コードの変更は「Change List (CL)」として管理されます。これは、Perforceなどのバージョン管理システムで使われる用語に由来します。各CLは、特定の目的を持った一連の変更(コミット)をまとめたもので、レビュープロセスを経てGoのリポジトリにマージされます。https://golang.org/cl/ のようなURLは、Goのコードレビューシステム(Gerrit)上の特定のCLを指します。

4. Goの net/http パッケージ

net/http パッケージは、Go言語でHTTPクライアントとサーバーを実装するための基本的な機能を提供します。これには、HTTPリクエスト、レスポンス、ヘッダー、クッキーなどの構造体や、HTTPサーバーの起動、クライアントからのリクエストの処理、レスポンスの送信などの機能が含まれます。httputil.ReverseProxy はこの net/http パッケージの上に構築されています。

技術的詳細

このコミットは、以前の変更を元に戻すことで、ReverseProxy のヘッダー処理の挙動を元に戻しています。

元のCL 5302057は、リバースプロキシがバックエンドにリクエストを転送する際に、クライアントから受け取った Connection ヘッダーを削除することを目的としていました。これは、プロキシとバックエンド間の接続を永続化させたい場合に重要です。もしクライアントが Connection: close を送ってきたとしても、プロキシはバックエンドとの接続を閉じたくないため、このヘッダーを削除する必要がありました。

この「undo」コミットでは、その Connection ヘッダーを削除するロジックが再び削除されます。これにより、クライアントから受け取った Connection ヘッダーが、そのままバックエンドに転送される可能性が再び生じます。これは、リバースプロキシの「永続接続」の意図に反する挙動に戻ることを意味します。

また、レスポンスヘッダーをコピーするために導入された copyHeader ヘルパー関数も削除され、そのロジックは ServeHTTP メソッド内でインライン化されています。これは、コードの重複を招く可能性がありますが、このコミットの主な目的は「rune change」のためのクリーンなベースラインを確保することであり、コードの美しさや重複の排除は二の次であったと考えられます。

この変更は一時的なものであり、後続のコミットで再び Connection ヘッダーの適切な処理が導入されるか、あるいは別の方法で問題が解決されることが期待されます。

コアとなるコードの変更箇所

このコミットによって変更されたファイルは以下の2つです。

  1. src/pkg/http/reverseproxy.go
  2. src/pkg/http/reverseproxy_test.go

src/pkg/http/reverseproxy.go の変更点

  • copyHeader 関数の削除:

    -func copyHeader(dst, src Header) {
    -	for k, vv := range src {
    -		for _, v := range vv {
    -			dst.Add(k, v)
    -		}
    -	}
    -}
    

    ヘッダーをコピーするためのヘルパー関数 copyHeader が削除されました。

  • Connection ヘッダー削除ロジックの削除:

    -	// Remove the connection header to the backend.  We want a
    -	// persistent connection, regardless of what the client sent
    -	// to us.  This is modifying the same underlying map from req
    -	// (shallow copied above) so we only copy it if necessary.
    -	if outreq.Header.Get("Connection") != "" {
    -		outreq.Header = make(Header)
    -		copyHeader(outreq.Header, req.Header)
    -		outreq.Header.Del("Connection")
    -	}
    

    バックエンドへのリクエスト (outreq) から Connection ヘッダーを削除するロジックが削除されました。これにより、クライアントからの Connection ヘッダーがバックエンドに転送される可能性があります。

  • レスポンスヘッダーコピーのインライン化:

    -	copyHeader(rw.Header(), res.Header)
    +	hdr := rw.Header()
    +	for k, vv := range res.Header {
    +		for _, v := range vv {
    +			hdr.Add(k, v)
    +		}
    +	}
    

    res.Header から rw.Header() へのヘッダーコピーが、削除された copyHeader 関数を呼び出す代わりに、手動のループでインライン化されました。

src/pkg/http/reverseproxy_test.go の変更点

  • Connection ヘッダー関連テストの削除:
    -		if c := r.Header.Get("Connection"); c != "" {
    -			t.Errorf("handler got Connection header value %q", c)
    -		}
    
    -	getReq.Header.Set("Connection", "close")
    -	getReq.Close = true
    
    ReverseProxyConnection ヘッダーを適切に削除しているかを確認するテストケースが削除されました。これは、ヘッダー削除ロジックが削除されたことと整合しています。また、テストリクエストに Connection: close ヘッダーを設定する部分も削除されています。

コアとなるコードの解説

このコミットの核心は、以前のコミットで導入された Connection ヘッダーの明示的な削除と、ヘッダーコピーのヘルパー関数を元に戻すことです。

reverseproxy.go において、ServeHTTP メソッドはリバースプロキシの主要なロジックを含んでいます。

  1. copyHeader 関数の削除とインライン化: 元のコミットでは、ヘッダーをコピーするための再利用可能な関数 copyHeader が導入されました。このコミットでは、その関数が削除され、そのロジックが ServeHTTP 内で直接記述される形に戻されました。これは、コードの重複を招きますが、この「undo」の目的が特定の変更を元に戻すことであったため、許容されたと考えられます。

  2. Connection ヘッダー削除ロジックの削除: 最も重要な変更は、バックエンドへのリクエスト (outreq) から Connection ヘッダーを削除する以下のブロックが削除されたことです。

    // Remove the connection header to the backend.  We want a
    // persistent connection, regardless of what the client sent
    // to us.  This is modifying the same underlying map from req
    // (shallow copied above) so we only copy it if necessary.
    if outreq.Header.Get("Connection") != "" {
        outreq.Header = make(Header)
        copyHeader(outreq.Header, req.Header)
        outreq.Header.Del("Connection")
    }
    

    このロジックは、クライアントが Connection: close を送ってきた場合でも、リバースプロキシとバックエンド間の接続を永続化させるために非常に重要でした。このブロックが削除されたことで、クライアントからの Connection ヘッダーがそのままバックエンドに転送される可能性があり、バックエンドが不要な接続終了を行う原因となる可能性があります。

reverseproxy_test.go におけるテストの削除は、上記のロジックの削除と直接関連しています。ヘッダー削除ロジックがなくなったため、その挙動をテストする意味がなくなったためです。

全体として、このコミットは機能的な後退を意味しますが、これは「rune change」というより大きな目的のための、一時的な措置であったと理解できます。

関連リンク

参考にした情報源リンク