feat!: listen to both http & https (#4536)

Co-authored-by: Andy Hsu <i@nn.ci>
pull/4588/head
BoYanZh 2023-06-10 22:26:09 +08:00 committed by Andy Hsu
parent 363e036bf0
commit f646d2a699
6 changed files with 79 additions and 29 deletions

View File

@ -14,5 +14,5 @@ COPY entrypoint.sh /entrypoint.sh
RUN apk add --no-cache bash ca-certificates su-exec tzdata; \ RUN apk add --no-cache bash ca-certificates su-exec tzdata; \
chmod +x /entrypoint.sh chmod +x /entrypoint.sh
ENV PUID=0 PGID=0 UMASK=022 ENV PUID=0 PGID=0 UMASK=022
EXPOSE 5244 EXPOSE 5244 5245
CMD [ "/entrypoint.sh" ] CMD [ "/entrypoint.sh" ]

View File

@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
"sync"
"syscall" "syscall"
"time" "time"
@ -41,42 +42,62 @@ the address is defined in config file`,
r := gin.New() r := gin.New()
r.Use(gin.LoggerWithWriter(log.StandardLogger().Out), gin.RecoveryWithWriter(log.StandardLogger().Out)) r.Use(gin.LoggerWithWriter(log.StandardLogger().Out), gin.RecoveryWithWriter(log.StandardLogger().Out))
server.Init(r) server.Init(r)
base := fmt.Sprintf("%s:%d", conf.Conf.Address, conf.Conf.Port) var httpSrv, httpsSrv *http.Server
utils.Log.Infof("start server @ %s", base) if !conf.Conf.Scheme.DisableHttp {
srv := &http.Server{Addr: base, Handler: r} httpBase := fmt.Sprintf("%s:%d", conf.Conf.Address, conf.Conf.Port)
go func() { utils.Log.Infof("start HTTP server @ %s", httpBase)
var err error httpSrv = &http.Server{Addr: httpBase, Handler: r}
if conf.Conf.Scheme.Https { go func() {
//err = r.RunTLS(base, conf.Conf.Scheme.CertFile, conf.Conf.Scheme.KeyFile) err := httpSrv.ListenAndServe()
err = srv.ListenAndServeTLS(conf.Conf.Scheme.CertFile, conf.Conf.Scheme.KeyFile) if err != nil && err != http.ErrServerClosed {
} else { utils.Log.Fatalf("failed to start: %s", err.Error())
err = srv.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 // Wait for interrupt signal to gracefully shutdown the server with
// a timeout of 5 seconds. // a timeout of 1 second.
quit := make(chan os.Signal) quit := make(chan os.Signal, 1)
// kill (no param) default send syscanll.SIGTERM // kill (no param) default send syscanll.SIGTERM
// kill -2 is syscall.SIGINT // kill -2 is syscall.SIGINT
// kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it // kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit <-quit
utils.Log.Println("Shutdown Server ...") utils.Log.Println("Shutdown server...")
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() defer cancel()
if err := srv.Shutdown(ctx); err != nil { var wg sync.WaitGroup
utils.Log.Fatal("Server Shutdown:", err) 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. if conf.Conf.Scheme.Https {
select { wg.Add(1)
case <-ctx.Done(): go func() {
utils.Log.Println("timeout of 1 seconds.") 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")
}, },
} }

View File

@ -6,6 +6,7 @@ services:
- '/etc/alist:/opt/alist/data' - '/etc/alist:/opt/alist/data'
ports: ports:
- '5244:5244' - '5244:5244'
- '5245:5245'
environment: environment:
- PUID=0 - PUID=0
- PGID=0 - PGID=0

View File

@ -20,9 +20,11 @@ type Database struct {
} }
type Scheme struct { type Scheme struct {
Https bool `json:"https" env:"HTTPS"` DisableHttp bool `json:"disable_http" env:"DISABLE_HTTP"`
CertFile string `json:"cert_file" env:"CERT_FILE"` Https bool `json:"https" env:"HTTPS"`
KeyFile string `json:"key_file" env:"KEY_FILE"` 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 { type LogConfig struct {
@ -38,6 +40,7 @@ type Config struct {
Force bool `json:"force" env:"FORCE"` Force bool `json:"force" env:"FORCE"`
Address string `json:"address" env:"ADDR"` Address string `json:"address" env:"ADDR"`
Port int `json:"port" env:"PORT"` Port int `json:"port" env:"PORT"`
HttpsPort int `json:"https_port" env:"HTTPS_PORT"`
SiteURL string `json:"site_url" env:"SITE_URL"` SiteURL string `json:"site_url" env:"SITE_URL"`
Cdn string `json:"cdn" env:"CDN"` Cdn string `json:"cdn" env:"CDN"`
JwtSecret string `json:"jwt_secret" env:"JWT_SECRET"` JwtSecret string `json:"jwt_secret" env:"JWT_SECRET"`
@ -60,6 +63,7 @@ func DefaultConfig() *Config {
return &Config{ return &Config{
Address: "0.0.0.0", Address: "0.0.0.0",
Port: 5244, Port: 5244,
HttpsPort: 5245,
JwtSecret: random.String(16), JwtSecret: random.String(16),
TokenExpiresIn: 48, TokenExpiresIn: 48,
TempDir: tempDir, TempDir: tempDir,

View File

@ -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()
}

View File

@ -21,6 +21,9 @@ func Init(e *gin.Engine) {
} }
Cors(e) Cors(e)
g := e.Group(conf.URL.Path) 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) { g.Any("/ping", func(c *gin.Context) {
c.String(200, "pong") c.String(200, "pong")
}) })