// +build go1.3 package graceful import ( "log" "net" "net/http" "github.com/zenazn/goji/graceful/listener" ) // This is a slightly hacky shim to disable keepalives when shutting a server // down. We could have added extra functionality in listener or signal.go to // deal with this case, but this seems simpler. type gracefulServer struct { net.Listener s *http.Server } func (g gracefulServer) Close() error { g.s.SetKeepAlivesEnabled(false) return g.Listener.Close() } // A chaining http.ConnState wrapper type connState func(net.Conn, http.ConnState) func (c connState) Wrap(nc net.Conn, s http.ConnState) { // There are a few other states defined, most notably StateActive. // Unfortunately it doesn't look like it's possible to make use of // StateActive to implement graceful shutdown, since StateActive is set // after a complete request has been read off the wire with an intent to // process it. If we were to race a graceful shutdown against a // connection that was just read off the wire (but not yet in // StateActive), we would accidentally close the connection out from // underneath an active request. // // We already needed to work around this for Go 1.2 by shimming out a // full net.Conn object, so we can just fall back to the old behavior // there. // // I started a golang-nuts thread about this here: // https://groups.google.com/forum/#!topic/golang-nuts/Xi8yjBGWfCQ // I'd be very eager to find a better way to do this, so reach out to me // if you have any ideas. switch s { case http.StateIdle: if err := listener.MarkIdle(nc); err != nil { log.Printf("error marking conn as idle: %v", err) } case http.StateHijacked: if err := listener.Disown(nc); err != nil { log.Printf("error disowning hijacked conn: %v", err) } } if c != nil { c(nc, s) } } // Serve behaves like the method on net/http.Server with the same name. func (srv *Server) Serve(l net.Listener) error { // Spawn a shadow http.Server to do the actual servering. We do this // because we need to sketch on some of the parameters you passed in, // and it's nice to keep our sketching to ourselves. shadow := *(*http.Server)(srv) shadow.ConnState = connState(shadow.ConnState).Wrap l = gracefulServer{l, &shadow} wrap := listener.Wrap(l, listener.Automatic) appendListener(wrap) err := shadow.Serve(wrap) return peacefulError(err) }