J'écris une application Web simple dans Go et je veux que mes réponses soient diffusées au client (c'est-à-dire non mises en mémoire tampon et envoyées en blocs une fois la demande entièrement traitée):
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")
}
Du point de vue client, les deux lignes seront envoyées en même temps. Toutes les suggestions sont appréciées :)
Il est possible de vider après chaque écriture que je fais personnellement, mais dans mon cas d'utilisation, ce n'est pas suffisant:
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()
Je veux que la sortie de mon cmd
soit également vidée. Quoi qu'il en soit pour "vider automatiquement" le ResponseWritter?
J'ai trouvé de l'aide sur la liste de diffusion de Golang. Il y a 2 façons d'y parvenir: utiliser pirate de l'air qui permet de reprendre la connexion sous-jacente TCP connexion de HTTP, ou canaliser la stdout et la stderr de la commande d'un coup routine qui va écrire et vider:
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
}
}
}
Encore plus agréable: http://play.golang.org/p/PpbPyXbtEs
Comme implicite dans la documentation , certains ResponseWriter
peuvent implémenter l'interface Flusher
.
Cela signifie que vous pouvez faire quelque chose comme ceci:
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")
}
Veillez à ce que la mise en mémoire tampon puisse se produire à de nombreux autres endroits du côté réseau ou client.
Désolé si j'ai mal compris votre question, mais est-ce que quelque chose comme ci-dessous ferait l'affaire?
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
retour:
param1 = valeur1 & param2 = valeur2
Vous pouvez toujours ajouter toutes les données que vous vouliez body
, ou lire plus d'octets dans le tampon d'ailleurs avant de tout réécrire.