capture/main.go

165 lines
3.9 KiB
Go
Raw Normal View History

2017-11-08 00:10:54 +01:00
package main
import (
"bytes"
"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"
"net/http/httputil"
2018-08-04 01:53:54 +02:00
"net/url"
2018-07-21 19:50:53 +02:00
"strconv"
"strings"
2017-11-08 00:10:54 +01:00
2017-11-21 22:36:40 +01:00
"github.com/googollee/go-socket.io"
)
2018-08-02 00:36:23 +02:00
type transport struct {
http.RoundTripper
2018-09-16 16:18:39 +02:00
maxItems int
currItemID int
}
2018-07-21 19:50:53 +02:00
var captures Captures
2018-08-04 01:53:54 +02:00
var dashboardSocket socketio.Socket
2017-11-08 00:10:54 +01:00
func main() {
2018-09-16 16:21:36 +02:00
config := ReadConfig()
2017-11-08 00:10:54 +01:00
2018-08-04 01:53:54 +02:00
transp := &transport{
RoundTripper: http.DefaultTransport,
2018-09-16 16:21:36 +02:00
maxItems: config.MaxCaptures,
2018-08-04 01:53:54 +02:00
currItemID: 0,
}
2018-07-21 19:50:53 +02:00
2018-09-16 16:21:36 +02:00
http.Handle("/", getProxyHandler(config.TargetURL, transp))
http.Handle("/socket.io/", getDashboardSocketHandler(config))
http.Handle(config.DashboardPath, getDashboardHandler())
http.Handle(config.DashboardClearPath, getDashboardClearHandler())
http.Handle(config.DashboardItemInfoPath, getDashboardItemInfoHandler())
2018-09-16 16:18:39 +02:00
2018-09-16 16:21:36 +02:00
proxyHost := fmt.Sprintf("http://localhost:%s", config.ProxyPort)
2017-11-18 13:23:36 +01:00
2018-08-04 01:53:54 +02:00
fmt.Printf("\nListening on %s", proxyHost)
2018-09-16 16:21:36 +02:00
fmt.Printf("\n %s/%s\n\n", proxyHost, 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-09-16 16:21:36 +02:00
func getDashboardSocketHandler(config Config) http.Handler {
2017-11-21 22:36:40 +01:00
server, err := socketio.NewServer(nil)
if err != nil {
2018-08-04 01:53:54 +02:00
fmt.Println("socket server error", 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-09-07 16:45:02 +02:00
emitToDashboard(captures)
2017-11-21 22:36:40 +01:00
})
server.On("error", func(so socketio.Socket, err error) {
2018-08-04 01:53:54 +02:00
fmt.Println("socket error", err)
2017-11-21 22:36:40 +01:00
})
return server
}
2018-09-07 16:45:02 +02:00
func getDashboardClearHandler() http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
captures = nil
emitToDashboard(captures)
res.Write([]byte(""))
})
}
2018-08-04 01:53:54 +02:00
func getDashboardHandler() http.Handler {
2018-08-02 00:36:23 +02:00
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
2018-08-04 01:53:54 +02:00
res.Header().Add("Content-Type", "text/html")
res.Write([]byte(dashboardHTML))
})
}
func getDashboardItemInfoHandler() http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
2018-09-16 16:18:39 +02:00
idStr := req.URL.Path[strings.LastIndex(req.URL.Path, "/")+1:]
idInt, _ := strconv.Atoi(idStr)
for _, c := range captures {
if c.ID == idInt {
res.Header().Add("Content-Type", "application/json")
2018-09-26 01:00:02 +02:00
json.NewEncoder(res).Encode(c)
break
2018-09-16 16:18:39 +02:00
}
}
2018-08-02 00:36:23 +02:00
})
}
2018-09-07 18:41:24 +02:00
func getProxyHandler(targetURL string, transp *transport) http.Handler {
url, _ := url.Parse(targetURL)
2018-08-04 01:53:54 +02:00
proxy := httputil.NewSingleHostReverseProxy(url)
proxy.Transport = transp
return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
request.Host = request.URL.Host
proxy.ServeHTTP(response, request)
2018-08-02 00:36:23 +02:00
})
}
2018-08-04 01:53:54 +02:00
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
2017-11-08 00:10:54 +01:00
2018-08-04 01:53:54 +02:00
reqDump, err := dumpRequest(req)
2017-11-19 16:51:26 +01:00
if err != nil {
2018-07-21 19:50:53 +02:00
return nil, err
2017-11-19 16:51:26 +01:00
}
2017-11-08 00:10:54 +01:00
2018-07-22 20:14:27 +02:00
res, err := t.RoundTripper.RoundTrip(req)
if err != nil {
return nil, fmt.Errorf("uh oh | %v | %s", err, req.URL)
2018-07-22 20:14:27 +02:00
}
2018-08-04 01:53:54 +02:00
resDump, err := dumpResponse(res)
2017-11-19 16:51:26 +01:00
if err != nil {
return nil, err
}
2018-07-21 19:50:53 +02:00
capture := Capture{
2018-08-04 01:53:54 +02:00
ID: t.NewItemID(),
2018-07-21 19:50:53 +02:00
Path: req.URL.Path,
Method: req.Method,
Status: res.StatusCode,
Request: string(reqDump),
Response: string(resDump),
}
2017-11-21 22:36:40 +01:00
captures.Add(capture)
2018-08-04 01:53:54 +02:00
captures.RemoveLastAfterReaching(t.maxItems)
2018-09-07 16:45:02 +02:00
emitToDashboard(captures)
2017-11-19 16:51:26 +01:00
return res, nil
2017-11-08 00:10:54 +01:00
}
2018-08-04 01:53:54 +02:00
func (t *transport) NewItemID() int {
t.currItemID++
return t.currItemID
}
func dumpRequest(req *http.Request) ([]byte, error) {
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) {
var originalBody bytes.Buffer
2018-07-22 20:14:27 +02:00
reader := io.TeeReader(res.Body, &originalBody)
if res.Header.Get("Content-Encoding") == "gzip" {
2018-07-22 20:14:27 +02:00
reader, _ = gzip.NewReader(reader)
}
2018-07-22 20:14:27 +02:00
res.Body = ioutil.NopCloser(reader)
2017-11-18 12:42:53 +01:00
resDump, err := httputil.DumpResponse(res, true)
res.Body = ioutil.NopCloser(&originalBody)
2017-11-18 12:42:53 +01:00
return resDump, err
}
2018-09-07 16:45:02 +02:00
func emitToDashboard(captures Captures) {
if dashboardSocket != nil {
dashboardSocket.Emit("captures", captures.MetadataOnly())
}
}