net - No almacenado en búfer http.ResponseWritter en Golang
golang web app example (2)
Perdón si he entendido mal su pregunta, pero ¿algo así como el siguiente puede hacer el truco?
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
devoluciones:
param1 = value1 & param2 = value2
Siempre puede agregar los datos que desee al body
, o leer más bytes en el búfer de otro lugar antes de volver a escribir todo.
Estoy escribiendo una aplicación web simple en Ir y quiero que mis respuestas se transmitan al cliente (es decir, no almacenadas en el búfer y enviadas en bloques una vez que la solicitud se procesa por completo):
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")
}
Desde el punto de vista del cliente, las dos líneas se enviarán al mismo tiempo. Cualquier sugerencia es apreciada :)
Editar después de @dystroy respuesta
Es posible enjuagar después de cada escritura que hago personalmente, pero en mi caso de uso no es suficiente:
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()
Quiero que la salida de mi cmd
se vacíe también. De todos modos para "autoflush" el ResponseWritter?
Solución
Encontré ayuda en la lista de correo de golang. Hay dos formas de lograr esto: usar un hijacker que permita tomar el control de la conexión TCP subyacente de HTTP, o conectar el stdout y el stderr del comando en una rutina go que se escribirá y vaciará:
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
}
}
}
Última actualización
Incluso más agradable: http://play.golang.org/p/PpbPyXbtEs
Tal como está implícito en la documentación , algunos ResponseWriter
pueden implementar la interfaz de Flusher
.
Esto significa que puedes hacer algo como esto:
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")
}
Tenga cuidado de que el almacenamiento en búfer pueda ocurrir en muchos otros lugares en la red o en el lado del cliente.