I'm writing a simple web app in Go and I want my responses to be streamed to the client (i.e. not buffered and sent in blocks once the request is fully processed) :
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") }
From the client point of view, the two lines will be sent at the same time. Any suggestions are appreciated :)
It's possible to flush after each write I personally make, but in my use case it's not enough:
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()
I want the output of my cmd
to be flushed as well. Anyway to "autoflush" the ResponseWritter ?
I found help on golang's mailing list. There is 2 way to achieve this: using hijacker that allow to take over the underlying TCP connection of HTTP, or piping the stdout and stderr of the command in a go routine that will write and 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 } } }
Even nicer: http://play.golang.org/p/PpbPyXbtEs
As implied in the documentation, some ResponseWriter
may implement the Flusher
interface.
This means you can do something like this :
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") }
Be careful that buffering can occur in many other places in the network or client side.
Sorry if I've misunderstood your question, but would something like the below do the trick?
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
returns:
param1=value1¶m2=value2
You could always append whatever data you wanted to body
, or read more bytes into the buffer from elsewhere before writing it all out again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With