aboutsummaryrefslogtreecommitdiffhomepage
path: root/service
diff options
context:
space:
mode:
authorGravatar Frédéric Guillot <fred@miniflux.net>2018-11-11 15:32:48 -0800
committerGravatar Frédéric Guillot <fred@miniflux.net>2018-11-11 15:32:48 -0800
commit487852f07eb191ef56967b7b7d7f01537a55eabd (patch)
tree0c5194c4371392128917a0860ac7a6b00f7784a2 /service
parentca45765c46de3b00c3b22936c885b57c37186e3d (diff)
Replace daemon and scheduler package with service package
Diffstat (limited to 'service')
-rw-r--r--service/httpd/doc.go10
-rw-r--r--service/httpd/httpd.go130
-rw-r--r--service/scheduler/doc.go10
-rw-r--r--service/scheduler/scheduler.go46
4 files changed, 196 insertions, 0 deletions
diff --git a/service/httpd/doc.go b/service/httpd/doc.go
new file mode 100644
index 0000000..8436b2b
--- /dev/null
+++ b/service/httpd/doc.go
@@ -0,0 +1,10 @@
+// Copyright 2018 Frédéric Guillot. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+/*
+
+Package httpd implements the HTTP service.
+
+*/
+package httpd // import "miniflux.app/service/httpd"
diff --git a/service/httpd/httpd.go b/service/httpd/httpd.go
new file mode 100644
index 0000000..4ab7cfd
--- /dev/null
+++ b/service/httpd/httpd.go
@@ -0,0 +1,130 @@
+// Copyright 2018 Frédéric Guillot. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package httpd // import "miniflux.app/service/httpd"
+
+import (
+ "crypto/tls"
+ "net/http"
+ "time"
+
+ "miniflux.app/api"
+ "miniflux.app/config"
+ "miniflux.app/fever"
+ "miniflux.app/logger"
+ "miniflux.app/middleware"
+ "miniflux.app/reader/feed"
+ "miniflux.app/storage"
+ "miniflux.app/ui"
+ "miniflux.app/worker"
+
+ "github.com/gorilla/mux"
+ "golang.org/x/crypto/acme/autocert"
+)
+
+// Serve starts a new HTTP server.
+func Serve(cfg *config.Config, store *storage.Storage, pool *worker.Pool, feedHandler *feed.Handler) *http.Server {
+ certFile := cfg.CertFile()
+ keyFile := cfg.KeyFile()
+ certDomain := cfg.CertDomain()
+ certCache := cfg.CertCache()
+ server := &http.Server{
+ ReadTimeout: 30 * time.Second,
+ WriteTimeout: 30 * time.Second,
+ IdleTimeout: 60 * time.Second,
+ Addr: cfg.ListenAddr(),
+ Handler: setupHandler(cfg, store, feedHandler, pool),
+ }
+
+ if certDomain != "" && certCache != "" {
+ cfg.IsHTTPS = true
+ startAutoCertTLSServer(server, certDomain, certCache)
+ } else if certFile != "" && keyFile != "" {
+ cfg.IsHTTPS = true
+ startTLSServer(server, certFile, keyFile)
+ } else {
+ startHTTPServer(server)
+ }
+
+ return server
+}
+
+func startAutoCertTLSServer(server *http.Server, certDomain, certCache string) {
+ server.Addr = ":https"
+ certManager := autocert.Manager{
+ Cache: autocert.DirCache(certCache),
+ Prompt: autocert.AcceptTOS,
+ HostPolicy: autocert.HostWhitelist(certDomain),
+ }
+
+ // Handle http-01 challenge.
+ s := &http.Server{
+ Handler: certManager.HTTPHandler(nil),
+ Addr: ":http",
+ }
+ go s.ListenAndServe()
+
+ go func() {
+ logger.Info(`Listening on %q by using auto-configured certificate for %q`, server.Addr, certDomain)
+ if err := server.Serve(certManager.Listener()); err != http.ErrServerClosed {
+ logger.Fatal(`Server failed to start: %v`, err)
+ }
+ }()
+}
+
+func startTLSServer(server *http.Server, certFile, keyFile string) {
+ // See https://blog.cloudflare.com/exposing-go-on-the-internet/
+ // And https://wiki.mozilla.org/Security/Server_Side_TLS
+ server.TLSConfig = &tls.Config{
+ MinVersion: tls.VersionTLS12,
+ PreferServerCipherSuites: true,
+ CurvePreferences: []tls.CurveID{
+ tls.CurveP256,
+ tls.X25519,
+ },
+ CipherSuites: []uint16{
+ tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+ tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+ tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ }
+
+ go func() {
+ logger.Info(`Listening on %q by using certificate %q and key %q`, server.Addr, certFile, keyFile)
+ if err := server.ListenAndServeTLS(certFile, keyFile); err != http.ErrServerClosed {
+ logger.Fatal(`Server failed to start: %v`, err)
+ }
+ }()
+}
+
+func startHTTPServer(server *http.Server) {
+ go func() {
+ logger.Info(`Listening on %q without TLS`, server.Addr)
+ if err := server.ListenAndServe(); err != http.ErrServerClosed {
+ logger.Fatal(`Server failed to start: %v`, err)
+ }
+ }()
+}
+
+func setupHandler(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handler, pool *worker.Pool) *mux.Router {
+ router := mux.NewRouter()
+ middleware := middleware.New(cfg, store, router)
+
+ if cfg.BasePath() != "" {
+ router = router.PathPrefix(cfg.BasePath()).Subrouter()
+ }
+
+ router.Use(middleware.ClientIP)
+ router.Use(middleware.HeaderConfig)
+ router.Use(middleware.Logging)
+
+ fever.Serve(router, cfg, store)
+ api.Serve(router, store, feedHandler)
+ ui.Serve(router, cfg, store, pool, feedHandler)
+
+ return router
+}
diff --git a/service/scheduler/doc.go b/service/scheduler/doc.go
new file mode 100644
index 0000000..94615ad
--- /dev/null
+++ b/service/scheduler/doc.go
@@ -0,0 +1,10 @@
+// Copyright 2018 Frédéric Guillot. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+/*
+
+Package scheduler implements the scheduler service.
+
+*/
+package scheduler // import "miniflux.app/service/scheduler"
diff --git a/service/scheduler/scheduler.go b/service/scheduler/scheduler.go
new file mode 100644
index 0000000..31c63e7
--- /dev/null
+++ b/service/scheduler/scheduler.go
@@ -0,0 +1,46 @@
+// Copyright 2018 Frédéric Guillot. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package scheduler // import "miniflux.app/service/scheduler"
+
+import (
+ "time"
+
+ "miniflux.app/config"
+ "miniflux.app/logger"
+ "miniflux.app/storage"
+ "miniflux.app/worker"
+)
+
+// Serve starts the internal scheduler.
+func Serve(cfg *config.Config, store *storage.Storage, pool *worker.Pool) {
+ go feedScheduler(store, pool, cfg.PollingFrequency(), cfg.BatchSize())
+ go cleanupScheduler(store, cfg.CleanupFrequency())
+}
+
+func feedScheduler(store *storage.Storage, pool *worker.Pool, frequency, batchSize int) {
+ c := time.Tick(time.Duration(frequency) * time.Minute)
+ for range c {
+ jobs, err := store.NewBatch(batchSize)
+ if err != nil {
+ logger.Error("[Scheduler:Feed] %v", err)
+ } else {
+ logger.Debug("[Scheduler:Feed] Pushing %d jobs", len(jobs))
+ pool.Push(jobs)
+ }
+ }
+}
+
+func cleanupScheduler(store *storage.Storage, frequency int) {
+ c := time.Tick(time.Duration(frequency) * time.Hour)
+ for range c {
+ nbSessions := store.CleanOldSessions()
+ nbUserSessions := store.CleanOldUserSessions()
+ logger.Info("[Scheduler:Cleanup] Cleaned %d sessions and %d user sessions", nbSessions, nbUserSessions)
+
+ if err := store.ArchiveEntries(); err != nil {
+ logger.Error("[Scheduler:Cleanup] %v", err)
+ }
+ }
+}