diff --git a/main.go b/main.go index 553f109..d6dbef2 100644 --- a/main.go +++ b/main.go @@ -24,6 +24,7 @@ import ( "arimelody-web/log" "arimelody-web/model" "arimelody-web/view" + amnet "arimelody-web/net" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" @@ -518,7 +519,18 @@ func main() { // start the web server! mux := createServeMux(&app) - fmt.Printf("Now serving at http://%s:%d\n", app.Config.Host, app.Config.Port) + server := &amnet.DualStackHTTPServer{ Handler: mux } + if err := server.ListenAndServe(app.Config.Host, "::", int(app.Config.Port)); err != nil { + panic(err) + } + fmt.Printf( + "Now listening on port %d (http://%s:%d, http://[%s]:%d)\n", + app.Config.Port, + app.Config.Host, + app.Config.Port, + "::", + app.Config.Port, + ) stdLog.Fatal( http.ListenAndServe(fmt.Sprintf("%s:%d", app.Config.Host, app.Config.Port), CheckRequest(&app, HTTPLog(DefaultHeaders(mux))), diff --git a/net/dualstack.go b/net/dualstack.go new file mode 100644 index 0000000..ee2f1d5 --- /dev/null +++ b/net/dualstack.go @@ -0,0 +1,40 @@ +package net + +import ( + "fmt" + "net" + "net/http" + "sync" +) + +type DualStackHTTPServer struct { + Handler http.Handler +} + +func (s *DualStackHTTPServer) ListenAndServe(ipv4 string, ipv6 string, port int) error { + v4Listener, err := net.Listen("tcp4", fmt.Sprintf("%s:%d", ipv4, port)) + if err != nil { + return fmt.Errorf("IPv4 listen: %v", err) + } + + v6Listener, err := net.Listen("tcp6", fmt.Sprintf("[%s]:%d", ipv6, port)) + if err != nil { + v4Listener.Close() + return fmt.Errorf("IPv6 listen: %v", err) + } + + srv := &http.Server{ Handler: s.Handler } + + var wg sync.WaitGroup + errs := make(chan error, 2) + + for _, listener := range []net.Listener{v4Listener, v6Listener} { + wg.Add(1) + go func(l net.Listener) { + defer wg.Done() + errs <- srv.Serve(l) + }(listener) + } + + return <-errs +}