Support redirecting to path or URL

This commit is contained in:
Trevor Slocum 2020-11-19 09:24:12 -08:00
parent 58a4ecd958
commit c64a59c44e
4 changed files with 62 additions and 34 deletions

View file

@ -87,12 +87,6 @@ One resource must be defined for each path.
Serve static files from specified root directory. 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 ##### Command
Serve output of system 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 `$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. 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 #### Attributes
Any number of attributes may be defined for a path. Any number of attributes may be defined for a path.
@ -220,6 +224,12 @@ hosts:
path: ^/cmd-example$ path: ^/cmd-example$
command: uname -a command: uname -a
cache: 0 # Do not cache cache: 0 # Do not cache
-
path: /redir-path-example
redirect: /other-resource
-
path: /redir-url-example
redirect: gemini://gemini.circumlunar.space/
- -
path: / path: /
root: /home/geminirocks/data/home root: /home/geminirocks/data/home

View file

@ -19,6 +19,7 @@ This page is also available at [gemini://twins.rocketnine.space](gemini://twins.
- TCP - TCP
- [FastCGI](https://en.wikipedia.org/wiki/FastCGI) - [FastCGI](https://en.wikipedia.org/wiki/FastCGI)
- Serve system command output - Serve system command output
- Redirect to path or URL
- Reload configuration on `SIGHUP` - Reload configuration on `SIGHUP`
## Proposals ## Proposals

View file

@ -22,8 +22,9 @@ type pathConfig struct {
// Resource to serve // Resource to serve
Root string Root string
Proxy string
Command string Command string
Proxy string
Redirect string
// Request input // Request input
Input string Input string
@ -164,12 +165,12 @@ func readconfig(configPath string) error {
if defaultPath.Root != "" && serve.Root == "" { if defaultPath.Root != "" && serve.Root == "" {
serve.Root = defaultPath.Root serve.Root = defaultPath.Root
} }
if defaultPath.Proxy != "" && serve.Proxy == "" {
serve.Proxy = defaultPath.Proxy
}
if defaultPath.Command != "" && serve.Command == "" { if defaultPath.Command != "" && serve.Command == "" {
serve.Command = defaultPath.Command serve.Command = defaultPath.Command
} }
if defaultPath.Proxy != "" && serve.Proxy == "" {
serve.Proxy = defaultPath.Proxy
}
if defaultPath.SymLinks { if defaultPath.SymLinks {
serve.SymLinks = defaultPath.SymLinks serve.SymLinks = defaultPath.SymLinks
} }
@ -206,17 +207,31 @@ func readconfig(configPath string) error {
for _, serve := range host.Paths { for _, serve := range host.Paths {
if serve.Path == "" { if serve.Path == "" {
log.Fatal("path must be specified in serve entry") log.Fatal("a path must be specified in each 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")
} }
if serve.Path[0] == '^' { if serve.Path[0] == '^' {
serve.r = regexp.MustCompile(serve.Path) 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 serve.cache = cacheUnset
if serve.Cache != "" { if serve.Cache != "" {
serve.cache, err = strconv.ParseInt(serve.Cache, 10, 64) 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 == "" { if serve.Root == "" {
log.Fatalf("root must be specified to use fastcgi resource %s of path %s%s", serve.FastCGI, hostname, serve.Path) 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) 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)
}
} }
} }
} }

View file

@ -174,7 +174,16 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) (int, int64) {
filePath = path.Join(root, resolvedPath) 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 return serveProxy(c, request, serve.Proxy), -1
} else if serve.FastCGI != "" { } else if serve.FastCGI != "" {
if filePath == "" { 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) serveFastCGI(c, config.fcgiPools[serve.FastCGI], request, filePath)
return statusSuccess, -1 return statusSuccess, -1
} else if serve.cmd != nil { } else if serve.Redirect != "" {
requireInput := serve.Input != "" || serve.SensitiveInput != "" return writeHeader(c, statusRedirectTemporary, serve.Redirect), -1
if requireInput {
newCommand := replaceWithUserInput(serve.cmd, request)
if newCommand != nil {
return serveCommand(c, serve, request, newCommand)
}
}
return serveCommand(c, serve, request, serve.cmd)
} }
if filePath == "" { if filePath == "" {