server.md - maoxiaoyue/hypgo GitHub Wiki

Server Package (pkg/server)

server 套件是 HypGo 框架的網路層核心,負責傾聽連接埠、處理連線,以及協調底層通訊協定與上層的 router。它支援多種協議的自動切換與同時運行,並提供優雅重啟 (Graceful Restart) 以達到零停機部署。

主要特色

  • 多通訊協定支援: 支援單純的 HTTP/1.1,也支援基於 h2c 的透明 HTTP/2,更支援基於 UDP QUIC 協定的 HTTP/3 服務。
  • Auto Protocol 模式: 只要開啟 TLS 並選擇 Auto 模式(或是設定 server.protocol: auto),便會同時啟動 HTTP/1.1、HTTP/2 與 HTTP/3 的服務,自動與瀏覽器協商最佳協定。
  • Graceful Shutdown: 收到 SIGINTSIGTERM 訊號時,伺服器將拒絕新連線但確保已建立的請求處理完畢後才退出,並支援超時強行關閉。HTTP/1+2 與 HTTP/3 伺服器並行 shutdown。
  • Graceful Restart (僅支援 Unix): 透過發送自訂訊號(例如預設的 SIGUSR2),能夠觸發 Fork 新程序接替傾聽相同的 Port,然後舊程序在處理完手頭的請求後退出,達成零停機。
  • 自動 PID 管理: 啟動時自動寫入 PID 檔案,方便使用腳本或監控系統追蹤程序。
  • AutoSync 自動同步: Start() 時自動產出 .hyp/context.yaml manifest,AI 永遠可取得最新專案結構。

基礎使用

利用 config 初始化並啟動伺服器:

package main

import (
	"log"

	"github.com/maoxiaoyue/hypgo/pkg/config"
	"github.com/maoxiaoyue/hypgo/pkg/logger"
	"github.com/maoxiaoyue/hypgo/pkg/server"
)

func main() {
	// 1. 讀取配置
	cfg, _ := config.LoadConfig("config.yaml")

	// 2. 初始化 Logger
	logInstance, _ := logger.New("info", "stdout", nil, true)

	// 3. 建立伺服器實例
	srv := server.New(cfg, logInstance)

	// 4. 定義路由 (取得內建的 Router)
	r := srv.Router()
	r.GET("/api/ping", func(c *context.Context) {
		c.String(200, "pong")
	})

	// 5. 啟動伺服器 (會阻塞直到收到關閉訊號)
	if err := srv.Start(); err != nil {
		log.Fatalf("伺服器發生錯誤: %v", err)
	}
}

配置範例

config.yaml 控制 Protocol 與 TLS 行為:

server:
  addr: ":443"
  protocol: "auto"        # 支援 http1, http2, http3, auto
  graceful_restart: true  # 監聽重啟訊號 (Unix only)
  tls:
    enabled: true         # 若是 http3 或 auto 這裡必須是 true
    cert_file: "/path/to/cert.pem"
    key_file: "/path/to/key.pem"

自訂路由與中間件結合

server.New() 已封裝並實例化 router.Router。透過 srv.Router() 呼叫所有 Router 功能:

r := srv.Router()

// 掛載全域中間件
r.Use(middleware.Logger(middleware.LoggerConfig{}))

// 掛載靜態檔案
r.Static("/public", "./assets")

// 自訂 404 處理器(委派給 Router)
srv.NotFound(func(c *context.Context) {
	c.String(404, "Page Not Found!")
})

// 自訂 405 處理器(委派給 Router)
srv.MethodNotAllowed(func(c *context.Context) {
	c.String(405, "Method Not Allowed!")
})

三協議架構設計

HTTP/1.1 (startHTTP1)

  • 標準 TCP 監聽
  • 支援 TLS(使用統一 cipher suites)
  • 可選 Keep-Alive

HTTP/2 (startHTTP2WithFallback)

  • 透過 h2c.NewHandler 支援明文 HTTP/2(開發環境)
  • TLS 模式自動 ALPN 協商 h2 / http/1.1
  • HTTP/2 設定驗證:
    • MaxReadFrameSize:限制 16KB~16MB(HTTP/2 spec 規範)
    • MaxConcurrentStreams:預設 250
    • IdleTimeout:可配置

