comparison main.go @ 11:350589d762a0 default tip

add cgi, remove tls, update docs
author Atarwn Gard <a@qwa.su>
date Thu, 19 Mar 2026 20:00:49 +0500
parents ec97184ea63d
children
comparison
equal deleted inserted replaced
10:560d4103e12e 11:350589d762a0
6 "fmt" 6 "fmt"
7 "io" 7 "io"
8 "log" 8 "log"
9 "net" 9 "net"
10 "net/http" 10 "net/http"
11 "net/http/cgi"
11 "net/http/httputil" 12 "net/http/httputil"
12 "net/textproto" 13 "net/textproto"
13 "net/url" 14 "net/url"
14 "os" 15 "os"
15 "path" 16 "path"
153 var out []portConfig 154 var out []portConfig
154 155
155 for _, b := range cfg.Blocks { 156 for _, b := range cfg.Blocks {
156 dirs := cfg.ResolveBlock(b, nil) 157 dirs := cfg.ResolveBlock(b, nil)
157 158
158 var cert, key string
159 for _, d := range dirs {
160 if d.Key == "tls" {
161 cert = safeArg(d.Args, 0)
162 key = safeArg(d.Args, 1)
163 }
164 }
165
166 for _, d := range dirs { 159 for _, d := range dirs {
167 switch d.Key { 160 switch d.Key {
168 case "port": 161 case "port":
169 addr := ":" + safeArg(d.Args, 0) 162 addr := ":" + safeArg(d.Args, 0)
170 if !seen[addr] { 163 if !seen[addr] {
171 seen[addr] = true 164 seen[addr] = true
172 out = append(out, portConfig{addr: addr}) 165 out = append(out, portConfig{addr: addr})
173 } 166 }
174 case "port+tls": 167 case "port+tls":
168 // port+tls <port> <cert> <key>
175 addr := ":" + safeArg(d.Args, 0) 169 addr := ":" + safeArg(d.Args, 0)
176 c := safeArg(d.Args, 1) 170 c := safeArg(d.Args, 1)
177 k := safeArg(d.Args, 2) 171 k := safeArg(d.Args, 2)
178 if c == "" {
179 c = cert
180 }
181 if k == "" {
182 k = key
183 }
184 if !seen[addr] { 172 if !seen[addr] {
185 seen[addr] = true 173 seen[addr] = true
186 out = append(out, portConfig{addr: addr, certFile: c, keyFile: k, isTLS: true}) 174 out = append(out, portConfig{addr: addr, certFile: c, keyFile: k, isTLS: true})
187 } 175 }
188 } 176 }
225 rootDir string 213 rootDir string
226 rootShow bool 214 rootShow bool
227 ndex []string 215 ndex []string
228 fcgiAddr string 216 fcgiAddr string
229 fcgiPat string 217 fcgiPat string
218 cgiExec string
219 cgiPat string
230 rprxAddr string 220 rprxAddr string
231 rdirCode int 221 rdirCode int
232 rdirURL string 222 rdirURL string
233 ) 223 )
234 224
243 fcgiAddr = safeArg(d.Args, 0) 233 fcgiAddr = safeArg(d.Args, 0)
244 fcgiPat = safeArg(d.Args, 1) 234 fcgiPat = safeArg(d.Args, 1)
245 if fcgiPat == "" { 235 if fcgiPat == "" {
246 fcgiPat = "*" 236 fcgiPat = "*"
247 } 237 }
238 case "cgi":
239 // cgi <executable> [<glob-pattern>]
240 cgiExec = safeArg(d.Args, 0)
241 cgiPat = safeArg(d.Args, 1)
242 if cgiPat == "" {
243 cgiPat = "*"
244 }
248 case "rprx": 245 case "rprx":
249 rprxAddr = safeArg(d.Args, 0) 246 rprxAddr = safeArg(d.Args, 0)
250 case "rdir": 247 case "rdir":
251 rdirCode, _ = strconv.Atoi(safeArg(d.Args, 0)) 248 rdirCode, _ = strconv.Atoi(safeArg(d.Args, 0))
252 rdirURL = safeArg(d.Args, 1) 249 rdirURL = safeArg(d.Args, 1)
270 verbosePrintf("d2o: fcgi -> %s (%s)", fcgiAddr, r.URL.Path) 267 verbosePrintf("d2o: fcgi -> %s (%s)", fcgiAddr, r.URL.Path)
271 if err := serveFCGI(w, r, fcgiAddr, rootDir); err != nil { 268 if err := serveFCGI(w, r, fcgiAddr, rootDir); err != nil {
272 verbosePrintf("d2o: fcgi error: %v", err) 269 verbosePrintf("d2o: fcgi error: %v", err)
273 http.Error(w, "gateway error", http.StatusBadGateway) 270 http.Error(w, "gateway error", http.StatusBadGateway)
274 } 271 }
272 return
273 }
274 if cgiExec != "" && matchGlob(cgiPat, r.URL.Path) {
275 verbosePrintf("d2o: cgi -> %s (%s)", cgiExec, r.URL.Path)
276 serveCGI(w, r, cgiExec, rootDir)
275 return 277 return
276 } 278 }
277 if rootDir != "" { 279 if rootDir != "" {
278 fsPath := path.Clean(r.URL.Path) 280 fsPath := path.Clean(r.URL.Path)
279 displayPath := fsPath 281 displayPath := fsPath
287 fsPath = trimmed 289 fsPath = trimmed
288 } 290 }
289 } 291 }
290 292
291 verbosePrintf("d2o: static -> %s (%s)", rootDir, r.URL.Path) 293 verbosePrintf("d2o: static -> %s (%s)", rootDir, r.URL.Path)
292 serveStatic(w, r, rootDir, rootShow, ndex, fcgiAddr, fcgiPat, fsPath, displayPath) 294 serveStatic(w, r, rootDir, rootShow, ndex, fcgiAddr, fcgiPat, cgiExec, cgiPat, fsPath, displayPath)
293 return 295 return
294 } 296 }
295 297
296 http.Error(w, "not found", http.StatusNotFound) 298 http.Error(w, "not found", http.StatusNotFound)
297 } 299 }
299 // --- Static ----------------------------------------------------------------- 301 // --- Static -----------------------------------------------------------------
300 302
301 // serveStatic serves files from rootDir. 303 // serveStatic serves files from rootDir.
302 // rootIndex == nil: directory listing forbidden (hide). 304 // rootIndex == nil: directory listing forbidden (hide).
303 // rootIndex != nil: try each as index candidate; if none found, show listing. 305 // rootIndex != nil: try each as index candidate; if none found, show listing.
304 func serveStatic(w http.ResponseWriter, r *http.Request, rootDir string, show bool, ndex []string, fcgiAddr, fcgiPat string, fsPath, displayPath string) { 306 func serveStatic(w http.ResponseWriter, r *http.Request, rootDir string, show bool, ndex []string, fcgiAddr, fcgiPat, cgiExec, cgiPat string, fsPath, displayPath string) {
305 fpath := filepath.Join(rootDir, filepath.FromSlash(path.Clean(fsPath))) 307 fpath := filepath.Join(rootDir, filepath.FromSlash(path.Clean(fsPath)))
306 308
307 info, err := os.Stat(fpath) 309 info, err := os.Stat(fpath)
308 if os.IsNotExist(err) { 310 if os.IsNotExist(err) {
309 http.Error(w, "not found", http.StatusNotFound) 311 http.Error(w, "not found", http.StatusNotFound)
325 log.Printf("d2o: fcgi error: %v", err) 327 log.Printf("d2o: fcgi error: %v", err)
326 http.Error(w, "gateway error", http.StatusBadGateway) 328 http.Error(w, "gateway error", http.StatusBadGateway)
327 } 329 }
328 return 330 return
329 } 331 }
332 if cgiExec != "" && matchGlob(cgiPat, idx) {
333 r2 := r.Clone(r.Context())
334 r2.URL.Path = path.Join(r.URL.Path, idx)
335 serveCGI(w, r2, cgiExec, rootDir)
336 return
337 }
330 http.ServeFile(w, r, idxPath) 338 http.ServeFile(w, r, idxPath)
331 return 339 return
332 } 340 }
333 } 341 }
334 if !show { 342 if !show {
390 proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) { 398 proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
391 verbosePrintf("d2o: rprx error: %v", err) 399 verbosePrintf("d2o: rprx error: %v", err)
392 http.Error(w, "bad gateway", http.StatusBadGateway) 400 http.Error(w, "bad gateway", http.StatusBadGateway)
393 } 401 }
394 proxy.ServeHTTP(w, r) 402 proxy.ServeHTTP(w, r)
403 }
404
405 // --- CGI --------------------------------------------------------------------
406
407 // serveCGI runs a CGI executable using net/http/cgi.
408 // cgiExec is the path to the executable (e.g. /usr/lib/cgit/cgit.cgi).
409 // docRoot is set as DOCUMENT_ROOT; may be empty.
410 func serveCGI(w http.ResponseWriter, r *http.Request, cgiExec, docRoot string) {
411 h := &cgi.Handler{
412 Path: cgiExec,
413 Dir: docRoot,
414 Env: []string{
415 "DOCUMENT_ROOT=" + docRoot,
416 "SERVER_SOFTWARE=d2o/1.1",
417 },
418 }
419 h.ServeHTTP(w, r)
395 } 420 }
396 421
397 // --- FastCGI ---------------------------------------------------------------- 422 // --- FastCGI ----------------------------------------------------------------
398 423
399 func serveFCGI(w http.ResponseWriter, r *http.Request, addr, docRoot string) error { 424 func serveFCGI(w http.ResponseWriter, r *http.Request, addr, docRoot string) error {