From a810417e580890028cf725f6a71d43dbc5c02235 Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Sat, 4 Jan 2020 06:39:30 +0000 Subject: Refator using le utils --- Makefile | 4 ++ go.mod | 1 + go.sum | 10 ++++ main.go | 52 +++++-------------- util/file.go | 19 +++++++ util/gin.go | 72 ++++++++++++++++++++++++++ util/http.go | 26 ++++++++++ util/ip.go | 24 +++++++++ util/viper.go | 36 +++++++++++++ web/controllers/acme.go | 4 +- web/controllers/ddns.go | 4 +- web/controllers/reflect_ip.go | 4 +- web/utils.go | 117 ------------------------------------------ 13 files changed, 212 insertions(+), 161 deletions(-) create mode 100644 Makefile create mode 100644 util/file.go create mode 100644 util/gin.go create mode 100644 util/http.go create mode 100644 util/ip.go create mode 100644 util/viper.go delete mode 100644 web/utils.go diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0ea2c0d --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +GO_FILES := $(shell find . -name '*.go') + +dns-service: main.go $(GO_FILES) + go build -o $@ $< diff --git a/go.mod b/go.mod index 53f7ef2..ccee475 100644 --- a/go.mod +++ b/go.mod @@ -6,5 +6,6 @@ require ( github.com/gin-gonic/gin v1.5.0 github.com/miekg/dns v1.1.26 github.com/spf13/cobra v0.0.5 + github.com/spf13/viper v1.3.2 golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect ) diff --git a/go.sum b/go.sum index a6a4091..ca97a18 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,7 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -19,34 +20,42 @@ github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEK github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/miekg/dns v1.1.26 h1:gPxPSwALAeHJSjarOs00QjVdV9QoBvc1D2ujQUr5BzU= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -76,6 +85,7 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/main.go b/main.go index 367814d..e657062 100644 --- a/main.go +++ b/main.go @@ -1,44 +1,19 @@ package main import ( - "fmt" "log" "os" - "strconv" "github.com/gin-gonic/gin" "github.com/spf13/cobra" + "github.com/spf13/viper" + "code.crute.me/mcrute/go_ddns_manager/util" "code.crute.me/mcrute/go_ddns_manager/web" "code.crute.me/mcrute/go_ddns_manager/web/controllers" "code.crute.me/mcrute/go_ddns_manager/web/middleware" ) -// TODO: use from a common package -func MustGetString(c *cobra.Command, k string) string { - f := c.Flags().Lookup(k) - if f == nil { - panic(fmt.Errorf("No flag named %s", k)) - } - - return f.Value.String() -} - -// TODO: use from a common package -func MustGetBool(c *cobra.Command, k string) bool { - f := c.Flags().Lookup(k) - if f == nil { - panic(fmt.Errorf("No flag named %s", k)) - } - - t, err := strconv.ParseBool(f.Value.String()) - if err != nil { - panic(err) - } - - return t -} - func makeServer(cfg *web.ServerConfig) *gin.Engine { router := gin.Default() router.Use(middleware.ConfigContextMiddleware(cfg)) @@ -68,9 +43,9 @@ func main() { Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { cfg, err := web.LoadServerConfig( - MustGetString(cmd, "zones-config"), - MustGetString(cmd, "auth-config"), - MustGetString(cmd, "view-name"), + viper.GetString("zones-config"), + viper.GetString("auth-config"), + viper.GetString("view-name"), args[0], ) if err != nil { @@ -78,18 +53,19 @@ func main() { os.Exit(1) } - web.GinRun( + util.GinRun( makeServer(cfg), - MustGetBool(cmd, "debug"), - MustGetString(cmd, "listen")) + viper.GetBool("debug"), + viper.GetString("listen")) }, } - cmd.Flags().StringP("zones-config", "z", "cfg/zones.conf", "Bind key and zones config file") - cmd.Flags().StringP("auth-config", "a", "cfg/server_auth.json", "Server auth configuration file") - cmd.Flags().StringP("view-name", "n", "external", "Name of view to update for ACME and DDNS") - cmd.Flags().StringP("listen", "l", ":9090", "Listen address and port") - cmd.Flags().BoolP("debug", "d", false, "Run server in debug mode with debug logs") + util.WrapViper(cmd, "DNS_MANAGE"). + BindString("zones-config", "z", "cfg/zones.conf", "Bind key and zones config file"). + BindString("auth-config", "a", "cfg/server_auth.json", "Server auth configuration file"). + BindString("view-name", "n", "external", "Name of view to update for ACME and DDNS"). + BindString("listen", "l", ":9090", "Listen address and port"). + BindBool("debug", "d", false, "Run server in debug mode with debug logs") if err := cmd.Execute(); err != nil { log.Println(err) diff --git a/util/file.go b/util/file.go new file mode 100644 index 0000000..b6fc86f --- /dev/null +++ b/util/file.go @@ -0,0 +1,19 @@ +package util + +import ( + "io" + "io/ioutil" +) + +func WriteReaderToFile(filename string, data io.Reader) error { + d, err := ioutil.ReadAll(data) + if err != nil { + return err + } + + if err = ioutil.WriteFile(filename, d, 0644); err != nil { + return err + } + + return nil +} diff --git a/util/gin.go b/util/gin.go new file mode 100644 index 0000000..9bba6ee --- /dev/null +++ b/util/gin.go @@ -0,0 +1,72 @@ +package util + +import ( + "context" + "log" + "net" + "net/http" + "os" + "os/signal" + + "github.com/gin-gonic/gin" +) + +// Copied from: https://github.com/gin-gonic/gin/blob/59ab588bf597f9f41faee4f217b5659893c2e925/utils.go#L137 +func resolveAddress(addr []string) string { + switch len(addr) { + case 0: + if port := os.Getenv("PORT"); port != "" { + log.Printf("Environment variable PORT=\"%s\"", port) + return ":" + port + } + log.Printf("Environment variable PORT is undefined. Using port :8080 by default") + return ":8080" + case 1: + return addr[0] + default: + panic("too many parameters") + } +} + +// Runs a gin.Engine instance in a way that can be canceled by an SIGINT +func GinRun(e *gin.Engine, debug bool, a ...string) { + if debug { + gin.SetMode(gin.DebugMode) + } else { + gin.SetMode(gin.ReleaseMode) + } + + srv := &http.Server{ + Addr: resolveAddress(a), + Handler: e, + } + + idleConnsClosed := make(chan struct{}) + + go func() { + sigint := make(chan os.Signal, 1) + signal.Notify(sigint, os.Interrupt) + <-sigint + + log.Println("Caught SIGINT, shutting down") + if err := srv.Shutdown(context.Background()); err != nil { + log.Printf("HTTP server Shutdown: %v", err) + } + + close(idleConnsClosed) + }() + + log.Printf("Listening and serving HTTP on %s\n", srv.Addr) + if err := srv.ListenAndServe(); err != http.ErrServerClosed { + log.Fatalf("HTTP server ListenAndServe: %v", err) + } + + <-idleConnsClosed +} + +func GetRequestIP(c *gin.Context) net.IP { + if xff := c.Request.Header.Get("X-Forwarded-For"); xff != "" { + return ParseIP(xff) + } + return ParseIP(c.Request.RemoteAddr) +} diff --git a/util/http.go b/util/http.go new file mode 100644 index 0000000..31cd94d --- /dev/null +++ b/util/http.go @@ -0,0 +1,26 @@ +package util + +import ( + "fmt" + "net/http" + "net/url" +) + +func MakeURL(r *http.Request, path string, subs ...interface{}) *url.URL { + scheme := "https" + if r.TLS == nil { + scheme = "http" + } + + // Always defer to whatever the proxy told us it was doing because this + // could be a mullet-VIP in either direction. + if fwProto := r.Header.Get("X-Forwarded-Proto"); fwProto != "" { + scheme = fwProto + } + + return &url.URL{ + Scheme: scheme, + Host: r.Host, + Path: fmt.Sprintf(path, subs...), + } +} diff --git a/util/ip.go b/util/ip.go new file mode 100644 index 0000000..f86d5b5 --- /dev/null +++ b/util/ip.go @@ -0,0 +1,24 @@ +package util + +import ( + "net" + "regexp" +) + +// Parses an IPv4 or IPv6 address with an optional port on the end. Returns +// match groups for the addresses. The first match is the IPv6 address and the +// second the IPv4 address. +var ipRegexp = regexp.MustCompile(`(?:\[([0-9a-f:]+)\]|(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))(?::\d+)?`) + +func ParseIP(s string) net.IP { + ips := ipRegexp.FindStringSubmatch(s) + if ips == nil { + return nil + } + + if v6, v4 := ips[1], ips[2]; v6 != "" { + return net.ParseIP(v6) + } else { + return net.ParseIP(v4) + } +} diff --git a/util/viper.go b/util/viper.go new file mode 100644 index 0000000..0f08a6c --- /dev/null +++ b/util/viper.go @@ -0,0 +1,36 @@ +package util + +import ( + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +type ViperWrap struct { + cmd *cobra.Command +} + +func WrapViper(cmd *cobra.Command, prefix string) *ViperWrap { + viper.SetEnvPrefix(prefix) + viper.AutomaticEnv() + return &ViperWrap{cmd} +} + +func (v *ViperWrap) bindViper(name, short string, defaultv interface{}, help string) { + vname := strings.ReplaceAll(name, "-", "_") + viper.BindPFlag(vname, v.cmd.Flags().Lookup(name)) + viper.SetDefault(vname, defaultv) +} + +func (v *ViperWrap) BindString(name, short, defaultv, help string) *ViperWrap { + v.cmd.Flags().StringP(name, short, defaultv, help) + v.bindViper(name, short, defaultv, help) + return v +} + +func (v *ViperWrap) BindBool(name, short string, defaultv bool, help string) *ViperWrap { + v.cmd.Flags().BoolP(name, short, defaultv, help) + v.bindViper(name, short, defaultv, help) + return v +} diff --git a/web/controllers/acme.go b/web/controllers/acme.go index f40b2ec..5090b70 100644 --- a/web/controllers/acme.go +++ b/web/controllers/acme.go @@ -10,7 +10,7 @@ import ( "github.com/gin-gonic/gin" "code.crute.me/mcrute/go_ddns_manager/dns" - "code.crute.me/mcrute/go_ddns_manager/web" + "code.crute.me/mcrute/go_ddns_manager/util" "code.crute.me/mcrute/go_ddns_manager/web/middleware" ) @@ -103,7 +103,7 @@ func CreateAcmeChallenge(c *gin.Context) { return } - url := web.MakeURL(c.Request, "/acme/%s", AcmeChallengeID{ + url := util.MakeURL(c.Request, "/acme/%s", AcmeChallengeID{ Zone: zone.Name, Prefix: prefix, Challenge: ch.Challenge, diff --git a/web/controllers/ddns.go b/web/controllers/ddns.go index 7300989..692b59f 100644 --- a/web/controllers/ddns.go +++ b/web/controllers/ddns.go @@ -7,7 +7,7 @@ import ( "github.com/gin-gonic/gin" "code.crute.me/mcrute/go_ddns_manager/dns" - "code.crute.me/mcrute/go_ddns_manager/web" + "code.crute.me/mcrute/go_ddns_manager/util" "code.crute.me/mcrute/go_ddns_manager/web/middleware" ) @@ -28,7 +28,7 @@ func UpdateDynamicDNS(c *gin.Context) { return } - inip := web.GetRequestIP(c) + inip := util.GetRequestIP(c) if inip == nil { log.Println("ddns: Unable to parse IP") c.AbortWithStatus(http.StatusInternalServerError) diff --git a/web/controllers/reflect_ip.go b/web/controllers/reflect_ip.go index d04b98c..199d736 100644 --- a/web/controllers/reflect_ip.go +++ b/web/controllers/reflect_ip.go @@ -5,11 +5,11 @@ import ( "github.com/gin-gonic/gin" - "code.crute.me/mcrute/go_ddns_manager/web" + "code.crute.me/mcrute/go_ddns_manager/util" ) func ReflectIP(c *gin.Context) { - ip := web.GetRequestIP(c) + ip := util.GetRequestIP(c) if ip == nil { c.String(http.StatusInternalServerError, "") return diff --git a/web/utils.go b/web/utils.go deleted file mode 100644 index 5467132..0000000 --- a/web/utils.go +++ /dev/null @@ -1,117 +0,0 @@ -package web - -import ( - "context" - "fmt" - "log" - "net" - "net/http" - "net/url" - "os" - "os/signal" - "regexp" - - "github.com/gin-gonic/gin" -) - -// Parses an IPv4 or IPv6 address with an optional port on the end. Returns -// match groups for the addresses. The first match is the IPv6 address and the -// second the IPv4 address. -var ipRegexp = regexp.MustCompile(`(?:\[([0-9a-f:]+)\]|(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))(?::\d+)?`) - -// TODO: use from a common package -func ParseIP(s string) net.IP { - ips := ipRegexp.FindStringSubmatch(s) - if ips == nil { - return nil - } - - if v6, v4 := ips[1], ips[2]; v6 != "" { - return net.ParseIP(v6) - } else { - return net.ParseIP(v4) - } -} - -// TODO: use from a common package -func GetRequestIP(c *gin.Context) net.IP { - if xff := c.Request.Header.Get("X-Forwarded-For"); xff != "" { - return ParseIP(xff) - } - return ParseIP(c.Request.RemoteAddr) -} - -// TODO: use from a common package -func MakeURL(r *http.Request, path string, subs ...interface{}) *url.URL { - scheme := "https" - if r.TLS == nil { - scheme = "http" - } - - // Always defer to whatever the proxy told us it was doing because this - // could be a mullet-VIP in either direction. - if fwProto := r.Header.Get("X-Forwarded-Proto"); fwProto != "" { - scheme = fwProto - } - - return &url.URL{ - Scheme: scheme, - Host: r.Host, - Path: fmt.Sprintf(path, subs...), - } -} - -// TODO: use from a common package -// Copied from: https://github.com/gin-gonic/gin/blob/59ab588bf597f9f41faee4f217b5659893c2e925/utils.go#L137 -func resolveAddress(addr []string) string { - switch len(addr) { - case 0: - if port := os.Getenv("PORT"); port != "" { - log.Printf("Environment variable PORT=\"%s\"", port) - return ":" + port - } - log.Printf("Environment variable PORT is undefined. Using port :8080 by default") - return ":8080" - case 1: - return addr[0] - default: - panic("too many parameters") - } -} - -// TODO: use from a common package -// Runs a gin.Engine instance in a way that can be canceled by an SIGINT -func GinRun(e *gin.Engine, debug bool, a ...string) { - if debug { - gin.SetMode(gin.DebugMode) - } else { - gin.SetMode(gin.ReleaseMode) - } - - srv := &http.Server{ - Addr: resolveAddress(a), - Handler: e, - } - - idleConnsClosed := make(chan struct{}) - - go func() { - sigint := make(chan os.Signal, 1) - signal.Notify(sigint, os.Interrupt) - <-sigint - - log.Println("Caught SIGINT, shutting down") - if err := srv.Shutdown(context.Background()); err != nil { - log.Printf("HTTP server Shutdown: %v", err) - } - - close(idleConnsClosed) - }() - - log.Printf("Listening and serving HTTP on %s\n", srv.Addr) - if err := srv.ListenAndServe(); err != http.ErrServerClosed { - log.Fatalf("HTTP server ListenAndServe: %v", err) - } - - <-idleConnsClosed -} -- cgit v1.2.3