2017-11-08 00:10:54 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-11-15 15:37:54 +01:00
|
|
|
"compress/gzip"
|
2018-07-21 19:50:53 +02:00
|
|
|
"encoding/json"
|
2017-11-18 13:23:36 +01:00
|
|
|
"fmt"
|
2017-11-08 00:10:54 +01:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
2018-11-22 22:45:20 +01:00
|
|
|
"net/http/httptest"
|
2017-11-08 00:10:54 +01:00
|
|
|
"net/http/httputil"
|
2018-08-04 01:53:54 +02:00
|
|
|
"net/url"
|
2018-07-21 19:50:53 +02:00
|
|
|
"strings"
|
2017-11-08 00:10:54 +01:00
|
|
|
|
2017-11-21 22:36:40 +01:00
|
|
|
"github.com/googollee/go-socket.io"
|
|
|
|
)
|
2017-11-15 15:37:54 +01:00
|
|
|
|
2018-08-04 01:53:54 +02:00
|
|
|
var dashboardSocket socketio.Socket
|
2018-08-02 11:09:00 +02:00
|
|
|
|
2017-11-08 00:10:54 +01:00
|
|
|
func main() {
|
2018-09-16 16:21:36 +02:00
|
|
|
config := ReadConfig()
|
2018-11-11 17:54:35 +01:00
|
|
|
startCapture(config)
|
|
|
|
}
|
2017-11-08 00:10:54 +01:00
|
|
|
|
2018-11-11 17:54:35 +01:00
|
|
|
func startCapture(config Config) {
|
2018-11-22 22:45:20 +01:00
|
|
|
|
|
|
|
repo := NewCapturesRepository(config.MaxCaptures)
|
|
|
|
|
2018-11-22 22:52:02 +01:00
|
|
|
http.Handle("/", NewRecorder(repo, NewProxyHandler(config.TargetURL)))
|
|
|
|
http.Handle(config.DashboardPath, NewDashboardHtmlHandler())
|
|
|
|
http.Handle(config.DashboardClearPath, NewDashboardClearHandler(repo))
|
|
|
|
http.Handle(config.DashboardItemInfoPath, NewDashboardItemInfoHandler(repo))
|
|
|
|
http.Handle(config.DashboardConnPath, NewDashboardSocketHandler(repo, config))
|
2018-09-16 16:18:39 +02:00
|
|
|
|
2018-11-16 22:39:53 +01:00
|
|
|
captureHost := fmt.Sprintf("http://localhost:%s", config.ProxyPort)
|
2017-11-18 13:23:36 +01:00
|
|
|
|
2018-11-16 22:39:53 +01:00
|
|
|
fmt.Printf("\nListening on %s", captureHost)
|
|
|
|
fmt.Printf("\n %s/%s\n\n", captureHost, config.Dashboard)
|
2017-11-18 13:23:36 +01:00
|
|
|
|
2018-09-16 16:21:36 +02:00
|
|
|
fmt.Println(http.ListenAndServe(":"+config.ProxyPort, nil))
|
2017-11-08 00:10:54 +01:00
|
|
|
}
|
|
|
|
|
2018-11-22 22:52:02 +01:00
|
|
|
func NewDashboardSocketHandler(repo CaptureRepository, config Config) http.Handler {
|
2017-11-21 22:36:40 +01:00
|
|
|
server, err := socketio.NewServer(nil)
|
|
|
|
if err != nil {
|
2018-11-16 22:39:53 +01:00
|
|
|
fmt.Printf("socket server error: %v\n", err)
|
2017-11-21 22:36:40 +01:00
|
|
|
}
|
|
|
|
server.On("connection", func(so socketio.Socket) {
|
2018-08-04 01:53:54 +02:00
|
|
|
dashboardSocket = so
|
2018-09-16 16:21:36 +02:00
|
|
|
dashboardSocket.Emit("config", config)
|
2018-11-22 22:45:20 +01:00
|
|
|
emitToDashboard(repo.FindAll())
|
2017-11-21 22:36:40 +01:00
|
|
|
})
|
|
|
|
server.On("error", func(so socketio.Socket, err error) {
|
2018-11-16 22:39:53 +01:00
|
|
|
fmt.Printf("socket error: %v\n", err)
|
2017-11-21 22:36:40 +01:00
|
|
|
})
|
|
|
|
return server
|
|
|
|
}
|
|
|
|
|
2018-11-22 22:52:02 +01:00
|
|
|
func NewDashboardClearHandler(repo CaptureRepository) http.Handler {
|
2018-11-16 22:39:53 +01:00
|
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
2018-11-22 22:45:20 +01:00
|
|
|
repo.RemoveAll()
|
|
|
|
emitToDashboard(nil)
|
2018-11-16 22:39:53 +01:00
|
|
|
rw.WriteHeader(http.StatusOK)
|
2018-09-07 16:45:02 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-11-22 22:52:02 +01:00
|
|
|
func NewDashboardHtmlHandler() http.Handler {
|
2018-11-16 22:39:53 +01:00
|
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
|
|
rw.Header().Add("Content-Type", "text/html")
|
|
|
|
fmt.Fprint(rw, dashboardHTML)
|
2018-08-04 01:53:54 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-11-22 22:52:02 +01:00
|
|
|
func NewDashboardItemInfoHandler(repo CaptureRepository) http.Handler {
|
2018-11-16 22:39:53 +01:00
|
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
2018-11-22 22:45:20 +01:00
|
|
|
id := req.URL.Path[strings.LastIndex(req.URL.Path, "/")+1:]
|
|
|
|
capture := repo.Find(id)
|
|
|
|
if capture == nil {
|
|
|
|
http.Error(rw, "Item Not Found", http.StatusNotFound)
|
|
|
|
return
|
2018-09-16 16:18:39 +02:00
|
|
|
}
|
2018-11-22 22:45:20 +01:00
|
|
|
rw.Header().Add("Content-Type", "application/json")
|
|
|
|
json.NewEncoder(rw).Encode(capture)
|
2018-08-02 00:36:23 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-11-22 22:45:20 +01:00
|
|
|
func NewRecorder(repo CaptureRepository, next http.Handler) http.Handler {
|
2018-11-11 17:54:35 +01:00
|
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
|
|
reqDump, err := dumpRequest(req)
|
|
|
|
if err != nil {
|
2018-11-16 22:39:53 +01:00
|
|
|
fmt.Printf("could not dump request: %v\n", err)
|
2018-11-11 17:54:35 +01:00
|
|
|
}
|
2017-11-08 00:10:54 +01:00
|
|
|
|
2018-11-22 22:45:20 +01:00
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
next.ServeHTTP(rec, req)
|
|
|
|
|
|
|
|
for k, v := range rec.HeaderMap {
|
|
|
|
rw.Header()[k] = v
|
|
|
|
}
|
|
|
|
rw.WriteHeader(rec.Code)
|
|
|
|
rw.Write(rec.Body.Bytes())
|
|
|
|
|
|
|
|
res := rec.Result()
|
|
|
|
resDump, err := dumpResponse(res)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("could not dump response: %v\n", err)
|
2018-11-11 17:54:35 +01:00
|
|
|
}
|
2018-11-22 22:45:20 +01:00
|
|
|
capture := Capture{
|
|
|
|
Path: req.URL.Path,
|
|
|
|
Method: req.Method,
|
|
|
|
Status: res.StatusCode,
|
|
|
|
Request: string(reqDump),
|
|
|
|
Response: string(resDump),
|
2018-11-11 17:54:35 +01:00
|
|
|
}
|
2018-11-22 22:45:20 +01:00
|
|
|
repo.Insert(capture)
|
|
|
|
emitToDashboard(repo.FindAll())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-11-22 22:52:02 +01:00
|
|
|
func NewProxyHandler(URL string) http.Handler {
|
2018-11-22 22:45:20 +01:00
|
|
|
url, _ := url.Parse(URL)
|
|
|
|
proxy := httputil.NewSingleHostReverseProxy(url)
|
|
|
|
proxy.ErrorHandler = func(rw http.ResponseWriter, req *http.Request, err error) {
|
|
|
|
fmt.Printf("uh oh | %v | %s %s\n", err, req.Method, req.URL)
|
|
|
|
}
|
|
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
|
|
req.Host = url.Host
|
|
|
|
req.URL.Host = url.Host
|
|
|
|
req.URL.Scheme = url.Scheme
|
2018-11-11 17:54:35 +01:00
|
|
|
proxy.ServeHTTP(rw, req)
|
|
|
|
})
|
2018-08-04 01:53:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func dumpRequest(req *http.Request) ([]byte, error) {
|
2018-11-11 17:54:35 +01:00
|
|
|
if req.Header.Get("Content-Encoding") == "gzip" {
|
2018-11-22 22:45:20 +01:00
|
|
|
var reqBody []byte
|
|
|
|
req.Body, reqBody = drain(req.Body)
|
|
|
|
reader, _ := gzip.NewReader(bytes.NewReader(reqBody))
|
2018-11-11 17:54:35 +01:00
|
|
|
req.Body = ioutil.NopCloser(reader)
|
|
|
|
reqDump, err := httputil.DumpRequest(req, true)
|
2018-11-22 22:45:20 +01:00
|
|
|
req.Body = ioutil.NopCloser(bytes.NewReader(reqBody))
|
2018-11-11 17:54:35 +01:00
|
|
|
return reqDump, err
|
|
|
|
}
|
2018-07-21 19:50:53 +02:00
|
|
|
return httputil.DumpRequest(req, true)
|
|
|
|
}
|
|
|
|
|
2018-08-04 01:53:54 +02:00
|
|
|
func dumpResponse(res *http.Response) ([]byte, error) {
|
2017-11-15 15:37:54 +01:00
|
|
|
if res.Header.Get("Content-Encoding") == "gzip" {
|
2018-11-22 22:45:20 +01:00
|
|
|
var resBody []byte
|
|
|
|
res.Body, resBody = drain(res.Body)
|
|
|
|
reader, _ := gzip.NewReader(bytes.NewReader(resBody))
|
2018-11-11 17:54:35 +01:00
|
|
|
res.Body = ioutil.NopCloser(reader)
|
|
|
|
resDump, err := httputil.DumpResponse(res, true)
|
2018-11-22 22:45:20 +01:00
|
|
|
res.Body = ioutil.NopCloser(bytes.NewReader(resBody))
|
2018-11-11 17:54:35 +01:00
|
|
|
return resDump, err
|
2017-11-15 15:37:54 +01:00
|
|
|
}
|
2018-11-11 17:54:35 +01:00
|
|
|
return httputil.DumpResponse(res, true)
|
2017-11-15 15:37:54 +01:00
|
|
|
}
|
2018-09-07 16:45:02 +02:00
|
|
|
|
2018-11-22 22:45:20 +01:00
|
|
|
func drain(b io.ReadCloser) (io.ReadCloser, []byte) {
|
|
|
|
all, _ := ioutil.ReadAll(b)
|
|
|
|
b.Close()
|
|
|
|
return ioutil.NopCloser(bytes.NewReader(all)), all
|
|
|
|
}
|
|
|
|
|
|
|
|
func emitToDashboard(captures []Capture) {
|
|
|
|
if dashboardSocket == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
metadatas := make([]CaptureMetadata, len(captures))
|
|
|
|
for i, capture := range captures {
|
|
|
|
metadatas[i] = capture.Metadata()
|
2018-09-07 16:45:02 +02:00
|
|
|
}
|
2018-11-22 22:45:20 +01:00
|
|
|
dashboardSocket.Emit("captures", metadatas)
|
2018-09-07 16:45:02 +02:00
|
|
|
}
|