Golang HTTP server wait for data to send to client - go

I am creating a streaming API similar to the Twitter firehose/streaming API.
As far as I can gather this is based on HTTP connections that are kept open and when the backend gets data it then writes to the chucked HTTP connection. It seems that any code I write closes the HTTP connection as soon as anything connects.
Is there a way to keep this open at all?
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))
}

When you want to send HTTP response to client not immediately but after some event, it's called long polling.
Here's simple example of long polling with request cancellation on client disconnect:
package main
import (
"context"
"fmt"
"net/http"
"time"
)
func longOperation(ctx context.Context, ch chan<- string) {
// Simulate long operation.
// Change it to more than 10 seconds to get server timeout.
select {
case <-time.After(time.Second * 3):
ch <- "Successful result."
case <-ctx.Done():
close(ch)
}
}
func handler(w http.ResponseWriter, _ *http.Request) {
notifier, ok := w.(http.CloseNotifier)
if !ok {
panic("Expected http.ResponseWriter to be an http.CloseNotifier")
}
ctx, cancel := context.WithCancel(context.Background())
ch := make(chan string)
go longOperation(ctx, ch)
select {
case result := <-ch:
fmt.Fprint(w, result)
cancel()
return
case <-time.After(time.Second * 10):
fmt.Fprint(w, "Server is busy.")
case <-notifier.CloseNotify():
fmt.Println("Client has disconnected.")
}
cancel()
<-ch
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe("localhost:8080", nil)
}
URLs:
anonymous struct and empty struct.
Send a chunked HTTP response from a Go server.
Go Concurrency Patterns: Context.
Gists:
Golang long polling example.
Golang long polling example with request cancellation.

Related

Start Go HTTP server, do something, then shut it down once task is done

I'm setting up an OAuth2 flow from a CLI application, I'm working on. I need to create a temporary HTTP server for the provider to send the callback to, e.g. localhost:8080/callback
Once the provider has sent the details I need, I want to be able to shut the HTTP server down, just to keep everything clean. I think what I'm looking for is Routines and Wait Groups, but I'm still quite new to this area.
This is what I have so far. I have redacted the part that sends the user to the provider, as my main issue is simply how to shut down the HTTP server once the token variable has been captured.
Server starts
User is directed to authorization URL at the provider site
User approves the request
Provider directs user back to localhost:8080/callback
URL includes client-side only params so I have to server HTML to use JS to capture the values and send it back to the server
Server receives token and can then shutdown
package main
import (
"fmt"
"log"
"net/http"
"sync"
)
func main() {
// Start local HTTP serevr to listen for response
serverDone := &sync.WaitGroup{}
serverDone.Add(1)
Start(serverDone)
// ... Process to start OAuth2 flow
// User is directed to provider website
// User approves
// Provider direct user back to localhost/callback
serverDone.Wait()
}
func Start(wg *sync.WaitGroup) {
srv := &http.Server{Addr: ":8080"}
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
token := r.URL.Query().Get("token")
if token != "" {
fmt.Println("Found Token:", token)
// Shut down server here
} else {
// Server HTML page to fetch token and return to server at /callback
}
})
go func() {
// let main know we are done cleaning up
defer wg.Done()
// ErrServerClosed on graceful close
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("ListenAndServe(): %v", err)
}
}()
}
Use:
var ctxShutdown, cancel = context.WithCancel(context.Background())
Then:
cancel() // to say sorry, above.
// graceful-shutdown
err := srv.Shutdown(context.Background())
Try this:
package main
import (
"context"
"fmt"
"log"
"net/http"
"sync"
)
func main() {
serverDone := &sync.WaitGroup{}
serverDone.Add(1)
Start(serverDone)
serverDone.Wait()
fmt.Println("Done that.")
}
var ctxShutdown, cancel = context.WithCancel(context.Background())
func Start(wg *sync.WaitGroup) {
srv := &http.Server{Addr: ":8080"}
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
select {
case <-ctxShutdown.Done():
fmt.Println("Sorry: Shuting down ...")
return
default:
}
token := r.URL.Query().Get("token")
if token != "" {
fmt.Println("Found Token:", token)
fmt.Println("Shuting down ...")
// Shut down server here
cancel() // to say sorry, above.
// graceful-shutdown
err := srv.Shutdown(context.Background())
if err != nil {
log.Println("server.Shutdown:", err)
}
} else {
fmt.Fprintln(w, "Hi") // Server HTML page to fetch token and return to server at /callback
}
})
go func() {
defer wg.Done()
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("ListenAndServe(): %v", err)
}
fmt.Println("Bye.")
}()
}
Run and open http://127.0.0.1:8080/callback?token=2
Output:
Found Token: 2
Shuting down ...
Bye.
Done that.

