changeset 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 560d4103e12e
children
files main.go man/d2obase.5.md
diffstat 2 files changed, 57 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/main.go	Tue Mar 17 22:27:30 2026 +0500
+++ b/main.go	Thu Mar 19 20:00:49 2026 +0500
@@ -8,6 +8,7 @@
 	"log"
 	"net"
 	"net/http"
+	"net/http/cgi"
 	"net/http/httputil"
 	"net/textproto"
 	"net/url"
@@ -155,14 +156,6 @@
 	for _, b := range cfg.Blocks {
 		dirs := cfg.ResolveBlock(b, nil)
 
-		var cert, key string
-		for _, d := range dirs {
-			if d.Key == "tls" {
-				cert = safeArg(d.Args, 0)
-				key = safeArg(d.Args, 1)
-			}
-		}
-
 		for _, d := range dirs {
 			switch d.Key {
 			case "port":
@@ -172,15 +165,10 @@
 					out = append(out, portConfig{addr: addr})
 				}
 			case "port+tls":
+				// port+tls <port> <cert> <key>
 				addr := ":" + safeArg(d.Args, 0)
 				c := safeArg(d.Args, 1)
 				k := safeArg(d.Args, 2)
-				if c == "" {
-					c = cert
-				}
-				if k == "" {
-					k = key
-				}
 				if !seen[addr] {
 					seen[addr] = true
 					out = append(out, portConfig{addr: addr, certFile: c, keyFile: k, isTLS: true})
@@ -227,6 +215,8 @@
 		ndex     []string
 		fcgiAddr string
 		fcgiPat  string
+		cgiExec  string
+		cgiPat   string
 		rprxAddr string
 		rdirCode int
 		rdirURL  string
@@ -245,6 +235,13 @@
 			if fcgiPat == "" {
 				fcgiPat = "*"
 			}
+		case "cgi":
+			// cgi <executable> [<glob-pattern>]
+			cgiExec = safeArg(d.Args, 0)
+			cgiPat = safeArg(d.Args, 1)
+			if cgiPat == "" {
+				cgiPat = "*"
+			}
 		case "rprx":
 			rprxAddr = safeArg(d.Args, 0)
 		case "rdir":
@@ -274,6 +271,11 @@
 		}
 		return
 	}
+	if cgiExec != "" && matchGlob(cgiPat, r.URL.Path) {
+		verbosePrintf("d2o: cgi -> %s (%s)", cgiExec, r.URL.Path)
+		serveCGI(w, r, cgiExec, rootDir)
+		return
+	}
 	if rootDir != "" {
 		fsPath := path.Clean(r.URL.Path)
 		displayPath := fsPath
@@ -289,7 +291,7 @@
 		}
 
 		verbosePrintf("d2o: static -> %s (%s)", rootDir, r.URL.Path)
-		serveStatic(w, r, rootDir, rootShow, ndex, fcgiAddr, fcgiPat, fsPath, displayPath)
+		serveStatic(w, r, rootDir, rootShow, ndex, fcgiAddr, fcgiPat, cgiExec, cgiPat, fsPath, displayPath)
 		return
 	}
 
@@ -301,7 +303,7 @@
 // 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, show bool, ndex []string, fcgiAddr, fcgiPat string, fsPath, displayPath string) {
+func serveStatic(w http.ResponseWriter, r *http.Request, rootDir string, show bool, ndex []string, fcgiAddr, fcgiPat, cgiExec, cgiPat string, fsPath, displayPath string) {
 	fpath := filepath.Join(rootDir, filepath.FromSlash(path.Clean(fsPath)))
 
 	info, err := os.Stat(fpath)
@@ -327,6 +329,12 @@
 					}
 					return
 				}
+				if cgiExec != "" && matchGlob(cgiPat, idx) {
+					r2 := r.Clone(r.Context())
+					r2.URL.Path = path.Join(r.URL.Path, idx)
+					serveCGI(w, r2, cgiExec, rootDir)
+					return
+				}
 				http.ServeFile(w, r, idxPath)
 				return
 			}
