servefile net golang example body app http stream go flush

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&param2=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.