From 2c83dac5d47195b7589a9e879598d00c00f1b302 Mon Sep 17 00:00:00 2001
From: zeripath <art27@cantab.net>
Date: Tue, 10 Dec 2019 12:23:26 +0000
Subject: [PATCH] FCGI: Allow FCGI over unix sockets (#9298)

* FCGI: Allow FCGI over unix sockets

* fixup! FCGI: Allow FCGI over unix sockets
---
 cmd/web.go                                            |  8 ++++++--
 cmd/web_graceful.go                                   |  4 ++--
 docs/content/doc/advanced/config-cheat-sheet.en-us.md |  4 ++--
 modules/setting/setting.go                            | 11 +++++++++++
 routers/routes/routes.go                              |  2 +-
 5 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/cmd/web.go b/cmd/web.go
index e0e47a181f..cc00a32198 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -122,6 +122,7 @@ func runWeb(ctx *cli.Context) error {
 		switch setting.Protocol {
 		case setting.UnixSocket:
 		case setting.FCGI:
+		case setting.FCGIUnix:
 		default:
 			// Save LOCAL_ROOT_URL if port changed
 			cfg := ini.Empty()
@@ -149,7 +150,7 @@ func runWeb(ctx *cli.Context) error {
 	}
 
 	listenAddr := setting.HTTPAddr
-	if setting.Protocol != setting.UnixSocket {
+	if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix {
 		listenAddr += ":" + setting.HTTPPort
 	}
 	log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
@@ -183,10 +184,13 @@ func runWeb(ctx *cli.Context) error {
 		err = runHTTPS("tcp", listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
 	case setting.FCGI:
 		NoHTTPRedirector()
-		err = runFCGI(listenAddr, context2.ClearHandler(m))
+		err = runFCGI("tcp", listenAddr, context2.ClearHandler(m))
 	case setting.UnixSocket:
 		NoHTTPRedirector()
 		err = runHTTP("unix", listenAddr, context2.ClearHandler(m))
+	case setting.FCGIUnix:
+		NoHTTPRedirector()
+		err = runFCGI("unix", listenAddr, context2.ClearHandler(m))
 	default:
 		log.Fatal("Invalid protocol: %s", setting.Protocol)
 	}
diff --git a/cmd/web_graceful.go b/cmd/web_graceful.go
index e303f71510..5f8b85b390 100644
--- a/cmd/web_graceful.go
+++ b/cmd/web_graceful.go
@@ -37,9 +37,9 @@ func NoMainListener() {
 	graceful.Manager.InformCleanup()
 }
 
-func runFCGI(listenAddr string, m http.Handler) error {
+func runFCGI(network, listenAddr string, m http.Handler) error {
 	// This needs to handle stdin as fcgi point
-	fcgiServer := graceful.NewServer("tcp", listenAddr)
+	fcgiServer := graceful.NewServer(network, listenAddr)
 
 	err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
 		return fcgi.Serve(listener, m)
diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
index 0d7a641b19..ee63b11b37 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -140,7 +140,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 
 ## Server (`server`)
 
-- `PROTOCOL`: **http**: \[http, https, fcgi, unix\]
+- `PROTOCOL`: **http**: \[http, https, fcgi, unix, fcgi+unix\]
 - `DOMAIN`: **localhost**: Domain name of this server.
 - `ROOT_URL`: **%(PROTOCOL)s://%(DOMAIN)s:%(HTTP\_PORT)s/**:
    Overwrite the automatically generated public URL.
@@ -155,7 +155,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 - `HTTP_ADDR`: **0.0.0.0**: HTTP listen address.
    - If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket
      defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings.
-   - If `PROTOCOL` is set to `unix`, this should be the name of the Unix socket file to use.
+   - If `PROTOCOL` is set to `unix` or `fcgi+unix`, this should be the name of the Unix socket file to use.
 - `HTTP_PORT`: **3000**: HTTP listen port.
    - If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket
      defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings.
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 832403e20f..4354d8cdb8 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -42,6 +42,7 @@ const (
 	HTTP       Scheme = "http"
 	HTTPS      Scheme = "https"
 	FCGI       Scheme = "fcgi"
+	FCGIUnix   Scheme = "fcgi+unix"
 	UnixSocket Scheme = "unix"
 )
 
@@ -553,6 +554,14 @@ func NewContext() {
 		KeyFile = sec.Key("KEY_FILE").String()
 	case "fcgi":
 		Protocol = FCGI
+	case "fcgi+unix":
+		Protocol = FCGIUnix
+		UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
+		UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
+		if err != nil || UnixSocketPermissionParsed > 0777 {
+			log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
+		}
+		UnixSocketPermission = uint32(UnixSocketPermissionParsed)
 	case "unix":
 		Protocol = UnixSocket
 		UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
@@ -607,6 +616,8 @@ func NewContext() {
 		defaultLocalURL = "http://unix/"
 	case FCGI:
 		defaultLocalURL = AppURL
+	case FCGIUnix:
+		defaultLocalURL = AppURL
 	default:
 		defaultLocalURL = string(Protocol) + "://"
 		if HTTPAddr == "0.0.0.0" {
diff --git a/routers/routes/routes.go b/routers/routes/routes.go
index bac7d37916..cdbbfaee04 100644
--- a/routers/routes/routes.go
+++ b/routers/routes/routes.go
@@ -133,7 +133,7 @@ func NewMacaron() *macaron.Macaron {
 	if setting.EnableGzip {
 		m.Use(gzip.Middleware())
 	}
-	if setting.Protocol == setting.FCGI {
+	if setting.Protocol == setting.FCGI || setting.Protocol == setting.FCGIUnix {
 		m.SetURLPrefix(setting.AppSubURL)
 	}
 	m.Use(public.Custom(