Registering an http URL handler in a slice of handlers

For a goal to broadcast message from a goroutine to multiple http URL handlers, I am trying to register these http URL handlers, with below code in main.go:
type webSocketHandler func(http.ResponseWriter, *http.Request)
type threadSafeSlice struct {
sync.Mutex
handlers []*webSocketHandler
}
var sliceOfHandlers threadSafeSlice
func (slice *threadSafeSlice) push(handle *webSocketHandler) { //register
slice.Lock()
defer slice.Unlock()
slice.handlers = append(slice.handlers, handle)
}
where forWardMsgToClient() is the http URL handler that need to be registered,
broadCastMessage() goroutine can broadcast message to multiple forWardMsgToClient() handlers, in the below code:
func main() {
go broadcastMessage()
http.HandleFunc("/websocket", forwardMsgToClient)
http.ListenAndServe(":3000", nil)
}
func forwardMsgToClient(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
for {
// Forward message to the client upon receiving a msg from publisher
}
}
All the above code is in main.go
But the problem is, goroutine forwardMsgToClient() gets spawned for respective client after rw, e := l.Accept() call in ../go/src/net/http/server.go.
Reason to register(push()) http URL handler function(forwardMsgToClient()) is to make broadcastMessage() goroutine know the number of channels to create for all http URL handlers and delete the channel when un-registering http URL handler function(forwardMsgToClient()).
Bit nervous, if we need to modify /go/src/net/http/server.go to achieve this goal
How to register(push()) a http URL handler function forwardMsgToClient() in sliceOfHandlers.handlers?
To broadcast a message to all connected websocket clients, do the following:
Add the connection to a collection on upgrade.
Remove the connection from a collection when the connection is closed.
Broadcast by iterating through the collection.
A simple approach is:
type Clients struct {
sync.Mutex
m map[*websocket.Conn]struct{}
}
var clients = Clients{m: map[*websocket.Conn]struct{}{}}
func (cs *Clients) add(c *websocket.Conn) {
cs.Lock()
cs.m[c] = struct{}{}
cs.Unlock()
}
func (cs *Clients) remove(c *websocket.Conn) {
cs.Lock()
delete(cs.m, c)
cs.Unlock()
}
func (cs *Clients) broadcast(message []byte) {
cs.Lock()
defer cs.Unlock()
for c, _ := range m {
c.WriteMessage(websocket.TextMessage, message)
}
}
The handler adds and removes connections from the collection as follows:
func forwardMsgToClient(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
// handle error
}
defer c.Close()
clients.add(c)
defer clients.remove(c)
// Read the connection as required by the package.
for {
if _, _, err := c.NextReader(); err != nil {
break
}
}
}
To send a message to all connected clients, call clients.broadcast(message).
This simple approach is not production ready for a couple of reasons: it does not the handle the error returned from WriteMessage, broadcast can block on a stuck client.
For a more robust solution, see Gorilla chat example hub. The hub interposes a channel between the broadcaster and the connection, thus allowing the hub to broadcast without blocking. The go broadcastMessage() in the question corresponds to go hub.run() in the Gorilla example. The forwardMsgToClient handler in the question will create a *client and sent it to hub register channel on upgrade and send that *client to the hub unregister channel on disconnect. The *client has a channel that's pumped to the connection.

Passing value to channel is blocking the thread for some reason

