Search code examples
restweb-servicesgowebservergoroutine

Goroutine already started in Go web server from request but client disconnects, is it possible for a web server to close that particular goroutine?


Whenever a web request comes in from the client it would spawn a goroutine to handle each. If the client just happen to disconnect from the connection, is it possible for a web server to close that particular goroutine or will that goroutine find out that the client had already diconnected after it had executed all its code?


Solution

  • Other than exiting on the return from a called handler on a read or write error - the executing go routine will not automatically handle cleaning up longer running operations, but Go provides nice ways of handling this.

    First, if you are not familiar with the context package - it is a powerful and idiomatic way to synchronize go routines with cancellation behavior, I highly recommend reading the blog Go Concurrency Patterns: Context.

    Something like the following:

    func MyServiceFunc(ctx context.Context) {
        for {
            select {
            case <-ctx.Done():
                break
            default:
                //do work
            }
        }
    }
    
    func MyRequestHandler(res http.ResponseWriter, req *http.Request) {
        MyServiceFunc(req.Context())       
       //write response...
    }
    

    Or you could use the CloseNotifier interface of which a http.ResponseWriter also implements, you could do something like the following simple example:

    func MyServiceFunc(notifier <-chan bool) {
        for {
            select {
            case <-notifier:
                break
            default:
                //do work
            }
        }
    }
    
    
    func MyRequestHandler(res http.ResponseWriter, req *http.Request) {
        notifier := res.(http.CloseNotifier).CloseNotify()
        MyServiceFunc(notifier)
        //write response...
    }
    

    Lastly, an example combining both approaches:

    func MyRequestHandler(res http.ResponseWriter, req *http.Request) {
    
        notifier := res.(http.CloseNotifier).CloseNotify()
        ctx, cancel := context.WithCancel(req.Context())
    
        go func(closer <-chan bool) {
            <-closer //the notifer blocks until the send
            cancel() //explicitly cancel all go routines
        }(notifier)
    
        go MyServiceFunc(ctx)
        MyOtherServiceFunc(ctx)
        //write response...
    }