😺
http ServerでのBaseContextを調査
はじめに
この記事はGo 言語 Advent Calendar 202323日目の記事です。
今回はgoのnet/http ServerのBaseContextというフィールドの話をします。
BaseContextとは
-
net/http
のServer
にはBaseContext
というフィールドが存在します。 - この BaseContext のデフォルトは context.Background() です。
// BaseContext optionally specifies a function that returns
// the base context for incoming requests on this server.
// The provided Listener is the specific Listener that's
// about to start accepting requests.
// If BaseContext is nil, the default is context.Background().
// If non-nil, it must return a non-nil context.
BaseContext func(net.Listener) context.Context
このフィールドに関数を登録すると、その関数によって提供される context.Context が返されます。例えば、特定の時間でタイムアウトさせたい場合は、ここで指定できます。
func(net.Listener) context.Context {
ctx, _ := context.WithTimeout(ctx, 100*time.Millisecond)
return ctx
}
Redditのこのスレッドで言及されていましたが、サーバーを立てる際に signal.NotifyContext を使用することがあります。これを BaseContext に渡すことで、シグナルを受け取った際にスムーズにキャンセルが伝播するのではないかと考え、試してみました。
実験
package main
import (
"context"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
)
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
srv := &http.Server{
Addr: ":8080",
BaseContext: func(_ net.Listener) context.Context {
return ctx
},
}
http.HandleFunc("/", func(w http.ResponseWriter, request *http.Request) {
select {
case <-request.Context().Done():
fmt.Fprintf(w, "cancel")
}
fmt.Fprintf(w, "Hello, World!")
})
go func() {
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Print(err)
}
}()
<-ctx.Done()
if err := srv.Shutdown(context.Background()); err != nil {
log.Print(err)
}
}
go build
別のコンソールからアクセスします。
curl http://localhost:8080/
ps
コマンドでプロセスIDを見つけ、SIGINTを送信します。
ps aux | grep xxxxx
kill -SIGINT id
すると、curl を実行しているコンソールに以下が表示されます。
cancelHello, World!
これにより、キャンセルが適切に伝播していることが確認できます。
まとめ
- net/http Server BaseContextフィールドを使用するとデフォルトのコンテキストを指定できる
- signal.NotifyContextを使用するとシグナルからのキャンセルをハンドラーに伝播させるコトが出来る
- 使いようによっては他にもつかえるので 便利な機能かも知れません
Discussion