Carpe Diem

備忘録

Goのnet/httpのtimeoutについて

概要

タイムアウトと一口に言ってもサーバ・クライアント、そして各フェーズによって細かく設定があります。
今回はGoのnet/httpのtimeoutについて1つ1つ説明していきます。

環境

Server

全体図

サーバ系timeoutと各フェーズは以下の関係になっています。

f:id:quoll00:20191001141248p:plain

各項目

項目 役割
http.Server.ReadHeaderTimeout request headersを読む際のtimeout
http.Server.ReadTimeout request headersやrequest bodyを読む際のtimeout。
SetReadDeadline()を呼び出してセットする
http.Server.WriteTimeout request bodyの読み込み〜responseの書き込みまで。
SetWriteDeadline()を呼び出してセットする
http.Server.IdleTimeout keep-alivesが有効な場合に次のリクエストが来るまで待つIdle時間

http.Server.ReadTimeouthttp.Server.WriteTimeoutに点線があるのは、TLS接続の場合Accept直後にSetReadDeadline()SetWriteDeadline()を呼ぶためです。

references

一通りセットすると以下のようになります。

srv := &http.Server{
    ReadTimeout:  5 * time.Second,
    WriteTimeout: 10 * time.Second,
    IdleTimeout:  120 * time.Second,
    Handler:      serveMux,
}
log.Println(srv.ListenAndServe())

Client

全体図

クライアント系timeoutと各フェーズは以下の関係になっています。

f:id:quoll00:20191001141258p:plain

各項目

項目 役割
http.Client.Timeout http clientのtimeout。
http.DefaultClientは未設定のため無限
net.Dialer.Timeout TCPコネクションの確立時のtimeout
http.Transport.TLSHandshakeTimeout TLS handshakeにかかる時間を制限
http.Transport.ResponseHeaderTimeout Response headersの読み込みtimeout
http.Transport.IdleConnTimeout コネクション閉じる前のIdle時間

一通りセットすると以下のようになります。

c := &http.Client{
    Transport: &http.Transport{
        DialContext: (&net.Dialer{
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
        }).DialContext,
        TLSHandshakeTimeout:   10 * time.Second,
        ResponseHeaderTimeout: 10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    },
    Timeout: 60 * time.Second,
}

references

その他

requestにcontext.WithTimeout付けた場合は?

以下のようにcontextによるTimeoutを設定したケースはどうなるか、です。

ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
req, err := http.NewRequest("GET", "http://httpbin.org/range/2048?duration=8&chunk_size=256", nil)
if err != nil {
    log.Fatal(err)
}
req = req.WithContext(ctx)

WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).

ref: context package - context - pkg.go.dev

とあるようにその時点でDeadlineを設定します。
なのでWithTimeout()を呼んでからレスポンスが返ってくるまでの範囲になります。

ExpectContinueTimeoutは?

http.Transport.ExpectContinueTimeoutという項目があります。

これはExpect: 100-continueヘッダーを付けてpreflightリクエストを送った際にレスポンスが返ってくるまでのtimeoutです。

f:id:quoll00:20191001193007p:plain

ref: Preflight (Mixin) - Level 3 REST

ゼロ値の場合はタイムアウトがないことを意味し、サーバーの承認を待たずにすぐにbodyが送信されます。

  • Expect: 100-continueヘッダーを付けている
  • ExpectContinueTimeoutを設定している

の2つの条件を場合に有効になります。

ソース