`json.NewDecoder().Decode()` は Go HTTP リクエストのコンテキスト期限を無視しますか?

Mary-Kate Olsen
リリース: 2024-10-29 04:41:29
オリジナル
672 人が閲覧しました

 Does `json.NewDecoder().Decode()` Ignore Context Deadlines in Go HTTP Requests?

Go json.NewDecoder().Decode() はコンテキストの期限を無視しますか?

問題:

Go プログラム内コンテキスト期限を指定した場合、ioutil.ReadAll() を使用して応答本文を読み取ると、予想期限超過エラーが発生します。ただし、json.NewDecoder(resp.Body).Decode() を使用すると、代わりに nil が返されます。

コード例:

<code class="go">package main

import (
    "context"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "time"
)

var url string = "http://ip.jsontest.com/"

func main() {
    readDoesntFail()
    readFails()
}

type IpResponse struct {
    Ip string
}

func readDoesntFail() {
    ctx, _ := context.WithTimeout(context.Background(), time.Second*5)

    req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
    if err != nil {
        panic(err)
    }
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }

    ipResponse := new(IpResponse)
    time.Sleep(time.Second * 6)
    fmt.Println("before reading response body, context error is:", ctx.Err())
    err = json.NewDecoder(resp.Body).Decode(ipResponse)
    if err != nil {
        panic(err)
    }
    fmt.Println("Expected panic but there was none")
}

func readFails() {
    ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
    if err != nil {
        panic(err)
    }
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }

    time.Sleep(time.Second * 6)
    fmt.Println("before reading response body, context error is:", ctx.Err())
    _, err = ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("received expected error", err)
    }
}</code>
ログイン後にコピー

答え:

net/http パッケージでは、リクエストの処理にバッファが使用される場合があります。したがって、受信応答本文は、読み取ろうとする前に、部分的または全体的に読み取られてバッファされる可能性があります。その結果、コンテキストの有効期限が切れても、本文の読み取りを完了することが妨げられることはありません。これはまさにこの状況で発生することです。

よりよく理解するために、コードを変更して、応答を意図的に延期するテスト HTTP サーバーを作成しましょう。

<code class="go">ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    s := []byte(`{"ip":"12.34.56.78"}`)
    w.Write(s[:10])
    if f, ok := w.(http.Flusher); ok {
        f.Flush()
    }
    time.Sleep(time.Second * 6)
    w.Write(s[10:])
}))
defer ts.Close()
url = ts.URL

readDoesntFail()
readFails()</code>
ログイン後にコピー

変更された例では、JSON が送信されます。このオブジェクトは ip.jsontest.com の応答に似ていますが、フラッシュする前に本文の最初の 10 バイトのみを送信します。次に、送信を 6 秒間一時停止し、クライアントにタイムアウトの機会を与えます。

readDoesntFail() を実行すると、次の動作が観察されます。

before reading response body, context error is: context deadline exceeded
panic: Get "http://127.0.0.1:38230": context deadline exceeded

goroutine 1 [running]:
main.readDoesntFail()
    /tmp/sandbox721114198/prog.go:46 +0x2b4
main.main()
    /tmp/sandbox721114198/prog.go:28 +0x93
ログイン後にコピー

このシナリオでは、データがまだバッファリングされていないため、 json.Decoder.Decode() は接続からの読み取りを試行します。コンテキストの有効期限が切れると、接続からさらに読み取ると期限超過エラーが発生します。ただし、元の例では、 json.Decoder.Decode() はすでにバッファリングされたデータを読み取っており、期限切れのコンテキストは無関係になります。

以上が`json.NewDecoder().Decode()` は Go HTTP リクエストのコンテキスト期限を無視しますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート