Mercurial Hosting > d2o
annotate main.go @ 14:a51dfcb0faeb default tip
forgot to remove bestPatPath variable that's not used anymore
| author | Atarwn Gard <a@qwa.su> |
|---|---|
| date | Mon, 23 Mar 2026 13:48:32 +0500 |
| parents | 84384cccda0e |
| children |
| rev | line source |
|---|---|
| 0 | 1 package main |
| 2 | |
| 3 import ( | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
4 "bufio" |
| 0 | 5 "crypto/tls" |
| 6 "fmt" | |
| 3 | 7 "io" |
| 0 | 8 "log" |
| 9 "net" | |
| 10 "net/http" | |
| 11 | 11 "net/http/cgi" |
| 0 | 12 "net/http/httputil" |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
13 "net/textproto" |
| 0 | 14 "net/url" |
| 15 "os" | |
| 16 "path" | |
| 17 "path/filepath" | |
| 18 "regexp" | |
| 19 "runtime" | |
| 20 "strconv" | |
| 21 "strings" | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
22 "time" |
| 0 | 23 |
| 24 "d2o/fcgi" | |
| 25 "d2o/icf" | |
| 26 ) | |
| 27 | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
28 type loggingMode uint8 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
29 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
30 const ( |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
31 logNone loggingMode = iota |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
32 logAccess |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
33 logVerbose |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
34 ) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
35 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
36 var ( |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
37 curLoggingMode loggingMode = logVerbose |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
38 accessLogger = log.New(os.Stderr, "", log.LstdFlags) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
39 ) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
40 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
41 func setLoggingMode(m loggingMode) { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
42 curLoggingMode = m |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
43 switch m { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
44 case logNone: |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
45 log.SetOutput(io.Discard) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
46 case logAccess: |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
47 log.SetOutput(io.Discard) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
48 case logVerbose: |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
49 // keep standard logger output |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
50 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
51 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
52 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
53 func accessEnabled() bool { return curLoggingMode == logAccess || curLoggingMode == logVerbose } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
54 func verboseEnabled() bool { return curLoggingMode == logVerbose } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
55 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
56 func accessPrintf(format string, args ...any) { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
57 if accessEnabled() { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
58 accessLogger.Printf(format, args...) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
59 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
60 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
61 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
62 func verbosePrintf(format string, args ...any) { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
63 if verboseEnabled() { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
64 log.Printf(format, args...) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
65 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
66 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
67 |
| 0 | 68 func main() { |
| 69 cfgPath := "/etc/d2obase" | |
| 70 if len(os.Args) > 1 { | |
| 71 cfgPath = os.Args[1] | |
| 72 } | |
| 73 | |
| 74 f, err := os.Open(cfgPath) | |
| 75 if err != nil { | |
| 76 log.Fatalf("d2o: cannot open config: %v", err) | |
| 77 } | |
| 78 cfg, err := icf.Parse(f) | |
| 79 f.Close() | |
| 80 if err != nil { | |
| 81 log.Fatalf("d2o: config error: %v", err) | |
| 82 } | |
| 83 | |
| 84 for _, d := range cfg.Abstract("d2o") { | |
| 85 switch d.Key { | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
86 case "logging": |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
87 switch strings.ToLower(safeArg(d.Args, 0)) { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
88 case "", "verbose": |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
89 setLoggingMode(logVerbose) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
90 case "none": |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
91 setLoggingMode(logNone) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
92 case "access": |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
93 setLoggingMode(logAccess) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
94 default: |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
95 log.Fatalf("d2o: unknown logging mode %q (expected none|access|verbose)", safeArg(d.Args, 0)) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
96 } |
| 0 | 97 case "threads": |
| 98 n, err := strconv.Atoi(safeArg(d.Args, 0)) | |
| 99 if err == nil && n > 0 { | |
| 100 runtime.GOMAXPROCS(n) | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
101 verbosePrintf("d2o: GOMAXPROCS = %d", n) |
| 0 | 102 } |
| 103 } | |
| 104 } | |
| 105 | |
| 106 ports := collectPorts(cfg) | |
| 107 if len(ports) == 0 { | |
| 108 log.Fatal("d2o: no port directives found in config") | |
| 109 } | |
| 110 | |
| 111 h := &handler{cfg: cfg} | |
| 112 errCh := make(chan error, len(ports)) | |
| 113 | |
| 114 for _, pc := range ports { | |
| 115 go func(pc portConfig) { | |
| 116 errCh <- pc.listen(h) | |
| 117 }(pc) | |
| 118 } | |
| 119 | |
| 120 log.Fatal(<-errCh) | |
| 121 } | |
| 122 | |
| 123 // --- Port collection -------------------------------------------------------- | |
| 124 | |
| 125 type portConfig struct { | |
| 126 addr string | |
| 127 certFile string | |
| 128 keyFile string | |
| 129 isTLS bool | |
| 130 } | |
| 131 | |
| 132 func (pc portConfig) listen(h http.Handler) error { | |
| 133 if !pc.isTLS { | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
134 verbosePrintf("d2o: listening on %s (http)", pc.addr) |
| 0 | 135 return http.ListenAndServe(pc.addr, h) |
| 136 } | |
| 137 cert, err := tls.LoadX509KeyPair(pc.certFile, pc.keyFile) | |
| 138 if err != nil { | |
| 139 return fmt.Errorf("d2o: tls: %w", err) | |
| 140 } | |
| 141 ln, err := tls.Listen("tcp", pc.addr, &tls.Config{ | |
| 142 Certificates: []tls.Certificate{cert}, | |
| 143 MinVersion: tls.VersionTLS12, | |
| 144 }) | |
| 145 if err != nil { | |
| 146 return fmt.Errorf("d2o: listen %s: %w", pc.addr, err) | |
| 147 } | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
148 verbosePrintf("d2o: listening on %s (https)", pc.addr) |
| 0 | 149 return http.Serve(ln, h) |
| 150 } | |
| 151 | |
| 152 func collectPorts(cfg *icf.Config) []portConfig { | |
| 153 seen := make(map[string]bool) | |
| 154 var out []portConfig | |
| 155 | |
| 156 for _, b := range cfg.Blocks { | |
| 157 dirs := cfg.ResolveBlock(b, nil) | |
| 158 | |
| 159 for _, d := range dirs { | |
| 160 switch d.Key { | |
| 161 case "port": | |
| 162 addr := ":" + safeArg(d.Args, 0) | |
| 163 if !seen[addr] { | |
| 164 seen[addr] = true | |
| 165 out = append(out, portConfig{addr: addr}) | |
| 166 } | |
| 167 case "port+tls": | |
| 11 | 168 // port+tls <port> <cert> <key> |
| 0 | 169 addr := ":" + safeArg(d.Args, 0) |
| 170 c := safeArg(d.Args, 1) | |
| 171 k := safeArg(d.Args, 2) | |
| 172 if !seen[addr] { | |
| 173 seen[addr] = true | |
| 174 out = append(out, portConfig{addr: addr, certFile: c, keyFile: k, isTLS: true}) | |
| 175 } | |
| 176 } | |
| 177 } | |
| 178 } | |
| 179 return out | |
| 180 } | |
| 181 | |
| 182 // --- HTTP Handler ----------------------------------------------------------- | |
| 183 | |
| 184 type handler struct { | |
| 185 cfg *icf.Config | |
| 186 } | |
| 187 | |
| 188 func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
189 start := time.Now() |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
190 rr := &respRecorder{ResponseWriter: w, status: 0} |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
191 |
| 0 | 192 host := stripPort(r.Host) |
| 193 reqPath := path.Clean(r.URL.Path) | |
| 194 | |
| 12 | 195 dirs, caps, subPath := h.cfg.Match(host + reqPath) |
| 0 | 196 if dirs == nil { |
| 12 | 197 dirs, caps, _ = h.cfg.Match(host) |
| 198 subPath = reqPath // host-only block: full path is the subpath | |
| 0 | 199 } |
| 200 if dirs == nil { | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
201 http.Error(rr, "not found", http.StatusNotFound) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
202 rr.ensureStatus(http.StatusNotFound) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
203 accessPrintf("d2o: %s %s%s -> %d %dB (%s)", r.Method, r.Host, r.URL.RequestURI(), rr.status, rr.bytes, time.Since(start).Truncate(time.Millisecond)) |
| 0 | 204 return |
| 205 } | |
| 206 | |
| 12 | 207 h.serve(rr, r, dirs, caps, subPath) |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
208 rr.ensureStatus(http.StatusOK) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
209 accessPrintf("d2o: %s %s%s -> %d %dB (%s)", r.Method, r.Host, r.URL.RequestURI(), rr.status, rr.bytes, time.Since(start).Truncate(time.Millisecond)) |
| 0 | 210 } |
| 211 | |
| 12 | 212 func (h *handler) serve(w http.ResponseWriter, r *http.Request, dirs []icf.Directive, caps map[string]string, subPath string) { |
| 0 | 213 var ( |
|
5
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
214 rootDir string |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
215 rootShow bool |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
216 ndex []string |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
217 fcgiAddr string |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
218 fcgiPat string |
| 11 | 219 cgiExec string |
| 220 cgiPat string | |
|
5
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
221 rprxAddr string |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
222 rdirCode int |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
223 rdirURL string |
| 0 | 224 ) |
| 225 | |
| 226 for _, d := range dirs { | |
| 227 switch d.Key { | |
| 228 case "root": | |
| 229 rootDir = safeArg(d.Args, 0) | |
|
5
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
230 rootShow = safeArg(d.Args, 1) == "show" |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
231 case "ndex": |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
232 ndex = d.Args |
| 0 | 233 case "fcgi": |
| 234 fcgiAddr = safeArg(d.Args, 0) | |
| 235 fcgiPat = safeArg(d.Args, 1) | |
| 236 if fcgiPat == "" { | |
| 237 fcgiPat = "*" | |
| 238 } | |
| 11 | 239 case "cgi": |
| 240 // cgi <executable> [<glob-pattern>] | |
| 241 cgiExec = safeArg(d.Args, 0) | |
| 242 cgiPat = safeArg(d.Args, 1) | |
| 243 if cgiPat == "" { | |
| 244 cgiPat = "*" | |
| 245 } | |
| 0 | 246 case "rprx": |
| 247 rprxAddr = safeArg(d.Args, 0) | |
|
5
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
248 case "rdir": |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
249 rdirCode, _ = strconv.Atoi(safeArg(d.Args, 0)) |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
250 rdirURL = safeArg(d.Args, 1) |
| 0 | 251 } |
| 252 } | |
| 253 | |
|
5
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
254 if rdirURL != "" { |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
255 if rdirCode == 0 { |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
256 rdirCode = http.StatusFound |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
257 } |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
258 verbosePrintf("d2o: rdir %d -> %s", rdirCode, rdirURL) |
|
5
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
259 http.Redirect(w, r, rdirURL, rdirCode) |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
260 return |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
261 } |
| 0 | 262 if rprxAddr != "" { |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
263 verbosePrintf("d2o: rprx -> %s", rprxAddr) |
| 0 | 264 serveReverseProxy(w, r, rprxAddr) |
| 265 return | |
| 266 } | |
| 267 if fcgiAddr != "" && matchGlob(fcgiPat, r.URL.Path) { | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
268 verbosePrintf("d2o: fcgi -> %s (%s)", fcgiAddr, r.URL.Path) |
| 0 | 269 if err := serveFCGI(w, r, fcgiAddr, rootDir); err != nil { |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
270 verbosePrintf("d2o: fcgi error: %v", err) |
| 0 | 271 http.Error(w, "gateway error", http.StatusBadGateway) |
| 272 } | |
| 273 return | |
| 274 } | |
| 11 | 275 if cgiExec != "" && matchGlob(cgiPat, r.URL.Path) { |
| 276 verbosePrintf("d2o: cgi -> %s (%s)", cgiExec, r.URL.Path) | |
| 277 serveCGI(w, r, cgiExec, rootDir) | |
| 278 return | |
| 279 } | |
| 0 | 280 if rootDir != "" { |
| 12 | 281 // subPath is the remainder after the matched path pattern, e.g. for |
| 282 // block "host/~<user>" and request "/~alice/about.html", subPath="/about.html". | |
| 283 // For host-only blocks it equals the full request path. | |
| 284 displayPath := path.Clean(r.URL.Path) | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
285 verbosePrintf("d2o: static -> %s (%s)", rootDir, r.URL.Path) |
| 12 | 286 serveStatic(w, r, rootDir, rootShow, ndex, fcgiAddr, fcgiPat, cgiExec, cgiPat, subPath, displayPath) |
| 0 | 287 return |
| 288 } | |
| 289 | |
| 290 http.Error(w, "not found", http.StatusNotFound) | |
| 291 } | |
| 292 | |
| 293 // --- Static ----------------------------------------------------------------- | |
| 3 | 294 |
| 295 // serveStatic serves files from rootDir. | |
| 296 // rootIndex == nil: directory listing forbidden (hide). | |
| 297 // rootIndex != nil: try each as index candidate; if none found, show listing. | |
| 11 | 298 func serveStatic(w http.ResponseWriter, r *http.Request, rootDir string, show bool, ndex []string, fcgiAddr, fcgiPat, cgiExec, cgiPat string, fsPath, displayPath string) { |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
299 fpath := filepath.Join(rootDir, filepath.FromSlash(path.Clean(fsPath))) |
| 0 | 300 |
| 12 | 301 // Use a cloned request with fsPath as URL.Path so that http.ServeFile and |
| 302 // fcgi/cgi handlers see a path relative to rootDir, not the original URL. | |
| 303 r2 := r.Clone(r.Context()) | |
| 304 r2.URL.Path = fsPath | |
| 305 | |
| 0 | 306 info, err := os.Stat(fpath) |
| 307 if os.IsNotExist(err) { | |
| 308 http.Error(w, "not found", http.StatusNotFound) | |
| 309 return | |
| 310 } | |
| 311 if err != nil { | |
| 312 http.Error(w, "internal error", http.StatusInternalServerError) | |
| 313 return | |
| 314 } | |
| 1 | 315 |
| 0 | 316 if info.IsDir() { |
|
5
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
317 for _, idx := range ndex { |
| 1 | 318 idxPath := filepath.Join(fpath, idx) |
| 319 if _, err := os.Stat(idxPath); err == nil { | |
|
5
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
320 if fcgiAddr != "" && matchGlob(fcgiPat, idx) { |
| 12 | 321 r3 := r2.Clone(r2.Context()) |
| 322 r3.URL.Path = path.Join(fsPath, idx) | |
| 323 if err := serveFCGI(w, r3, fcgiAddr, rootDir); err != nil { | |
|
5
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
324 log.Printf("d2o: fcgi error: %v", err) |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
325 http.Error(w, "gateway error", http.StatusBadGateway) |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
326 } |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
327 return |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
328 } |
| 11 | 329 if cgiExec != "" && matchGlob(cgiPat, idx) { |
| 12 | 330 r3 := r2.Clone(r2.Context()) |
| 331 r3.URL.Path = path.Join(fsPath, idx) | |
| 332 serveCGI(w, r3, cgiExec, rootDir) | |
| 11 | 333 return |
| 334 } | |
| 12 | 335 http.ServeFile(w, r2, idxPath) |
| 1 | 336 return |
| 337 } | |
| 338 } | |
|
5
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
339 if !show { |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
340 http.Error(w, "forbidden", http.StatusForbidden) |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
341 return |
|
07b6f06899e0
my tired ass deleted a fix that was partially working
Atarwn Gard <a@qwa.su>
parents:
3
diff
changeset
|
342 } |
| 12 | 343 listDir(w, r2, fpath, displayPath) |
| 0 | 344 return |
| 345 } | |
| 1 | 346 |
| 12 | 347 http.ServeFile(w, r2, fpath) |
| 0 | 348 } |
| 349 | |
| 350 func listDir(w http.ResponseWriter, r *http.Request, dir, urlPath string) { | |
| 351 entries, err := os.ReadDir(dir) | |
| 352 if err != nil { | |
| 353 http.Error(w, "cannot read directory", http.StatusInternalServerError) | |
| 354 return | |
| 355 } | |
| 356 w.Header().Set("Content-Type", "text/html; charset=utf-8") | |
|
7
8e4813b4e509
update index style + justfile additions
Atarwn Gard <a@qwa.su>
parents:
5
diff
changeset
|
357 fmt.Fprintf(w, "<html><head><title>Index of %s</title><style>body{font-family:monospace}</style></head><body>\n", urlPath) |
|
8e4813b4e509
update index style + justfile additions
Atarwn Gard <a@qwa.su>
parents:
5
diff
changeset
|
358 fmt.Fprintf(w, "<h2>Index of %s</h2><pre>\n", urlPath) |
| 0 | 359 if urlPath != "/" { |
| 360 fmt.Fprintf(w, "<a href=\"..\">..</a>\n") | |
| 361 } | |
| 362 for _, e := range entries { | |
| 363 name := e.Name() | |
| 364 if e.IsDir() { | |
| 365 name += "/" | |
| 366 } | |
| 367 fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", path.Join(urlPath, name), name) | |
| 368 } | |
|
7
8e4813b4e509
update index style + justfile additions
Atarwn Gard <a@qwa.su>
parents:
5
diff
changeset
|
369 fmt.Fprintf(w, "</pre><i>d2o webserver</i></body></html>") |
| 0 | 370 } |
| 371 | |
| 372 // --- Reverse proxy ---------------------------------------------------------- | |
| 373 | |
| 374 func serveReverseProxy(w http.ResponseWriter, r *http.Request, target string) { | |
| 375 if !strings.HasPrefix(target, "http://") && !strings.HasPrefix(target, "https://") { | |
| 376 target = "http://" + target | |
| 377 } | |
| 378 u, err := url.Parse(target) | |
| 379 if err != nil { | |
| 380 http.Error(w, "bad gateway config", http.StatusInternalServerError) | |
| 381 return | |
| 382 } | |
| 383 proxy := httputil.NewSingleHostReverseProxy(u) | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
384 if verboseEnabled() { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
385 origDirector := proxy.Director |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
386 proxy.Director = func(req *http.Request) { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
387 origDirector(req) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
388 log.Printf("d2o: rprx upstream request: %s %s", req.Method, req.URL.String()) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
389 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
390 proxy.ModifyResponse = func(resp *http.Response) error { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
391 log.Printf("d2o: rprx upstream response: %d %s", resp.StatusCode, resp.Status) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
392 return nil |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
393 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
394 } |
| 0 | 395 proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) { |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
396 verbosePrintf("d2o: rprx error: %v", err) |
| 0 | 397 http.Error(w, "bad gateway", http.StatusBadGateway) |
| 398 } | |
| 399 proxy.ServeHTTP(w, r) | |
| 400 } | |
| 401 | |
| 11 | 402 // --- CGI -------------------------------------------------------------------- |
| 403 | |
| 404 // serveCGI runs a CGI executable using net/http/cgi. | |
| 405 // cgiExec is the path to the executable (e.g. /usr/lib/cgit/cgit.cgi). | |
| 406 // docRoot is set as DOCUMENT_ROOT; may be empty. | |
| 407 func serveCGI(w http.ResponseWriter, r *http.Request, cgiExec, docRoot string) { | |
| 408 h := &cgi.Handler{ | |
| 409 Path: cgiExec, | |
| 410 Dir: docRoot, | |
| 411 Env: []string{ | |
| 412 "DOCUMENT_ROOT=" + docRoot, | |
| 413 "SERVER_SOFTWARE=d2o/1.1", | |
| 414 }, | |
| 415 } | |
| 416 h.ServeHTTP(w, r) | |
| 417 } | |
| 418 | |
| 0 | 419 // --- FastCGI ---------------------------------------------------------------- |
| 420 | |
| 421 func serveFCGI(w http.ResponseWriter, r *http.Request, addr, docRoot string) error { | |
| 422 network, address := parseFCGIAddr(addr) | |
| 3 | 423 client, err := fcgi.Dial(network, address) |
| 0 | 424 if err != nil { |
| 425 return fmt.Errorf("connect %s: %w", addr, err) | |
| 426 } | |
| 3 | 427 defer client.Close() |
| 0 | 428 |
| 429 scriptPath := r.URL.Path | |
| 430 if docRoot != "" { | |
| 431 scriptPath = filepath.Join(docRoot, filepath.FromSlash(r.URL.Path)) | |
| 432 } | |
| 433 | |
| 434 params := map[string]string{ | |
| 435 "REQUEST_METHOD": r.Method, | |
| 436 "SCRIPT_FILENAME": scriptPath, | |
| 437 "SCRIPT_NAME": r.URL.Path, | |
| 438 "REQUEST_URI": r.URL.RequestURI(), | |
| 439 "QUERY_STRING": r.URL.RawQuery, | |
| 440 "SERVER_PROTOCOL": r.Proto, | |
| 441 "SERVER_NAME": stripPort(r.Host), | |
| 442 "DOCUMENT_ROOT": docRoot, | |
| 443 "GATEWAY_INTERFACE": "CGI/1.1", | |
| 9 | 444 "SERVER_SOFTWARE": "d2o/1.1", |
| 0 | 445 } |
| 446 if r.TLS != nil { | |
| 447 params["HTTPS"] = "on" | |
| 448 } | |
| 449 for k, vs := range r.Header { | |
| 450 key := "HTTP_" + strings.ToUpper(strings.ReplaceAll(k, "-", "_")) | |
| 451 params[key] = strings.Join(vs, ", ") | |
| 452 } | |
| 453 if ct := r.Header.Get("Content-Type"); ct != "" { | |
| 454 params["CONTENT_TYPE"] = ct | |
| 455 } | |
| 456 if r.ContentLength >= 0 { | |
| 457 params["CONTENT_LENGTH"] = strconv.FormatInt(r.ContentLength, 10) | |
| 458 } | |
| 459 | |
| 3 | 460 // Use Do() instead of Request() — php-fpm returns CGI response (no HTTP status line), |
| 461 // not a full HTTP response. Request() expects "HTTP/1.1 200 OK" and panics on code 0. | |
| 462 cgiReader, err := client.Do(params, r.Body) | |
| 463 if err != nil { | |
| 464 return fmt.Errorf("fcgi request: %w", err) | |
| 465 } | |
| 466 | |
| 467 // Parse CGI headers manually | |
| 468 br := bufio.NewReader(cgiReader) | |
| 469 tp := textproto.NewReader(br) | |
| 470 mime, err := tp.ReadMIMEHeader() | |
| 471 if err != nil && len(mime) == 0 { | |
| 472 return fmt.Errorf("fcgi response headers: %w", err) | |
| 473 } | |
| 474 | |
| 475 status := http.StatusOK | |
| 476 if s := mime.Get("Status"); s != "" { | |
| 477 code, _, _ := strings.Cut(s, " ") | |
| 478 if n, err := strconv.Atoi(code); err == nil && n > 0 { | |
| 479 status = n | |
| 480 } | |
| 481 mime.Del("Status") | |
| 482 } | |
| 483 | |
| 484 for k, vs := range mime { | |
| 485 for _, v := range vs { | |
| 486 w.Header().Add(k, v) | |
| 487 } | |
| 488 } | |
| 489 w.WriteHeader(status) | |
| 490 io.Copy(w, br) | |
| 491 return nil | |
| 0 | 492 } |
| 493 | |
| 494 func parseFCGIAddr(addr string) (network, address string) { | |
| 495 if strings.HasPrefix(addr, "unix:") { | |
| 496 return "unix", strings.TrimPrefix(addr, "unix:") | |
| 497 } | |
| 498 return "tcp", addr | |
| 499 } | |
| 500 | |
| 501 // --- Helpers ---------------------------------------------------------------- | |
| 502 | |
| 503 func stripPort(host string) string { | |
| 504 if h, _, err := net.SplitHostPort(host); err == nil { | |
| 505 return h | |
| 506 } | |
| 507 return host | |
| 508 } | |
| 509 | |
| 510 func safeArg(args []string, i int) string { | |
| 511 if i < len(args) { | |
| 512 return args[i] | |
| 513 } | |
| 514 return "" | |
| 515 } | |
| 516 | |
| 517 func matchGlob(pattern, s string) bool { | |
| 518 if pattern == "*" { | |
| 519 return true | |
| 520 } | |
| 521 regPat := "^" + strings.ReplaceAll(regexp.QuoteMeta(pattern), regexp.QuoteMeta("*"), ".*") + "$" | |
| 522 matched, err := regexp.MatchString(regPat, s) | |
| 523 return err == nil && matched | |
|
8
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
524 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
525 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
526 type respRecorder struct { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
527 http.ResponseWriter |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
528 status int |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
529 bytes int64 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
530 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
531 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
532 func (rr *respRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
533 h, ok := rr.ResponseWriter.(http.Hijacker) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
534 if !ok { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
535 return nil, nil, fmt.Errorf("hijack not supported") |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
536 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
537 return h.Hijack() |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
538 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
539 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
540 func (rr *respRecorder) Flush() { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
541 if f, ok := rr.ResponseWriter.(http.Flusher); ok { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
542 f.Flush() |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
543 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
544 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
545 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
546 func (rr *respRecorder) Push(target string, opts *http.PushOptions) error { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
547 if p, ok := rr.ResponseWriter.(http.Pusher); ok { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
548 return p.Push(target, opts) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
549 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
550 return http.ErrNotSupported |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
551 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
552 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
553 func (rr *respRecorder) ReadFrom(src io.Reader) (int64, error) { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
554 // Preserve io.Copy optimizations and count bytes. |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
555 rf, ok := rr.ResponseWriter.(io.ReaderFrom) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
556 if !ok { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
557 return io.Copy(rr, src) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
558 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
559 if rr.status == 0 { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
560 rr.status = http.StatusOK |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
561 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
562 n, err := rf.ReadFrom(src) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
563 rr.bytes += n |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
564 return n, err |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
565 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
566 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
567 func (rr *respRecorder) WriteHeader(code int) { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
568 rr.status = code |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
569 rr.ResponseWriter.WriteHeader(code) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
570 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
571 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
572 func (rr *respRecorder) Write(p []byte) (int, error) { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
573 if rr.status == 0 { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
574 rr.status = http.StatusOK |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
575 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
576 n, err := rr.ResponseWriter.Write(p) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
577 rr.bytes += int64(n) |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
578 return n, err |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
579 } |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
580 |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
581 func (rr *respRecorder) ensureStatus(defaultCode int) { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
582 if rr.status == 0 { |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
583 rr.status = defaultCode |
|
2ffb8028ccbb
add loggingmodes and fix path matching
Atarwn Gard <a@qwa.su>
parents:
7
diff
changeset
|
584 } |
| 12 | 585 } |
