From e99d5309e7a06aad3dcbefbfbb7f931aa39e155b Mon Sep 17 00:00:00 2001 From: William Perron Date: Wed, 8 Nov 2023 13:32:39 -0500 Subject: [PATCH] add httpcat utility --- cmd/httpcat/README.md | 5 ++++ cmd/httpcat/main.go | 59 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 cmd/httpcat/README.md create mode 100644 cmd/httpcat/main.go diff --git a/cmd/httpcat/README.md b/cmd/httpcat/README.md new file mode 100644 index 0000000..99c62f9 --- /dev/null +++ b/cmd/httpcat/README.md @@ -0,0 +1,5 @@ +# HTTP Cat + +An HTTP server that returns the HTTP request as text in the response body. +Useful for debugging proxy configurations. + diff --git a/cmd/httpcat/main.go b/cmd/httpcat/main.go new file mode 100644 index 0000000..e44cf82 --- /dev/null +++ b/cmd/httpcat/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "flag" + "fmt" + "io" + "log" + "net/http" + "strings" +) + +var ( + ports = flag.String("ports", "", "comma-separated list of ports to listen on") +) + +type CatHandler struct{} + +var _ http.Handler = &CatHandler{} + +func (cs *CatHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + log.Printf("[%s] %s %s", req.Host, req.Method, req.URL.Path) + + w.Write([]byte(fmt.Sprintf("HTTP1.1 %s %s\n", req.Method, req.URL.Path))) + for k, v := range req.Header { + w.Write([]byte(fmt.Sprintf("%s: %s\n", k, strings.Join(v, ", ")))) + } + w.Write([]byte{'\n'}) + + if req.Body != nil { + io.Copy(w, req.Body) + } +} + +func main() { + flag.Parse() + + errch := make(chan error) + done := make(chan struct{}) + for _, p := range strings.Split(*ports, ",") { + port := strings.TrimSpace(p) + if port[0] != ':' { + port = ":" + port + } + go func() { + log.Printf("listening on %s\n", port) + if err := http.ListenAndServe(port, new(CatHandler)); err != nil { + log.Println(err) + errch <- err + } + }() + } + + go func() { + <-errch + done <- struct{}{} + }() + + <-done +}