mirror of
https://github.com/dstotijn/hetty.git
synced 2025-07-01 18:47:29 -04:00
Start work on request log
This commit is contained in:
63
cmd/main.go
63
cmd/main.go
@ -1,15 +1,23 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/dstotijn/gurp/pkg/proxy"
|
"github.com/dstotijn/gurp/pkg/proxy"
|
||||||
|
"github.com/dstotijn/gurp/pkg/reqlog"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,6 +41,8 @@ func main() {
|
|||||||
log.Fatalf("[FATAL] Could not parse CA: %v", err)
|
log.Fatalf("[FATAL] Could not parse CA: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reqLog := reqlog.NewRequestLog()
|
||||||
|
|
||||||
p, err := proxy.NewProxy(caCert, tlsCA.PrivateKey)
|
p, err := proxy.NewProxy(caCert, tlsCA.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("[FATAL] Could not create Proxy: %v", err)
|
log.Fatalf("[FATAL] Could not create Proxy: %v", err)
|
||||||
@ -40,34 +50,73 @@ func main() {
|
|||||||
|
|
||||||
p.UseRequestModifier(func(next proxy.RequestModifyFunc) proxy.RequestModifyFunc {
|
p.UseRequestModifier(func(next proxy.RequestModifyFunc) proxy.RequestModifyFunc {
|
||||||
return func(req *http.Request) {
|
return func(req *http.Request) {
|
||||||
log.Printf("[DEBUG] Incoming request: %v", req.URL)
|
|
||||||
next(req)
|
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 {
|
p.UseResponseModifier(func(next proxy.ResponseModifyFunc) proxy.ResponseModifyFunc {
|
||||||
return func(res *http.Response) error {
|
return func(res *http.Response) error {
|
||||||
log.Printf("[DEBUG] Downstream response: %v %v %v", res.Proto, res.StatusCode, http.StatusText(res.StatusCode))
|
if err := next(res); err != nil {
|
||||||
return next(res)
|
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)
|
var adminHandler http.Handler
|
||||||
adminRouter := router.Host("gurp.proxy")
|
|
||||||
|
|
||||||
if *dev {
|
if *dev {
|
||||||
adminURL, err := url.Parse("http://localhost:3000")
|
adminURL, err := url.Parse("http://localhost:3000")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("[FATAL] Invalid admin URL: %v", err)
|
log.Fatalf("[FATAL] Invalid admin URL: %v", err)
|
||||||
}
|
}
|
||||||
adminRouter.Handler(httputil.NewSingleHostReverseProxy(adminURL))
|
adminHandler = httputil.NewSingleHostReverseProxy(adminURL)
|
||||||
} else {
|
} else {
|
||||||
if *adminPath == "" {
|
if *adminPath == "" {
|
||||||
log.Fatal("[FATAL] `adminPath` must be set")
|
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)
|
router.PathPrefix("").Handler(p)
|
||||||
|
|
||||||
s := &http.Server{
|
s := &http.Server{
|
||||||
|
44
pkg/reqlog/reqlog.go
Normal file
44
pkg/reqlog/reqlog.go
Normal 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})
|
||||||
|
}
|
Reference in New Issue
Block a user