aboutsummaryrefslogtreecommitdiff
path: root/handler.go
diff options
context:
space:
mode:
Diffstat (limited to 'handler.go')
-rw-r--r--handler.go54
1 files changed, 39 insertions, 15 deletions
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)
}