Maison  >  Article  >  développement back-end  >  `json.NewDecoder().Decode()` ignore-t-il les délais de contexte dans les requêtes HTTP Go ?

`json.NewDecoder().Decode()` ignore-t-il les délais de contexte dans les requêtes HTTP Go ?

Mary-Kate Olsen
Mary-Kate Olsenoriginal
2024-10-29 04:41:29674parcourir

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

Go json.NewDecoder().Decode() ne tient pas compte de la date limite de contexte ?

Problème :

Dans un programme Go avec une date limite contextuelle, la lecture d'un corps de réponse à l'aide de ioutil.ReadAll() entraîne une erreur de dépassement du délai attendu. Cependant, l'utilisation de json.NewDecoder(resp.Body).Decode() renvoie nil à la place.

Exemple de code :

<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>

Réponse :

Dans le package net/http, des tampons peuvent être utilisés pour traiter les requêtes. Par conséquent, le corps de la réponse entrante peut être partiellement ou entièrement lu et mis en mémoire tampon avant que vous tentiez de le lire. Par conséquent, un contexte expirant ne peut pas vous empêcher de terminer la lecture du corps. C'est précisément ce qui se produit dans cette situation.

Pour mieux comprendre, modifions le code pour créer un serveur HTTP de test qui reporte intentionnellement la réponse :

<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>

L'exemple modifié envoie un JSON objet similaire à la réponse de ip.jsontest.com, mais ne transmet que les 10 premiers octets du corps avant de le vider. Il suspend ensuite la transmission pendant 6 secondes, donnant au client la possibilité d'expirer.

Lorsque nous exécutons readDoesntFail(), nous observons le comportement suivant :

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

Dans ce scénario, json.Decoder.Decode() tente de lire à partir de la connexion car les données n'ont pas encore été mises en mémoire tampon. Une fois le contexte expiré, une lecture plus approfondie de la connexion entraîne une erreur de dépassement du délai. Cependant, dans l'exemple d'origine, json.Decoder.Decode() lit des données déjà mises en mémoire tampon, ce qui rend le contexte expiré non pertinent.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn