From e13f65b953be4bd4d4ede9eed11b617ff1ad69c5 Mon Sep 17 00:00:00 2001 From: Matt Holt Date: Wed, 4 Apr 2018 07:56:26 -0600 Subject: [PATCH] serve restic: Print actual listener address --- cmd/serve/http/http.go | 6 ++- cmd/serve/httplib/httplib.go | 71 ++++++++++++++++++++++++++++++------ cmd/serve/restic/restic.go | 6 ++- cmd/serve/webdav/webdav.go | 6 ++- fs/rc/rc.go | 6 ++- 5 files changed, 80 insertions(+), 15 deletions(-) diff --git a/cmd/serve/http/http.go b/cmd/serve/http/http.go index ef032a384..a03488820 100644 --- a/cmd/serve/http/http.go +++ b/cmd/serve/http/http.go @@ -72,8 +72,12 @@ func newServer(f fs.Fs, opt *httplib.Options) *server { // serve runs the http server - doesn't return func (s *server) serve() { + err := s.srv.Serve() + if err != nil { + fs.Errorf(s.f, "Opening listener: %v", err) + } fs.Logf(s.f, "Serving on %s", s.srv.URL()) - s.srv.Serve() + s.srv.Wait() } // handler reads incoming requests and dispatches them diff --git a/cmd/serve/httplib/httplib.go b/cmd/serve/httplib/httplib.go index b473bc8ed..c10c94985 100644 --- a/cmd/serve/httplib/httplib.go +++ b/cmd/serve/httplib/httplib.go @@ -7,6 +7,7 @@ import ( "fmt" "io/ioutil" "log" + "net" "net/http" "time" @@ -24,10 +25,11 @@ var Help = ` Use --addr to specify which IP address and port the server should listen on, eg --addr 1.2.3.4:8000 or --addr :8080 to listen to all -IPs. By default it only listens on localhost. +IPs. By default it only listens on localhost. You can use port +:0 to let the OS choose an available port. If you set --addr to listen on a public or LAN accessible IP address -then using Authentication if advised - see the next section for info. +then using Authentication is advised - see the next section for info. --server-read-timeout and --server-write-timeout can be used to control the timeouts on the server. Note that this is the total time @@ -98,6 +100,8 @@ var DefaultOpt = Options{ type Server struct { Opt Options handler http.Handler // original handler + listener net.Listener + waitChan chan struct{} // for waiting on the listener to close httpServer *http.Server basicPassHashed string useSSL bool // if server is configured for SSL/TLS @@ -178,15 +182,52 @@ func NewServer(handler http.Handler, opt *Options) *Server { return s } -// Serve runs the server - doesn't return -func (s *Server) Serve() { - var err error - if s.useSSL { - err = s.httpServer.ListenAndServeTLS(s.Opt.SslCert, s.Opt.SslKey) - } else { - err = s.httpServer.ListenAndServe() +// Serve runs the server - returns an error only if +// the listener was not started; does not block, so +// use s.Wait() to block on the listener indefinitely. +func (s *Server) Serve() error { + ln, err := net.Listen("tcp", s.httpServer.Addr) + if err != nil { + return err } - log.Printf("Error while serving HTTP: %v", err) + s.listener = ln + s.waitChan = make(chan struct{}) + go func() { + var err error + if s.useSSL { + // hacky hack to get this to work with old Go versions, which + // don't have ServeTLS on http.Server; see PR #2194. + type tlsServer interface { + ServeTLS(ln net.Listener, cert, key string) error + } + srvIface := interface{}(s.httpServer) + if tlsSrv, ok := srvIface.(tlsServer); ok { + // yay -- we get easy TLS support with HTTP/2 + err = tlsSrv.ServeTLS(s.listener, s.Opt.SslCert, s.Opt.SslKey) + } else { + // oh well -- we can still do TLS but might not have HTTP/2 + tlsConfig := new(tls.Config) + tlsConfig.Certificates = make([]tls.Certificate, 1) + tlsConfig.Certificates[0], err = tls.LoadX509KeyPair(s.Opt.SslCert, s.Opt.SslKey) + if err != nil { + log.Printf("Error loading key pair: %v", err) + } + tlsLn := tls.NewListener(s.listener, tlsConfig) + err = s.httpServer.Serve(tlsLn) + } + } else { + err = s.httpServer.Serve(s.listener) + } + if err != nil { + log.Printf("Error on serving HTTP server: %v", err) + } + }() + return nil +} + +// Wait blocks while the listener is open. +func (s *Server) Wait() { + <-s.waitChan } // Close shuts the running server down @@ -194,7 +235,9 @@ func (s *Server) Close() { err := closeServer(s.httpServer) if err != nil { log.Printf("Error on closing HTTP server: %v", err) + return } + close(s.waitChan) } // URL returns the serving address of this server @@ -203,5 +246,11 @@ func (s *Server) URL() string { if s.useSSL { proto = "https" } - return fmt.Sprintf("%s://%s/", proto, s.Opt.ListenAddr) + addr := s.Opt.ListenAddr + if s.listener != nil { + // prefer actual listener address; required if using 0-port + // (i.e. port assigned by operating system) + addr = s.listener.Addr().String() + } + return fmt.Sprintf("%s://%s/", proto, addr) } diff --git a/cmd/serve/restic/restic.go b/cmd/serve/restic/restic.go index c30343a71..c4d451dde 100644 --- a/cmd/serve/restic/restic.go +++ b/cmd/serve/restic/restic.go @@ -169,8 +169,12 @@ func newServer(f fs.Fs, opt *httplib.Options) *server { // serve runs the http server - doesn't return func (s *server) serve() { + err := s.srv.Serve() + if err != nil { + fs.Errorf(s.f, "Opening listener: %v", err) + } fs.Logf(s.f, "Serving restic REST API on %s", s.srv.URL()) - s.srv.Serve() + s.srv.Wait() } var matchData = regexp.MustCompile("(?:^|/)data/([^/]{2,})$") diff --git a/cmd/serve/webdav/webdav.go b/cmd/serve/webdav/webdav.go index 615322025..7549125f7 100644 --- a/cmd/serve/webdav/webdav.go +++ b/cmd/serve/webdav/webdav.go @@ -88,8 +88,12 @@ func newWebDAV(f fs.Fs, opt *httplib.Options) *WebDAV { // serve runs the http server - doesn't return func (w *WebDAV) serve() { + err := w.srv.Serve() + if err != nil { + fs.Errorf(w.f, "Opening listener: %v", err) + } fs.Logf(w.f, "WebDav Server started on %s", w.srv.URL()) - w.srv.Serve() + w.srv.Wait() } // logRequest is called by the webdav module on every request diff --git a/fs/rc/rc.go b/fs/rc/rc.go index eef6c169e..4ca73ff6f 100644 --- a/fs/rc/rc.go +++ b/fs/rc/rc.go @@ -58,8 +58,12 @@ func newServer(opt *Options) *server { // serve runs the http server - doesn't return func (s *server) serve() { + err := s.srv.Serve() + if err != nil { + fs.Errorf(nil, "Opening listener: %v", err) + } fs.Logf(nil, "Serving remote control on %s", s.srv.URL()) - s.srv.Serve() + s.srv.Wait() } // handler reads incoming requests and dispatches them