feat: http stream filter
This commit is contained in:
parent
998e678a7f
commit
8428c2c01f
@ -51,6 +51,13 @@ func (svr *Service) RunAdminServer(address string) (err error) {
|
|||||||
http.Redirect(w, r, "/static/", http.StatusMovedPermanently)
|
http.Redirect(w, r, "/static/", http.StatusMovedPermanently)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// http filter
|
||||||
|
router.HandleFunc("/api/http/list", svr.httpFilterList).Methods("GET")
|
||||||
|
router.HandleFunc("/api/http/raw_data/{id}", svr.httpFilterGetRaw).Methods("GET")
|
||||||
|
router.HandleFunc("/api/http/replay/{id}", svr.httpFilterReplay).Methods("PUT")
|
||||||
|
router.HandleFunc("/api/http/remove/{id}", svr.httpFilterRemove).Methods("DELETE")
|
||||||
|
router.HandleFunc("/api/http/clear", svr.httpFilterClear).Methods("PUT")
|
||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: address,
|
Addr: address,
|
||||||
Handler: router,
|
Handler: router,
|
||||||
|
87
client/http_filter_api.go
Normal file
87
client/http_filter_api.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/plugin/interceptor"
|
||||||
|
"github.com/fatedier/frp/pkg/util/cache"
|
||||||
|
"github.com/fatedier/frp/pkg/util/log"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (svr *Service) httpFilterList(w http.ResponseWriter, r *http.Request) {
|
||||||
|
keys := cache.DefaultCache.Keys()
|
||||||
|
if len(keys) <= 0 {
|
||||||
|
log.Debug("there is no http stream be cached")
|
||||||
|
w.WriteHeader(200)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, _ := json.Marshal(keys)
|
||||||
|
|
||||||
|
w.WriteHeader(200)
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svr *Service) httpFilterGetRaw(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
dataRaw, ok := cache.DefaultCache.Get(id)
|
||||||
|
if !ok {
|
||||||
|
log.Warn("key not found: %v", id)
|
||||||
|
w.WriteHeader(404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, _ := json.Marshal(dataRaw)
|
||||||
|
|
||||||
|
w.WriteHeader(200)
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svr *Service) httpFilterReplay(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
dataRaw, ok := cache.DefaultCache.Get(id)
|
||||||
|
if !ok {
|
||||||
|
log.Warn("key not found: %v", id)
|
||||||
|
w.WriteHeader(404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pair, ok := dataRaw.(interceptor.Pair)
|
||||||
|
if !ok {
|
||||||
|
log.Warn("data type not match")
|
||||||
|
w.WriteHeader(500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req, _ := http.NewRequest(pair.Req.Method, pair.Req.URL, bytes.NewBuffer(pair.Req.Body))
|
||||||
|
req.Header = pair.Req.Header
|
||||||
|
req.Host = pair.Req.Host
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("replay request: %v got error: %v", pair.Req, err)
|
||||||
|
w.WriteHeader(500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
data, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
log.Debug("replay get response body: %v", string(data))
|
||||||
|
|
||||||
|
w.WriteHeader(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svr *Service) httpFilterRemove(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
cache.DefaultCache.Remove(id)
|
||||||
|
|
||||||
|
w.WriteHeader(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svr *Service) httpFilterClear(w http.ResponseWriter, r *http.Request) {
|
||||||
|
cache.DefaultCache.Purge()
|
||||||
|
w.WriteHeader(200)
|
||||||
|
}
|
@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
plugin "github.com/fatedier/frp/pkg/plugin/client"
|
plugin "github.com/fatedier/frp/pkg/plugin/client"
|
||||||
|
"github.com/fatedier/frp/pkg/plugin/filter"
|
||||||
"github.com/fatedier/frp/pkg/proto/udp"
|
"github.com/fatedier/frp/pkg/proto/udp"
|
||||||
"github.com/fatedier/frp/pkg/util/limit"
|
"github.com/fatedier/frp/pkg/util/limit"
|
||||||
frpNet "github.com/fatedier/frp/pkg/util/net"
|
frpNet "github.com/fatedier/frp/pkg/util/net"
|
||||||
@ -128,6 +129,8 @@ type TCPProxy struct {
|
|||||||
|
|
||||||
cfg *config.TCPProxyConf
|
cfg *config.TCPProxyConf
|
||||||
proxyPlugin plugin.Plugin
|
proxyPlugin plugin.Plugin
|
||||||
|
|
||||||
|
streamFilter *filter.HTTPStreamFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *TCPProxy) Run() (err error) {
|
func (pxy *TCPProxy) Run() (err error) {
|
||||||
@ -137,6 +140,14 @@ func (pxy *TCPProxy) Run() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pxy.cfg.StreamFilterType != "" {
|
||||||
|
pxy.streamFilter, err = filter.NewHTTPStreamFilter(pxy.cfg.LocalSvrConf, pxy.cfg.StreamFilterParams)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +159,7 @@ func (pxy *TCPProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *TCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
func (pxy *TCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
||||||
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
|
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
|
||||||
conn, []byte(pxy.clientCfg.Token), m)
|
conn, []byte(pxy.clientCfg.Token), m, pxy.streamFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TCP Multiplexer
|
// TCP Multiplexer
|
||||||
@ -157,6 +168,8 @@ type TCPMuxProxy struct {
|
|||||||
|
|
||||||
cfg *config.TCPMuxProxyConf
|
cfg *config.TCPMuxProxyConf
|
||||||
proxyPlugin plugin.Plugin
|
proxyPlugin plugin.Plugin
|
||||||
|
|
||||||
|
streamFilter *filter.HTTPStreamFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *TCPMuxProxy) Run() (err error) {
|
func (pxy *TCPMuxProxy) Run() (err error) {
|
||||||
@ -166,6 +179,14 @@ func (pxy *TCPMuxProxy) Run() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pxy.cfg.StreamFilterType != "" {
|
||||||
|
pxy.streamFilter, err = filter.NewHTTPStreamFilter(pxy.cfg.LocalSvrConf, pxy.cfg.StreamFilterParams)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +198,7 @@ func (pxy *TCPMuxProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *TCPMuxProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
func (pxy *TCPMuxProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
||||||
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
|
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
|
||||||
conn, []byte(pxy.clientCfg.Token), m)
|
conn, []byte(pxy.clientCfg.Token), m, pxy.streamFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
@ -186,6 +207,8 @@ type HTTPProxy struct {
|
|||||||
|
|
||||||
cfg *config.HTTPProxyConf
|
cfg *config.HTTPProxyConf
|
||||||
proxyPlugin plugin.Plugin
|
proxyPlugin plugin.Plugin
|
||||||
|
|
||||||
|
streamFilter *filter.HTTPStreamFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *HTTPProxy) Run() (err error) {
|
func (pxy *HTTPProxy) Run() (err error) {
|
||||||
@ -195,6 +218,14 @@ func (pxy *HTTPProxy) Run() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pxy.cfg.StreamFilterType != "" {
|
||||||
|
pxy.streamFilter, err = filter.NewHTTPStreamFilter(pxy.cfg.LocalSvrConf, pxy.cfg.StreamFilterParams)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +237,7 @@ func (pxy *HTTPProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *HTTPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
func (pxy *HTTPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
||||||
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
|
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
|
||||||
conn, []byte(pxy.clientCfg.Token), m)
|
conn, []byte(pxy.clientCfg.Token), m, pxy.streamFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPS
|
// HTTPS
|
||||||
@ -215,6 +246,8 @@ type HTTPSProxy struct {
|
|||||||
|
|
||||||
cfg *config.HTTPSProxyConf
|
cfg *config.HTTPSProxyConf
|
||||||
proxyPlugin plugin.Plugin
|
proxyPlugin plugin.Plugin
|
||||||
|
|
||||||
|
streamFilter *filter.HTTPStreamFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *HTTPSProxy) Run() (err error) {
|
func (pxy *HTTPSProxy) Run() (err error) {
|
||||||
@ -224,6 +257,14 @@ func (pxy *HTTPSProxy) Run() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pxy.cfg.StreamFilterType != "" {
|
||||||
|
pxy.streamFilter, err = filter.NewHTTPStreamFilter(pxy.cfg.LocalSvrConf, pxy.cfg.StreamFilterParams)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,7 +276,7 @@ func (pxy *HTTPSProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *HTTPSProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
func (pxy *HTTPSProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
||||||
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
|
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
|
||||||
conn, []byte(pxy.clientCfg.Token), m)
|
conn, []byte(pxy.clientCfg.Token), m, pxy.streamFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// STCP
|
// STCP
|
||||||
@ -244,6 +285,8 @@ type STCPProxy struct {
|
|||||||
|
|
||||||
cfg *config.STCPProxyConf
|
cfg *config.STCPProxyConf
|
||||||
proxyPlugin plugin.Plugin
|
proxyPlugin plugin.Plugin
|
||||||
|
|
||||||
|
streamFilter *filter.HTTPStreamFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *STCPProxy) Run() (err error) {
|
func (pxy *STCPProxy) Run() (err error) {
|
||||||
@ -253,6 +296,14 @@ func (pxy *STCPProxy) Run() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pxy.cfg.StreamFilterType != "" {
|
||||||
|
pxy.streamFilter, err = filter.NewHTTPStreamFilter(pxy.cfg.LocalSvrConf, pxy.cfg.StreamFilterParams)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +315,7 @@ func (pxy *STCPProxy) Close() {
|
|||||||
|
|
||||||
func (pxy *STCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
func (pxy *STCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
||||||
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
|
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
|
||||||
conn, []byte(pxy.clientCfg.Token), m)
|
conn, []byte(pxy.clientCfg.Token), m, pxy.streamFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// XTCP
|
// XTCP
|
||||||
@ -273,6 +324,8 @@ type XTCPProxy struct {
|
|||||||
|
|
||||||
cfg *config.XTCPProxyConf
|
cfg *config.XTCPProxyConf
|
||||||
proxyPlugin plugin.Plugin
|
proxyPlugin plugin.Plugin
|
||||||
|
|
||||||
|
streamFilter *filter.HTTPStreamFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *XTCPProxy) Run() (err error) {
|
func (pxy *XTCPProxy) Run() (err error) {
|
||||||
@ -282,6 +335,14 @@ func (pxy *XTCPProxy) Run() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pxy.cfg.StreamFilterType != "" {
|
||||||
|
pxy.streamFilter, err = filter.NewHTTPStreamFilter(pxy.cfg.LocalSvrConf, pxy.cfg.StreamFilterParams)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,7 +475,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
|
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
|
||||||
muxConn, []byte(pxy.cfg.Sk), m)
|
muxConn, []byte(pxy.cfg.Sk), m, pxy.streamFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *XTCPProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, content []byte) (err error) {
|
func (pxy *XTCPProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, content []byte) (err error) {
|
||||||
@ -722,7 +783,9 @@ func (pxy *SUDPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
|||||||
|
|
||||||
// Common handler for tcp work connections.
|
// Common handler for tcp work connections.
|
||||||
func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin,
|
func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin,
|
||||||
baseInfo *config.BaseProxyConf, limiter *rate.Limiter, workConn net.Conn, encKey []byte, m *msg.StartWorkConn) {
|
baseInfo *config.BaseProxyConf, limiter *rate.Limiter, workConn net.Conn, encKey []byte, m *msg.StartWorkConn,
|
||||||
|
streamFilter *filter.HTTPStreamFilter) {
|
||||||
|
|
||||||
xl := xlog.FromContextSafe(ctx)
|
xl := xlog.FromContextSafe(ctx)
|
||||||
var (
|
var (
|
||||||
remote io.ReadWriteCloser
|
remote io.ReadWriteCloser
|
||||||
@ -790,6 +853,13 @@ func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if streamFilter != nil {
|
||||||
|
xl.Debug("handle by filter")
|
||||||
|
streamFilter.Handle(remote, workConn)
|
||||||
|
xl.Debug("handle by filter finish")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
localConn, err := frpNet.ConnectServer("tcp", fmt.Sprintf("%s:%d", localInfo.LocalIP, localInfo.LocalPort))
|
localConn, err := frpNet.ConnectServer("tcp", fmt.Sprintf("%s:%d", localInfo.LocalIP, localInfo.LocalPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
workConn.Close()
|
workConn.Close()
|
||||||
|
1
go.mod
1
go.mod
@ -12,6 +12,7 @@ require (
|
|||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.2.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4
|
||||||
github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c
|
github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c
|
||||||
github.com/klauspost/cpuid v1.2.0 // indirect
|
github.com/klauspost/cpuid v1.2.0 // indirect
|
||||||
github.com/klauspost/reedsolomon v1.9.1 // indirect
|
github.com/klauspost/reedsolomon v1.9.1 // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -205,6 +205,8 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
|||||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
@ -73,6 +73,9 @@ type LocalSvrConf struct {
|
|||||||
// PluginParams specify parameters to be passed to the plugin, if one is
|
// PluginParams specify parameters to be passed to the plugin, if one is
|
||||||
// being used. By default, this value is an empty map.
|
// being used. By default, this value is an empty map.
|
||||||
PluginParams map[string]string `ini:"-"`
|
PluginParams map[string]string `ini:"-"`
|
||||||
|
|
||||||
|
StreamFilterType string `ini:"stream_filter_type" json:"stream_filter_type"`
|
||||||
|
StreamFilterParams map[string]string `ini:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HealthCheckConf configures health checking. This can be useful for load
|
// HealthCheckConf configures health checking. This can be useful for load
|
||||||
|
@ -23,6 +23,8 @@ import (
|
|||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/plugin/interceptor"
|
||||||
|
"github.com/fatedier/frp/pkg/util/listener"
|
||||||
frpNet "github.com/fatedier/frp/pkg/util/net"
|
frpNet "github.com/fatedier/frp/pkg/util/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,13 +39,15 @@ type HTTP2HTTPSPlugin struct {
|
|||||||
localAddr string
|
localAddr string
|
||||||
headers map[string]string
|
headers map[string]string
|
||||||
|
|
||||||
l *Listener
|
l *listener.Listener
|
||||||
s *http.Server
|
s *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTP2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
func NewHTTP2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
||||||
localAddr := params["plugin_local_addr"]
|
localAddr := params["plugin_local_addr"]
|
||||||
hostHeaderRewrite := params["plugin_host_header_rewrite"]
|
hostHeaderRewrite := params["plugin_host_header_rewrite"]
|
||||||
|
filter := params["stream_filter_type"]
|
||||||
|
|
||||||
headers := make(map[string]string)
|
headers := make(map[string]string)
|
||||||
for k, v := range params {
|
for k, v := range params {
|
||||||
if !strings.HasPrefix(k, "plugin_header_") {
|
if !strings.HasPrefix(k, "plugin_header_") {
|
||||||
@ -58,7 +62,7 @@ func NewHTTP2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
|||||||
return nil, fmt.Errorf("plugin_local_addr is required")
|
return nil, fmt.Errorf("plugin_local_addr is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
listener := NewProxyListener()
|
listener := listener.NewProxyListener()
|
||||||
|
|
||||||
p := &HTTP2HTTPSPlugin{
|
p := &HTTP2HTTPSPlugin{
|
||||||
localAddr: localAddr,
|
localAddr: localAddr,
|
||||||
@ -67,8 +71,11 @@ func NewHTTP2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
|||||||
l: listener,
|
l: listener,
|
||||||
}
|
}
|
||||||
|
|
||||||
tr := &http.Transport{
|
tr := interceptor.NewTransportWrapper(&http.Transport{
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
})
|
||||||
|
if filter == "http" {
|
||||||
|
tr.WithCacheInterceptor()
|
||||||
}
|
}
|
||||||
|
|
||||||
rp := &httputil.ReverseProxy{
|
rp := &httputil.ReverseProxy{
|
||||||
|
@ -22,6 +22,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/plugin/interceptor"
|
||||||
|
"github.com/fatedier/frp/pkg/util/listener"
|
||||||
frpNet "github.com/fatedier/frp/pkg/util/net"
|
frpNet "github.com/fatedier/frp/pkg/util/net"
|
||||||
|
|
||||||
frpIo "github.com/fatedier/golib/io"
|
frpIo "github.com/fatedier/golib/io"
|
||||||
@ -35,21 +37,32 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HTTPProxy struct {
|
type HTTPProxy struct {
|
||||||
l *Listener
|
l *listener.Listener
|
||||||
s *http.Server
|
s *http.Server
|
||||||
AuthUser string
|
AuthUser string
|
||||||
AuthPasswd string
|
AuthPasswd string
|
||||||
|
|
||||||
|
Transport http.RoundTripper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPProxyPlugin(params map[string]string) (Plugin, error) {
|
func NewHTTPProxyPlugin(params map[string]string) (Plugin, error) {
|
||||||
user := params["plugin_http_user"]
|
user := params["plugin_http_user"]
|
||||||
passwd := params["plugin_http_passwd"]
|
passwd := params["plugin_http_passwd"]
|
||||||
listener := NewProxyListener()
|
filter := params["stream_filter_type"]
|
||||||
|
|
||||||
|
listener := listener.NewProxyListener()
|
||||||
|
|
||||||
|
tr := interceptor.NewTransportWrapper(http.DefaultTransport)
|
||||||
|
if filter == "http" {
|
||||||
|
tr.WithCacheInterceptor()
|
||||||
|
}
|
||||||
|
|
||||||
hp := &HTTPProxy{
|
hp := &HTTPProxy{
|
||||||
l: listener,
|
l: listener,
|
||||||
AuthUser: user,
|
AuthUser: user,
|
||||||
AuthPasswd: passwd,
|
AuthPasswd: passwd,
|
||||||
|
|
||||||
|
Transport: tr,
|
||||||
}
|
}
|
||||||
|
|
||||||
hp.s = &http.Server{
|
hp.s = &http.Server{
|
||||||
@ -115,7 +128,7 @@ func (hp *HTTPProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||||||
func (hp *HTTPProxy) HTTPHandler(rw http.ResponseWriter, req *http.Request) {
|
func (hp *HTTPProxy) HTTPHandler(rw http.ResponseWriter, req *http.Request) {
|
||||||
removeProxyHeaders(req)
|
removeProxyHeaders(req)
|
||||||
|
|
||||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
resp, err := hp.Transport.RoundTrip(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
@ -23,6 +23,8 @@ import (
|
|||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/plugin/interceptor"
|
||||||
|
"github.com/fatedier/frp/pkg/util/listener"
|
||||||
frpNet "github.com/fatedier/frp/pkg/util/net"
|
frpNet "github.com/fatedier/frp/pkg/util/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,7 +41,7 @@ type HTTPS2HTTPPlugin struct {
|
|||||||
localAddr string
|
localAddr string
|
||||||
headers map[string]string
|
headers map[string]string
|
||||||
|
|
||||||
l *Listener
|
l *listener.Listener
|
||||||
s *http.Server
|
s *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +70,9 @@ func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
|
|||||||
return nil, fmt.Errorf("plugin_local_addr is required")
|
return nil, fmt.Errorf("plugin_local_addr is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
listener := NewProxyListener()
|
filter := params["stream_filter_type"]
|
||||||
|
|
||||||
|
listener := listener.NewProxyListener()
|
||||||
|
|
||||||
p := &HTTPS2HTTPPlugin{
|
p := &HTTPS2HTTPPlugin{
|
||||||
crtPath: crtPath,
|
crtPath: crtPath,
|
||||||
@ -79,6 +83,11 @@ func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
|
|||||||
l: listener,
|
l: listener,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tr := interceptor.NewTransportWrapper(http.DefaultTransport)
|
||||||
|
if filter == "http" {
|
||||||
|
tr.WithCacheInterceptor()
|
||||||
|
}
|
||||||
|
|
||||||
rp := &httputil.ReverseProxy{
|
rp := &httputil.ReverseProxy{
|
||||||
Director: func(req *http.Request) {
|
Director: func(req *http.Request) {
|
||||||
req.URL.Scheme = "http"
|
req.URL.Scheme = "http"
|
||||||
@ -90,6 +99,8 @@ func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
|
|||||||
req.Header.Set(k, v)
|
req.Header.Set(k, v)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Transport: tr,
|
||||||
}
|
}
|
||||||
|
|
||||||
p.s = &http.Server{
|
p.s = &http.Server{
|
||||||
|
@ -23,6 +23,8 @@ import (
|
|||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/plugin/interceptor"
|
||||||
|
"github.com/fatedier/frp/pkg/util/listener"
|
||||||
frpNet "github.com/fatedier/frp/pkg/util/net"
|
frpNet "github.com/fatedier/frp/pkg/util/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,7 +41,7 @@ type HTTPS2HTTPSPlugin struct {
|
|||||||
localAddr string
|
localAddr string
|
||||||
headers map[string]string
|
headers map[string]string
|
||||||
|
|
||||||
l *Listener
|
l *listener.Listener
|
||||||
s *http.Server
|
s *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +70,9 @@ func NewHTTPS2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
|||||||
return nil, fmt.Errorf("plugin_local_addr is required")
|
return nil, fmt.Errorf("plugin_local_addr is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
listener := NewProxyListener()
|
filter := params["stream_filter_type"]
|
||||||
|
|
||||||
|
listener := listener.NewProxyListener()
|
||||||
|
|
||||||
p := &HTTPS2HTTPSPlugin{
|
p := &HTTPS2HTTPSPlugin{
|
||||||
crtPath: crtPath,
|
crtPath: crtPath,
|
||||||
@ -79,8 +83,11 @@ func NewHTTPS2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
|||||||
l: listener,
|
l: listener,
|
||||||
}
|
}
|
||||||
|
|
||||||
tr := &http.Transport{
|
tr := interceptor.NewTransportWrapper(&http.Transport{
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
})
|
||||||
|
if filter == "http" {
|
||||||
|
tr.WithCacheInterceptor()
|
||||||
}
|
}
|
||||||
|
|
||||||
rp := &httputil.ReverseProxy{
|
rp := &httputil.ReverseProxy{
|
||||||
|
@ -18,9 +18,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/fatedier/golib/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Creators is used for create plugins to handle connections.
|
// Creators is used for create plugins to handle connections.
|
||||||
@ -49,44 +46,3 @@ type Plugin interface {
|
|||||||
Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte)
|
Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte)
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Listener struct {
|
|
||||||
conns chan net.Conn
|
|
||||||
closed bool
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProxyListener() *Listener {
|
|
||||||
return &Listener{
|
|
||||||
conns: make(chan net.Conn, 64),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) Accept() (net.Conn, error) {
|
|
||||||
conn, ok := <-l.conns
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("listener closed")
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) PutConn(conn net.Conn) error {
|
|
||||||
err := errors.PanicToError(func() {
|
|
||||||
l.conns <- conn
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) Close() error {
|
|
||||||
l.mu.Lock()
|
|
||||||
defer l.mu.Unlock()
|
|
||||||
if !l.closed {
|
|
||||||
close(l.conns)
|
|
||||||
l.closed = true
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) Addr() net.Addr {
|
|
||||||
return (*net.TCPAddr)(nil)
|
|
||||||
}
|
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/util/listener"
|
||||||
frpNet "github.com/fatedier/frp/pkg/util/net"
|
frpNet "github.com/fatedier/frp/pkg/util/net"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
@ -36,7 +37,7 @@ type StaticFilePlugin struct {
|
|||||||
httpUser string
|
httpUser string
|
||||||
httpPasswd string
|
httpPasswd string
|
||||||
|
|
||||||
l *Listener
|
l *listener.Listener
|
||||||
s *http.Server
|
s *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ func NewStaticFilePlugin(params map[string]string) (Plugin, error) {
|
|||||||
httpUser := params["plugin_http_user"]
|
httpUser := params["plugin_http_user"]
|
||||||
httpPasswd := params["plugin_http_passwd"]
|
httpPasswd := params["plugin_http_passwd"]
|
||||||
|
|
||||||
listener := NewProxyListener()
|
listener := listener.NewProxyListener()
|
||||||
|
|
||||||
sp := &StaticFilePlugin{
|
sp := &StaticFilePlugin{
|
||||||
localPath: localPath,
|
localPath: localPath,
|
||||||
|
83
pkg/plugin/filter/http_proxy_filter.go
Normal file
83
pkg/plugin/filter/http_proxy_filter.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package filter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/config"
|
||||||
|
"github.com/fatedier/frp/pkg/plugin/interceptor"
|
||||||
|
"github.com/fatedier/frp/pkg/util/listener"
|
||||||
|
frpNet "github.com/fatedier/frp/pkg/util/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
StreamFilterName = "StreamFilterName"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HTTPStreamFilter struct {
|
||||||
|
l *listener.Listener
|
||||||
|
s *http.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHTTPStreamFilter(filterConf config.LocalSvrConf, params map[string]string) (*HTTPStreamFilter, error) {
|
||||||
|
listener := listener.NewProxyListener()
|
||||||
|
|
||||||
|
p := &HTTPStreamFilter{
|
||||||
|
l: listener,
|
||||||
|
}
|
||||||
|
|
||||||
|
localAddr := fmt.Sprintf("%v:%v", filterConf.LocalIP, filterConf.LocalPort)
|
||||||
|
|
||||||
|
tr := interceptor.NewTransportWrapper(&http.Transport{
|
||||||
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
return net.Dial("tcp", localAddr)
|
||||||
|
},
|
||||||
|
ResponseHeaderTimeout: time.Duration(90) * time.Second,
|
||||||
|
ForceAttemptHTTP2: true,
|
||||||
|
MaxIdleConns: 100,
|
||||||
|
IdleConnTimeout: 90 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
})
|
||||||
|
tr.WithCacheInterceptor()
|
||||||
|
|
||||||
|
rp := &httputil.ReverseProxy{
|
||||||
|
Director: func(req *http.Request) {
|
||||||
|
req.URL.Scheme = "http"
|
||||||
|
req.URL.Host = localAddr
|
||||||
|
req.Host = localAddr
|
||||||
|
},
|
||||||
|
Transport: tr,
|
||||||
|
}
|
||||||
|
|
||||||
|
p.s = &http.Server{
|
||||||
|
Handler: rp,
|
||||||
|
}
|
||||||
|
|
||||||
|
go p.s.Serve(listener)
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *HTTPStreamFilter) Handle(conn io.ReadWriteCloser, realConn net.Conn) {
|
||||||
|
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
|
||||||
|
p.l.PutConn(wrapConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *HTTPStreamFilter) Name() string {
|
||||||
|
return StreamFilterName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *HTTPStreamFilter) Close() error {
|
||||||
|
if err := p.s.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
106
pkg/plugin/interceptor/cache_interceprot.go
Normal file
106
pkg/plugin/interceptor/cache_interceprot.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package interceptor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/util/cache"
|
||||||
|
"github.com/fatedier/frp/pkg/util/log"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultCacheDataInterceptor = NewCacheDataInterceptor(cache.DefaultCache)
|
||||||
|
)
|
||||||
|
|
||||||
|
type CacheDataInterceptor struct {
|
||||||
|
c cache.Cacher
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCacheDataInterceptor(c cache.Cacher) *CacheDataInterceptor {
|
||||||
|
return &CacheDataInterceptor{
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *CacheDataInterceptor) Intercept(req *http.Request, resp *http.Response, elapseMs int) {
|
||||||
|
var (
|
||||||
|
bodyReq []byte
|
||||||
|
bodyResp []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
uniqueId := uuid.NewString()
|
||||||
|
|
||||||
|
if req.Body != nil {
|
||||||
|
bodyReq, _ = ioutil.ReadAll(req.Body)
|
||||||
|
req.Body = ioutil.NopCloser(bytes.NewReader(bodyReq))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("get request url: %v, path: %v, host: %v, header: %v, body: %v", req.URL.String(), req.URL.Path, req.Host, req.Header, string(bodyReq))
|
||||||
|
rawReq := RawRequest{
|
||||||
|
Method: req.Method,
|
||||||
|
URL: req.URL.String(),
|
||||||
|
Host: req.Host,
|
||||||
|
Header: req.Header,
|
||||||
|
Body: bodyReq,
|
||||||
|
ContentLength: req.ContentLength,
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyResp, _ = ioutil.ReadAll(resp.Body)
|
||||||
|
resp.Body = ioutil.NopCloser(bytes.NewReader(bodyResp))
|
||||||
|
|
||||||
|
log.Debug("get response header: %v, body: %v, use: %v ms", resp.Header, string(bodyResp), elapseMs)
|
||||||
|
rawResp := RawResponse{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Header: resp.Header,
|
||||||
|
Body: bodyResp,
|
||||||
|
ContentLength: resp.ContentLength,
|
||||||
|
}
|
||||||
|
|
||||||
|
pair := Pair{
|
||||||
|
Key: uniqueId,
|
||||||
|
|
||||||
|
Req: rawReq,
|
||||||
|
Resp: rawResp,
|
||||||
|
|
||||||
|
TrafficCaptureTime: time.Now().Unix(),
|
||||||
|
|
||||||
|
ElapseMs: elapseMs,
|
||||||
|
}
|
||||||
|
|
||||||
|
pairByte, _ := json.Marshal(pair)
|
||||||
|
if len(pairByte) <= 1024*1024 {
|
||||||
|
// limit data size less than 1MB
|
||||||
|
cc.c.Add(uniqueId, pair)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RawRequest struct {
|
||||||
|
Method string `json:"method,omitempty"`
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
Host string `json:"host,omitempty"`
|
||||||
|
Header http.Header `json:"header,omitempty"`
|
||||||
|
Body []byte `json:"body,omitempty"`
|
||||||
|
ContentLength int64 `json:"content_length,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RawResponse struct {
|
||||||
|
StatusCode int `json:"status_code,omitempty"`
|
||||||
|
Header http.Header `json:"header,omitempty"`
|
||||||
|
Body []byte `json:"body,omitempty"`
|
||||||
|
ContentLength int64 `json:"content_length,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pair struct {
|
||||||
|
Key string `json:"key,omitempty"`
|
||||||
|
|
||||||
|
Req RawRequest `json:"req,omitempty"`
|
||||||
|
Resp RawResponse `json:"resp,omitempty"`
|
||||||
|
|
||||||
|
TrafficCaptureTime int64 `json:"traffic_capture_time,omitempty"`
|
||||||
|
|
||||||
|
ElapseMs int `json:"elapse_ms,omitempty"`
|
||||||
|
}
|
49
pkg/plugin/interceptor/transport.go
Normal file
49
pkg/plugin/interceptor/transport.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package interceptor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Interceptor func(req *http.Request, resp *http.Response, elapsMs int)
|
||||||
|
|
||||||
|
type TransportWrapper struct {
|
||||||
|
tr http.RoundTripper
|
||||||
|
interceptors []Interceptor
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTransportWrapper(tr http.RoundTripper, is ...Interceptor) *TransportWrapper {
|
||||||
|
tw := &TransportWrapper{
|
||||||
|
tr: tr,
|
||||||
|
}
|
||||||
|
tw.interceptors = append(tw.interceptors, is...)
|
||||||
|
|
||||||
|
return tw
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tw *TransportWrapper) WithInterceptors(i ...Interceptor) {
|
||||||
|
tw.interceptors = append(tw.interceptors, i...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tw *TransportWrapper) WithCacheInterceptor() {
|
||||||
|
tw.WithInterceptors(defaultCacheDataInterceptor.Intercept)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tw *TransportWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
resp, err := tw.tr.RoundTrip(req)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
elapseMs := time.Since(startTime).Milliseconds()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for _, op := range tw.interceptors {
|
||||||
|
op(req, resp, int(elapseMs))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
26
pkg/util/cache/cache.go
vendored
Normal file
26
pkg/util/cache/cache.go
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cacher interface {
|
||||||
|
Add(key, value interface{}) (evicted bool)
|
||||||
|
Contains(key interface{}) bool
|
||||||
|
Get(key interface{}) (value interface{}, ok bool)
|
||||||
|
Keys() []interface{}
|
||||||
|
Len() int
|
||||||
|
Purge()
|
||||||
|
Remove(key interface{}) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type lruCache struct {
|
||||||
|
*lru.Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCache(size int) Cacher {
|
||||||
|
l, _ := lru.New(size)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultCache = NewCache(1024)
|
50
pkg/util/listener/listener.go
Normal file
50
pkg/util/listener/listener.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package listener
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fatedier/golib/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Listener struct {
|
||||||
|
conns chan net.Conn
|
||||||
|
closed bool
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxyListener() *Listener {
|
||||||
|
return &Listener{
|
||||||
|
conns: make(chan net.Conn, 64),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Accept() (net.Conn, error) {
|
||||||
|
conn, ok := <-l.conns
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("listener closed")
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) PutConn(conn net.Conn) error {
|
||||||
|
err := errors.PanicToError(func() {
|
||||||
|
l.conns <- conn
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Close() error {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
if !l.closed {
|
||||||
|
close(l.conns)
|
||||||
|
l.closed = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Addr() net.Addr {
|
||||||
|
return (*net.TCPAddr)(nil)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user