From 487852f07eb191ef56967b7b7d7f01537a55eabd Mon Sep 17 00:00:00 2001 From: Frédéric Guillot Date: Sun, 11 Nov 2018 15:32:48 -0800 Subject: Replace daemon and scheduler package with service package --- service/httpd/doc.go | 10 ++++ service/httpd/httpd.go | 130 +++++++++++++++++++++++++++++++++++++++++ service/scheduler/doc.go | 10 ++++ service/scheduler/scheduler.go | 46 +++++++++++++++ 4 files changed, 196 insertions(+) create mode 100644 service/httpd/doc.go create mode 100644 service/httpd/httpd.go create mode 100644 service/scheduler/doc.go create mode 100644 service/scheduler/scheduler.go (limited to 'service') 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) + } + } +} -- cgit v1.2.3