aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fileutils.go30
-rw-r--r--handler.go54
-rw-r--r--input.go7
3 files changed, 76 insertions, 15 deletions
diff --git a/fileutils.go b/fileutils.go
new file mode 100644
index 0000000..03e71fb
--- /dev/null
+++ b/fileutils.go
@@ -0,0 +1,30 @@
+package main
+
+import (
+ "mime"
+ "os"
+ "path/filepath"
+)
+
+func absPathMime(path string) string {
+ ext := filepath.Ext(path)
+ return mime.TypeByExtension(ext)
+}
+
+func isFile(path string) bool {
+ fileInfo, err := os.Stat(path)
+ if err != nil {
+ return false
+ }
+
+ return fileInfo.Mode().IsRegular()
+}
+
+func isExecutable(path string) bool {
+ stat, err := os.Stat(path)
+ if err != nil {
+ return false
+ }
+
+ return stat.Mode().Perm()&0111 == 0111
+}
diff --git a/handler.go b/handler.go
index d3b025d..0c81c2e 100644
--- a/handler.go
+++ b/handler.go
@@ -1,31 +1,21 @@
package main
import (
+ "bytes"
+ "context"
"fmt"
+ "io/ioutil"
"log"
- "mime"
"net/url"
"os"
+ "os/exec"
"path/filepath"
"strings"
+ "time"
gemini "git.sr.ht/~yotam/go-gemini"
)
-func absPathMime(path string) string {
- ext := filepath.Ext(path)
- return mime.TypeByExtension(ext)
-}
-
-func isFile(path string) bool {
- fileInfo, err := os.Stat(path)
- if err != nil {
- return false
- }
-
- return fileInfo.Mode().IsRegular()
-}
-
// Handler is the main handler of the server
type Handler struct {
cfg Config
@@ -83,6 +73,36 @@ func (h Handler) getFilePath(rawURL string) (string, error) {
return "", gemini.Error{Err: fmt.Errorf("file not found"), Status: gemini.StatusNotFound}
}
+func (h Handler) serveExecutable(r gemini.Request, path string) gemini.Response {
+ ctx, cancel := context.WithTimeout(context.Background(), time.Duration(h.cfg.ExecTimeout)*time.Second)
+ defer cancel()
+
+ cmd := exec.CommandContext(ctx, path)
+
+ stdin, err := cmd.StdinPipe()
+ if err != nil {
+ return gemini.ErrorResponse(err)
+ }
+ defer stdin.Close()
+
+ _, err = fmt.Fprintf(stdin, "%s\r\n", r.URL)
+ if err != nil {
+ return gemini.ErrorResponse(err)
+ }
+
+ // The gemini library api make it hard to stream stdout instead of reading it into memory
+ out, err := cmd.Output()
+ if err != nil {
+ return gemini.ErrorResponse(err)
+ }
+
+ if ctx.Err() == context.DeadlineExceeded {
+ return gemini.ErrorResponse(ctx.Err())
+ }
+
+ return gemini.Response{Status: 20, Meta: "text/gemini", Body: ioutil.NopCloser(bytes.NewReader(out))}
+}
+
func (h Handler) serveFile(path string) gemini.Response {
log.Println("Serving file from", path)
@@ -107,5 +127,9 @@ func (h Handler) Handle(r gemini.Request) gemini.Response {
return gemini.ErrorResponse(err)
}
+ if isExecutable(path) {
+ return h.serveExecutable(r, path)
+ }
+
return h.serveFile(path)
}
diff --git a/input.go b/input.go
index 26e65f5..6b3d0e8 100644
--- a/input.go
+++ b/input.go
@@ -39,6 +39,9 @@ type Config struct {
// default to ["index.gmi"]
IndexFiles []string `toml:"index_files"`
+ // default to 5
+ ExecTimeout int64 `toml:"exec_timeout"`
+
TLSCert string `toml:"tls_certificate"`
TLSKey string `toml:"tls_key"`
}
@@ -64,5 +67,9 @@ func getConfig(path string) (Config, error) {
cfg.IndexFiles = []string{"index.gmi"}
}
+ if cfg.ExecTimeout == 0 {
+ cfg.ExecTimeout = 5
+ }
+
return cfg, nil
}