Nie buforowany http.ResponseWritter in Golang
[4]} piszę prostą aplikację internetową W Go i chcę, aby moje odpowiedzi były przesyłane strumieniowo do klienta (tzn. nie buforowane i wysyłane w blokach po pełnym przetworzeniu żądania):
func handle(res http.ResponseWriter, req *http.Request) {
fmt.Fprintf(res, "sending first line of data")
sleep(10) //not real code
fmt.Fprintf(res, "sending second line of data")
}
Z punktu widzenia klienta, dwie linie zostaną wysłane w tym samym czasie. Wszelkie sugestie są mile widziane:)
Edit after @ dystroy odpowiedz
Możliwe jest spłukanie po każdym wpisie, który osobiście wykonuję, ale w moim przypadku użycia to za mało:
cmd := exec.Command("a long command that outputs lots of lines")
cmd.Stdout = res //where res is a http.ResponseWritter
cmd.Stderr = res
err := cmd.Run()
Chcę aby wyjście mojego cmd
być spłukane. W każdym razie, aby "automatycznie" ResponseWritter ?
Rozwiązanie
Znalazłem pomoc na liście mailingowej golanga. Istnieje 2 sposoby, aby to osiągnąć: użycie hijacker, które pozwala przejąć podstawowe połączenie TCP HTTP, lub Orurowanie stdout i stderr komendy w rutynę go, która będzie pisać i flush:pipeReader, pipeWriter := io.Pipe()
cmd.Stdout = pipeWriter
cmd.Stderr = pipeWriter
go writeCmdOutput(res, pipeReader)
err := cmd.Run()
pipeWriter.Close()
//---------------------
func writeCmdOutput(res http.ResponseWriter, pipeReader *io.PipeReader) {
buffer := make([]byte, BUF_LEN)
for {
n, err := pipeReader.Read(buffer)
if err != nil {
pipeReader.Close()
break
}
data := buffer[0:n]
res.Write(data)
if f, ok := res.(http.Flusher); ok {
f.Flush()
}
//reset buffer
for i := 0; i < n; i++ {
buffer[i] = 0
}
}
}
Ostatnia aktualizacja
Jeszcze ładniej: http://play.golang.org/p/PpbPyXbtEs
2 answers
Jak sugerowano w dokumentacji , niektóre ResponseWriter
mogą zaimplementować interfejs Flusher
.
Oznacza to, że możesz zrobić coś takiego:
func handle(res http.ResponseWriter, req *http.Request) {
fmt.Fprintf(res, "sending first line of data")
if f, ok := res.(http.Flusher); ok {
f.Flush()
} else {
log.Println("Damn, no flush");
}
sleep(10) //not real code
fmt.Fprintf(res, "sending second line of data")
}
Należy uważać, aby buforowanie mogło wystąpić w wielu innych miejscach w sieci lub po stronie klienta.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-10 09:48:43
Przepraszam, jeśli źle zrozumiałem twoje pytanie, ale czy coś takiego jak poniżej zadziała?
package main
import (
"bytes"
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
body := make([]byte, int(r.ContentLength))
b := bytes.NewBuffer(body)
if _, err := b.ReadFrom(r.Body); err != nil {
fmt.Fprintf(w, "%s", err)
}
if _, err := b.WriteTo(w); err != nil {
fmt.Fprintf(w, "%s", err)
}
}
func main() {
http.HandleFunc("/", handler)
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
$ curl --data "param1=value1¶m2=value2" http://localhost:8080
Zwraca:
Param1 = value1¶m2=value2
Możesz zawsze dołączyć dowolne dane body
lub odczytać więcej bajtów do bufora z innego miejsca przed ponownym zapisaniem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-10 13:01:46