| @@ -10,9 +10,11 @@ import ( |
| 10 | 10 | "net" |
| 11 | 11 | "net/http" |
| 12 | 12 | "os" |
| 13 | "os/signal" |
| 13 | 14 | "path/filepath" |
| 14 | 15 | "strconv" |
| 15 | 16 | "strings" |
| 17 | "syscall" |
| 16 | 18 | "time" |
| 17 | 19 | |
| 18 | 20 | "github.com/spf13/cobra" |
| @@ -146,6 +148,11 @@ func serveCmd() *cobra.Command { |
| 146 | 148 | } |
| 147 | 149 | defer stopHookd() |
| 148 | 150 | |
| 151 | // SIGTERM is how a deploy restarts the daemon: stop accepting, |
| 152 | // let what is in flight finish, exit 0 (#105). |
| 153 | ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) |
| 154 | defer stop() |
| 155 | |
| 149 | 156 | // Outbound webhook deliveries. The retry base is overridable |
| 150 | 157 | // for tests via GITBAY_WEBHOOK_RETRY_BASE. |
| 151 | 158 | retryBase := 30 * time.Second |
| @@ -173,6 +180,8 @@ func serveCmd() *cobra.Command { |
| 173 | 180 | }, buildinfo.String()).Run(whCtx) |
| 174 | 181 | |
| 175 | 182 | errCh := make(chan error, 3) |
| 183 | var sshSrv *sshd.Server |
| 184 | var sshLn, gitLn net.Listener |
| 176 | 185 | if cfg.SSH.Mode == "embedded" { |
| 177 | 186 | srv, err := sshd.New(cfg, st) |
| 178 | 187 | if err != nil { |
| @@ -183,6 +192,7 @@ func serveCmd() *cobra.Command { |
| 183 | 192 | return err |
| 184 | 193 | } |
| 185 | 194 | slog.Info("ssh listening", "addr", ln.Addr()) |
| 195 | sshSrv, sshLn = srv, ln |
| 186 | 196 | go func() { errCh <- srv.Serve(ln) }() |
| 187 | 197 | } else { |
| 188 | 198 | // system mode: the host sshd owns the SSH port and invokes |
| @@ -191,7 +201,16 @@ func serveCmd() *cobra.Command { |
| 191 | 201 | } |
| 192 | 202 | |
| 193 | 203 | web := httpd.New(cfg, st) |
| 194 | | hs := &http.Server{Addr: cfg.HTTP.Addr, Handler: web.Handler()} |
| 204 | // Header and idle timeouts bound what an idle or slow client can |
| 205 | // hold open. No write timeout: archives and upload-pack stream |
| 206 | // for as long as they take (#104). |
| 207 | hs := &http.Server{ |
| 208 | Addr: cfg.HTTP.Addr, |
| 209 | Handler: web.Handler(), |
| 210 | ReadHeaderTimeout: 10 * time.Second, |
| 211 | IdleTimeout: 2 * time.Minute, |
| 212 | MaxHeaderBytes: 64 << 10, |
| 213 | } |
| 195 | 214 | go func() { |
| 196 | 215 | slog.Info("http listening", "addr", cfg.HTTP.Addr, "tls", cfg.HTTP.TLS) |
| 197 | 216 | switch cfg.HTTP.TLS { |
| @@ -250,7 +269,9 @@ func serveCmd() *cobra.Command { |
| 250 | 269 | }) |
| 251 | 270 | go func() { |
| 252 | 271 | slog.Info("acme http listening", "addr", addr) |
| 253 | | if err := http.ListenAndServe(addr, m.HTTPHandler(redirect)); err != nil { |
| 272 | acmeHTTP := &http.Server{Addr: addr, Handler: m.HTTPHandler(redirect), |
| 273 | ReadHeaderTimeout: 10 * time.Second, IdleTimeout: time.Minute} |
| 274 | if err := acmeHTTP.ListenAndServe(); err != nil { |
| 254 | 275 | slog.Warn("acme http listener failed; continuing with TLS-ALPN only", "err", err) |
| 255 | 276 | } |
| 256 | 277 | }() |
| @@ -266,10 +287,33 @@ func serveCmd() *cobra.Command { |
| 266 | 287 | return err |
| 267 | 288 | } |
| 268 | 289 | slog.Info("git-daemon listening", "addr", gln.Addr()) |
| 290 | gitLn = gln |
| 269 | 291 | go func() { errCh <- gitd.New(cfg, st).Serve(gln) }() |
| 270 | 292 | } |
| 271 | 293 | |
| 272 | | return <-errCh |
| 294 | select { |
| 295 | case err := <-errCh: |
| 296 | return err |
| 297 | case <-ctx.Done(): |
| 298 | } |
| 299 | slog.Info("shutting down") |
| 300 | stop() |
| 301 | for _, ln := range []net.Listener{sshLn, gitLn} { |
| 302 | if ln != nil { |
| 303 | ln.Close() |
| 304 | } |
| 305 | } |
| 306 | drain, cancel := context.WithTimeout(context.Background(), 30*time.Second) |
| 307 | defer cancel() |
| 308 | if err := hs.Shutdown(drain); err != nil { |
| 309 | slog.Warn("http shutdown", "err", err) |
| 310 | } |
| 311 | if sshSrv != nil { |
| 312 | if err := sshSrv.Shutdown(drain); err != nil { |
| 313 | slog.Warn("ssh shutdown", "err", err) |
| 314 | } |
| 315 | } |
| 316 | return nil |
| 273 | 317 | }, |
| 274 | 318 | } |
| 275 | 319 | } |