Instead of trying to restart the server, switch the handler:

package main

import (
"fmt"
"io/ioutil"
"log"
"net/http"
"strconv"
)

// Some state so we can show the old handler is in effect...
type Foo struct {
SomeVar int
}

func (f *Foo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Printf("In handler wrapper, f = %p\n", f)
w.Write([]byte(strconv.Itoa(f.SomeVar)))
}

type switchable struct {
h http.Handler
}

func (s *switchable) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.h.ServeHTTP(w, r)
}

func main() {
log.SetFlags(log.Lshortfile)

f1 := &Foo{SomeVar: 1}
f2 := &Foo{SomeVar: 2}

handler := &switchable{f1}

go func() {
log.Fatalln(http.ListenAndServe("0.0.0.0:8090", handler))
}()

fmt.Println(doRequest())

handler.h = f2

fmt.Println(doRequest())
}

func doRequest() string {
client := &http.Client{}
req, err := http.NewRequest("GET", "http://0.0.0.0:8090";, nil)
if err != nil {
log.Fatalln(err)
}

resp, err := client.Do(req)
if err != nil {
log.Fatalln(err)
}

bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
}

return string(bytes)
}

Output:
$ go run main.go
In handler wrapper, f = 0xc420060fa0
1
In handler wrapper, f = 0xc420060fa8
2

I'm not sure if there is a race condition here or not...

On Tuesday, April 18, 2017 at 7:02:17 AM UTC+2, Ken Simon wrote:
>
> Hello golang-nuts!
>
> I have some code that creates a short-lived HTTP server, and I'm trying to 
> write tests for it.  I'm hitting a case where HTTP handlers seem to stick 
> around even though I'm creating a brand new `http.ServeMux`, `http.Server`, 
> and `net.Listener` objects.  How do I get rid of old handlers?
>
> Here's a semi-simple reproducible case:
>
> package main
>
> import (
> "fmt"
> "io/ioutil"
> "net"
> "net/http"
> "os"
> "strconv"
> "time"
> )
>
> // Some state so we can show the old handler is in effect...
> type Foo struct {
> SomeVar int
> }
>
> func (f *Foo) Serve(addr string) (net.Listener, error) {
> l, err := net.Listen("tcp", addr)
> if err != nil {
> return nil, err
> }
> mux := http.NewServeMux()
> mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
> fmt.Printf("In handler wrapper, f = %p\n", f)
> w.Write([]byte(strconv.Itoa(f.SomeVar)))
> })
> srv := &http.Server{
> Handler: mux,
> }
>
> go srv.Serve(l)
> return l, nil
> }
>
> func main() {
> f1 := &Foo{SomeVar: 1}
> f2 := &Foo{SomeVar: 2}
>
> l, err := f1.Serve("0.0.0.0:8090")
> if err != nil {
> fmt.Println(err)
> os.Exit(1)
> }
> fmt.Println(doRequest())
> l.Close()
> // Ok it didn't actually close, wait a bit or it'll show the address as
> // already in use...
> time.Sleep(1 * time.Second)
>
> l, err = f2.Serve("0.0.0.0:8090")
> if err != nil {
> fmt.Println(err)
> os.Exit(1)
> }
> fmt.Println(doRequest())
> l.Close()
> }
>
> func doRequest() string {
> client := &http.Client{}
> req, _ := http.NewRequest("GET", "http://0.0.0.0:8090";, nil)
> resp, _ := client.Do(req)
> bytes, _ := ioutil.ReadAll(resp.Body)
> return string(bytes)
> }
>
> Output:
>
> $ go run main.go
> In handler wrapper, f = 0xc42000d200
> 1
> In handler wrapper, f = 0xc42000d200
> 1
>
> Note the pointer to the method receiver is the same in both cases, 
> indicating the old ServeMux is still handling the new request... (boy did 
> it take me a long time to figure out what was going on in a non-trivial 
> codebase...)
>
> What's happening here?  Is the old http.Server still "attached" to the 
> tcp.Listener?  What can I do to fix this?
>
> Thanks so much!
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to