aboutsummaryrefslogtreecommitdiffstats
path: root/main.go
blob: 6404940c3c98dbdc2f40a77a1fe51e82896ad803 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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"
)

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"`
}

func Run(ctx context.Context, fs *flag.FlagSet, args []string) error {
	var cfgFile string
	fs.StringVar(&cfgFile, "config", "config.toml", "Path to config file")
	fs.Parse(args)

	cfg, err := LoadConfig(cfgFile)
	if err != nil {
		return err
	}

	logger := logging.SetupLogger(cfg.Log)
	ctx = logging.WithLogger(ctx, logger)

	h, err := repo.New(cfg.Repos)
	if err != nil {
		return fmt.Errorf(": %v", err)
	}

	mux := http.NewServeMux()
	mux.Handle("GET /robots.txt", Robot())
	mux.Handle("GET /", h)

	return RunServer(ctx, handler.New(mux), cfg.Server)
}

func LoadConfig(cfgFile string) (Config, error) {
	data, err := os.ReadFile(cfgFile)
	if err != nil {
		return Config{}, fmt.Errorf("read config: %w", err)
	}

	var cfg Config
	if err := toml.Unmarshal(data, &cfg); err != nil {
		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)
	}
}