@@ -394,6 +402,23 @@
 	proxy.ServeHTTP(w, r)
 }
 
+// --- CGI --------------------------------------------------------------------
+
+// serveCGI runs a CGI executable using net/http/cgi.
+// cgiExec is the path to the executable (e.g. /usr/lib/cgit/cgit.cgi).
+// docRoot is set as DOCUMENT_ROOT; may be empty.
+func serveCGI(w http.ResponseWriter, r *http.Request, cgiExec, docRoot string) {
+	h := &cgi.Handler{
+		Path: cgiExec,
+		Dir:  docRoot,
+		Env: []string{
+			"DOCUMENT_ROOT=" + docRoot,
+			"SERVER_SOFTWARE=d2o/1.1",
+		},
+	}
+	h.ServeHTTP(w, r)
+}
+
 // --- FastCGI ----------------------------------------------------------------
 
 func serveFCGI(w http.ResponseWriter, r *http.Request, addr, docRoot string) error {
--- a/man/d2obase.5.md	Tue Mar 17 22:27:30 2026 +0500
+++ b/man/d2obase.5.md	Thu Mar 19 20:00:49 2026 +0500
@@ -80,11 +80,8 @@
 **port** *number*
 : Listen for plain HTTP on the given port.
 
-**port+tls** *number* [*cert* *key*]
-: Listen for HTTPS on the given port. Certificate and key paths are optional if a **tls** directive is present in the same block.
-
-**tls** *cert* *key*
-: Set the default certificate and key for **port+tls** directives in this block that do not specify their own paths.
+**port+tls** *number* *cert* *key*
+: Listen for HTTPS on the given port. Certificate and key paths are required.
 
 ## Serving
 
@@ -92,11 +89,14 @@
 : Serve static files from *path*. Without **show**, directory listing is forbidden (403). With **show**, an HTML directory listing is returned.
 
 **ndex** *file...*
-: List of index filenames to try when a directory is requested, checked left to right. If a matching file passes the **fcgi** pattern, it is handled by FastCGI instead of served directly.
+: List of index filenames to try when a directory is requested, checked left to right. If a matching file passes the **fcgi** or **cgi** pattern, it is handled accordingly instead of served directly.
 
 **fcgi** *address* [*pattern*]
 : Forward matching requests to a FastCGI server. *address* is either `unix:///path/to/socket` or `host:port`. *pattern* is a glob matched against the request path (default `*`). When used together with **root**, only requests matching the pattern are forwarded; everything else is served as static.
 
+**cgi** *executable* [*pattern*]
+: Run *executable* as a CGI program for matching requests. *pattern* is a glob matched against the request path (default `*`). The executable is invoked per-request with standard CGI environment variables (`REQUEST_METHOD`, `QUERY_STRING`, `PATH_INFO`, etc.). When used together with **root**, only requests matching the pattern are handled by CGI; everything else is served as static. Useful for simple CGI programs such as **cgit**(1).
+
 **rprx** *address*
 : Reverse-proxy all requests to *address*. The `http://` scheme is assumed if not specified.
 
@@ -150,8 +150,17 @@
     |> port+tls 443 $TLS.{crt,key}
     |> root $WWW show
 
+A minimal setup with cgit:
+
+    git.example.org
+    |> port 80
+    |> port+tls 443 /etc/acme/git.example.org.{crt,key}
+    |> root /usr/share/cgit
+    |> ndex cgit.cgi
+    |> cgi /usr/lib/cgit/cgit.cgi *.cgi
+
 # CAVEATS
-1. ICF does not support strings with spaces - there are **no quotes or escapes**. Every character except space is treated as part of a token, including `!`, `*`, `/`, and so on. Glob patterns passed to **fcgi** are forwarded as-is to the server.
+1. ICF does not support strings with spaces - there are **no quotes or escapes**. Every character except space is treated as part of a token, including `!`, `*`, `/`, and so on. Glob patterns passed to **fcgi** and **cgi** are forwarded as-is to the server.
 
 2. Variables are evaluated top-down at the point of declaration. Forward references do not work: if `B=$A` appears before `A=value`, `B` will contain the literal `$A`.