GitBucket
4.21.2
Toggle navigation
Snippets
Sign in
Files
Branches
1
Releases
Issues
1
Pull requests
Labels
Priorities
Milestones
Wiki
Forks
mark.george
/
httpcat
Browse code
WIP refactoring.
master
1 parent
ac587a7
commit
0706d58e5db27c5d257575e229ad9f12d3514621
Mark George
authored
on 27 Jan 2022
Patch
Showing
7 changed files
src/client.go
src/go.mod
src/go.sum
src/httpcat.go
src/main.go
src/proxy.go
src/server.go
Ignore Space
Show notes
View
src/client.go
0 → 100644
package main import ( "fmt" ) type ClientCommand struct { URL string `short:"u" long:"uri" description:"The URI to send the reqest to." required:"true"` Accept string `short:"a" long:"accept" description:"Add 'Accept' header to request."` Method string `short:"m" long:"method" description:"HTTP method for request" default:"GET"` Body string `short:"b" long:"body" description:"Request body to send (for POST/PUT/PATCH)"` } func (c *ClientCommand) Execute(args []string) error { fmt.Println("Send request to ", c.URL) return nil } // func sendRequest(uri string) { // client := http.Client{} // request, _ := http.NewRequest("GET", uri, nil) // // add Accept header if required by user // if accept != "" { // request.Header.Add("Accept", accept) // } // response, err := client.Do(request) // if err != nil { // fmt.Fprintln(os.Stderr, err) // } else { // // print entire response // if entire { // dump, _ := httputil.DumpResponse(response, true) // fmt.Println(string(dump[:])) // // print only the response body // } else { // body, _ := ioutil.ReadAll(response.Body) // fmt.Println(string(body[:])) // } // } // }
Ignore Space
Show notes
View
src/go.mod
module httpcat go 1.17 require ( github.com/fatih/color v1.13.0 github.com/jessevdk/go-flags v1.5.0 ) require ( github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect )
module proxy go 1.16 require ( github.com/fatih/color v1.13.0 )
Ignore Space
Show notes
View
src/go.sum
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Ignore Space
Show notes
View
src/httpcat.go
/** Author: Mark George <mark.george@otago.ac.nz> Warranty: None. Works for me. If it doesn't work for you then you already have the source code... License: WTFPL v2 <http://www.wtfpl.net/txt/copying/> */ package main // import ( // "flag" // "fmt" // "io/ioutil" // "net/http" // "net/http/httputil" // "os" // "strconv" // ) // var ( // port int // status int // body string // entire bool // verbose bool // server bool // uri string // accept string // separator string // cors bool // ) // func requestHandler(resp http.ResponseWriter, req *http.Request) { // if verbose { // fmt.Printf("Received a %s request to %s\n", req.Method, req.RequestURI) // } // // print entire request // if entire { // dump, _ := httputil.DumpRequest(req, true) // fmt.Println(string(dump[:])) // // print only request body // } else { // body, _ := ioutil.ReadAll(req.Body) // fmt.Println(string(body[:])) // } // // cors // if cors && req.Method == http.MethodOptions { // if verbose { // fmt.Println("CORS preflight") // } // if origin := req.Header.Get("Origin"); origin != "" { // resp.Header().Set("Access-Control-Allow-Origin", origin) // if headers := req.Header.Get("Access-Control-Request-Headers"); headers != "" { // resp.Header().Set("Access-Control-Allow-Headers", headers) // } // if method := req.Header.Get("Access-Control-Request-Method"); method != "" { // resp.Header().Set("Access-Control-Allow-Method", method) // } // resp.WriteHeader(200) // return // } else { // if verbose { // fmt.Println("Preflight did not contain 'Origin' header!") // } // } // } // // send response // if body != "" { // if status == 204 { // status = 200 // } // if we have a body then we don't want to return the default 204 status // resp.WriteHeader(status) // fmt.Fprintf(resp, body) // } else { // resp.WriteHeader(status) // } // if separator != "" { // fmt.Println(separator) // } // } // func startServer() { // http.HandleFunc("/", requestHandler) // if err := http.ListenAndServe(":"+strconv.Itoa(port), nil); err != nil { // fmt.Fprintf(os.Stderr, "Could not start server. Is port %d available?\n", port) // } // } // func parseCommandLine() { // flag.IntVar(&port, "port", 8080, "") // flag.IntVar(&port, "p", 8080, "") // flag.IntVar(&status, "response", 204, "") // flag.IntVar(&status, "r", 204, "") // flag.StringVar(&body, "body", "", "") // flag.StringVar(&body, "b", "", "") // flag.BoolVar(&entire, "entire", false, "") // flag.BoolVar(&entire, "e", false, "") // flag.StringVar(&separator, "separator", "", "") // flag.StringVar(&separator, "s", "", "") // flag.BoolVar(&verbose, "verbose", false, "") // flag.BoolVar(&verbose, "v", false, "") // flag.StringVar(&accept, "accept", "", "") // flag.StringVar(&accept, "a", "", "") // flag.BoolVar(&cors, "cors", false, "") // flag.BoolVar(&cors, "c", false, "") // serverMode := flag.Bool("server", false, "") // clientMode := flag.Bool("client", false, "") // flag.Usage = usage // flag.Parse() // if !(*serverMode || *clientMode) || (*serverMode && *clientMode) { // usage() // os.Exit(1) // } else { // server = *serverMode // } // if *clientMode { // uri = flag.Arg(0) // if uri == "" { // usage() // os.Exit(1) // } // } // } // var usage = func() { // fmt.Fprintf(os.Stderr, "Usage:\n") // fmt.Fprintf(os.Stderr, "\n") // fmt.Fprintf(os.Stderr, " Client mode\n") // fmt.Fprintf(os.Stderr, " httpcat -client [options] http://uri-to-send-request-to.com\n") // fmt.Fprintf(os.Stderr, "\n") // fmt.Fprintf(os.Stderr, " Currently only supports GET requests.\n") // fmt.Fprintf(os.Stderr, "\n") // fmt.Fprintf(os.Stderr, " Server mode\n") // fmt.Fprintf(os.Stderr, " httpcat -server [options]\n") // fmt.Fprintf(os.Stderr, "\n") // fmt.Fprintf(os.Stderr, " Options (either mode)\n") // fmt.Fprintf(os.Stderr, " -entire or -e : Display entire request/response instead of just the body.\n") // fmt.Fprintf(os.Stderr, " -verbose or -v : Be verbose.\n") // fmt.Fprintf(os.Stderr, "\n") // fmt.Fprintf(os.Stderr, " Options (client mode only)\n") // fmt.Fprintf(os.Stderr, " -accept or -a [accept string] : Adds 'Accept' header to request.\n") // fmt.Fprintf(os.Stderr, "\n") // fmt.Fprintf(os.Stderr, " Options (server mode only)\n") // fmt.Fprintf(os.Stderr, " -body or -b [body message] : Body to respond with. Response code will default to 200.\n") // fmt.Fprintf(os.Stderr, " -port or -p [port] : Port to listen on.\n") // fmt.Fprintf(os.Stderr, " -response or -r [response code] : Status code to respond with. Defaults to 204.\n") // fmt.Fprintf(os.Stderr, " -cors or -c : Enable Cross Origin Resource Sharing support.\n") // fmt.Fprintf(os.Stderr, " -separator or -s [separator string] : Use the provided separator to separate messages.\n") // } // func xmain() { // parseCommandLine() // if server { // // server mode // if verbose { // fmt.Printf("Listening on port %d.\n", port) // if entire { // fmt.Println("Displaying entire request details.\n") // } else { // fmt.Println("Displaying bodies only.\n") // } // } // startServer() // } else { // // client mode // if verbose { // fmt.Println("Sending GET request to " + uri) // } // sendRequest(uri) // } // }
/** Author: Mark George <mark.george@otago.ac.nz> Warranty: None. Works for me. If it doesn't work for you then you already have the source code... License: WTFPL v2 <http://www.wtfpl.net/txt/copying/> */ package main import ( "flag" "fmt" "io/ioutil" "net/http" "net/http/httputil" "os" "strconv" ) var ( port int status int body string entire bool verbose bool server bool uri string accept string separator string cors bool ) func requestHandler(resp http.ResponseWriter, req *http.Request) { if verbose { fmt.Printf("Received a %s request to %s\n", req.Method, req.RequestURI) } // print entire request if entire { dump, _ := httputil.DumpRequest(req, true) fmt.Println(string(dump[:])) // print only request body } else { body, _ := ioutil.ReadAll(req.Body) fmt.Println(string(body[:])) } // cors if cors && req.Method == http.MethodOptions { if verbose { fmt.Println("CORS preflight") } if origin := req.Header.Get("Origin"); origin != "" { resp.Header().Set("Access-Control-Allow-Origin", origin) if headers := req.Header.Get("Access-Control-Request-Headers"); headers != "" { resp.Header().Set("Access-Control-Allow-Headers", headers) } if method := req.Header.Get("Access-Control-Request-Method"); method != "" { resp.Header().Set("Access-Control-Allow-Method", method) } resp.WriteHeader(200) return } else { if verbose { fmt.Println("Preflight did not contain 'Origin' header!") } } } // send response if body != "" { if status == 204 { status = 200 } // if we have a body then we don't want to return the default 204 status resp.WriteHeader(status) fmt.Fprintf(resp, body) } else { resp.WriteHeader(status) } if separator != "" { fmt.Println(separator) } } func startServer() { http.HandleFunc("/", requestHandler) if err := http.ListenAndServe(":"+strconv.Itoa(port), nil); err != nil { fmt.Fprintf(os.Stderr, "Could not start server. Is port %d available?\n", port) } } func sendRequest(uri string) { client := http.Client{} request, _ := http.NewRequest("GET", uri, nil) // add Accept header if required by user if accept != "" { request.Header.Add("Accept", accept) } response, err := client.Do(request) if err != nil { fmt.Fprintln(os.Stderr, err) } else { // print entire response if entire { dump, _ := httputil.DumpResponse(response, true) fmt.Println(string(dump[:])) // print only the response body } else { body, _ := ioutil.ReadAll(response.Body) fmt.Println(string(body[:])) } } } func parseCommandLine() { flag.IntVar(&port, "port", 8080, "") flag.IntVar(&port, "p", 8080, "") flag.IntVar(&status, "response", 204, "") flag.IntVar(&status, "r", 204, "") flag.StringVar(&body, "body", "", "") flag.StringVar(&body, "b", "", "") flag.BoolVar(&entire, "entire", false, "") flag.BoolVar(&entire, "e", false, "") flag.StringVar(&separator, "separator", "", "") flag.StringVar(&separator, "s", "", "") flag.BoolVar(&verbose, "verbose", false, "") flag.BoolVar(&verbose, "v", false, "") flag.StringVar(&accept, "accept", "", "") flag.StringVar(&accept, "a", "", "") flag.BoolVar(&cors, "cors", false, "") flag.BoolVar(&cors, "c", false, "") serverMode := flag.Bool("server", false, "") clientMode := flag.Bool("client", false, "") flag.Usage = usage flag.Parse() if !(*serverMode || *clientMode) || (*serverMode && *clientMode) { usage() os.Exit(1) } else { server = *serverMode } if *clientMode { uri = flag.Arg(0) if uri == "" { usage() os.Exit(1) } } } var usage = func() { fmt.Fprintf(os.Stderr, "Usage:\n") fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, " Client mode\n") fmt.Fprintf(os.Stderr, " httpcat -client [options] http://uri-to-send-request-to.com\n") fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, " Currently only supports GET requests.\n") fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, " Server mode\n") fmt.Fprintf(os.Stderr, " httpcat -server [options]\n") fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, " Options (either mode)\n") fmt.Fprintf(os.Stderr, " -entire or -e : Display entire request/response instead of just the body.\n") fmt.Fprintf(os.Stderr, " -verbose or -v : Be verbose.\n") fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, " Options (client mode only)\n") fmt.Fprintf(os.Stderr, " -accept or -a [accept string] : Adds 'Accept' header to request.\n") fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, " Options (server mode only)\n") fmt.Fprintf(os.Stderr, " -body or -b [body message] : Body to respond with. Response code will default to 200.\n") fmt.Fprintf(os.Stderr, " -port or -p [port] : Port to listen on.\n") fmt.Fprintf(os.Stderr, " -response or -r [response code] : Status code to respond with. Defaults to 204.\n") fmt.Fprintf(os.Stderr, " -cors or -c : Enable Cross Origin Resource Sharing support.\n") fmt.Fprintf(os.Stderr, " -separator or -s [separator string] : Use the provided separator to separate messages.\n") } func xmain() { parseCommandLine() if server { // server mode if verbose { fmt.Printf("Listening on port %d.\n", port) if entire { fmt.Println("Displaying entire request details.\n") } else { fmt.Println("Displaying bodies only.\n") } } startServer() } else { // client mode if verbose { fmt.Println("Sending GET request to " + uri) } sendRequest(uri) } }
Ignore Space
Show notes
View
src/main.go
0 → 100644
package main import ( "fmt" "os" "github.com/jessevdk/go-flags" ) const version string = "2.0" type GlobalOptions struct { Verbose bool `short:"v" long:"verbose" description:"Print more details"` HeadersOnly bool `long:"headers" description:"Show headers only (default is everything)"` BodiesOnly bool `long:"bodies" description:"Show bodies only (default is everything)"` NoColour bool `long:"nocolour" description:"Don't use colours in the output"` } type VersionCommand struct { } func (c *VersionCommand) Execute(args []string) error { fmt.Println("httpcat v" + version) return nil } var ( globalOptions = new(GlobalOptions) ) func main() { parser := flags.NewParser(globalOptions, flags.Default) parser.AddCommand("server", "Start a mock HTTP server", "", &ServerCommand{}) parser.AddCommand("client", "Send an HTTP request", "", &ClientCommand{}) parser.AddCommand("proxy", "Start reverse HTTP logging proxy", "", &ProxyCommand{}) parser.AddCommand("version", "Display version", "", &VersionCommand{}) // _, err := parser.ParseArgs(args[:]) _, err := parser.Parse() // is this really how you do error handling in Go? if err != nil { parseErr := err.(*flags.Error) if parseErr.Type.String() == flags.ErrCommandRequired.String() { os.Stderr.WriteString("\n") parser.WriteHelp(os.Stderr) } } }
Ignore Space
Show notes
View
src/proxy.go
/** Author: Mark George <mark.george@otago.ac.nz License: WTFPL v2 <http://www.wtfpl.net/txt/copying/> Original Source: https://gist.github.com/yowu/f7dc34bd4736a65ff28d */ package main import ( // "flag" "fmt" // "io" // "log" "net/http" "net/http/httputil" "net/url" // "strings" "time" "github.com/fatih/color" ) type ProxyCommand struct { port string `short:"p" long:"port" description:"The port that the proxy listens on."` target string `short:"t" long:"target" descrtiption:"The target web server that the proxy forwards requests to."` } func (c *ProxyCommand) Execute(args []string) error { fmt.Println("Start proxy") return nil } func copyHeader(dst, src http.Header) { for k, vv := range src { for _, v := range vv { dst.Add(k, v) } } } func CreateProxy(target string) (*httputil.ReverseProxy, error) { url, err := url.Parse(target) // bad target URL if err != nil { return nil, err } proxy := httputil.NewSingleHostReverseProxy(url) OriginalDirector := proxy.Director proxy.Director = func(req *http.Request) { FixHeaders(url, req) LogRequest(req) // send request to target OriginalDirector(req) } proxy.ModifyResponse = func(rsp *http.Response) error { LogResponse(rsp) return nil } proxy.ErrorHandler = func(http.ResponseWriter, *http.Request, error) { fmt.Println("Could not connect to target server\n") } return proxy, nil } func FixHeaders(url *url.URL, req *http.Request) { // this may be necessary for proxying HTTPS (need to test) req.URL.Host = url.Host req.URL.Scheme = url.Scheme req.Host = url.Host } func RequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { proxy.ServeHTTP(w, r) } } func LogRequest(req *http.Request) error { color.Set(color.FgWhite) fmt.Println(time.Now().Format(time.StampMilli)) dumpReq, _ := httputil.DumpRequest(req, true) color.Set(color.FgBlue) fmt.Println(string(dumpReq[:])) color.Unset() return nil } func LogResponse(rsp *http.Response) { color.Set(color.FgWhite) fmt.Println(time.Now().Format(time.StampMilli)) dumpRsp, _ := httputil.DumpResponse(rsp, true) color.Set(color.FgRed) fmt.Println(string(dumpRsp[:])) color.Unset() } func ymain() { // flag.StringVar(&listen, "listen", "9090", "The port that the proxy listens on.") // flag.StringVar(&listen, "l", "9090", "The port that the proxy listens on.") // flag.StringVar(&target, "target", "8080", "The port that the proxy forwards requests to.") // flag.StringVar(&target, "t", "8080", "The port that the proxy forwards requests to.") // flag.Parse() // handler := &proxy{} // log.Println("Forwarding port", listen, "to port", target) // if err := http.ListenAndServe("localhost:"+listen, handler); err != nil { // log.Fatal("ListenAndServe:", err) // } target := "http://localhost:8080" listenPort := "9090" proxy, err := CreateProxy(target) if err != nil { panic(err) } http.HandleFunc("/", RequestHandler(proxy)) if err := http.ListenAndServe(":"+listenPort, nil); err != nil { panic(err) } }
/** Author: Mark George <mark.george@otago.ac.nz License: WTFPL v2 <http://www.wtfpl.net/txt/copying/> Original Source: https://gist.github.com/yowu/f7dc34bd4736a65ff28d */ package main import ( // "flag" "fmt" // "io" // "log" "net/http" "net/http/httputil" "net/url" // "strings" "time" "github.com/fatih/color" ) var ( listen string target string ) func copyHeader(dst, src http.Header) { for k, vv := range src { for _, v := range vv { dst.Add(k, v) } } } func ServeProxy(target string, rsp http.ResponseWriter, req *http.Request) { url, _ := url.Parse(target) proxy := httputil.NewSingleHostReverseProxy(url) req.URL.Host = url.Host req.URL.Scheme = url.Scheme req.Header.Set("X-Forwarded-Host", req.Header.Get("Host")) proxy.ServeHTTP(rsp, req) // fmt.Println() // log.Println("\n") // // log the original request dumpReq, _ := httputil.DumpRequest(req, true) color.Set(color.FgBlue) fmt.Println(string(dumpReq[:])) fmt.Println() // // log the response // dumpRsp, _ := httputil.DumpResponse(resp, true) // color.Set(color.FgRed) // fmt.Println(string(dumpRsp[:])) // copyHeader(wr.Header(), resp.Header) // wr.WriteHeader(resp.StatusCode) // io.Copy(wr, resp.Body) // color.Unset() // defer resp.Body.Close() } func CreateProxy(target string) (*httputil.ReverseProxy, error) { url, err := url.Parse(target) // bad target URL if (err != nil) { return nil, err } proxy := httputil.NewSingleHostReverseProxy(url) OriginalDirector := proxy.Director proxy.Director = func(req *http.Request) { LogRequest(req) // send request to target OriginalDirector(req) } proxy.ModifyResponse = func (rsp *http.Response) error { LogResponse(rsp) return nil } proxy.ErrorHandler = func (http.ResponseWriter, *http.Request, error) { fmt.Println("Could not connect to target server\n") } return proxy, nil } func RequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { proxy.ServeHTTP(w, r) } } func LogRequest(req *http.Request) error { color.Set(color.FgWhite) fmt.Println(time.Now().Format(time.StampMilli)) dumpReq, _ := httputil.DumpRequest(req, true) color.Set(color.FgBlue) fmt.Println(string(dumpReq[:])) color.Unset() return nil } func LogResponse(rsp *http.Response) { color.Set(color.FgWhite) fmt.Println(time.Now().Format(time.StampMilli)) dumpRsp, _ := httputil.DumpResponse(rsp, true) color.Set(color.FgRed) fmt.Println(string(dumpRsp[:])) color.Unset() } func main() { // flag.StringVar(&listen, "listen", "9090", "The port that the proxy listens on.") // flag.StringVar(&listen, "l", "9090", "The port that the proxy listens on.") // flag.StringVar(&target, "target", "8080", "The port that the proxy forwards requests to.") // flag.StringVar(&target, "t", "8080", "The port that the proxy forwards requests to.") // flag.Parse() // handler := &proxy{} // log.Println("Forwarding port", listen, "to port", target) // if err := http.ListenAndServe("localhost:"+listen, handler); err != nil { // log.Fatal("ListenAndServe:", err) // } target := "http://localhost:8080" listenPort := "9090" proxy, err := CreateProxy(target) if err != nil { panic(err) } http.HandleFunc("/", RequestHandler(proxy)) if err := http.ListenAndServe(":"+listenPort, nil); err != nil { panic(err) } }
Ignore Space
Show notes
View
src/server.go
0 → 100644
package main import ( "fmt" "io/ioutil" "net/http" "net/http/httputil" "os" "strconv" "time" ) type ServerCommand struct { Body string `short:"b" long:"body" description:"Body message to respond with."` Port int `short:"p" long:"port" description:"Port to listen on." default:"8080"` Status int `short:"s" long:"status" description:"Status code to respond with." default:"204"` Cors bool `shot:"c" long:"cors" description:"Enable Cross Origin Resource Sharing (CORS)."` } var ( serverOptions *ServerCommand ) func (c *ServerCommand) Execute(args []string) error { fmt.Println("HTTP server listening on port " + strconv.Itoa(c.Port) + "\n") http.HandleFunc("/", serverRequestHandler) serverOptions = c if err := http.ListenAndServe(":"+strconv.Itoa(c.Port), nil); err != nil { fmt.Fprintf(os.Stderr, "Could not start server. Is port %d available?\n", c.Port) os.Exit(1) } return nil } func serverRequestHandler(rsp http.ResponseWriter, req *http.Request) { if globalOptions.Verbose { fmt.Printf("Received a %s request to %s\n", req.Method, req.RequestURI) } fmt.Println(time.Now().Format(time.StampMilli)) if globalOptions.BodiesOnly { body, _ := ioutil.ReadAll(req.Body) fmt.Println(string(body[:])) } else if globalOptions.HeadersOnly { fmt.Println(req.Method + " " + req.URL.Path + " " + req.Proto) for k := range req.Header { fmt.Println(k + ": " + req.Header[k][0]) } // TODO } else { // dump entire request dump, _ := httputil.DumpRequest(req, true) fmt.Println(string(dump[:])) } // cors if serverOptions.Cors && req.Method == http.MethodOptions { if globalOptions.Verbose { fmt.Println("CORS preflight") } if origin := req.Header.Get("Origin"); origin != "" { rsp.Header().Set("Access-Control-Allow-Origin", origin) if headers := req.Header.Get("Access-Control-Request-Headers"); headers != "" { rsp.Header().Set("Access-Control-Allow-Headers", headers) } if method := req.Header.Get("Access-Control-Request-Method"); method != "" { rsp.Header().Set("Access-Control-Allow-Method", method) } rsp.WriteHeader(200) return } else { if globalOptions.Verbose { fmt.Println("Preflight did not contain 'Origin' header!") } } } // send response if serverOptions.Body != "" { if serverOptions.Status == 204 { // body exists, so send a 200 response instead of 204 serverOptions.Status = 200 } rsp.WriteHeader(serverOptions.Status) fmt.Fprintf(rsp, serverOptions.Body) } else { // no body, so just write status header rsp.WriteHeader(serverOptions.Status) } }
Show line notes below