メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

#77441active

net/http: add Server.HandleRequest

新規提案

要約

AIによる要約であり、誤りを含む場合があります。

概要

net/http.Serverが内部で行っているリクエスト処理(OPTIONS *への応答、DefaultServeMuxへのフォールバック、パニックのリカバリ)を、外部から任意のResponseWriter/Requestペアに対して適用できるようにする新メソッドを追加する提案です。

ステータス変更

(新規)active
2026年2月4日に提案されたのち、命名を巡る議論を経て2026年3月13日にaclements氏(proposal reviewグループ)によってactive columnに追加され、今後は毎週のproposal reviewミーティングで審査されることになりました。まだ承認(accept)ではなく、審査対象として正式に取り上げられた段階です。

技術的背景

現状の問題点

net/http.Serverがリクエストをハンドラに渡す前には、以下のような処理が暗黙的に行われています。

  • Server.DisableGeneralOptionsHandlerがfalseならOPTIONS *リクエストに200 OK・Content-Length: 0で応答
  • Server.Handlernilならhttp.DefaultServeMuxを使用
  • ハンドラ内のパニックをリカバリし、ErrAbortHandler以外はログ出力
    これらのロジックはnet/httpパッケージ内部にしか存在せず、net/http.Serverのリスナー機構(TCP接続のAcceptとHTTP/1.1・HTTP/2のパース処理)を経由しない限り利用できません。例えば、HTTP/3実装(quic-goなど)が独自のServer.ServePacketConnのようなメソッドでQUIC上のリクエストを処理する場合、この共通ミドルウェアロジックを自前で再実装するか、コードを重複させる必要がありました。

提案された解決策

Serverに新しいメソッドを追加し、任意のResponseWriter/*Requestをあたかもサーバーが受信したかのように処理できるようにします。当初はServeHTTPという名前で提案されましたが、議論を経て最終的に以下のシグネチャに落ち着きました。

package http
// HandleRequest handles a request as if it was received by the server.
//
//   - If Server.DisableGeneralOptionsHandler is not true,
//     it responds to "OPTIONS *" requests with 200 OK and Content-Length: 0.
//   - If Server.Handler is nil, it uses [http.DefaultServeMux].
//   - Panics in the handler are recovered.
//     If the panic is not [ErrAbortHandler], the panic is logged.
func (*Server) HandleRequest(ResponseWriter, *Request)

これによって何ができるようになるか

主な想定用途は、HTTP/1.1・HTTP/2以外のプロトコル(HTTP/3等)でサーバーを実装する開発者が、net/http.Serverのミドルウェア相当の挙動を再利用できるようにすることです。また、テストコードでhttp.ReadRequest等から得たリクエストに対して、実サーバーと同等の前処理を再現したい場合にも利用できます。

コード例

// Before: HTTP/3実装者は独自にOPTIONS処理・パニックリカバリ等を再実装する必要があった
func (s *H3Server) ServePacketConn(pc net.PacketConn) {
    for {
        req := readQUICRequest(pc)
        defer func() {
            if r := recover(); r != nil && r != http.ErrAbortHandler {
                log.Println(r) // 独自に再実装
            }
        }()
        if req.Method == "OPTIONS" && req.RequestURI == "*" {
            // 独自にOPTIONS応答を再実装
        }
        s.httpServer.Handler.ServeHTTP(w, req)
    }
}
// After: net/http.Serverの標準ロジックをそのまま再利用
func (s *H3Server) ServePacketConn(pc net.PacketConn) {
    for {
        req := readQUICRequest(pc)
        s.httpServer.HandleRequest(w, req) // OPTIONS処理・DefaultServeMux・panicリカバリを内包
    }
}

議論のハイライト

  • 当初案のServeHTTPは、http.Serve/http.ServeTLSと紛らわしく「サーバーを起動する」機能だと誤解されるとの指摘(@apparentlymart)があり、命名が主要な論点となった。
  • 代替案としてRespondHandlerを返すServerHandler() Handler、ミドルウェア的に振る舞うPreprocess(h Handler) Handlerなどが検討されたが、いずれも既存のServer.HandlerフィールドやServer.Serveとの混同を招くとして見送られた。
  • 最終的にHandleRequestが採用された理由として、(1) 既存のServerメソッド名と紛らわしくない、(2) 動詞を含みHandlerフィールドとの違いが明確、(3) http.Handlerインターフェースを実装しないため誤ってサーバー同士を接続してしまうリスクが低い、という3点が挙げられた(@bradfitz、@neild周辺の議論)。
  • 動機として、CSRF対策(cross-origin protection、#73626)のように今後net/http.Serverに追加されるミドルウェア的機能が増えるほど、この共通処理を独立して呼び出せる手段の必要性が高まるという背景が示された。
  • 関連提案として#36673(ServeConnでnet.Connを処理)や#70292(パニックリカバリの無効化オプション)などが挙げられ、net/http.Serverの内部処理を外部から利用可能にする一連の流れの中に位置づけられる。

関連リンク