#77441likely_accept
net/http: add Server.HandleRequest
ステータス変更: active likely_accept
要約
AIによる要約であり、誤りを含む場合があります。
概要
net/http.Server に HandleRequest メソッドを追加する提案です。ネットワークから受信したのではない *http.Request にも、Server が内部で行っている前処理を適用できるようにします。前処理とは、OPTIONS * への応答、ハンドラの選択、panic の回復とログ出力のことです。主な利用者として、HTTP/3 などのプロトコル実装者と、サーバー全体の挙動を確かめたいテストを想定しています。
ステータス変更
active → likely_accept
2026年9月から10月にかけて、Proposal Review Meeting で3回議論されました。
- 反対論: 「利用者がごく少ない(プロトコル実装者は世界で10人未満程度)」という意見がありました。
- 必要性の根拠: この処理を現在担っている内部関数が「linkname hall of shame」に載っています。
//go:linknameで非公開関数を外部から無理やり参照している実例を集めたリストのことです。Go チーム外にも実際の需要があると判断されました。 - 名前: 最後まで議論が続きました。
Respondは短すぎ、かえって気軽に使われやすいとして退けられました。他に説得力のある候補が出なかったため、パッケージ全体で「server が request を handle する」という表現を使っていることとの一貫性からHandleRequestに決まりました。 - panic を error として返す仕様: 「複雑すぎる」という意見がありました。しかし通常のリクエスト処理経路でも同じ処理が必要なので、複雑さを理由に外す根拠にはならないとされました。
技術的背景
現状の問題点
net/http.Server がリクエストを処理する流れは次のとおりです。
- HTTP/1、HTTP/2、(将来の)HTTP/3 の各実装が、ネットワーク上のデータから
*Requestを組み立てる - サーバー内部のハンドラが前処理をする
OPTIONS *に 200 OK を返す(DisableGeneralOptionsHandlerで無効化できる)Server.Handlerが nil ならDefaultServeMuxを使う- panic を回復し、
ErrAbortHandler以外ならログに出す
- ユーザーのミドルウェアと最終的なハンドラが処理する
2 の処理は非公開で、ネットワークから読んだリクエストでなければ通せません。そのため外部の HTTP/3 実装(quic-go など)は、同じロジックを自前で書き直す必要があります。HTTP/2 も標準ライブラリに取り込まれる前は、この処理を重複して持っていました。
さらに、今後この前処理に機能が増える可能性があります。例として、サーバー全体にCrossOriginProtectionを既定で適用する設定が検討されています(#73626 での議論)。機能が増えるほど、重複実装の問題は大きくなります。
提案された解決策
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.
//
// HandleRequest returns a non-nil error if the handler panics.
// It returns [ErrAbortHandler] if the handler panicked with that value.
func (*Server) HandleRequest(ResponseWriter, *Request) error
これによって何ができるようになるか
- 独自プロトコルのサーバー実装: HTTP/3 サーバーが
ServePacketConnなどでリクエストを受け取ったあと、net/http.Serverと同じ前処理を重複実装せずに適用できます。今後Serverに追加される機能(CSRF 対策など)にも自動的に追随できます。 - テスト: 個別のハンドラ関数だけでなく、「
Server全体がこのリクエストをどう処理するか」を、実際のソケットを使わずに検証できます。 - panic の検出: 戻り値の error を見れば、ハンドラが panic したかどうかを呼び出し側で判断できます。たとえばストリームを中断(abort)する、といった対応が可能です。
コード例
// Before: 独自プロトコル実装で、Server の内部処理を自前で再現する必要がある
func (s *H3Server) handle(w http.ResponseWriter, r *http.Request) {
if r.Method == "OPTIONS" && r.URL.Path == "*" && !s.srv.DisableGeneralOptionsHandler {
w.Header().Set("Content-Length", "0")
w.WriteHeader(http.StatusOK)
return
}
h := s.srv.Handler
if h == nil {
h = http.DefaultServeMux
}
defer func() {
if p := recover(); p != nil && p != http.ErrAbortHandler {
log.Printf("http: panic serving: %v", p)
}
}()
h.ServeHTTP(w, r) // 将来 Server に追加される機能には追随できない
}
// After: Server と同じ処理をそのまま適用できる
func (s *H3Server) handle(w http.ResponseWriter, r *http.Request) {
if err := s.srv.HandleRequest(w, r); err != nil {
// ハンドラが panic した。ErrAbortHandler の場合も含む
s.abortStream(r)
}
}
議論のハイライト
- 名前の変遷: 当初は
ServeHTTPという名前でした。しかしServer.Serveのような「長時間動くサーバー起動」と紛らわしく、しかもhttp.Handlerインタフェースを満たしてしまうため見直されました。ServerHandler() Handlerも「srv.Handlerとの違いが分かりにくい」と指摘され(@bradfitz)、HandleRequestになりました。 - あえて
Handlerを実装しない:HandleRequestは error を返すのでhttp.Handlerを満たしません。これは意図的な設計です。あるServerを別のServerのハンドラとして安易に入れ子にする使い方を防ぎます。 - ミドルウェア形式の案は却下:
Preprocess(h Handler) Handlerという案も出ました。しかしこの提案の目的は「サーバー自身のハンドラで、受信したかのように処理する」ことです。任意のハンドラをラップする機能とは目的が異なるため退けられました(@neild)。 - error の戻り値を追加: 議論の途中で、panic を検知できるように error の戻り値が追加されました。panic の値そのものは返しませんが、将来
HandlerPanicError{PanicValue any}のような型で拡張する余地を残しています。 - 残っている懸念:
HandleRequestという名前が魅力的すぎて、本番コードで不必要に使われることを心配する声があります(@earthboundkid)。httpgutsのような別パッケージに置く案も出ましたが、最終的にServerのメソッドとして進めることになりました。