2014-11-06 14 views
10

Sto creando un server Go test HTTP e sto inviando un'intestazione di risposta di Transfer-Encoding: chunked in modo da poter inviare continuamente nuovi dati mentre lo recupero. Questo server dovrebbe scrivere un chunk su questo server ogni secondo. Il cliente dovrebbe essere in grado di riceverli su richiesta.Invia una risposta HTTP Chunked da un server Go

Sfortunatamente, il client (arricciatura in questo caso), riceve tutti i pezzi alla fine della durata, 5 secondi, invece di ricevere un pezzo ogni secondo. Inoltre, Go sembra inviare il Content-Length per me. Voglio inviare il Content-Length, alla fine, e voglio che il valore del colpo di testa di essere 0.

Ecco il codice del server:

package main 

import (
    "fmt" 
    "io" 
    "log" 
    "net/http" 
    "time" 
) 

func main() { 
    http.HandleFunc("/test", HandlePost); 
    log.Fatal(http.ListenAndServe(":8080", nil)) 
} 

func HandlePost(w http.ResponseWriter, r *http.Request) { 
    w.Header().Set("Connection", "Keep-Alive") 
    w.Header().Set("Transfer-Encoding", "chunked") 
    w.Header().Set("X-Content-Type-Options", "nosniff") 

    ticker := time.NewTicker(time.Second) 
    go func() { 
     for t := range ticker.C { 
      io.WriteString(w, "Chunk") 
      fmt.Println("Tick at", t) 
     } 
    }() 
    time.Sleep(time.Second * 5) 
    ticker.Stop() 
    fmt.Println("Finished: should return Content-Length: 0 here") 
    w.Header().Set("Content-Length", "0") 
} 
+0

Andrew Gerrand del team Go postato qualcosa di simile l'altro giorno ... cercherò di trovarlo per voi. –

+0

Ci scusiamo, non è esattamente la stessa cosa: dal client alla fine della richiesta. Tuttavia, potresti imparare qualcosa da questo: https://github.com/nf/dl/blob/master/dl.go –

risposta

15

Il trucco sembra essere che è sufficiente chiama Flusher.Flush() dopo aver scritto ogni chunk. Si noti inoltre che l'intestazione "Transfer-Encoding" sarà gestita dallo scrittore implicitamente, quindi non è necessario impostarla.

func main() { 
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 
    flusher, ok := w.(http.Flusher) 
    if !ok { 
     panic("expected http.ResponseWriter to be an http.Flusher") 
    } 
    w.Header().Set("X-Content-Type-Options", "nosniff") 
    for i := 1; i <= 10; i++ { 
     fmt.Fprintf(w, "Chunk #%d\n", i) 
     flusher.Flush() // Trigger "chunked" encoding and send a chunk... 
     time.Sleep(500 * time.Millisecond) 
    } 
    }) 

    log.Print("Listening on localhost:8080") 
    log.Fatal(http.ListenAndServe(":8080", nil)) 
} 

È possibile verificare usando telnet:

$ telnet localhost 8080 
Trying ::1... 
Connected to localhost. 
Escape character is '^]'. 
GET/HTTP/1.1 

HTTP/1.1 200 OK 
Date: Tue, 02 Jun 2015 18:16:38 GMT 
Content-Type: text/plain; charset=utf-8 
Transfer-Encoding: chunked 

9 
Chunk #1 

9 
Chunk #2 

... 

Potrebbe essere necessario fare qualche ricerca per verificare che http.ResponseWriters supporta l'accesso simultaneo per l'utilizzo da parte di più goroutines.

Inoltre, vedere questa domanda per ulteriori informazioni su "X-Content-Type-Options" header.

+1

La tua versione flusher.Flush() ha funzionato per me, ma solo dopo aver aggiunto l'intestazione nosniff dal Esempio di OP. La tua versione funziona bene in telnet e wget, ma suppongo che i browser stiano cercando di essere più intelligenti sul buffering del contenuto, quindi a meno che tu non specifichi nosniff, Chrome e Firefox sembrano rifiutarsi di elaborare i dati fino a quando non arrivano tutti. – johnbr

+4

Per chi si chiede, vedere [questa risposta] (http://stackoverflow.com/questions/18337630/what-is-x-content-type-options-nosniff). Fondamentalmente ciò che accade è che alcuni browser tenteranno di dedurre il Content-Type della pagina, anche * dopo * il server dice esplicitamente al client che è, ad esempio, un text/html. Usando 'X-Content-Type-Options: nosniff' disabiliterà questa funzione da detti browser. – Howl

Problemi correlati