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) {