| @@ -10,9 +10,11 @@ import ( |
| 10 | "net" |
10 | "net" |
| 11 | "net/http" |
11 | "net/http" |
| 12 | "os" |
12 | "os" |
| |
13 | "os/signal" |
| 13 | "path/filepath" |
14 | "path/filepath" |
| 14 | "strconv" |
15 | "strconv" |
| 15 | "strings" |
16 | "strings" |
| |
17 | "syscall" |
| 16 | "time" |
18 | "time" |
| 17 | |
19 | |
| 18 | "github.com/spf13/cobra" |
20 | "github.com/spf13/cobra" |
| @@ -146,6 +148,11 @@ func serveCmd() *cobra.Command { |
| 146 | } |
148 | } |
| 147 | defer stopHookd() |
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 | // Outbound webhook deliveries. The retry base is overridable |
156 | // Outbound webhook deliveries. The retry base is overridable |
| 150 | // for tests via GITBAY_WEBHOOK_RETRY_BASE. |
157 | // for tests via GITBAY_WEBHOOK_RETRY_BASE. |
| 151 | retryBase := 30 * time.Second |
158 | retryBase := 30 * time.Second |
| @@ -173,6 +180,8 @@ func serveCmd() *cobra.Command { |
| 173 | }, buildinfo.String()).Run(whCtx) |
180 | }, buildinfo.String()).Run(whCtx) |
| 174 | |
181 | |
| 175 | errCh := make(chan error, 3) |
182 | errCh := make(chan error, 3) |
| |
183 | var sshSrv *sshd.Server |
| |
184 | var sshLn, gitLn net.Listener |
| 176 | if cfg.SSH.Mode == "embedded" { |
185 | if cfg.SSH.Mode == "embedded" { |
| 177 | srv, err := sshd.New(cfg, st) |
186 | srv, err := sshd.New(cfg, st) |
| 178 | if err != nil { |
187 | if err != nil { |
| @@ -183,6 +192,7 @@ func serveCmd() *cobra.Command { |
| 183 | return err |
192 | return err |
| 184 | } |
193 | } |
| 185 | slog.Info("ssh listening", "addr", ln.Addr()) |
194 | slog.Info("ssh listening", "addr", ln.Addr()) |
| |
195 | sshSrv, sshLn = srv, ln |
| 186 | go func() { errCh <- srv.Serve(ln) }() |
196 | go func() { errCh <- srv.Serve(ln) }() |
| 187 | } else { |
197 | } else { |
| 188 | // system mode: the host sshd owns the SSH port and invokes |
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 | web := httpd.New(cfg, st) |
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 | go func() { |
214 | go func() { |
| 196 | slog.Info("http listening", "addr", cfg.HTTP.Addr, "tls", cfg.HTTP.TLS) |
215 | slog.Info("http listening", "addr", cfg.HTTP.Addr, "tls", cfg.HTTP.TLS) |
| 197 | switch cfg.HTTP.TLS { |
216 | switch cfg.HTTP.TLS { |
| @@ -250,7 +269,9 @@ func serveCmd() *cobra.Command { |
| 250 | }) |
269 | }) |
| 251 | go func() { |
270 | go func() { |
| 252 | slog.Info("acme http listening", "addr", addr) |
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 | slog.Warn("acme http listener failed; continuing with TLS-ALPN only", "err", err) |
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 | return err |
287 | return err |
| 267 | } |
288 | } |
| 268 | slog.Info("git-daemon listening", "addr", gln.Addr()) |
289 | slog.Info("git-daemon listening", "addr", gln.Addr()) |
| |
290 | gitLn = gln |
| 269 | go func() { errCh <- gitd.New(cfg, st).Serve(gln) }() |
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 | } |