Files
sub2api-cn-relay-manager/internal/app/app.go
phamnazage-jpg 77b7f7f660
Some checks failed
CI / Build & Test (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Release (push) Has been cancelled
feat: harden runtime import and frontend verification workflows
2026-06-04 20:02:36 +08:00

74 lines
1.5 KiB
Go

package app
import (
"context"
"errors"
"net"
"net/http"
"time"
)
type ListenerFactory func(network, address string) (net.Listener, error)
type Server struct {
server *http.Server
listen ListenerFactory
}
func NewServer(listenAddr string, handler http.Handler, listenerFactory ListenerFactory) *Server {
if handler == nil {
handler = NewAPIHandler("", ActionSet{})
}
server := &Server{
server: &http.Server{
Addr: listenAddr,
Handler: handler,
ReadTimeout: 30 * time.Second,
ReadHeaderTimeout: 10 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
MaxHeaderBytes: 1 << 20, // 1MB
},
listen: net.Listen,
}
if listenerFactory != nil {
server.listen = listenerFactory
}
return server
}
func (s *Server) Addr() string {
return s.server.Addr
}
func (s *Server) Run(ctx context.Context) error {
listener, err := s.listen("tcp", s.server.Addr)
if err != nil {
return err
}
return s.Serve(ctx, listener)
}
func (s *Server) Serve(ctx context.Context, listener net.Listener) error {
errCh := make(chan error, 1)
go func() {
err := s.server.Serve(listener)
if errors.Is(err, http.ErrServerClosed) {
err = nil
}
errCh <- err
}()
select {
case <-ctx.Done():
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := s.server.Shutdown(shutdownCtx); err != nil {
return err
}
return <-errCh
case err := <-errCh:
return err
}
}