5824: Move HTTP server code to SDK.
authorTom Clegg <tom@curoverse.com>
Fri, 12 Jun 2015 05:01:52 +0000 (01:01 -0400)
committerTom Clegg <tom@curoverse.com>
Tue, 11 Aug 2015 00:25:44 +0000 (20:25 -0400)
sdk/go/httpserver/httpserver.go [new file with mode: 0644]
services/arv-git-httpd/server.go

diff --git a/sdk/go/httpserver/httpserver.go b/sdk/go/httpserver/httpserver.go
new file mode 100644 (file)
index 0000000..396fe42
--- /dev/null
@@ -0,0 +1,87 @@
+package httpserver
+
+import (
+       "net"
+       "net/http"
+       "sync"
+       "time"
+)
+
+type Server struct {
+       http.Server
+       Addr     string // host:port where the server is listening.
+       err      error
+       cond     *sync.Cond
+       running  bool
+       listener *net.TCPListener
+       wantDown bool
+}
+
+// Start is essentially (*http.Server)ListenAndServe() with two more
+// features: (1) by the time Start() returns, Addr is changed to the
+// address:port we ended up listening to -- which makes listening on
+// ":0" useful in test suites -- and (2) the server can be shut down
+// without killing the process -- which is useful in test cases, and
+// makes it possible to shut down gracefully on SIGTERM without
+// killing active connections.
+func (srv *Server) Start() error {
+       addr, err := net.ResolveTCPAddr("tcp", srv.Addr)
+       if err != nil {
+               return err
+       }
+       srv.listener, err = net.ListenTCP("tcp", addr)
+       if err != nil {
+               return err
+       }
+       srv.Addr = srv.listener.Addr().String()
+
+       mutex := &sync.RWMutex{}
+       srv.cond = sync.NewCond(mutex.RLocker())
+       srv.running = true
+       go func() {
+               err = srv.Serve(tcpKeepAliveListener{srv.listener})
+               if !srv.wantDown {
+                       srv.err = err
+               }
+               mutex.Lock()
+               srv.running = false
+               srv.cond.Broadcast()
+               mutex.Unlock()
+       }()
+       return nil
+}
+
+// Close shuts down the server and returns when it has stopped.
+func (srv *Server) Close() error {
+       srv.wantDown = true
+       srv.listener.Close()
+       return srv.Wait()
+}
+
+// Wait returns when the server has shut down.
+func (srv *Server) Wait() error {
+       if srv.cond == nil {
+               return nil
+       }
+       srv.cond.L.Lock()
+       defer srv.cond.L.Unlock()
+       for srv.running {
+               srv.cond.Wait()
+       }
+       return srv.err
+}
+
+// tcpKeepAliveListener is copied from net/http because not exported.
+type tcpKeepAliveListener struct {
+       *net.TCPListener
+}
+
+func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
+       tc, err := ln.AcceptTCP()
+       if err != nil {
+               return
+       }
+       tc.SetKeepAlive(true)
+       tc.SetKeepAlivePeriod(3 * time.Minute)
+       return tc, nil
+}
index 716b2760f3a890860138580b03be7f262f20d7c5..9e804810758d2d077ea4ba14c0ba6d126a4a8d0e 100644 (file)
@@ -1,21 +1,13 @@
 package main
 
 import (
-       "net"
        "net/http"
        "net/http/cgi"
-       "sync"
-       "time"
+       "git.curoverse.com/arvados.git/sdk/go/httpserver"
 )
 
 type server struct {
-       http.Server
-       Addr     string // host:port where the server is listening.
-       err      error
-       cond     *sync.Cond
-       running  bool
-       listener *net.TCPListener
-       wantDown bool
+       httpserver.Server
 }
 
 func (srv *server) Start() error {
@@ -29,77 +21,9 @@ func (srv *server) Start() error {
                InheritEnv: []string{"PATH"},
                Args:       []string{"http-backend"},
        }
-
-       // The rest of the work here is essentially
-       // http.ListenAndServe() with two more features: (1) whoever
-       // called Start() can discover which address:port we end up
-       // listening to -- which makes listening on ":0" useful in
-       // test suites -- and (2) the server can be shut down without
-       // killing the process -- which is useful in test cases, and
-       // makes it possible to shut down gracefully on SIGTERM
-       // without killing active connections.
-
-       addr, err := net.ResolveTCPAddr("tcp", theConfig.Addr)
-       if err != nil {
-               return err
-       }
-       srv.listener, err = net.ListenTCP("tcp", addr)
-       if err != nil {
-               return err
-       }
-       srv.Addr = srv.listener.Addr().String()
        mux := http.NewServeMux()
        mux.Handle("/", &authHandler{gitHandler})
        srv.Handler = mux
-
-       mutex := &sync.RWMutex{}
-       srv.cond = sync.NewCond(mutex.RLocker())
-       srv.running = true
-       go func() {
-               err = srv.Serve(tcpKeepAliveListener{srv.listener})
-               if !srv.wantDown {
-                       srv.err = err
-               }
-               mutex.Lock()
-               srv.running = false
-               srv.cond.Broadcast()
-               mutex.Unlock()
-       }()
-       return nil
-}
-
-// Wait returns when the server has shut down.
-func (srv *server) Wait() error {
-       if srv.cond == nil {
-               return nil
-       }
-       srv.cond.L.Lock()
-       defer srv.cond.L.Unlock()
-       for srv.running {
-               srv.cond.Wait()
-       }
-       return srv.err
-}
-
-// Close shuts down the server and returns when it has stopped.
-func (srv *server) Close() error {
-       srv.wantDown = true
-       srv.listener.Close()
-       return srv.Wait()
-}
-
-// tcpKeepAliveListener is copied from net/http because not exported.
-//
-type tcpKeepAliveListener struct {
-       *net.TCPListener
-}
-
-func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
-       tc, err := ln.AcceptTCP()
-       if err != nil {
-               return
-       }
-       tc.SetKeepAlive(true)
-       tc.SetKeepAlivePeriod(3 * time.Minute)
-       return tc, nil
+       srv.Addr = theConfig.Addr
+       return srv.Server.Start()
 }