fix: keep RunShutdown on HTTP path

This commit is contained in:
wjqserver 2026-04-07 07:46:06 +08:00
parent 863f984990
commit d12e887858
2 changed files with 94 additions and 14 deletions

View file

@ -46,26 +46,30 @@ func getShutdownTimeout(timeouts []time.Duration) time.Duration {
return defaultShutdownTimeout
}
// serveServer 根据显式指定的启动模式运行 HTTP 或 HTTPS 服务器.
func serveServer(srv *http.Server, serveTLS bool) error {
if serveTLS {
// 对于 HTTPS 服务器,如果 srv.TLSConfig.Certificates 已配置,
// ListenAndServeTLS 的前两个参数可以为空字符串
return srv.ListenAndServeTLS("", "")
}
return srv.ListenAndServe()
}
// runServer 是一个内部辅助函数,负责在一个新的 goroutine 中启动一个 http.Server,
// 并处理其启动失败的致命错误
// serverType 用于在日志中标识服务器类型 (例如 "HTTP", "HTTPS")
func runServer(serverType string, srv *http.Server) {
func runServer(serverType string, srv *http.Server, serveTLS bool) {
go func() {
var err error
protocol := "http"
if srv.TLSConfig != nil {
if serveTLS {
protocol = "https"
}
log.Printf("Touka %s server listening on %s://%s", serverType, protocol, srv.Addr)
if srv.TLSConfig != nil {
// 对于 HTTPS 服务器,如果 srv.TLSConfig.Certificates 已配置,
// ListenAndServeTLS 的前两个参数可以为空字符串
err = srv.ListenAndServeTLS("", "")
} else {
err = srv.ListenAndServe()
}
err := serveServer(srv, serveTLS)
// 如果服务器停止不是因为被优雅关闭 (http.ErrServerClosed),
// 则认为是一个严重错误,并终止程序
@ -236,7 +240,7 @@ func (engine *Engine) RunShutdown(addr string, timeouts ...time.Duration) error
engine.ServerConfigurator(srv)
}
runServer("HTTP", srv)
runServer("HTTP", srv, false)
return handleGracefulShutdown([]*http.Server{srv}, getShutdownTimeout(timeouts), engine.LogReco)
}
@ -293,7 +297,7 @@ func (engine *Engine) RunTLS(addr string, tlsConfig *tls.Config, timeouts ...tim
engine.ServerConfigurator(srv)
}
runServer("HTTPS", srv)
runServer("HTTPS", srv, true)
return handleGracefulShutdown([]*http.Server{srv}, getShutdownTimeout(timeouts), engine.LogReco)
}
@ -361,8 +365,8 @@ func (engine *Engine) RunTLSRedir(httpAddr, httpsAddr string, tlsConfig *tls.Con
}
// --- 启动服务器和优雅关闭 ---
runServer("HTTPS", httpsSrv)
runServer("HTTP Redirect", httpSrv)
runServer("HTTPS", httpsSrv, true)
runServer("HTTP Redirect", httpSrv, false)
return handleGracefulShutdown([]*http.Server{httpsSrv, httpSrv}, getShutdownTimeout(timeouts), engine.LogReco)
}

76
serve_test.go Normal file
View file

@ -0,0 +1,76 @@
package touka
import (
"context"
"crypto/tls"
"errors"
"io"
"net"
"net/http"
"testing"
"time"
)
func TestServeServerHTTPModeIgnoresTLSConfig(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen on ephemeral port: %v", err)
}
addr := listener.Addr().String()
if err := listener.Close(); err != nil {
t.Fatalf("close temporary listener: %v", err)
}
srv := &http.Server{
Addr: addr,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("ok"))
}),
// RunShutdown uses the HTTP startup path and must not let a shared
// ServerConfigurator accidentally turn it into HTTPS.
TLSConfig: &tls.Config{},
}
errCh := make(chan error, 1)
go func() {
errCh <- serveServer(srv, false)
}()
client := &http.Client{Timeout: 200 * time.Millisecond}
var resp *http.Response
requestURL := "http://" + addr
deadline := time.Now().Add(3 * time.Second)
for time.Now().Before(deadline) {
resp, err = client.Get(requestURL)
if err == nil {
break
}
time.Sleep(20 * time.Millisecond)
}
if err != nil {
t.Fatalf("expected HTTP server to accept plain HTTP with TLSConfig set: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatalf("read response body: %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("unexpected status code: got %d want %d", resp.StatusCode, http.StatusOK)
}
if string(body) != "ok" {
t.Fatalf("unexpected body: got %q want %q", string(body), "ok")
}
shutdownCtx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
t.Fatalf("shutdown server: %v", err)
}
if err := <-errCh; !errors.Is(err, http.ErrServerClosed) {
t.Fatalf("serveServer should stop with ErrServerClosed after shutdown, got %v", err)
}
}