Roman Alexandrovich
Roman Alexandrovich

Reputation: 350

Detect failed http connections in go

Let's suppose client uploads heavy image by http with slow internet connection, he opens connection to server, starts to write data and suddenly he loses this connection because of network problems. How can I detect this on server if handler function was not yet called. The solution I found is to check the connection state. But the problem is that it's not scalable, because a lot of goroutines will interact with global variable. Are there any more elegant solutions?

package main

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

// current state of connections
var connStates = make(map[string]http.ConnState)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("got new request")
        fmt.Println(connStates)
    })

    srv := &http.Server{
        Addr: ":8080",
        ConnState: func(conn net.Conn, event http.ConnState) {
            if event != http.StateClosed {
                connStates[conn.RemoteAddr().String()] = event
            } else {
                if connStates[conn.RemoteAddr().String()] == http.StateActive {
                    fmt.Println("client cancelled request")
                }
                delete(connStates, conn.RemoteAddr().String())
            }
        },
    }
    log.Fatal(srv.ListenAndServe())
}

Upvotes: 4

Views: 2402

Answers (2)

nbari
nbari

Reputation: 26925

You could use context within your handler, for example, this would detect when the client disconnects and return and http.StatusPartialContent besides calling someCleanup() in where you could have your logging logic.

https://play.golang.org/p/5Yr_HBuyiZW

func helloWorld(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()

    ch := make(chan struct{})

    go func() {
            time.Sleep(5 * time.Second)
            fmt.Fprintln(w, "Hello World!")
            ch <- struct{}{}
    }()

    select {
    case <-ch:
    case <-ctx.Done():
            http.Error(w, ctx.Err().Error(), http.StatusPartialContent)
            someCleanUP()
    }

}

Upvotes: 1

user5715068
user5715068

Reputation:

If you only need to have logs you can even simplify the code:

srv := &http.Server{
        Addr: ":8080",
        ConnState: func(conn net.Conn, event http.ConnState) {
            log.Printf("addr: %s, changed state to: %s", conn.RemoteAddr(), event.String())
        },
    }

That callback will be triggered on each change of the conn

Upvotes: 0

Related Questions