2014-11-06 5 views
10

Ich erstelle einen Test Go HTTP-Server, und ich sende eine Antwort-Header von Transfer-Encoding: Chunked, damit ich kontinuierlich neue Daten senden kann, wie ich es abrufen. Dieser Server sollte jede Sekunde einen Chunk auf diesen Server schreiben. Der Kunde sollte sie auf Anfrage erhalten können.Senden Sie eine Chunked HTTP-Antwort von einem Go-Server

Leider empfängt der Client (curl in diesem Fall) alle Chunks am Ende der Dauer, 5 Sekunden, anstatt einen Chunk alle eine Sekunde zu empfangen. Außerdem scheint Go die Inhaltslänge für mich zu senden. Ich mag die Content-Length am Ende senden, und ich mag der Wert ist der Header 0.

Hier zu sein, ist der Server-Code:

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 des Go-Teams so etwas wie dies neulich geschrieben ... Ich werde versuchen, es für Sie zu finden. –

+0

Entschuldigung, es ist nicht ganz das gleiche - es ist vom Client-Ende der Anfrage. Trotzdem könntest du etwas daraus lernen: https://github.com/nf/dl/blob/master/dl.go –

Antwort

15

Der Trick scheint zu sein, dass Sie einfach müssen Rufen Sie Flusher.Flush() nach jedem Stück wird geschrieben. Beachten Sie auch, dass der Header "Transfer-Encoding" vom Writer implizit gehandhabt wird, also muss er nicht gesetzt werden.

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

können Sie überprüfen, indem Sie 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 

... 

Sie könnten einige der Forschung tun müssen, um sicherzustellen, dass http.ResponseWriters den gleichzeitigen Zugriff für die Verwendung durch mehrere goroutines unterstützen. Weitere Informationen zum "X-Content-Type-Options" header finden Sie auch unter dieser Frage.

+1

Deine flusher.Flush() - Version funktionierte für mich, aber erst nachdem ich den nosniff-Header von der OPs Beispiel. Ihre Version funktioniert gut in telnet und wget, aber ich schätze, die Browser versuchen cleverer zu sein, Inhalte zu puffern. Wenn Sie also nosniff nicht angeben, scheinen Chrome und Firefox beide die Verarbeitung der Daten zu verweigern, bis alles ankommt. – johnbr

+4

Für diejenigen, die sich wundern, siehe [diese Antwort] (http://stackoverflow.com/questions/18337630/what-is-x-content-type-options-nosniff). Grundsätzlich werden einige Browser versuchen, den Inhaltstyp der Seite abzuleiten, sogar * nachdem * der Server dem Client explizit mitteilt, dass es sich beispielsweise um einen Text/HTML handelt. Mit 'X-Content-Type-Options: nosniff' wird diese Funktion von den genannten Browsern deaktiviert. – Howl

Verwandte Themen