Не буферизованный http.ResponseWritter в Golang
Я пишу простое веб-приложение в Go, и я хочу, чтобы мои ответы передавались клиенту (т. е. не буферизовались и отправлялись блоками после полной обработки запроса):
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")
}
С точки зрения клиента, две строки будут отправлены одновременно. Любые предложения приветствуются :)
редактировать после @dystroy answer
можно смывать после каждой записи, которую я лично делаю, но в моем случае это не достаточно:
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()
Я хочу вывод моего cmd
чтобы быть очищены, а также. В любом случае, чтобы" autoflush " ResponseWritter ?
решение
Я нашел помощь в рассылке golang это. Существует 2 способа достичь этого: используя удалить, которые позволяют взять на себя базовое TCP-соединение HTTP или конвейер stdout и stderr команды в подпрограмме go, которая будет писать и очищать:
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
}
}
}
последнее обновление
даже лучше: http://play.golang.org/p/PpbPyXbtEs
2 ответов
как это подразумевается в документация, несколько ResponseWriter
может реализовать Flusher
интерфейс.
Это означает, что вы можете сделать что-то вроде этого :
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")
}
будьте осторожны, что буферизация может произойти во многих других местах в сети или на стороне клиента.
Извините, если я неправильно понял ваш вопрос, но что-то вроде ниже сделает трюк?
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
возвращает:
param1=value1¶m2=value2
вы всегда можете добавить любые данные, которые вы хотели body
, или прочитайте больше байтов в буфер из другого места, прежде чем записывать все это снова.