HTTP/3 (startHTTP3)

  • 基於 QUIC(UDP),要求 TLS 1.3
  • 使用 quic-go/http3 套件
  • 自動設定 Alt-Svc header 引導瀏覽器升級

Auto Protocol

同時啟動 HTTP/3(UDP goroutine)+ HTTP/2+1.1(TCP 主線程):

Client → TCP → HTTP/1.1 or HTTP/2 (via ALPN)
Client → UDP → HTTP/3 (via Alt-Svc header upgrade)

TLS 安全設計

統一 Cipher Suites

三種協議共用 strongCipherSuites,確保一致的安全等級:

var strongCipherSuites = []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_SHA256,
    tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
    tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
    tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
}
  • HTTP/1.1 + HTTP/2:MinVersion = TLS 1.2
  • HTTP/3:MinVersion = TLS 1.3(QUIC 強制要求)

0-RTT Session Cache(防 Replay Attack)

TLS 1.3 的 0-RTT 恢復功能使用帶保護的 SessionCache

// SessionCache 特性:
// - maxSize = 10,000 條目(防止記憶體耗盡 DoS)
// - TTL = 24 小時(過期自動淘汰)
// - LRU 淘汰:超過上限時移除最舊條目
// - GetAndDelete 原子操作:防止 race window replay attack
操作 安全保證
Put() 檢查大小上限 → 淘汰過期 → 淘汰最舊 → 儲存
GetAndDelete() 原子取得並刪除,同一 session 只能使用一次

證書載入

loadCertificate() 回傳 (tls.Certificate, error) 而非 panic,確保生產環境不會因證書問題導致整個伺服器崩潰。

Graceful Shutdown

並行 Shutdown

HTTP/1+2 和 HTTP/3 伺服器同時關閉,共用 context timeout:

// Shutdown 內部行為:
// 1. shuttingDown.Store(true)  ← atomic,無競態
// 2. listener.Close()          ← 停止接受新連線
// 3. 並行:httpServer.Shutdown(ctx) + h3Server.Close()
// 4. close(shutdownChan)       ← 通知 graceful restart goroutine 退出

Graceful Restart(Unix)

1. 收到 SIGUSR2
2. Fork 新進程(傳遞 listener FD)
3. Poll 等待新進程就緒(每 200ms × 15 次 = 3 秒)
4. 舊進程 Shutdown(30 秒 timeout)
5. 移除 PID 檔案 → Exit

安全修復

  • signal.Stop 確保信號訂閱被清理
  • file.Close() 防止 FD 洩漏
  • getInheritedListener 驗證 FD 3 確實是 TCP socket

shuttingDown 競態防護

使用 atomic.Bool 取代 bool,確保 Shutdown() 寫入與 Health() 讀取之間無 data race:

// 寫入(Shutdown 時)
s.shuttingDown.Store(true)

// 讀取(Health 檢查)
if s.shuttingDown.Load() { ... }

Project Manifest(AI 協作)

Server 提供 Manifest() 方法,自動掃描已註冊的路由、設定與 schema metadata,產出機器可讀的專案描述:

m := srv.Manifest()
manifest.WriteYAML(os.Stdout, m)
manifest.SaveToFile(".hyp/manifest.yaml", m, "yaml")

輸出範例:

version: "1.0"
framework: HypGo
server:
  addr: ":8080"
  protocol: http2
  tls: true
routes:
  - method: POST
    path: /api/users
    handler_names: [controllers.CreateUser]
    summary: "建立使用者"
    tags: [users]
    input_type: CreateUserRequest
    output_type: UserResponse
database:
  driver: postgres
  has_replicas: true

也可透過 CLI 使用:

hyp context              # YAML 到 stdout
hyp context -f json      # JSON 格式
hyp context -o manifest.yaml  # 儲存到檔案

AutoSync 自動同步

Server.Start() 在啟動時自動呼叫 autosync.SyncSafe(),產出 .hyp/context.yaml

  • 原子寫入:使用 temp file + os.Rename() 防止寫入中途損壞
  • 安全:不包含密碼、token、DSN 等敏感資訊
  • 自動:每次 Server 啟動時更新,無需額外命令