author | Alberto Bertogli
<albertito@blitiri.com.ar> 2020-05-04 10:14:55 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2020-05-04 10:16:18 UTC |
parent | ceaa4729bbb5e0af8c1a8558ebc6378bf7aec9b9 |
proxy/http.go | +46 | -0 |
trace/trace.go | +13 | -0 |
diff --git a/proxy/http.go b/proxy/http.go index d29419c..890db2a 100644 --- a/proxy/http.go +++ b/proxy/http.go @@ -6,6 +6,7 @@ import ( "errors" golog "log" "net/http" + "net/http/cgi" "net/http/httputil" "net/url" "strings" @@ -49,6 +50,8 @@ func httpServer(conf config.HTTP) *http.Server { mux.Handle(from, makeStatic(from, *toURL)) case "redirect": mux.Handle(from, makeRedirect(from, *toURL)) + case "cgi": + mux.Handle(from, makeCGI(from, *toURL)) default: log.Fatalf("route %q -> %q: invalid destination scheme %q", from, to, toURL.Scheme) @@ -198,6 +201,49 @@ func makeStatic(from string, to url.URL) http.Handler { ) } +func makeCGI(from string, to url.URL) http.Handler { + from = stripDomain(from) + path := to.Path + if path == "" { + // This happens for relative paths, which are fine in this context. + path = to.Opaque + } + args := queryToArgs(to.RawQuery) + + return WithLogging("http:cgi", + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + tr, _ := trace.FromContext(r.Context()) + tr.Debugf("exec %q %q", path, args) + h := cgi.Handler{ + Path: path, + Args: args, + Root: from, + Logger: golog.New(tr, "", golog.Lshortfile), + Stderr: tr, + } + h.ServeHTTP(w, r) + }), + ) +} + +func queryToArgs(query string) []string { + args := []string{} + for query != "" { + comp := query + if i := strings.IndexAny(comp, "&;"); i >= 0 { + comp, query = comp[:i], comp[i+1:] + } else { + query = "" + } + + comp, _ = url.QueryUnescape(comp) + args = append(args, comp) + + } + + return args +} + func makeRedirect(from string, to url.URL) http.Handler { from = stripDomain(from) diff --git a/trace/trace.go b/trace/trace.go index 709dd64..3a59353 100644 --- a/trace/trace.go +++ b/trace/trace.go @@ -88,6 +88,19 @@ func (t *Trace) Finish() { t.t.Finish() } +// Write so Trace implements io.Writer, which means it can be used as output +// for log.Logger. +func (t *Trace) Write(p []byte) (n int, err error) { + lines := strings.Split(string(p), "\n") + for _, line := range lines { + if strings.TrimSpace(line) == "" { + continue + } + t.Printf("%s", line) + } + return len(p), nil +} + // EventLog is used for tracing long-lived objects. type EventLog struct { family string