diff --git a/CONFIGURATION.md b/CONFIGURATION.md index 8b506ad..21e7af2 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -87,12 +87,6 @@ One resource must be defined for each path. Serve static files from specified root directory. -##### Proxy - -Forward requests to Gemini server at specified URL. - -Use the pseudo-scheme `gemini-insecure://` to disable certificate verification. - ##### Command Serve output of system command. @@ -101,6 +95,16 @@ When input is requested from the user, it is available as a pseudo-variable `$USERINPUT` which does not require surrounding quotes. It may be used as an argument to the command, otherwise user input is passed via standard input. +##### Proxy + +Forward requests to Gemini server at specified URL. + +Use the pseudo-scheme `gemini-insecure://` to disable certificate verification. + +##### Redirect + +Redirect requests to specified path or URL. + #### Attributes Any number of attributes may be defined for a path. @@ -220,6 +224,12 @@ hosts: path: ^/cmd-example$ command: uname -a cache: 0 # Do not cache + - + path: /redir-path-example + redirect: /other-resource + - + path: /redir-url-example + redirect: gemini://gemini.circumlunar.space/ - path: / root: /home/geminirocks/data/home diff --git a/README.md b/README.md index cf7c3c3..dcfa2f1 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ This page is also available at [gemini://twins.rocketnine.space](gemini://twins. - TCP - [FastCGI](https://en.wikipedia.org/wiki/FastCGI) - Serve system command output +- Redirect to path or URL - Reload configuration on `SIGHUP` ## Proposals diff --git a/config.go b/config.go index 9c5b43f..bb2a768 100644 --- a/config.go +++ b/config.go @@ -21,9 +21,10 @@ type pathConfig struct { Path string // Resource to serve - Root string - Proxy string - Command string + Root string + Command string + Proxy string + Redirect string // Request input Input string @@ -164,12 +165,12 @@ func readconfig(configPath string) error { if defaultPath.Root != "" && serve.Root == "" { serve.Root = defaultPath.Root } - if defaultPath.Proxy != "" && serve.Proxy == "" { - serve.Proxy = defaultPath.Proxy - } if defaultPath.Command != "" && serve.Command == "" { serve.Command = defaultPath.Command } + if defaultPath.Proxy != "" && serve.Proxy == "" { + serve.Proxy = defaultPath.Proxy + } if defaultPath.SymLinks { serve.SymLinks = defaultPath.SymLinks } @@ -206,17 +207,31 @@ func readconfig(configPath string) error { for _, serve := range host.Paths { if serve.Path == "" { - log.Fatal("path must be specified in serve entry") - } else if (serve.Root != "" && (serve.Proxy != "" || serve.Command != "")) || - (serve.Proxy != "" && (serve.Root != "" || serve.Command != "")) || - (serve.Command != "" && (serve.Root != "" || serve.Proxy != "")) { - log.Fatal("only one root, proxy or command resource may specified for a path") + log.Fatal("a path must be specified in each serve entry") } - if serve.Path[0] == '^' { serve.r = regexp.MustCompile(serve.Path) } + var resources int + if serve.Root != "" { + resources++ + } + if serve.Command != "" { + resources++ + } + if serve.Proxy != "" { + resources++ + } + if serve.Redirect != "" { + resources++ + } + if resources == 0 { + log.Fatalf("a resource must specified for path %s%s", hostname, serve.Path) + } else if resources > 1 { + log.Fatalf("only one resource (root, command, proxy or redirect) may specified for path %s%s", hostname, serve.Path) + } + serve.cache = cacheUnset if serve.Cache != "" { serve.cache, err = strconv.ParseInt(serve.Cache, 10, 64) @@ -225,7 +240,12 @@ func readconfig(configPath string) error { } } - if serve.FastCGI != "" { + if serve.Command != "" { + serve.cmd, err = shellquote.Split(serve.Command) + if err != nil { + log.Fatalf("failed to parse command %s: %s", serve.cmd, err) + } + } else if serve.FastCGI != "" { if serve.Root == "" { log.Fatalf("root must be specified to use fastcgi resource %s of path %s%s", serve.FastCGI, hostname, serve.Path) } @@ -238,11 +258,6 @@ func readconfig(configPath string) error { config.fcgiPools[serve.FastCGI] = gofast.SimpleConnFactory(f.Scheme, f.Host+f.Path) } - } else if serve.Command != "" { - serve.cmd, err = shellquote.Split(serve.Command) - if err != nil { - log.Fatalf("failed to parse command %s: %s", serve.cmd, err) - } } } } diff --git a/server.go b/server.go index 96e4fe5..4025483 100644 --- a/server.go +++ b/server.go @@ -174,7 +174,16 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) (int, int64) { filePath = path.Join(root, resolvedPath) } - if serve.Proxy != "" { + if serve.cmd != nil { + requireInput := serve.Input != "" || serve.SensitiveInput != "" + if requireInput { + newCommand := replaceWithUserInput(serve.cmd, request) + if newCommand != nil { + return serveCommand(c, serve, request, newCommand) + } + } + return serveCommand(c, serve, request, serve.cmd) + } else if serve.Proxy != "" { return serveProxy(c, request, serve.Proxy), -1 } else if serve.FastCGI != "" { if filePath == "" { @@ -189,15 +198,8 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) (int, int64) { serveFastCGI(c, config.fcgiPools[serve.FastCGI], request, filePath) return statusSuccess, -1 - } else if serve.cmd != nil { - requireInput := serve.Input != "" || serve.SensitiveInput != "" - if requireInput { - newCommand := replaceWithUserInput(serve.cmd, request) - if newCommand != nil { - return serveCommand(c, serve, request, newCommand) - } - } - return serveCommand(c, serve, request, serve.cmd) + } else if serve.Redirect != "" { + return writeHeader(c, statusRedirectTemporary, serve.Redirect), -1 } if filePath == "" {