Отправлять отклики HTTP-ответа с сервера Go

Я создаю тестовый HTTP-сервер HTTP, и я отправляю ответный заголовок Transfer-Encoding: chunked, поэтому я могу постоянно отправлять новые данные по мере их получения. Этот сервер должен писать кусок на этот сервер каждую секунду. Клиент должен иметь возможность получать их по требованию.

К сожалению, клиент (завиток в этом случае) получает все куски в конце продолжительности, 5 секунд, вместо того, чтобы получать один кусок каждую секунду. Кроме того, Go, похоже, отправляет Content-Length для меня. Я хочу отправить Content-Length в конце, и я хочу, чтобы значение заголовка было 0.

Вот код сервера:

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")
}

Ответ 1

Кажется, что трюк вам просто нужно вызвать Flusher.Flush() после каждого фрагмента. Также обратите внимание, что заголовок "Transfer-Encoding" будет обрабатываться автором неявно, поэтому нет необходимости его устанавливать.

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))
}

Вы можете проверить, используя 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

...

Вам может потребоваться провести некоторое исследование, чтобы убедиться, что http.ResponseWriters поддерживают параллельный доступ для использования несколькими goroutines.

Также см. этот вопрос для получения дополнительной информации о "X-Content-Type-Options" в заголовке.

Ответ 2

Похоже, httputil предоставляет NewChunkedReader функцию