从Go服务器发送分块的HTTP响应 [英] Send a chunked HTTP response from a Go server

查看:870
本文介绍了从Go服务器发送分块的HTTP响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个测试Go HTTP服务器,并且正在发送一个Transfer-Encoding响应头:分块,这样我就可以在检索它时不断发送新数据。该服务器应该每隔一秒向该服务器写入一个块。客户应该能够按需接收它们。



不幸的是,客户端(在这种情况下是curl)在持续时间5秒结束时接收所有的块,而不是每秒接收一个块。另外,Go似乎为我发送Content-Length。我希望在最后发送Content-Length,并且我希望头的值为0。



以下是服务器代码:

 包主

导入(
fmt
io
log
net / http
time


func main(){
http.HandleFunc(/ test,HandlePost) ;
log.Fatal(http.ListenAndServe(:8080,nil))
}

func HandlePost(w http.ResponseWriter,r * http.Request){
w.Header()。Set(Connection,Keep-Alive)
w.Header()。Set(Transfer-Encoding,chunked)
w.Header( ).Set(X-Content-Type-Options,nosniff)

ticker:= time.NewTicker(time.Second)
go func(){
对于t:=范围ticker.C {
io.WriteString(w,Chunk)
fmt.Println(Tick at,t)
}
}()
time.Sleep(time.Second * 5)
ticker.Stop()
fmt.Println(Finished:should return Content-Length:0 here)
w。 Header()。Set(Content-Length,0)
}


解决方案

这个技巧似乎只需要调用写入每个块后, Flusher.Flush() 。请注意,Transfer-Encoding标题将由作者隐式处理,因此不需要设置它。

  func main(){
http.HandleFunc(/,func(w http.ResponseWriter,r * http.Request){
flusher,ok:= w。(http.Flusher)
如果!ok {
panic(预期http.ResponseWriter是一个http.Flusher)
}
w.Header()。Set(X-Content-Type-Options, nosniff)
for i:= 1; i <= 10; i ++ {
fmt.Fprintf(w,Chunk#%d \,i)
flusher。 Flush()//触发chunked编码并发送一个块...
time.Sleep(500 * time.Millisecond)
}
})

log.Print(Listening on localhost:8080)
log.Fatal(http.ListenAndServe(:8080,nil))
}
pre>

您可以使用telnet进行验证:

  $ telnet localhost 8080 
Trying :: 1 ...
连接到本地主机。
转义字符是'^]'。
GET / HTTP / 1.1

HTTP / 1.1 200 OK
日期:2015年6月2日,星期二18:16:38 GMT
Content-Type:text / plain ; charset = utf-8
Transfer-Encoding:chunked

9
大块#1

9
大块#2

...

您可能需要做一些研究来验证http.ResponseWriters是否支持并发访问以供多个goroutine使用。



另外,请参阅此问题以获取有关X-Content-Type-Options标题


I am creating a test Go HTTP server, and I am sending a response header of Transfer-Encoding: chunked so I can continually send new data as I retrieve it. This server should write a chunk to this server every one second. The client should be able to receive them on demand.

Unfortunately, the client(curl in this case), receives all of the chunks at the end of the duration, 5 seconds, rather than receiving one chunk every one second. Also, Go seems to send the Content-Length for me. I want to send the Content-Length at the end, and I want the the header's value to be 0.

Here is the server code:

package main

import (
    "fmt"
    "io"
    "log"
    "net/http"
    "time"
)

func main() {
    http.HandleFunc("/test", HandlePost);
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func HandlePost(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Connection", "Keep-Alive")
    w.Header().Set("Transfer-Encoding", "chunked")
    w.Header().Set("X-Content-Type-Options", "nosniff")

    ticker := time.NewTicker(time.Second)
    go func() {
        for t := range ticker.C {
            io.WriteString(w, "Chunk")
            fmt.Println("Tick at", t)
        }
    }()
    time.Sleep(time.Second * 5)
    ticker.Stop()
    fmt.Println("Finished: should return Content-Length: 0 here")
    w.Header().Set("Content-Length", "0")
}

解决方案

The trick appears to be that you simply need to call Flusher.Flush() after each chunk is written. Note also that the "Transfer-Encoding" header will be handled by the writer implicitly, so no need to set it.

func main() {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    flusher, ok := w.(http.Flusher)
    if !ok {
      panic("expected http.ResponseWriter to be an http.Flusher")
    }
    w.Header().Set("X-Content-Type-Options", "nosniff")
    for i := 1; i <= 10; i++ {
      fmt.Fprintf(w, "Chunk #%d\n", i)
      flusher.Flush() // Trigger "chunked" encoding and send a chunk...
      time.Sleep(500 * time.Millisecond)
    }
  })

  log.Print("Listening on localhost:8080")
  log.Fatal(http.ListenAndServe(":8080", nil))
}

You can verify by using telnet:

$ telnet localhost 8080
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1

HTTP/1.1 200 OK
Date: Tue, 02 Jun 2015 18:16:38 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked

9
Chunk #1

9
Chunk #2

...

You might need to do some research to verify that http.ResponseWriters support concurrent access for use by multiple goroutines.

Also, see this question for more information about the "X-Content-Type-Options" header.

这篇关于从Go服务器发送分块的HTTP响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