summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.go31
-rw-r--r--server/server.go103
2 files changed, 90 insertions, 44 deletions
diff --git a/main.go b/main.go
index f0c8c51..e47d311 100644
--- a/main.go
+++ b/main.go
@@ -5,8 +5,13 @@ package main
import (
"flag"
+ "net"
+ "net/http"
"os"
+ "strconv"
+ "time"
+ "github.com/gorilla/mux"
"github.com/rs/zerolog"
"go.terinstock.com/cgit-httpd/handlers/cgit"
"go.terinstock.com/cgit-httpd/handlers/git"
@@ -30,10 +35,7 @@ func main() {
logger.Info().Interface("config", cfg).Send()
- srv := server.New(server.Options{
- Host: cfg.HTTP.Host,
- Port: cfg.HTTP.Port,
- })
+ gitMux := mux.NewRouter()
cgit.New(cgit.Options{
CGI: cfg.CGit.CGI,
@@ -42,7 +44,7 @@ func main() {
ConfigFile: cfg.CGit.ConfigFile,
Logger: logger.With().Str("handler", "cgit").Logger(),
}).
- WithRegister(srv).
+ WithRegister(RegistererFunc(gitMux.Handle)).
Build()
git.New(git.Options{
@@ -51,13 +53,28 @@ func main() {
ExportAll: cfg.Git.ExportAll,
Logger: logger.With().Str("handler", "git").Logger(),
}).
- WithRegister(srv).
+ WithRegister(RegistererFunc(gitMux.Handle)).
Build()
m := manager.New()
- m.Add(srv)
+ m.Add(&server.Server{
+ Name: "git",
+ Server: &http.Server{
+ Addr: net.JoinHostPort(cfg.HTTP.Host, strconv.Itoa(cfg.HTTP.Port)),
+ Handler: gitMux,
+ MaxHeaderBytes: 1 << 20,
+ IdleTimeout: 90 * time.Second,
+ ReadHeaderTimeout: 32 * time.Second,
+ },
+ })
if err := m.Start(signals.SetupSignalHandler()); err != nil {
logger.Info().Err(err).Msg("manager stopped")
}
}
+
+type RegistererFunc func(string, http.Handler) *mux.Route
+
+func (r RegistererFunc) Register(name string, handler http.Handler) {
+ r(name, handler)
+}
diff --git a/server/server.go b/server/server.go
index 424f359..92f590c 100644
--- a/server/server.go
+++ b/server/server.go
@@ -1,67 +1,96 @@
-// Copyright 2022 Terin Stock.
-// SPDX-License-Identifier: MPL-2.0
+// Copyright 2022 The Kubernetes Authors.
+// SPDX-License-Identifier: Apache-2.0
+/*
+Copyright 2022 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
package server
import (
"context"
+ "errors"
+ "log/slog"
"net"
"net/http"
- "strconv"
"time"
-
- "github.com/gorilla/mux"
)
+// Server is a general purpose HTTP server Runnable for a manager.
type Server struct {
- mux *mux.Router
- host string
- port int
-}
+ // Name is an optional string that describes the purpose of the server. It is used in logs to distinguish
+ // among multiple servers.
+ Name string
-type Options struct {
- Host string
- Port int
-}
+ // Server is the HTTP server to run. It is required.
+ Server *http.Server
-func New(options Options) *Server {
- return &Server{
- mux: mux.NewRouter(),
- host: options.Host,
- port: options.Port,
- }
-}
+ // Listener is an optional listener to use. If not set, the server start a listener using the server.Addr.
+ // Using a listener is useful when the port reservation needs to happen in advance of this runnable starting.
+ Listener net.Listener
-func (s *Server) Register(path string, handler http.Handler) {
- s.mux.Handle(path, handler)
+ // ShutdownTimeout is an optional duration that indicates how long to wait for the server to shutdown gracefully. If not set,
+ // the server will wait indefinitely for all connections to close.
+ ShutdownTimeout *time.Duration
}
+// Start starts the server. It will block until the server is stopped or an error occurs.
func (s *Server) Start(ctx context.Context) error {
- ln, err := net.Listen("tcp", net.JoinHostPort(s.host, strconv.Itoa(s.port)))
- if err != nil {
- return err
- }
+ serverShutdown := make(chan struct{})
- srv := &http.Server{
- Handler: s.mux,
- MaxHeaderBytes: 1 << 20,
- IdleTimeout: 90 * time.Second,
- ReadHeaderTimeout: 32 * time.Second,
+ logger := slog.With("addr", s.addr())
+ if s.Name != "" {
+ logger = logger.With("name", s.Name)
}
- shutdownCh := make(chan struct{})
go func() {
<-ctx.Done()
- if err := srv.Shutdown(context.Background()); err != nil {
- _ = err
+ logger.Info("shutting down server")
+ shutdownCtx := context.Background()
+ if s.ShutdownTimeout != nil {
+ var shutdownCancel context.CancelFunc
+ shutdownCtx, shutdownCancel = context.WithTimeout(context.Background(), *s.ShutdownTimeout)
+ defer shutdownCancel()
}
- close(shutdownCh)
+
+ if err := s.Server.Shutdown(shutdownCtx); err != nil {
+ logger.Error("error shutting down server", "error", err)
+ }
+ close(serverShutdown)
}()
- if err := srv.Serve(ln); err != nil && err != http.ErrServerClosed {
+ logger.Info("starting server")
+ if err := s.serve(); err != nil && !errors.Is(err, http.ErrServerClosed) {
return err
}
- <-shutdownCh
+ <-serverShutdown
return nil
}
+
+func (s *Server) addr() string {
+ if s.Listener != nil {
+ return s.Listener.Addr().String()
+ }
+
+ return s.Server.Addr
+}
+
+func (s *Server) serve() error {
+ if s.Listener != nil {
+ return s.Server.Serve(s.Listener)
+ }
+
+ return s.Server.ListenAndServe()
+}