|
2 | 2 | package main |
3 | 3 |
|
4 | 4 | import ( |
| 5 | + "encoding/json" |
| 6 | + "errors" |
5 | 7 | "flag" |
| 8 | + "fmt" |
6 | 9 | "log" |
7 | | - |
8 | | - "github.com/piger/nginx-lab/server" |
| 10 | + "log/slog" |
| 11 | + "net/http" |
| 12 | + "os" |
| 13 | + "strings" |
| 14 | + "time" |
9 | 15 | ) |
10 | 16 |
|
11 | | -var ( |
12 | | - listenAddress = flag.String("addr", "0.0.0.0", "Address to bind to") |
13 | | - listenPort = flag.Int("port", 4444, "Port to listen on") |
14 | | -) |
| 17 | +type logMiddleware struct { |
| 18 | + inner http.Handler |
| 19 | + logger *slog.Logger |
| 20 | +} |
15 | 21 |
|
16 | | -func run() error { |
17 | | - flag.Parse() |
| 22 | +func (l logMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
| 23 | + start := time.Now().UTC() |
| 24 | + l.inner.ServeHTTP(w, r) |
| 25 | + l.logger.Info("request", "method", r.Method, "url", r.URL.String(), "remote_addr", r.RemoteAddr, "elapsed", time.Since(start).String()) |
| 26 | +} |
| 27 | + |
| 28 | +func withLog(h http.HandlerFunc, logger *slog.Logger) http.Handler { |
| 29 | + return logMiddleware{inner: h, logger: logger} |
| 30 | +} |
| 31 | + |
| 32 | +type echoResponse struct { |
| 33 | + Method string `json:"method"` |
| 34 | + URL string `json:"url"` |
| 35 | + Proto string `json:"proto"` |
| 36 | + Headers map[string]string `json:"headers"` |
| 37 | + Host string `json:"host"` |
| 38 | + RequestURI string `json:"request_uri"` |
| 39 | + RemoteAddr string `json:"remote_addr"` |
| 40 | +} |
18 | 41 |
|
19 | | - srv := server.New(*listenAddress, *listenPort) |
20 | | - if err := srv.Run(); err != nil { |
| 42 | +// echoHandler is an handler that returns a JSON object containing information about the received request. |
| 43 | +func echoHandler(w http.ResponseWriter, r *http.Request) { |
| 44 | + headers := make(map[string]string) |
| 45 | + for k, v := range r.Header { |
| 46 | + headers[k] = strings.Join(v, ", ") |
| 47 | + } |
| 48 | + |
| 49 | + resp := echoResponse{ |
| 50 | + Method: r.Method, |
| 51 | + URL: r.URL.String(), |
| 52 | + Proto: r.Proto, |
| 53 | + Headers: headers, |
| 54 | + Host: r.Host, |
| 55 | + RequestURI: r.RequestURI, |
| 56 | + RemoteAddr: r.RemoteAddr, |
| 57 | + } |
| 58 | + |
| 59 | + w.Header().Set("Content-Type", "application/json") |
| 60 | + |
| 61 | + if err := json.NewEncoder(w).Encode(resp); err != nil { |
| 62 | + w.WriteHeader(http.StatusInternalServerError) |
| 63 | + fmt.Fprintf(w, "ERROR: %s\n", err) |
| 64 | + return |
| 65 | + } |
| 66 | +} |
| 67 | + |
| 68 | +func run(addr string, logger *slog.Logger) error { |
| 69 | + mux := http.NewServeMux() |
| 70 | + mux.Handle("/", withLog(echoHandler, logger)) |
| 71 | + |
| 72 | + srv := http.Server{ |
| 73 | + Addr: addr, |
| 74 | + Handler: mux, |
| 75 | + ReadTimeout: 10 * time.Second, |
| 76 | + WriteTimeout: 10 * time.Second, |
| 77 | + MaxHeaderBytes: 1 << 20, // 1 MiB |
| 78 | + } |
| 79 | + |
| 80 | + if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { |
21 | 81 | return err |
22 | 82 | } |
23 | 83 |
|
24 | 84 | return nil |
25 | 85 | } |
26 | 86 |
|
27 | 87 | func main() { |
28 | | - if err := run(); err != nil { |
| 88 | + flagAddr := flag.String("addr", ":4444", "Address to listen to (e.g. 0.0.0.0:4444)") |
| 89 | + flag.Parse() |
| 90 | + |
| 91 | + logger := slog.New(slog.NewJSONHandler(os.Stderr, nil)) |
| 92 | + slog.SetDefault(logger) |
| 93 | + |
| 94 | + if err := run(*flagAddr, logger); err != nil { |
29 | 95 | log.Fatalf("error: %s", err) |
30 | 96 | } |
31 | 97 | } |
0 commit comments