diff --git a/Dockerfile b/Dockerfile index 5534aedd0ab..97d1b9e8811 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,5 +14,5 @@ COPY entrypoint.sh /entrypoint.sh RUN apk add --no-cache bash ca-certificates su-exec tzdata; \ chmod +x /entrypoint.sh ENV PUID=0 PGID=0 UMASK=022 -EXPOSE 5244 +EXPOSE 5244 5245 CMD [ "/entrypoint.sh" ] diff --git a/cmd/server.go b/cmd/server.go index 20850f50e37..4b1edd14e8b 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -6,6 +6,7 @@ import ( "net/http" "os" "os/signal" + "sync" "syscall" "time" @@ -41,42 +42,62 @@ the address is defined in config file`, r := gin.New() r.Use(gin.LoggerWithWriter(log.StandardLogger().Out), gin.RecoveryWithWriter(log.StandardLogger().Out)) server.Init(r) - base := fmt.Sprintf("%s:%d", conf.Conf.Address, conf.Conf.Port) - utils.Log.Infof("start server @ %s", base) - srv := &http.Server{Addr: base, Handler: r} - go func() { - var err error - if conf.Conf.Scheme.Https { - //err = r.RunTLS(base, conf.Conf.Scheme.CertFile, conf.Conf.Scheme.KeyFile) - err = srv.ListenAndServeTLS(conf.Conf.Scheme.CertFile, conf.Conf.Scheme.KeyFile) - } else { - err = srv.ListenAndServe() - } - if err != nil && err != http.ErrServerClosed { - utils.Log.Fatalf("failed to start: %s", err.Error()) - } - }() + var httpSrv, httpsSrv *http.Server + if !conf.Conf.Scheme.DisableHttp { + httpBase := fmt.Sprintf("%s:%d", conf.Conf.Address, conf.Conf.Port) + utils.Log.Infof("start HTTP server @ %s", httpBase) + httpSrv = &http.Server{Addr: httpBase, Handler: r} + go func() { + err := httpSrv.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + utils.Log.Fatalf("failed to start: %s", err.Error()) + } + }() + } + if conf.Conf.Scheme.Https { + httpsBase := fmt.Sprintf("%s:%d", conf.Conf.Address, conf.Conf.HttpsPort) + utils.Log.Infof("start HTTPS server @ %s", httpsBase) + httpsSrv = &http.Server{Addr: httpsBase, Handler: r} + go func() { + err := httpsSrv.ListenAndServeTLS(conf.Conf.Scheme.CertFile, conf.Conf.Scheme.KeyFile) + if err != nil && err != http.ErrServerClosed { + utils.Log.Fatalf("failed to start: %s", err.Error()) + } + }() + } // Wait for interrupt signal to gracefully shutdown the server with - // a timeout of 5 seconds. - quit := make(chan os.Signal) + // a timeout of 1 second. + quit := make(chan os.Signal, 1) // kill (no param) default send syscanll.SIGTERM // kill -2 is syscall.SIGINT // kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit - utils.Log.Println("Shutdown Server ...") + utils.Log.Println("Shutdown server...") ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - if err := srv.Shutdown(ctx); err != nil { - utils.Log.Fatal("Server Shutdown:", err) + var wg sync.WaitGroup + if !conf.Conf.Scheme.DisableHttp { + wg.Add(1) + go func() { + defer wg.Done() + if err := httpSrv.Shutdown(ctx); err != nil { + utils.Log.Fatal("HTTP server shutdown:", err) + } + }() } - // catching ctx.Done(). timeout of 3 seconds. - select { - case <-ctx.Done(): - utils.Log.Println("timeout of 1 seconds.") + if conf.Conf.Scheme.Https { + wg.Add(1) + go func() { + defer wg.Done() + if err := httpsSrv.Shutdown(ctx); err != nil { + utils.Log.Fatal("HTTPS server shutdown:", err) + } + }() } - utils.Log.Println("Server exiting") + wg.Wait() + utils.Log.Println("Server exit") }, } diff --git a/docker-compose.yml b/docker-compose.yml index bad9b07ff6e..05e9f8d790f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,6 +6,7 @@ services: - '/etc/alist:/opt/alist/data' ports: - '5244:5244' + - '5245:5245' environment: - PUID=0 - PGID=0 diff --git a/internal/conf/config.go b/internal/conf/config.go index 57e14a2ab2d..d0c8709365d 100644 --- a/internal/conf/config.go +++ b/internal/conf/config.go @@ -20,9 +20,11 @@ type Database struct { } type Scheme struct { - Https bool `json:"https" env:"HTTPS"` - CertFile string `json:"cert_file" env:"CERT_FILE"` - KeyFile string `json:"key_file" env:"KEY_FILE"` + DisableHttp bool `json:"disable_http" env:"DISABLE_HTTP"` + Https bool `json:"https" env:"HTTPS"` + ForceHttps bool `json:"force_https" env:"FORCE_HTTPS"` + CertFile string `json:"cert_file" env:"CERT_FILE"` + KeyFile string `json:"key_file" env:"KEY_FILE"` } type LogConfig struct { @@ -38,6 +40,7 @@ type Config struct { Force bool `json:"force" env:"FORCE"` Address string `json:"address" env:"ADDR"` Port int `json:"port" env:"PORT"` + HttpsPort int `json:"https_port" env:"HTTPS_PORT"` SiteURL string `json:"site_url" env:"SITE_URL"` Cdn string `json:"cdn" env:"CDN"` JwtSecret string `json:"jwt_secret" env:"JWT_SECRET"` @@ -60,6 +63,7 @@ func DefaultConfig() *Config { return &Config{ Address: "0.0.0.0", Port: 5244, + HttpsPort: 5245, JwtSecret: random.String(16), TokenExpiresIn: 48, TempDir: tempDir, diff --git a/server/middlewares/https.go b/server/middlewares/https.go new file mode 100644 index 00000000000..bb628be1e7d --- /dev/null +++ b/server/middlewares/https.go @@ -0,0 +1,21 @@ +package middlewares + +import ( + "fmt" + "strings" + + "github.com/alist-org/alist/v3/internal/conf" + "github.com/gin-gonic/gin" +) + +func ForceHttps(c *gin.Context) { + if c.Request.TLS == nil { + host := c.Request.Host + // change port to https port + host = strings.Replace(host, fmt.Sprintf(":%d", conf.Conf.Port), fmt.Sprintf(":%d", conf.Conf.HttpsPort), 1) + c.Redirect(302, "https://"+host+c.Request.RequestURI) + c.Abort() + return + } + c.Next() +} diff --git a/server/router.go b/server/router.go index 213eae43bd8..62f4fe7c8a8 100644 --- a/server/router.go +++ b/server/router.go @@ -21,6 +21,9 @@ func Init(e *gin.Engine) { } Cors(e) g := e.Group(conf.URL.Path) + if conf.Conf.Scheme.Https && conf.Conf.Scheme.ForceHttps && !conf.Conf.Scheme.DisableHttp { + g.Use(middlewares.ForceHttps) + } g.Any("/ping", func(c *gin.Context) { c.String(200, "pong") })