I'm using a channel to pass messages from an HTTP handler:
package server
import (
"bytes"
"errors"
"io/ioutil"
"log"
"net/http"
)
type Server struct {}
func (s Server) Listen() chan interface{} {
ch := make(chan interface{})
http.HandleFunc("/", handle(ch))
go http.ListenAndServe(":8080", nil)
return ch
}
func handle(ch chan interface{}) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
b, err := ioutil.ReadAll(r.Body)
defer r.Body.Close()
if err != nil {
ch <- errors.New(string(500))
return
}
w.Write([]byte("Hello World"))
log.Print("about to pass to handler channel")
ch <- bytes.NewBuffer(b)
log.Print("passed to handler channel")
}
}
When I make a request to the server running on port 8080, the thread blocks on this line:
ch <- bytes.NewBuffer(b)
Why is this happening? If you notice, I'm running the listener in a goroutine. I also figured that HTTP handles happen in a separate thread. If I delete the above line, the thread becomes unblocked and the program works as expected. What am I doing wrong?
To clarify, I want to be able to pass the body of a POST request to a channel. Help.
UPDATE:
I'm reading from the channel on the main thread:
listenerChan := n.Listen()
go SendRequest("POST", "http://localhost:8080", []byte("hello"))
for listenedMsg := range listenerChan {
log.Print("listened message>>>> ", listenedMsg)
}
But the thread still blocks on the same line. For clarification, there is nothing wrong with how im sending the request. If I remove the channel send line above, the thread doesnt block.
Because the channel is unbuffered, the send operation blocks until there's someone who is ready to receive from them. Making the channel buffered will only defer the blocking, so you always need some reading goroutine.
Update to your update: the control flow of the program would go like this:
Server starts listening
main sends the request and waits for the response
Server receives the request and tries to write to the channel
main reads from the channel
4 may happen only after 2, which is blocked by 3 which is blocked because 4 is not happening yet. A classical deadlock.
I think #bereal gave a good explanation about using an unbuffered or synchronous channel.
Another way to make things work is to make the channel buffered by changing the line that creates the channel to:
ch := make(chan interface{}, 1) // added the 1
This will prevent the function from being blocked.
I added missing parts in your code and run it, everything works well. I don't see any block. Here's the code:
package main
import (
"bytes"
"errors"
"io/ioutil"
"log"
"net/http"
"time"
)
type Server struct{}
func (s *Server) Listen() chan interface{} {
ch := make(chan interface{})
http.HandleFunc("/", handle(ch))
go http.ListenAndServe(":8080", nil)
return ch
}
func handle(ch chan interface{}) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
b, err := ioutil.ReadAll(r.Body)
defer r.Body.Close()
if err != nil {
ch <- errors.New(string(500))
return
}
w.Write([]byte("Hello World"))
log.Print("about to pass to handler channel")
ch <- bytes.NewBuffer(b)
log.Print("passed to handler channel")
}
}
// SendRequest send request
func SendRequest(method string, url string, data []byte) {
tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
}
client := &http.Client{Transport: tr}
reader := bytes.NewReader(data)
req, err := http.NewRequest(method, url, reader)
if err != nil {
panic(err)
}
client.Do(req)
}
func main() {
n := new(Server)
listenerChan := n.Listen()
go SendRequest("POST", "http://localhost:8080", []byte("hello"))
for listenedMsg := range listenerChan {
log.Print("listened message>>>> ", listenedMsg)
}
}
And the output are:
2018/06/28 17:22:10 about to pass to handler channel
2018/06/28 17:22:10 passed to handler channel
2018/06/28 17:22:10 listened message>>>> hello

Websocket freezes if disconnected abnormally

