comparison main.go @ 12:84384cccda0e default tip

bubfix subpaths and remove hardcode
author atarwn@g5
date Mon, 23 Mar 2026 12:05:05 +0500
parents 350589d762a0
children
comparison
equal deleted inserted replaced
11:350589d762a0 12:84384cccda0e
190 rr := &respRecorder{ResponseWriter: w, status: 0} 190 rr := &respRecorder{ResponseWriter: w, status: 0}
191 191
192 host := stripPort(r.Host) 192 host := stripPort(r.Host)
193 reqPath := path.Clean(r.URL.Path) 193 reqPath := path.Clean(r.URL.Path)
194 194
195 dirs, caps := h.cfg.Match(host + reqPath) 195 dirs, caps, subPath := h.cfg.Match(host + reqPath)
196 if dirs == nil { 196 if dirs == nil {
197 dirs, caps = h.cfg.Match(host) 197 dirs, caps, _ = h.cfg.Match(host)
198 subPath = reqPath // host-only block: full path is the subpath
198 } 199 }
199 if dirs == nil { 200 if dirs == nil {
200 http.Error(rr, "not found", http.StatusNotFound) 201 http.Error(rr, "not found", http.StatusNotFound)
201 rr.ensureStatus(http.StatusNotFound) 202 rr.ensureStatus(http.StatusNotFound)
202 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)) 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))
203 return 204 return
204 } 205 }
205 206
206 h.serve(rr, r, dirs, caps) 207 h.serve(rr, r, dirs, caps, subPath)
207 rr.ensureStatus(http.StatusOK) 208 rr.ensureStatus(http.StatusOK)
208 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)) 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))
209 } 210 }
210 211
211 func (h *handler) serve(w http.ResponseWriter, r *http.Request, dirs []icf.Directive, caps map[string]string) { 212 func (h *handler) serve(w http.ResponseWriter, r *http.Request, dirs []icf.Directive, caps map[string]string, subPath string) {
212 var ( 213 var (
213 rootDir string 214 rootDir string
214 rootShow bool 215 rootShow bool
215 ndex []string 216 ndex []string
216 fcgiAddr string 217 fcgiAddr string
275 verbosePrintf("d2o: cgi -> %s (%s)", cgiExec, r.URL.Path) 276 verbosePrintf("d2o: cgi -> %s (%s)", cgiExec, r.URL.Path)
276 serveCGI(w, r, cgiExec, rootDir) 277 serveCGI(w, r, cgiExec, rootDir)
277 return 278 return
278 } 279 }
279 if rootDir != "" { 280 if rootDir != "" {
280 fsPath := path.Clean(r.URL.Path) 281 // subPath is the remainder after the matched path pattern, e.g. for
281 displayPath := fsPath 282 // block "host/~<user>" and request "/~alice/about.html", subPath="/about.html".
282 if user, ok := caps["user"]; ok && user != "" { 283 // For host-only blocks it equals the full request path.
283 mount := "/~" + user 284 displayPath := path.Clean(r.URL.Path)
284 if fsPath == mount || strings.HasPrefix(fsPath, mount+"/") {
285 trimmed := strings.TrimPrefix(fsPath, mount)
286 if trimmed == "" {
287 trimmed = "/"
288 }
289 fsPath = trimmed
290 }
291 }
292
293 verbosePrintf("d2o: static -> %s (%s)", rootDir, r.URL.Path) 285 verbosePrintf("d2o: static -> %s (%s)", rootDir, r.URL.Path)
294 serveStatic(w, r, rootDir, rootShow, ndex, fcgiAddr, fcgiPat, cgiExec, cgiPat, fsPath, displayPath) 286 serveStatic(w, r, rootDir, rootShow, ndex, fcgiAddr, fcgiPat, cgiExec, cgiPat, subPath, displayPath)
295 return 287 return
296 } 288 }
297 289
298 http.Error(w, "not found", http.StatusNotFound) 290 http.Error(w, "not found", http.StatusNotFound)
299 } 291 }
304 // rootIndex == nil: directory listing forbidden (hide). 296 // rootIndex == nil: directory listing forbidden (hide).
305 // rootIndex != nil: try each as index candidate; if none found, show listing. 297 // rootIndex != nil: try each as index candidate; if none found, show listing.
306 func serveStatic(w http.ResponseWriter, r *http.Request, rootDir string, show bool, ndex []string, fcgiAddr, fcgiPat, cgiExec, cgiPat string, fsPath, displayPath string) { 298 func serveStatic(w http.ResponseWriter, r *http.Request, rootDir string, show bool, ndex []string, fcgiAddr, fcgiPat, cgiExec, cgiPat string, fsPath, displayPath string) {
307 fpath := filepath.Join(rootDir, filepath.FromSlash(path.Clean(fsPath))) 299 fpath := filepath.Join(rootDir, filepath.FromSlash(path.Clean(fsPath)))
308 300
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
309 info, err := os.Stat(fpath) 306 info, err := os.Stat(fpath)
310 if os.IsNotExist(err) { 307 if os.IsNotExist(err) {
311 http.Error(w, "not found", http.StatusNotFound) 308 http.Error(w, "not found", http.StatusNotFound)
312 return 309 return
313 } 310 }
319 if info.IsDir() { 316 if info.IsDir() {
320 for _, idx := range ndex { 317 for _, idx := range ndex {
321 idxPath := filepath.Join(fpath, idx) 318 idxPath := filepath.Join(fpath, idx)
322 if _, err := os.Stat(idxPath); err == nil { 319 if _, err := os.Stat(idxPath); err == nil {
323 if fcgiAddr != "" && matchGlob(fcgiPat, idx) { 320 if fcgiAddr != "" && matchGlob(fcgiPat, idx) {
324 r2 := r.Clone(r.Context()) 321 r3 := r2.Clone(r2.Context())
325 r2.URL.Path = path.Join(r.URL.Path, idx) 322 r3.URL.Path = path.Join(fsPath, idx)
326 if err := serveFCGI(w, r2, fcgiAddr, rootDir); err != nil { 323 if err := serveFCGI(w, r3, fcgiAddr, rootDir); err != nil {
327 log.Printf("d2o: fcgi error: %v", err) 324 log.Printf("d2o: fcgi error: %v", err)
328 http.Error(w, "gateway error", http.StatusBadGateway) 325 http.Error(w, "gateway error", http.StatusBadGateway)
329 } 326 }
330 return 327 return
331 } 328 }
332 if cgiExec != "" && matchGlob(cgiPat, idx) { 329 if cgiExec != "" && matchGlob(cgiPat, idx) {
333 r2 := r.Clone(r.Context()) 330 r3 := r2.Clone(r2.Context())
334 r2.URL.Path = path.Join(r.URL.Path, idx) 331 r3.URL.Path = path.Join(fsPath, idx)
335 serveCGI(w, r2, cgiExec, rootDir) 332 serveCGI(w, r3, cgiExec, rootDir)
336 return 333 return
337 } 334 }
338 http.ServeFile(w, r, idxPath) 335 http.ServeFile(w, r2, idxPath)
339 return 336 return
340 } 337 }
341 } 338 }
342 if !show { 339 if !show {
343 http.Error(w, "forbidden", http.StatusForbidden) 340 http.Error(w, "forbidden", http.StatusForbidden)
344 return 341 return
345 } 342 }
346 listDir(w, r, fpath, displayPath) 343 listDir(w, r2, fpath, displayPath)
347 return 344 return
348 } 345 }
349 346
350 http.ServeFile(w, r, fpath) 347 http.ServeFile(w, r2, fpath)
351 } 348 }
352 349
353 func listDir(w http.ResponseWriter, r *http.Request, dir, urlPath string) { 350 func listDir(w http.ResponseWriter, r *http.Request, dir, urlPath string) {
354 entries, err := os.ReadDir(dir) 351 entries, err := os.ReadDir(dir)
355 if err != nil { 352 if err != nil {