httpserver.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * Public Domain Software
  3. *
  4. * I (Matthias Ladkau) am the author of the source code in this file.
  5. * I have placed the source code in this file in the public domain.
  6. *
  7. * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
  8. */
  9. /*
  10. Package httputil contains a HTTP/HTTPS Server which can be stopped via signals
  11. or a Shutdown() call.
  12. */
  13. package httputil
  14. import (
  15. "crypto/tls"
  16. "errors"
  17. "fmt"
  18. "net"
  19. "net/http"
  20. "os"
  21. "os/signal"
  22. "strings"
  23. "sync"
  24. "syscall"
  25. "time"
  26. )
  27. /*
  28. HTTPServer data structure
  29. */
  30. type HTTPServer struct {
  31. signalling chan os.Signal // Channel for receiving signals
  32. LastError error // Last recorded error
  33. Running bool // Flag if the server is running
  34. listener signalTCPListener // TCP listener of the server
  35. }
  36. /*
  37. Shutdown sends a shutdown signal.
  38. */
  39. func (hs *HTTPServer) Shutdown() {
  40. if hs.signalling != nil {
  41. hs.signalling <- syscall.SIGINT
  42. }
  43. }
  44. /*
  45. RunHTTPServer starts a HTTP Server which can be stopped via ^C (Control-C).
  46. It is assumed that all routes have been added prior to this call.
  47. laddr should be the local address which should be given to net.Listen.
  48. wgStatus is an optional wait group which will be notified once the server is listening
  49. and once the server has shutdown.
  50. This function will not return unless the server is shutdown.
  51. */
  52. func (hs *HTTPServer) RunHTTPServer(laddr string, wgStatus *sync.WaitGroup) error {
  53. hs.Running = false
  54. // Create normal TCP listener
  55. originalListener, err := net.Listen("tcp", laddr)
  56. if err != nil {
  57. hs.LastError = err
  58. if wgStatus != nil {
  59. wgStatus.Done()
  60. }
  61. return err
  62. }
  63. // Wrap listener in a signal aware listener
  64. sl := newSignalTCPListener(originalListener, originalListener.(*net.TCPListener), wgStatus)
  65. return hs.runServer(sl, wgStatus)
  66. }
  67. /*
  68. RunHTTPSServer starts a HTTPS Server which can be stopped via ^C (Control-C).
  69. It is assumed that all routes have been added prior to this call.
  70. keypath should be set to a path containing the TLS certificate and key.
  71. certFile should be the file containing the TLS certificate.
  72. keyFile should be the file containing the private key for the TLS connection.
  73. laddr should be the local address which should be given to net.Listen.
  74. wgStatus is an optional wait group which will be notified once the server is listening
  75. and once the server has shutdown.
  76. This function will not return unless the server is shutdown.
  77. */
  78. func (hs *HTTPServer) RunHTTPSServer(keypath string, certFile string, keyFile string,
  79. laddr string, wgStatus *sync.WaitGroup) error {
  80. // Check parameters
  81. if keypath != "" && !strings.HasSuffix(keypath, "/") {
  82. keypath += "/"
  83. }
  84. // Load key pair and create a TLS config
  85. cert, err := tls.LoadX509KeyPair(keypath+certFile, keypath+keyFile)
  86. if err != nil {
  87. hs.LastError = err
  88. if wgStatus != nil {
  89. wgStatus.Done()
  90. }
  91. return err
  92. }
  93. hs.Running = false
  94. // Create normal TCP listener
  95. originalListener, err := net.Listen("tcp", laddr)
  96. if err != nil {
  97. hs.LastError = err
  98. if wgStatus != nil {
  99. wgStatus.Done()
  100. }
  101. return err
  102. }
  103. // Wrap the listener in a TLS listener
  104. config := tls.Config{Certificates: []tls.Certificate{cert}}
  105. originalTLSListener := tls.NewListener(originalListener, &config)
  106. // Wrap listeners in a signal aware listener
  107. sl := newSignalTCPListener(originalTLSListener, originalListener.(*net.TCPListener), wgStatus)
  108. return hs.runServer(sl, wgStatus)
  109. }
  110. /*
  111. runServer starts the actual server and notifies the wait group.
  112. */
  113. func (hs *HTTPServer) runServer(sl *signalTCPListener, wgStatus *sync.WaitGroup) error {
  114. // Use the http server from the standard library
  115. server := http.Server{}
  116. // Attach SIGINT handler - on unix and windows this is send
  117. // when the user presses ^C (Control-C).
  118. hs.signalling = make(chan os.Signal)
  119. signal.Notify(hs.signalling, syscall.SIGINT)
  120. // Put the serve call into a wait group so we can wait until shutdown
  121. // completed
  122. var wg sync.WaitGroup
  123. wg.Add(1)
  124. go func() {
  125. defer wg.Done()
  126. hs.Running = true
  127. server.Serve(sl)
  128. }()
  129. for true {
  130. signal := <-hs.signalling
  131. if signal == syscall.SIGINT {
  132. // Shutdown the server
  133. sl.Shutdown()
  134. // Wait until the server has shut down
  135. wg.Wait()
  136. hs.Running = false
  137. break
  138. }
  139. }
  140. if wgStatus != nil {
  141. wgStatus.Done()
  142. }
  143. return nil
  144. }
  145. /*
  146. signalTCPListener models a TCPListener which can receive signals.
  147. */
  148. type signalTCPListener struct {
  149. net.Listener // Wrapped new.Listener
  150. tcpListener *net.TCPListener // TCP listener which accepts connections
  151. Signals chan int // Channel used for signalling
  152. wgStatus *sync.WaitGroup // Optional Waitgroup to be notified after start
  153. }
  154. /*
  155. SigShutdown is used to signal a request for shutdown
  156. */
  157. const SigShutdown = 1
  158. /*
  159. ErrSigShutdown indicates that a signal was received
  160. */
  161. var ErrSigShutdown = errors.New("Server was shut down")
  162. /*
  163. newSignalTCPListener wraps a given TCPListener.
  164. */
  165. func newSignalTCPListener(l net.Listener, tl *net.TCPListener, wgStatus *sync.WaitGroup) *signalTCPListener {
  166. return &signalTCPListener{l, tl, make(chan int), wgStatus}
  167. }
  168. /*
  169. Accept waits for a new connection. This accept call will check every
  170. second if a signal or other shutdown event was received.
  171. */
  172. func (sl *signalTCPListener) Accept() (net.Conn, error) {
  173. for {
  174. // Wait up to a second for a new connection
  175. sl.tcpListener.SetDeadline(time.Now().Add(time.Second))
  176. newConn, err := sl.Listener.Accept()
  177. // Notify wgStatus if it was specified
  178. if sl.wgStatus != nil {
  179. sl.wgStatus.Done()
  180. sl.wgStatus = nil
  181. }
  182. // Check for a received signal
  183. select {
  184. case sig := <-sl.Signals:
  185. // Check which signal was received
  186. if sig == SigShutdown {
  187. return nil, ErrSigShutdown
  188. }
  189. panic(fmt.Sprintf("Unknown signal received: %v", sig))
  190. default:
  191. netErr, ok := err.(net.Error)
  192. // If we got a connection or error at this point return it
  193. if (err != nil && (!ok || !(netErr.Timeout() && netErr.Temporary()))) || newConn != nil {
  194. return newConn, err
  195. }
  196. }
  197. }
  198. }
  199. /*
  200. Shutdown sends a shutdown signal.
  201. */
  202. func (sl *signalTCPListener) Shutdown() {
  203. sl.Signals <- SigShutdown
  204. close(sl.Signals)
  205. }