httpd.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. // Copyright 2018 Frédéric Guillot. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package httpd // import "miniflux.app/service/httpd"
  5. import (
  6. "crypto/tls"
  7. "net/http"
  8. "time"
  9. "miniflux.app/api"
  10. "miniflux.app/config"
  11. "miniflux.app/fever"
  12. "miniflux.app/logger"
  13. "miniflux.app/middleware"
  14. "miniflux.app/reader/feed"
  15. "miniflux.app/storage"
  16. "miniflux.app/ui"
  17. "miniflux.app/worker"
  18. "github.com/gorilla/mux"
  19. "golang.org/x/crypto/acme/autocert"
  20. )
  21. // Serve starts a new HTTP server.
  22. func Serve(cfg *config.Config, store *storage.Storage, pool *worker.Pool, feedHandler *feed.Handler) *http.Server {
  23. certFile := cfg.CertFile()
  24. keyFile := cfg.KeyFile()
  25. certDomain := cfg.CertDomain()
  26. certCache := cfg.CertCache()
  27. server := &http.Server{
  28. ReadTimeout: 30 * time.Second,
  29. WriteTimeout: 30 * time.Second,
  30. IdleTimeout: 60 * time.Second,
  31. Addr: cfg.ListenAddr(),
  32. Handler: setupHandler(cfg, store, feedHandler, pool),
  33. }
  34. if certDomain != "" && certCache != "" {
  35. cfg.IsHTTPS = true
  36. startAutoCertTLSServer(server, certDomain, certCache)
  37. } else if certFile != "" && keyFile != "" {
  38. cfg.IsHTTPS = true
  39. startTLSServer(server, certFile, keyFile)
  40. } else {
  41. startHTTPServer(server)
  42. }
  43. return server
  44. }
  45. func startAutoCertTLSServer(server *http.Server, certDomain, certCache string) {
  46. server.Addr = ":https"
  47. certManager := autocert.Manager{
  48. Cache: autocert.DirCache(certCache),
  49. Prompt: autocert.AcceptTOS,
  50. HostPolicy: autocert.HostWhitelist(certDomain),
  51. }
  52. // Handle http-01 challenge.
  53. s := &http.Server{
  54. Handler: certManager.HTTPHandler(nil),
  55. Addr: ":http",
  56. }
  57. go s.ListenAndServe()
  58. go func() {
  59. logger.Info(`Listening on %q by using auto-configured certificate for %q`, server.Addr, certDomain)
  60. if err := server.Serve(certManager.Listener()); err != http.ErrServerClosed {
  61. logger.Fatal(`Server failed to start: %v`, err)
  62. }
  63. }()
  64. }
  65. func startTLSServer(server *http.Server, certFile, keyFile string) {
  66. // See https://blog.cloudflare.com/exposing-go-on-the-internet/
  67. // And https://wiki.mozilla.org/Security/Server_Side_TLS
  68. server.TLSConfig = &tls.Config{
  69. MinVersion: tls.VersionTLS12,
  70. PreferServerCipherSuites: true,
  71. CurvePreferences: []tls.CurveID{
  72. tls.CurveP256,
  73. tls.X25519,
  74. },
  75. CipherSuites: []uint16{
  76. tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
  77. tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
  78. tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
  79. tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
  80. tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
  81. tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
  82. },
  83. }
  84. go func() {
  85. logger.Info(`Listening on %q by using certificate %q and key %q`, server.Addr, certFile, keyFile)
  86. if err := server.ListenAndServeTLS(certFile, keyFile); err != http.ErrServerClosed {
  87. logger.Fatal(`Server failed to start: %v`, err)
  88. }
  89. }()
  90. }
  91. func startHTTPServer(server *http.Server) {
  92. go func() {
  93. logger.Info(`Listening on %q without TLS`, server.Addr)
  94. if err := server.ListenAndServe(); err != http.ErrServerClosed {
  95. logger.Fatal(`Server failed to start: %v`, err)
  96. }
  97. }()
  98. }
  99. func setupHandler(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handler, pool *worker.Pool) *mux.Router {
  100. router := mux.NewRouter()
  101. middleware := middleware.New(cfg, store, router)
  102. if cfg.BasePath() != "" {
  103. router = router.PathPrefix(cfg.BasePath()).Subrouter()
  104. }
  105. router.Use(middleware.ClientIP)
  106. router.Use(middleware.HeaderConfig)
  107. router.Use(middleware.Logging)
  108. fever.Serve(router, cfg, store)
  109. api.Serve(router, store, feedHandler)
  110. ui.Serve(router, cfg, store, pool, feedHandler)
  111. return router
  112. }