Capstan is a Golang web framework that shares some similarities with others in its segment.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

373 lines
10KB

  1. package capstan
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "crypto/x509"
  6. "fmt"
  7. "net"
  8. "net/http"
  9. "strings"
  10. "time"
  11. "git.destrealm.org/go/capstan/internal/sys"
  12. )
  13. // TODO: Consider refactoring named listeners to store their network address
  14. // instead.
  15. // Apply http.Server instances to all stored listeners. Errors will be returned
  16. // via the error channel errc.
  17. //
  18. // This increments the Router waitgroup once for each configured listener.
  19. func (r *Router) listen(errc chan error) {
  20. for _, listener := range r.listeners {
  21. r.AttachListener(listener)
  22. }
  23. }
  24. // Setup listeners to all configured addresses. Currently this only sets up 3
  25. // listeners for each of the following, if configured: ListenAddress,
  26. // ListenAddressTLS, and ListenSocket.
  27. //
  28. // If you wish to attach custom listeners, you must call AddListener (below) and
  29. // it should implement both net.Listener and syscall.Conn. While simply
  30. // implementing net.Listener should be sufficient, it may fail for high
  31. // availability configurations that expect graceful restart capabilities.
  32. func (r *Router) setupListeners() error {
  33. var requiresPriv bool
  34. config := r.app.Config().Server
  35. r.proxy.Switch(r.mux)
  36. if len(r.listeners) == 0 {
  37. requiresPriv = isPrivPort(r.app.Config().Server.ListenAddress)
  38. if listener, err := r.ListenHTTP(r.app.Config().Server.ListenAddress); err != nil {
  39. return err
  40. } else {
  41. r.namedListeners.tcp = listener
  42. r.AddListener(listener)
  43. }
  44. requiresPriv = isPrivPort(r.app.Config().Server.ListenAddressTLS)
  45. if listener, err := r.ListenTLS(r.app.Config().Server.ListenAddressTLS); err != nil {
  46. return err
  47. } else {
  48. r.namedListeners.tls = listener
  49. r.AddListener(listener)
  50. }
  51. if listener, err := r.ListenSocket(r.app.Config().Server.ListenSocket); err != nil {
  52. return err
  53. } else {
  54. r.namedListeners.sock = listener
  55. r.AddListener(listener)
  56. }
  57. }
  58. if requiresPriv && sys.HasSuperuser() {
  59. if config.RunAs == "" {
  60. return fmt.Errorf("application is running as superuser with no RunAs configured")
  61. }
  62. err := sys.SwitchUser(config.RunAs)
  63. if err != nil {
  64. return fmt.Errorf("unable to drop privileges: %v", err)
  65. }
  66. }
  67. return nil
  68. }
  69. // AddListener accepts a listener against which the Chi handler will be attached
  70. // and will be used as the base listener for an HTTP or HTTPS service.
  71. //
  72. // This listener will be mapped to the configured network management as defined
  73. // in network.go and netlisten_*.go. If the specified listener does not
  74. // implement both net.Listen and syscall.Conn it will not be used for high
  75. // availability graceful restart. If you depend on a custom listener, you may
  76. // need to implement graceful restart yourself or implement syscall.Conn.
  77. func (r *Router) AddListener(listener net.Listener) {
  78. if listener != nil {
  79. r.listeners = append(r.listeners, listener)
  80. }
  81. }
  82. // AttachListener to the current route handler.
  83. //
  84. // This may be used by external code to enable listeners.
  85. func (r *Router) AttachListener(listener net.Listener) {
  86. var server HTTPServer
  87. server.Handler = r.proxy
  88. server.ReadHeaderTimeout = time.Second * time.Duration(r.app.Config().Server.HeaderTimeout)
  89. server.IdleTimeout = time.Second * time.Duration(r.app.Config().Server.IdleConnectionTimeout)
  90. server.MaxHeaderBytes = r.app.Config().Server.MaxHeaderLength
  91. r.servers = append(r.servers, &server)
  92. r.wg.Add(1)
  93. go func(l net.Listener) {
  94. if err := server.Serve(l); err != http.ErrServerClosed {
  95. r.Close()
  96. }
  97. r.wg.Done()
  98. }(listener)
  99. }
  100. func (r *Router) AttachNamedListener(name string, listener net.Listener) {
  101. switch name {
  102. case "tls":
  103. r.namedListeners.tls = listener
  104. case "tcp":
  105. r.namedListeners.tcp = listener
  106. case "sock":
  107. fallthrough
  108. case "socket":
  109. fallthrough
  110. case "file":
  111. r.namedListeners.sock = listener
  112. }
  113. r.AttachListener(listener)
  114. }
  115. // Close all configured listeners gracefully. This calls Shutdown on each
  116. // http.Server and waits until all connections have closed before terminating.
  117. func (r *Router) Close() {
  118. r.app.Logger().Noticef("Shutting down (waiting max %d second(s) for connections to close)...", r.app.Config().Server.ShutdownTimeout)
  119. for _, server := range r.servers {
  120. ctx, cancel := context.WithTimeout(r.shutdownCtx,
  121. time.Second*time.Duration(r.app.Config().Server.ShutdownTimeout))
  122. defer cancel()
  123. r.app.Logger().Debugf("closing listener: (%s) %s", server.Listener().Addr().Network(), server.Listener().Addr().String())
  124. err := server.Shutdown(ctx)
  125. if err != nil {
  126. r.app.Logger().Warning("Error shutting down server:", err)
  127. }
  128. }
  129. for _, listener := range r.listeners {
  130. listener.Close()
  131. }
  132. }
  133. // CloseNamed listener.
  134. //
  135. // This will gracefully shut down the named listener, if available. The special
  136. // names "tls," "tcp," and "file" are used to close reserved listeners for TLS,
  137. // TCP, and Unix domain socket traffic.
  138. //
  139. // This will also remove the listener from the router's listeners store.
  140. func (r *Router) CloseNamed(name string) (err error) {
  141. var listener net.Listener
  142. switch name {
  143. case "tls":
  144. listener = r.namedListeners.tls
  145. case "tcp":
  146. listener = r.namedListeners.tcp
  147. case "sock":
  148. fallthrough
  149. case "socket":
  150. fallthrough
  151. case "file":
  152. listener = r.namedListeners.sock
  153. }
  154. if listener == nil {
  155. return
  156. }
  157. l := make([]net.Listener, 0)
  158. for _, mapped := range r.listeners {
  159. if mapped != listener {
  160. l = append(l, mapped)
  161. }
  162. }
  163. r.listeners = l
  164. r.app.Logger().Error("Shutting down listener (waiting max 10 seconds for connections to close)...")
  165. ctx, _ := context.WithTimeout(r.shutdownCtx, time.Second*10)
  166. for _, server := range r.servers {
  167. if server.Listener() == listener {
  168. if err = server.Shutdown(ctx); err != nil {
  169. r.app.Logger().Warning("error shutting down listener:", err)
  170. return
  171. } else {
  172. StopListening(listener)
  173. }
  174. }
  175. }
  176. switch name {
  177. case "tls":
  178. r.namedListeners.tls = nil
  179. case "tcp":
  180. r.namedListeners.tcp = nil
  181. case "sock":
  182. fallthrough
  183. case "socket":
  184. fallthrough
  185. case "file":
  186. r.namedListeners.sock = nil
  187. }
  188. return
  189. }
  190. // Listen on all configured listeners and wait until they close.
  191. //
  192. // If an error is received by this function it will call Router.Close itself and
  193. // begin the shutdown process.
  194. func (r *Router) Listen() error {
  195. var err error
  196. errc := make(chan error)
  197. if err := r.setupListeners(); err != nil {
  198. return err
  199. }
  200. r.listen(errc)
  201. go func() {
  202. err = <-errc
  203. if err != nil {
  204. r.Close()
  205. r.wg.Done()
  206. return
  207. }
  208. }()
  209. r.wg.Wait()
  210. return err
  211. }
  212. // ListenHTTP allows client code to configure a new port to listen on. This
  213. // should not be called if the server is already listening on an HTTP port. This
  214. // is called internally if ListenAddress is set.
  215. func (r *Router) ListenHTTP(addr string) (net.Listener, error) {
  216. // An HTTP port most liekly wasn't specified.
  217. if addr == "" {
  218. return nil, nil
  219. }
  220. return Listen("tcp", addr)
  221. }
  222. // ListenSocket allows client code to configure a new UNIX domain socket to
  223. // listen on. This should not be called if the server is already listening on
  224. // the same socket. This is called internally if ListenSocket is set.
  225. func (r *Router) ListenSocket(sock string) (net.Listener, error) {
  226. if sock == "" {
  227. return nil, nil
  228. }
  229. return Listen("unix", sock)
  230. }
  231. // ListenTLS allows client code to configure a new TLS port to listen on. This
  232. // should not be called if the server is already listening on a TLS port. This
  233. // is called internally if ListenAddressTLS is set.
  234. func (r *Router) ListenTLS(addr string) (net.Listener, error) {
  235. // TLS most likely wasn't specified.
  236. if addr == "" {
  237. return nil, nil
  238. }
  239. var err error
  240. var tc *tls.Config
  241. var pool *x509.CertPool
  242. ciphers := []uint16{
  243. tls.TLS_AES_256_GCM_SHA384,
  244. tls.TLS_AES_128_GCM_SHA256,
  245. tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
  246. tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
  247. tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
  248. tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
  249. tls.TLS_CHACHA20_POLY1305_SHA256,
  250. tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
  251. tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
  252. tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
  253. // No forward secrecy but disabling these may reduce client
  254. // compatibility.
  255. tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
  256. tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
  257. }
  258. config := r.app.Config().Server
  259. if config.TLSConfig != nil {
  260. tc = config.TLSConfig
  261. } else {
  262. host := config.CalculatedHostname()
  263. if strings.Contains(host, ":") {
  264. host, _, err = net.SplitHostPort(host)
  265. if err != nil && host == "" {
  266. host = "localhost"
  267. } else if err != nil {
  268. return nil, err
  269. }
  270. }
  271. if config.MinVersion == 0 {
  272. config.MinVersion = tls.VersionTLS11
  273. }
  274. if config.MaxVersion == 0 {
  275. config.MaxVersion = tls.VersionTLS13
  276. }
  277. pool = x509.NewCertPool()
  278. tc = &tls.Config{
  279. Certificates: config.Certificates,
  280. RootCAs: pool,
  281. ServerName: host,
  282. MinVersion: config.MinVersion,
  283. MaxVersion: config.MaxVersion,
  284. CipherSuites: ciphers,
  285. NextProtos: []string{"h2"},
  286. // https://blog.cloudflare.com/exposing-go-on-the-internet/
  287. PreferServerCipherSuites: true,
  288. CurvePreferences: []tls.CurveID{
  289. tls.CurveP256,
  290. tls.X25519,
  291. },
  292. }
  293. }
  294. listener, err := Listen("tcp", addr)
  295. if err != nil {
  296. return nil, err
  297. }
  298. return tls.NewListener(listener, tc), nil
  299. }
  300. // HasSocket returns true if Capstan was configured to listen on a domain
  301. // socket.
  302. //
  303. // As with other Has* functions for listeners, this does not indicate a status
  304. // for manually-configured listeners and applies only to those that were started
  305. // via the Listen* methods (either automatically, via configuration, or
  306. // manually).
  307. func (r *Router) HasSocket() bool {
  308. return r.namedListeners.sock != nil
  309. }
  310. // HasTCP returns true if Capstan was configured to listen on a TCP socket.
  311. //
  312. // As with other Has* functions for listeners, this does not indicate a status
  313. // for manually-configured listeners and applies only to those that were started
  314. // via the Listen* methods (either automatically, via configuration, or
  315. // manually).
  316. func (r *Router) HasTCP() bool {
  317. return r.namedListeners.tcp != nil
  318. }
  319. // HasTLS returns true if Capstan was configured to listen on a TLS socket.
  320. //
  321. // As with other Has* functions for listeners, this does not indicate a status
  322. // for manually-configured listeners and applies only to those that were started
  323. // via the Listen* methods (either automatically, via configuration, or
  324. // manually).
  325. func (r *Router) HasTLS() bool {
  326. return r.namedListeners.tls != nil
  327. }