comment code

This commit is contained in:
Fabricio 2018-12-01 20:59:33 -02:00
parent e48bc52d12
commit b336d33dd3
3 changed files with 55 additions and 21 deletions

View file

@ -9,6 +9,7 @@ import (
var captureID int
var captures CaptureList
// CaptureList stores all captures
type CaptureList struct {
items []Capture
mux sync.Mutex
@ -17,12 +18,14 @@ type CaptureList struct {
Updated chan struct{}
}
// Capture saves our traffic data
type Capture struct {
ID int
Req *http.Request
Res *http.Response
}
// CaptureMetadata is the data for each list item in the dashboard
type CaptureMetadata struct {
ID int `json:"id"`
Path string `json:"path"`
@ -30,12 +33,14 @@ type CaptureMetadata struct {
Status int `json:"status"`
}
// CaptureDump saves all the dumps shown in the dashboard
type CaptureDump struct {
Request string `json:"request"`
Response string `json:"response"`
Curl string `json:"curl"`
}
// Metadata returns the metadada of a capture
func (c *Capture) Metadata() CaptureMetadata {
return CaptureMetadata{
ID: c.ID,
@ -45,6 +50,7 @@ func (c *Capture) Metadata() CaptureMetadata {
}
}
// NewCaptureList creates a new list of captures
func NewCaptureList(maxItems int) *CaptureList {
return &CaptureList{
maxItems: maxItems,
@ -52,6 +58,7 @@ func NewCaptureList(maxItems int) *CaptureList {
}
}
// Insert adds a new capture
func (c *CaptureList) Insert(capture Capture) {
c.mux.Lock()
defer c.mux.Unlock()
@ -63,6 +70,7 @@ func (c *CaptureList) Insert(capture Capture) {
c.signalsItemsChange()
}
// Find finds a capture by its id
func (c *CaptureList) Find(captureID string) *Capture {
c.mux.Lock()
defer c.mux.Unlock()
@ -75,6 +83,7 @@ func (c *CaptureList) Find(captureID string) *Capture {
return nil
}
// RemoveAll removes all the captures
func (c *CaptureList) RemoveAll() {
c.mux.Lock()
defer c.mux.Unlock()
@ -82,10 +91,12 @@ func (c *CaptureList) RemoveAll() {
c.signalsItemsChange()
}
// Items returns all the captures
func (c *CaptureList) Items() []Capture {
return c.items
}
// ItemsAsMetadata returns all the captures as metadata
func (c *CaptureList) ItemsAsMetadata() []CaptureMetadata {
c.mux.Lock()
defer c.mux.Unlock()

View file

@ -5,6 +5,7 @@ import (
"fmt"
)
// Config has all the configuration parsed from the command line
type Config struct {
TargetURL string `json:"targetURL"`
ProxyPort string `json:"proxyPort"`
@ -17,6 +18,7 @@ type Config struct {
DashboardItemInfoPath string `json:"dashboardItemInfoPath"`
}
// ReadConfig reads the arguments from the command line
func ReadConfig() Config {
targetURL := flag.String("url", "https://jsonplaceholder.typicode.com", "Required. Set the base url you want to capture")
proxyPort := flag.String("port", "9000", "Set the proxy port")

63
main.go
View file

@ -33,7 +33,7 @@ func startCapture(config Config) {
handler := NewPlugin(NewRecorder(list, NewProxyHandler(config.TargetURL)))
http.HandleFunc("/", handler)
http.HandleFunc(config.DashboardPath, NewDashboardHtmlHandler(config))
http.HandleFunc(config.DashboardPath, NewDashboardHTMLHandler(config))
http.HandleFunc(config.DashboardConnPath, NewDashboardConnHandler(list))
http.HandleFunc(config.DashboardClearPath, NewDashboardClearHandler(list))
http.HandleFunc(config.DashboardRetryPath, NewDashboardRetryHandler(list, handler))
@ -47,6 +47,8 @@ func startCapture(config Config) {
fmt.Println(http.ListenAndServe(":"+config.ProxyPort, nil))
}
// NewDashboardConnHandler opens an event stream connection with the dashboard
// so that it is notified everytime a new capture arrives
func NewDashboardConnHandler(list *CaptureList) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
if _, ok := rw.(http.Flusher); !ok {
@ -74,6 +76,7 @@ func NewDashboardConnHandler(list *CaptureList) http.HandlerFunc {
}
}
// NewDashboardClearHandler clears all the captures
func NewDashboardClearHandler(list *CaptureList) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
list.RemoveAll()
@ -81,7 +84,8 @@ func NewDashboardClearHandler(list *CaptureList) http.HandlerFunc {
}
}
func NewDashboardHtmlHandler(config Config) http.HandlerFunc {
// NewDashboardHTMLHandler returns the dashboard html page
func NewDashboardHTMLHandler(config Config) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
rw.Header().Add("Content-Type", "text/html")
t, err := template.New("dashboard template").Delims("<<", ">>").Parse(dashboardHTML)
@ -95,6 +99,7 @@ func NewDashboardHtmlHandler(config Config) http.HandlerFunc {
}
}
// NewDashboardRetryHandler retries a request
func NewDashboardRetryHandler(list *CaptureList, next http.HandlerFunc) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
id := req.URL.Path[strings.LastIndex(req.URL.Path, "/")+1:]
@ -112,6 +117,7 @@ func NewDashboardRetryHandler(list *CaptureList, next http.HandlerFunc) http.Han
}
}
// NewDashboardItemInfoHandler returns the full capture info
func NewDashboardItemInfoHandler(list *CaptureList) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
id := req.URL.Path[strings.LastIndex(req.URL.Path, "/")+1:]
@ -125,6 +131,7 @@ func NewDashboardItemInfoHandler(list *CaptureList) http.HandlerFunc {
}
}
// NewPlugin setups plugin handler for requests and resposes
func NewPlugin(next http.HandlerFunc) http.HandlerFunc {
p, err := plugin.Open("plugin.so")
if err != nil {
@ -146,6 +153,7 @@ func NewPlugin(next http.HandlerFunc) http.HandlerFunc {
return pluginFn(next)
}
// NewRecorder saves all the traffic data
func NewRecorder(list *CaptureList, next http.HandlerFunc) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
@ -171,6 +179,7 @@ func NewRecorder(list *CaptureList, next http.HandlerFunc) http.HandlerFunc {
}
}
// NewProxyHandler is the reverse proxy handler
func NewProxyHandler(URL string) http.HandlerFunc {
url, _ := url.Parse(URL)
proxy := httputil.NewSingleHostReverseProxy(url)
@ -205,37 +214,49 @@ func dump(c *Capture) CaptureDump {
func dumpRequest(req *http.Request) ([]byte, error) {
if req.Header.Get("Content-Encoding") == "gzip" {
var reqBody []byte
req.Body, reqBody = drain(req.Body)
reader, _ := gzip.NewReader(bytes.NewReader(reqBody))
req.Body = ioutil.NopCloser(reader)
reqDump, err := httputil.DumpRequest(req, true)
req.Body = ioutil.NopCloser(bytes.NewReader(reqBody))
return reqDump, err
return dumpGzipRequest(req)
}
return httputil.DumpRequest(req, true)
}
func dumpGzipRequest(req *http.Request) ([]byte, error) {
var reqBody []byte
req.Body, reqBody = drain(req.Body)
reader, _ := gzip.NewReader(bytes.NewReader(reqBody))
req.Body = ioutil.NopCloser(reader)
reqDump, err := httputil.DumpRequest(req, true)
req.Body = ioutil.NopCloser(bytes.NewReader(reqBody))
return reqDump, err
}
func dumpResponse(res *http.Response) ([]byte, error) {
if res.StatusCode == StatusInternalProxyError {
// dumps only the body when we have an proxy error.
// This body is set in NewProxyHandler()
var resBody []byte
res.Body, resBody = drain(res.Body)
return resBody, nil
return dumpInternalProxyError(res)
}
if res.Header.Get("Content-Encoding") == "gzip" {
var resBody []byte
res.Body, resBody = drain(res.Body)
reader, _ := gzip.NewReader(bytes.NewReader(resBody))
res.Body = ioutil.NopCloser(reader)
resDump, err := httputil.DumpResponse(res, true)
res.Body = ioutil.NopCloser(bytes.NewReader(resBody))
return resDump, err
return dumpGzipResponse(res)
}
return httputil.DumpResponse(res, true)
}
// Dumps only the body when we have an proxy error.
// This body is set in NewProxyHandler() in proxy.ErrorHandler
func dumpInternalProxyError(res *http.Response) ([]byte, error) {
var resBody []byte
res.Body, resBody = drain(res.Body)
return resBody, nil
}
func dumpGzipResponse(res *http.Response) ([]byte, error) {
var resBody []byte
res.Body, resBody = drain(res.Body)
reader, _ := gzip.NewReader(bytes.NewReader(resBody))
res.Body = ioutil.NopCloser(reader)
resDump, err := httputil.DumpResponse(res, true)
res.Body = ioutil.NopCloser(bytes.NewReader(resBody))
return resDump, err
}
func drain(b io.ReadCloser) (io.ReadCloser, []byte) {
all, _ := ioutil.ReadAll(b)
b.Close()