v2.31.0-dev5 4162c86

handler.go RawHistoryFolder

1// Package proxy implements the HTTP adapter for the reverse proxy.
2package proxy
3
4import (
5	"net"
6	"net/http"
7	"sync/atomic"
8
9	"github.com/bnema/zerowrap"
10
11	"github.com/bnema/gordon/internal/adapters/in/http/middleware"
12	"github.com/bnema/gordon/internal/boundaries/in"
13)
14
15// Handler implements http.Handler for the reverse proxy.
16// It owns all HTTP-level concerns: concurrency limiting, body size enforcement,
17// transport selection, and reverse proxy execution.
18// Routing decisions are delegated to the ProxyService usecase.
19type Handler struct {
20	proxySvc          in.ProxyService
21	log               zerowrap.Logger
22	trustedNets       []*net.IPNet
23	appTransport      http.RoundTripper
24	h2cTransport      http.RoundTripper
25	registryTransport http.RoundTripper
26	activeConns       atomic.Int64
27}
28
29// NewHandler creates a new proxy HTTP handler.
30func NewHandler(proxySvc in.ProxyService, trustedNets []*net.IPNet, log zerowrap.Logger) *Handler {
31	return &Handler{
32		proxySvc:          proxySvc,
33		log:               log,
34		trustedNets:       trustedNets,
35		appTransport:      newAppTransport(),
36		h2cTransport:      newH2CTransport(),
37		registryTransport: newRegistryTransport(),
38	}
39}
40
41// ServeHTTP handles incoming HTTP requests and proxies them to the appropriate backend.
42func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
43	cfg := h.proxySvc.ProxyConfig()
44
45	// SECURITY: Limit concurrent connections to prevent resource exhaustion.
46	if cfg.MaxConcurrentConns > 0 {
47		current := h.activeConns.Add(1)
48		if current > int64(cfg.MaxConcurrentConns) {
49			h.activeConns.Add(-1)
50			proxyError(w, "Service Unavailable", http.StatusServiceUnavailable)
51			return
52		}
53		defer h.activeConns.Add(-1)
54	}
55
56	// SECURITY: Limit request body size to prevent resource exhaustion.
57	if cfg.MaxBodySize > 0 {
58		r.Body = http.MaxBytesReader(w, r.Body, cfg.MaxBodySize)
59	}
60
61	// Enrich request context with fields for downstream logging
62	ctx := zerowrap.CtxWithFields(r.Context(), map[string]any{
63		zerowrap.FieldLayer:    "adapter",
64		zerowrap.FieldAdapter:  "http",
65		zerowrap.FieldMethod:   r.Method,
66		zerowrap.FieldPath:     r.URL.Path,
67		zerowrap.FieldHost:     r.Host,
68		zerowrap.FieldClientIP: middleware.GetClientIP(r, h.trustedNets),
69	})
70	r = r.WithContext(ctx)
71	log := zerowrap.FromCtx(ctx)
72
73	// Check if this is the registry domain
74	if h.proxySvc.IsRegistryDomain(r.Host) {
75		log.Debug().Msg("routing request to registry")
76		h.forwardToRegistry(w, r, cfg.RegistryPort)
77		return
78	}
79
80	// Get target for this domain
81	log.Debug().Str("resolving_target_for", r.Host).Msg("looking up proxy target")
82	target, err := h.proxySvc.GetTarget(ctx, r.Host)
83	if err != nil {
84		log.Warn().Err(err).Msg("no route found for domain")
85		proxyError(w, "404 page not found", http.StatusNotFound)
86		return
87	}
88
89	log.Debug().
90		Str("host", target.Host).
91		Int("port", target.Port).
92		Str("container_id", target.ContainerID).
93		Msg("resolved proxy target")
94
95	h.forwardToTarget(w, r, target, cfg.MaxResponseSize)
96}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close