From 178e0bfc2d8709ae4d3abd7519b260df09b2d3b5 Mon Sep 17 00:00:00 2001 From: Marc Pervaz Boocha Date: Sat, 2 Aug 2025 22:23:13 +0530 Subject: Switched to go.sudomsg.com/kit --- cmd/gopkgserver/main.go | 26 +-------- go.mod | 7 ++- go.sum | 4 ++ logging/handler/handler.go | 75 ------------------------- logging/log.go | 56 ------------------ main.go | 87 ++++++---------------------- repo/repo.go | 7 ++- repo/repo_test.go | 137 +++++++++++++++++++++++++++++++++++++++++++++ robot.go | 17 ++++++ 9 files changed, 189 insertions(+), 227 deletions(-) delete mode 100644 logging/handler/handler.go delete mode 100644 logging/log.go create mode 100644 repo/repo_test.go create mode 100644 robot.go diff --git a/cmd/gopkgserver/main.go b/cmd/gopkgserver/main.go index 9e40b72..7248136 100644 --- a/cmd/gopkgserver/main.go +++ b/cmd/gopkgserver/main.go @@ -1,30 +1,10 @@ package main import ( - "context" - "flag" - "go-pkg-server/logging" - "log/slog" - "os" - "os/signal" - - gopkgserver "go-pkg-server" + "go.sudomsg.com/gopkgserver" + "go.sudomsg.com/kit/runner" ) func main() { - ctx := context.Background() - defer func() { - err := recover() - if err != nil { - logging.RecoverLog(ctx, err) - os.Exit(2) - } - }() - - ctx, stop := signal.NotifyContext(ctx, os.Interrupt) - defer stop() - - if err := gopkgserver.Run(ctx, flag.CommandLine, os.Args[1:]); err != nil { - slog.Log(ctx, slog.LevelError, "Program returned an error", "error", err) - } + runner.Load(gopkgserver.Run) } diff --git a/go.mod b/go.mod index a12fb29..ac10f3e 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,9 @@ module go.sudomsg.com/gopkgserver go 1.24.1 -require github.com/pelletier/go-toml/v2 v2.2.4 +require ( + github.com/pelletier/go-toml/v2 v2.2.4 + go.sudomsg.com/kit v0.1.1 +) + +require golang.org/x/sync v0.16.0 // indirect diff --git a/go.sum b/go.sum index 3cf50e1..e5f4434 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,6 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +go.sudomsg.com/kit v0.1.1 h1:mrwLCE+1jnjaA5aj0AE6cZ9YdRgg/GDsR/OX/5faJ14= +go.sudomsg.com/kit v0.1.1/go.mod h1:rFO8lXUV2Ij1deDS0OrfdZ4xpH5akf0jHtVwC0N8fBQ= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= diff --git a/logging/handler/handler.go b/logging/handler/handler.go deleted file mode 100644 index 1501715..0000000 --- a/logging/handler/handler.go +++ /dev/null @@ -1,75 +0,0 @@ -package handler - -import ( - "go-pkg-server/logging" - "log/slog" - "net/http" - "time" -) - -type traceWriter struct { - http.ResponseWriter - status int - bytes int -} - -var _ http.ResponseWriter = &traceWriter{} - -func (t *traceWriter) Write(b []byte) (int, error) { - if t.status == 0 { - t.WriteHeader(http.StatusOK) - } - c, err := t.ResponseWriter.Write(b) - t.bytes += c - return c, err -} - -func (tw *traceWriter) WriteHeader(statusCode int) { - if tw.status == 0 { - tw.status = http.StatusOK - } - tw.status = statusCode - tw.ResponseWriter.WriteHeader(statusCode) -} - -func New(next http.Handler) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - logger := logging.FromContext(ctx) - - defer func() { - err := recover() - if err != nil { - logging.RecoverLog(ctx, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - }() - - logger = logger.With("method", r.Method, - "host", r.Host, - "remote_address", r.RemoteAddr, - "path", r.RequestURI, - ) - - ctx = logging.WithLogger(ctx, logger) - - r = r.WithContext(ctx) - - tw := &traceWriter{ResponseWriter: w} - - start := time.Now() - next.ServeHTTP(tw, r) - duration := time.Since(start) - - logger.Log(ctx, slog.LevelInfo, "Request Handled", - "protocol", r.Proto, - "bytes_recieved", r.ContentLength, - "status", tw.status, - "time", duration, - "bytes_sent", tw.bytes, - "user_agent", r.UserAgent(), - "referer", r.Referer(), - ) - - } -} diff --git a/logging/log.go b/logging/log.go deleted file mode 100644 index 8e3078f..0000000 --- a/logging/log.go +++ /dev/null @@ -1,56 +0,0 @@ -package logging - -import ( - "context" - "io" - "log/slog" - "os" - "runtime" -) - -type Config struct { - Level slog.Level `toml:"level"` - File string `toml:"file"` -} - -func SetupLogger(cfg Config) *slog.Logger { - opts := &slog.HandlerOptions{Level: cfg.Level} - var handler slog.Handler - - var writter io.Writer - writter = os.Stdout - if cfg.File != "" { - f, _ := os.OpenFile(cfg.File, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - writter = f - } - handler = slog.NewJSONHandler(writter, opts) - - logger := slog.New(handler) - slog.SetDefault(logger) - return logger -} - -type contextKeyType struct{} - -var contextKey contextKeyType - -func WithLogger(ctx context.Context, logger *slog.Logger) context.Context { - return context.WithValue(ctx, contextKey, logger) -} - -func FromContext(ctx context.Context) *slog.Logger { - if logger, ok := ctx.Value(contextKey).(*slog.Logger); ok { - return logger - } - return slog.Default() -} - -func RecoverLog(ctx context.Context, err any) { - logger := FromContext(ctx) - - const size = 64 << 10 - buf := make([]byte, size) - buf = buf[:runtime.Stack(buf, false)] - - logger.Log(ctx, slog.LevelError, "Program Paniced", "error", err, "stack", string(buf)) -} diff --git a/main.go b/main.go index 6404940..3fe9001 100644 --- a/main.go +++ b/main.go @@ -3,31 +3,24 @@ package gopkgserver import ( "context" _ "embed" - "errors" "flag" "fmt" - "io" + "log/slog" - "net" "net/http" "os" "github.com/pelletier/go-toml/v2" - - "go-pkg-server/logging" - "go-pkg-server/logging/handler" - "go-pkg-server/repo" + "go.sudomsg.com/gopkgserver/repo" + httpServer "go.sudomsg.com/kit/http" + "go.sudomsg.com/kit/logging" + logHandler "go.sudomsg.com/kit/logging/http" ) -type ServerConfig struct { - Proto string `toml:"proto"` - Address string `toml:"address"` -} - type Config struct { - Server ServerConfig `toml:"server"` - Log logging.Config `toml:"log"` - Repos map[string]repo.Repo `toml:"repo"` + Server []httpServer.ServerConfig `toml:"server"` + Logging logging.LogConfig `toml:"logging"` + Repos map[string]repo.Repo `toml:"repo"` } func Run(ctx context.Context, fs *flag.FlagSet, args []string) error { @@ -40,8 +33,10 @@ func Run(ctx context.Context, fs *flag.FlagSet, args []string) error { return err } - logger := logging.SetupLogger(cfg.Log) - ctx = logging.WithLogger(ctx, logger) + if err := logging.Setup(cfg.Logging); err != nil { + return err + } + ctx = logging.WithLogger(ctx, slog.Default()) h, err := repo.New(cfg.Repos) if err != nil { @@ -52,7 +47,12 @@ func Run(ctx context.Context, fs *flag.FlagSet, args []string) error { mux.Handle("GET /robots.txt", Robot()) mux.Handle("GET /", h) - return RunServer(ctx, handler.New(mux), cfg.Server) + handler := logHandler.New(mux, nil) + + lns, err := httpServer.OpenListeners(ctx, cfg.Server) + defer lns.CloseAll() + + return httpServer.RunHTTPServers(ctx, lns, handler) } func LoadConfig(cfgFile string) (Config, error) { @@ -66,56 +66,5 @@ func LoadConfig(cfgFile string) (Config, error) { return Config{}, fmt.Errorf("parse config: %w", err) } - if cfg.Server.Proto == "" { - cfg.Server.Proto = "tcp" - } - return cfg, nil } - -func RunServer(ctx context.Context, mux http.Handler, config ServerConfig) error { - logger := logging.FromContext(ctx) - - srv := &http.Server{ - Handler: mux, - BaseContext: func(l net.Listener) context.Context { - logger := logger.With("address", l.Addr()) - ctx = logging.WithLogger(ctx, logger) - return ctx - }, - ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError), - } - - ln, err := net.Listen(config.Proto, config.Address) - if err != nil { - return err - } - - logger.Log(ctx, slog.LevelError, "Server Started on", "address", ln.Addr()) - - go func() { - <-ctx.Done() - shutdownCtx := context.Background() - // We received an interrupt signal, shut down. - if err := srv.Shutdown(shutdownCtx); err != nil { - slog.Log(shutdownCtx, slog.LevelError, "HTTP server Shutdown", "error", err) - } - }() - - if err := srv.Serve(ln); err != nil && !errors.Is(err, http.ErrServerClosed) { - return err - } - - return nil -} - -func Robot() http.HandlerFunc { - robots := `User-agent: * -Disallow: / -` - return func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Cache-Control", "public, max-age=86400, immutable") - w.Header().Set("Content-Type", "text/plain") - io.WriteString(w, robots) - } -} diff --git a/repo/repo.go b/repo/repo.go index 2c052d6..b3eeef0 100644 --- a/repo/repo.go +++ b/repo/repo.go @@ -6,7 +6,7 @@ import ( "fmt" "html/template" "net/http" - "strings" + "path" ) //go:embed meta.html @@ -45,8 +45,9 @@ func New(repo map[string]Repo) (*RepoHandler, error) { } func (h *RepoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - key := fmt.Sprintf("%s%s", r.Host, r.URL.Path) - key = strings.TrimSuffix(key, "/") + host := r.Host + path := path.Clean(r.URL.Path) + key := host + path page, ok := h.Pages[key] if !ok { diff --git a/repo/repo_test.go b/repo/repo_test.go new file mode 100644 index 0000000..57f249c --- /dev/null +++ b/repo/repo_test.go @@ -0,0 +1,137 @@ +package repo_test + +import ( + "bytes" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "go.sudomsg.com/gopkgserver/repo" +) + +func TestMetaExec(t *testing.T) { + t.Parallel() + + meta, err := repo.NewMeta() + if err != nil { + t.Fatalf("failed to create Meta: %v", err) + } + + pkg := "example.com/foo" + r := repo.Repo{ + VCS: "git", + Repository: "https://github.com/user/foo", + Home: "https://github.com/user/foo", + Directory: "https://github.com/user/foo/tree/master{/dir}", + File: "https://github.com/user/foo/blob/master{/dir}/{file}#L{line}", + } + + html, err := meta.Exec(pkg, r) + if err != nil { + t.Fatalf("Exec failed: %v", err) + } + + htmlStr := string(html) + if !strings.Contains(htmlStr, `