I've created a simple websocket that publishes a JSON stream. I't works fine most of the time except for few cases where I think while looping through the clients to send them message, it gets hung up on a client that is being disconnected abnormally. What measure can I add to this code to mitigate it?
Client.go
import (
"github.com/gorilla/websocket"
)
type client struct {
socket *websocket.Conn
send chan *Message
}
func (c *client) read() {
defer c.socket.Close()
for {
_, _, err := c.socket.ReadMessage()
if err != nil {
log.Info("Websocket: %s", err)
break
}
}
}
func (c *client) write() {
defer c.socket.Close()
for msg := range c.send {
err := c.socket.WriteJSON(msg)
if err != nil {
break
}
}
}
Stream.go
import (
"net/http"
"github.com/gorilla/websocket"
)
const (
socketBufferSize = 1024
messageBufferSize = 256
)
var upgrader = &websocket.Upgrader{
ReadBufferSize: socketBufferSize,
WriteBufferSize: socketBufferSize,
}
type Stream struct {
Send chan *Message
join chan *client
leave chan *client
clients map[*client]bool
}
func (s *Stream) Run() {
for {
select {
case client := <-s.join: // joining
s.clients[client] = true
case client := <-s.leave: // leaving
delete(s.clients, client)
close(client.send)
case msg := <-s.Send: // send message to all clients
for client := range s.clients {
client.send <- msg
}
}
}
}
func (s *Stream) ServeHTTP(w http.ResponseWriter, res *http.Request) {
socket, err := upgrader.Upgrade(w, res, nil)
if err != nil {
log.Error(err)
return
}
defer func() {
socket.Close()
}()
client := &client{
socket: socket,
send: make(chan *Message, messageBufferSize),
}
s.join <- client
defer func() { s.leave <- client }()
go client.write()
client.read()
}
See the Gorilla Chat Application for an example of how to avoid blocking on a client.
The key parts are:
Use a buffered channel for sending to the client. Your application is already doing this.
Send to the client using select/default to avoid blocking. Assume that the client is blocked on write when the client cannot immediately receive a message. Close the client's channel in this situation to cause the client's write loop to exit.
Write with a deadline.

Gracefully Shutdown Gorilla Server

I'm building a server in go using gorilla multiplexer library found in https://github.com/gorilla/mux.
The problem is, I want it to gracefully shutdown when I'm using Ctrl+C, or when there is a specific API call, for example "/shutdown".
I already know that in Go 1.8, graceful shutdown is already implemented. But how to combine it with gorilla multiplexer? Also, how to combine it with SIGINT signal?
Can anyone show me how to do it?
Channel can be used to capture shutdown request through API call (/shutdown) or interrupt signal (Ctrl+C).
Embed http.Server into a custom struct, so we can call http Server.Shutdown later
Add channel field (shutdownReq) for passing shutdown request from API call /shutdown
Register http handlers including /shutdown in gorilla/mux's router, then assign the router to http.Server.Handler
Register os.Interrupt/syscall.SIGINT, syscall.SIGTERM handler
Use select to capture shutdown request through API call or interrupt signal
Perform clean shutdown by calling Server.Shutdown
Below is the example code:
package main
import (
"context"
"log"
"net/http"
"sync/atomic"
"syscall"
"time"
"os"
"os/signal"
"github.com/gorilla/mux"
)
type myServer struct {
http.Server
shutdownReq chan bool
reqCount uint32
}
func NewServer() *myServer {
//create server
s := &myServer{
Server: http.Server{
Addr: ":8080",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
},
shutdownReq: make(chan bool),
}
router := mux.NewRouter()
//register handlers
router.HandleFunc("/", s.RootHandler)
router.HandleFunc("/shutdown", s.ShutdownHandler)
//set http server handler
s.Handler = router
return s
}
func (s *myServer) WaitShutdown() {
irqSig := make(chan os.Signal, 1)
signal.Notify(irqSig, syscall.SIGINT, syscall.SIGTERM)
//Wait interrupt or shutdown request through /shutdown
select {
case sig := <-irqSig:
log.Printf("Shutdown request (signal: %v)", sig)
case sig := <-s.shutdownReq:
log.Printf("Shutdown request (/shutdown %v)", sig)
}
log.Printf("Stoping http server ...")
//Create shutdown context with 10 second timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
//shutdown the server
err := s.Shutdown(ctx)
if err != nil {
log.Printf("Shutdown request error: %v", err)
}
}
func (s *myServer) RootHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello Gorilla MUX!\n"))
}
func (s *myServer) ShutdownHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Shutdown server"))
//Do nothing if shutdown request already issued
//if s.reqCount == 0 then set to 1, return true otherwise false
if !atomic.CompareAndSwapUint32(&s.reqCount, 0, 1) {
log.Printf("Shutdown through API call in progress...")
return
}
go func() {
s.shutdownReq <- true
}()
}
func main() {
//Start the server
server := NewServer()
done := make(chan bool)
go func() {
err := server.ListenAndServe()
if err != nil {
log.Printf("Listen and serve: %v", err)
}
done <- true
}()
//wait shutdown
server.WaitShutdown()
<-done
log.Printf("DONE!")
}
Note: Please watch this issue which is related to gracefull shutdown.

Resources