package eghttp import ( "context" "net" "net/http" "strconv" "sync/atomic" "time" ) const ( DefaultAddr = ":8080" DefaultReadHeaderTimeout = time.Second ) type Option interface { apply(cfg *config) } func WithPort(port int) Option { return optionFunc(func(cfg *config) { cfg.server.Addr = net.JoinHostPort("", strconv.Itoa(port)) }) } func WithHandler(handler http.Handler) Option { return optionFunc(func(cfg *config) { cfg.server.Handler = handler }) } func WithServer(server *http.Server) Option { return optionFunc(func(cfg *config) { cfg.server = server }) } func WithStartFn(fn func(server *http.Server) error) Option { return optionFunc(func(cfg *config) { cfg.startFn = fn }) } // WithServerOption 使用 fn 配置 [http.Server]; func WithServerOption(fn func(server *http.Server)) Option { return optionFunc(func(cfg *config) { fn(cfg.server) }) } type config struct { server *http.Server startFn func(server *http.Server) error } func newDefaultConfig() *config { return &config{ server: &http.Server{ Addr: DefaultAddr, ReadHeaderTimeout: DefaultReadHeaderTimeout, }, startFn: func(server *http.Server) error { return server.ListenAndServe() }, } } type optionFunc func(cfg *config) func (fn optionFunc) apply(cfg *config) { fn(cfg) } // HTTPListenAndServe 创建 [http.Server] 并提供启动和停止函数; func HTTPListenAndServe(opts ...Option) (func(ctx context.Context) error, func(ctx context.Context)) { cfg := newDefaultConfig() for _, opt := range opts { opt.apply(cfg) } inShutdown := &atomic.Bool{} c := make(chan error, 1) goFunc := func(_ context.Context) error { err := cfg.startFn(cfg.server) if inShutdown.Load() { err = <-c } return err } stopFunc := func(ctx context.Context) { inShutdown.Store(true) c <- cfg.server.Shutdown(ctx) } return goFunc, stopFunc }