Mercurial Hosting > d2o
diff main.go @ 3:eb705d4cdcd7
fix fcgi
| author | Atarwn Gard <a@qwa.su> |
|---|---|
| date | Mon, 09 Mar 2026 02:16:06 +0500 |
| parents | d19133be91ba |
| children | 07b6f06899e0 |
line wrap: on
line diff
--- a/main.go Mon Mar 09 01:55:11 2026 +0500 +++ b/main.go Mon Mar 09 02:16:06 2026 +0500 @@ -2,8 +2,11 @@ import ( "crypto/tls" + "bufio" "fmt" + "io" "log" + "net/textproto" "net" "net/http" "net/http/httputil" @@ -16,6 +19,7 @@ "strconv" "strings" + "d2o/fcgi" "d2o/icf" ) @@ -160,23 +164,31 @@ func (h *handler) serve(w http.ResponseWriter, r *http.Request, dirs []icf.Directive, _ map[string]string) { var ( - rootDir string - rootShow bool - ndex []string - fcgiAddr string - fcgiPat string - rprxAddr string - rdirCode int - rdirURL string + rootDir string + rootIndex []string // nil = hide; non-nil = show, try these index files first + fcgiAddr string + fcgiPat string + rprxAddr string ) for _, d := range dirs { switch d.Key { case "root": rootDir = safeArg(d.Args, 0) - rootShow = safeArg(d.Args, 1) == "show" - case "ndex": - ndex = d.Args + switch safeArg(d.Args, 1) { + case "show": + // root /path show [index.php index.html ...] + // up to 12 index file candidates; default is index.html + if len(d.Args) >= 3 { + rootIndex = d.Args[2:] + } else { + rootIndex = []string{"index.html"} + } + case "hide", "": + rootIndex = nil + default: + log.Printf("d2o: root: unknown mode %q (want show|hide)", safeArg(d.Args, 1)) + } case "fcgi": fcgiAddr = safeArg(d.Args, 0) fcgiPat = safeArg(d.Args, 1) @@ -185,19 +197,10 @@ } case "rprx": rprxAddr = safeArg(d.Args, 0) - case "rdir": - rdirCode, _ = strconv.Atoi(safeArg(d.Args, 0)) - rdirURL = safeArg(d.Args, 1) } } - if rdirURL != "" { - if rdirCode == 0 { - rdirCode = http.StatusFound - } - http.Redirect(w, r, rdirURL, rdirCode) - return - } + // Priority: rprx > fcgi > static root if rprxAddr != "" { serveReverseProxy(w, r, rprxAddr) return @@ -210,7 +213,7 @@ return } if rootDir != "" { - serveStatic(w, r, rootDir, rootShow, ndex) + serveStatic(w, r, rootDir, rootIndex) return } @@ -218,7 +221,11 @@ } // --- Static ----------------------------------------------------------------- -func serveStatic(w http.ResponseWriter, r *http.Request, rootDir string, show bool, ndex []string) { + +// serveStatic serves files from rootDir. +// rootIndex == nil: directory listing forbidden (hide). +// rootIndex != nil: try each as index candidate; if none found, show listing. +func serveStatic(w http.ResponseWriter, r *http.Request, rootDir string, rootIndex []string) { fpath := filepath.Join(rootDir, filepath.FromSlash(path.Clean(r.URL.Path))) info, err := os.Stat(fpath) @@ -232,17 +239,17 @@ } if info.IsDir() { - for _, idx := range ndex { + if rootIndex == nil { + http.Error(w, "forbidden", http.StatusForbidden) + return + } + for _, idx := range rootIndex { idxPath := filepath.Join(fpath, idx) if _, err := os.Stat(idxPath); err == nil { http.ServeFile(w, r, idxPath) return } } - if !show { - http.Error(w, "forbidden", http.StatusForbidden) - return - } listDir(w, r, fpath, r.URL.Path) return } @@ -295,11 +302,11 @@ func serveFCGI(w http.ResponseWriter, r *http.Request, addr, docRoot string) error { network, address := parseFCGIAddr(addr) - conn, err := net.Dial(network, address) + client, err := fcgi.Dial(network, address) if err != nil { return fmt.Errorf("connect %s: %w", addr, err) } - defer conn.Close() + defer client.Close() scriptPath := r.URL.Path if docRoot != "" { @@ -332,7 +339,38 @@ params["CONTENT_LENGTH"] = strconv.FormatInt(r.ContentLength, 10) } - return fcgi.Do(w, r, conn, params) + // Use Do() instead of Request() — php-fpm returns CGI response (no HTTP status line), + // not a full HTTP response. Request() expects "HTTP/1.1 200 OK" and panics on code 0. + cgiReader, err := client.Do(params, r.Body) + if err != nil { + return fmt.Errorf("fcgi request: %w", err) + } + + // Parse CGI headers manually + br := bufio.NewReader(cgiReader) + tp := textproto.NewReader(br) + mime, err := tp.ReadMIMEHeader() + if err != nil && len(mime) == 0 { + return fmt.Errorf("fcgi response headers: %w", err) + } + + status := http.StatusOK + if s := mime.Get("Status"); s != "" { + code, _, _ := strings.Cut(s, " ") + if n, err := strconv.Atoi(code); err == nil && n > 0 { + status = n + } + mime.Del("Status") + } + + for k, vs := range mime { + for _, v := range vs { + w.Header().Add(k, v) + } + } + w.WriteHeader(status) + io.Copy(w, br) + return nil } func parseFCGIAddr(addr string) (network, address string) {
