Golang HTTP server waiting to send data to client

I am creating a streaming API similar to the firehose / streaming Twitter API.

As far as I can understand, this is based on HTTP connections that remain open and when the backend receives data and then writes to the honed HTTP connection. It seems like any code I've written will close the HTTP connection as soon as something is connected.

Is there a way to fully expose this?

func startHTTP(pathPrefix string) {
    log.Println("Starting HTTPS Server")
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // Wait here until a write happens to w
        // Or we timeout, we can reset this timeout after each write
    })

    log.Print("HTTPS listening on :5556")
    log.Fatal(http.ListenAndServeTLS(":5556", pathPrefix+".crt", pathPrefix+".key", nil))
}

      

+3


source to share


1 answer


If you want to send an HTTP response to the client not immediately, but after some event, it triggered a long poll .

Here's a simple example of a long polling with a cancellation request when the client disconnects:

package main

import (
    "context"
    "fmt"
    "net/http"
    "time"
)

func doLongOperation(w http.ResponseWriter, ctx context.Context, done chan<- bool) {
    flusher, ok := w.(http.Flusher)
    if !ok {
        panic("Expected http.ResponseWriter to be an http.Flusher")
    }

    select {
    case <-time.After(time.Second * 3):
    case <-ctx.Done():
        done <- false // Cancel job.
        return
    }

    fmt.Fprint(w, "Sup dude?") // Write chunk.
    flusher.Flush()            // Send it to client.
    done <- true               // Finish job.
}

func handler(w http.ResponseWriter, r *http.Request) {
    done := make(chan bool)
    closeNotifier, ok := w.(http.CloseNotifier)
    if !ok {
        panic("Expected http.ResponseWriter to be an http.CloseNotifier")
    }

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    go doLongOperation(w, ctx, done)
    select {
    case <-done:
    case <-time.After(time.Second * 10):
        cancel()
        if !<-done {
            fmt.Fprint(w, "Server is busy.")
        }
    case <-closeNotifier.CloseNotify():
        cancel()
        fmt.Println("Client has disconnected.")
        <-done
    }
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe("localhost:8080", nil)
}

      

Additional links:



d:

+3


source







All Articles