Start work on request log

This commit is contained in:
David Stotijn
2019-12-01 14:07:12 +01:00
parent 94363fe196
commit 1164837247
2 changed files with 100 additions and 7 deletions

View File

@ -1,15 +1,23 @@
package main
import (
"bytes"
"crypto/tls"
"crypto/x509"
"flag"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
"os"
"strings"
"github.com/dstotijn/gurp/pkg/proxy"
"github.com/dstotijn/gurp/pkg/reqlog"
"github.com/gorilla/mux"
)
@ -33,6 +41,8 @@ func main() {
log.Fatalf("[FATAL] Could not parse CA: %v", err)
}
reqLog := reqlog.NewRequestLog()
p, err := proxy.NewProxy(caCert, tlsCA.PrivateKey)
if err != nil {
log.Fatalf("[FATAL] Could not create Proxy: %v", err)
@ -40,34 +50,73 @@ func main() {
p.UseRequestModifier(func(next proxy.RequestModifyFunc) proxy.RequestModifyFunc {
return func(req *http.Request) {
log.Printf("[DEBUG] Incoming request: %v", req.URL)
next(req)
clone := req.Clone(req.Context())
var body []byte
if req.Body != nil {
// TODO: Use io.LimitReader.
body, err := ioutil.ReadAll(req.Body)
if err != nil {
log.Printf("[ERROR] Could not read request body for logging: %v", err)
return
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
}
reqLog.AddRequest(*clone, body)
}
})
p.UseResponseModifier(func(next proxy.ResponseModifyFunc) proxy.ResponseModifyFunc {
return func(res *http.Response) error {
log.Printf("[DEBUG] Downstream response: %v %v %v", res.Proto, res.StatusCode, http.StatusText(res.StatusCode))
return next(res)
if err := next(res); err != nil {
return err
}
clone := *res
var body []byte
if res.Body != nil {
// TODO: Use io.LimitReader.
var err error
body, err = ioutil.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("could not read response body: %v", err)
}
res.Body = ioutil.NopCloser(bytes.NewBuffer(body))
}
reqLog.AddResponse(clone, body)
return nil
}
})
router := mux.NewRouter().SkipClean(true)
adminRouter := router.Host("gurp.proxy")
var adminHandler http.Handler
if *dev {
adminURL, err := url.Parse("http://localhost:3000")
if err != nil {
log.Fatalf("[FATAL] Invalid admin URL: %v", err)
}
adminRouter.Handler(httputil.NewSingleHostReverseProxy(adminURL))
adminHandler = httputil.NewSingleHostReverseProxy(adminURL)
} else {
if *adminPath == "" {
log.Fatal("[FATAL] `adminPath` must be set")
}
adminRouter.Handler(http.FileServer(http.Dir(*adminPath)))
adminHandler = http.FileServer(http.Dir(*adminPath))
}
router := mux.NewRouter().SkipClean(true)
adminRouter := router.MatcherFunc(func(req *http.Request, match *mux.RouteMatch) bool {
hostname, _ := os.Hostname()
host, _, _ := net.SplitHostPort(req.Host)
return strings.EqualFold(host, hostname) || req.Host == "gurp.proxy"
})
// GraphQL server.
// adminRouter.Path("/graphql").Handler(...)
// Admin interface.
adminRouter.Handler(adminHandler)
// Fallback (default) is the Proxy handler.
router.PathPrefix("").Handler(p)
s := &http.Server{

44
pkg/reqlog/reqlog.go Normal file
View File

@ -0,0 +1,44 @@
package reqlog
import (
"net/http"
"sync"
)
type request struct {
req http.Request
body []byte
}
type response struct {
res http.Response
body []byte
}
type RequestLog struct {
reqStore []request
resStore []response
reqMu sync.Mutex
resMu sync.Mutex
}
func NewRequestLog() RequestLog {
return RequestLog{
reqStore: make([]request, 0),
resStore: make([]response, 0),
}
}
func (rl *RequestLog) AddRequest(req http.Request, body []byte) {
rl.reqMu.Lock()
defer rl.reqMu.Unlock()
rl.reqStore = append(rl.reqStore, request{req, body})
}
func (rl *RequestLog) AddResponse(res http.Response, body []byte) {
rl.resMu.Lock()
defer rl.resMu.Unlock()
rl.resStore = append(rl.resStore, response{res, body})
}