问题
因为目前写的工具中,采用了Resty作为http 客户端,在调试插件的过程中,需要将request
包以及response
保存并且打印出来,方便后续对攻击请求的包进行分析。
Resty内部有一个方法简单看了下,应该是可以满足此需求的,方法名为,存在与
/github.com/go-resty/resty/[email protected]/client.go:898 outputLogTo ()
实现
但是由于是内部方法,根本无法调用,所以采取了自定义RoundTripper
的方式对请求的发送包以及响应包进行记录,简易代码如下:
/*
Copyright © 2020 sky [email protected]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"crypto/tls"
"encoding/base64"
"fmt"
"github.com/go-resty/resty/v2"
"net/http"
"net/http/httputil"
"strings"
"sync"
"time"
)
func main() {
var m sync.Map
r, err := resty.New().
SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
SetTimeout(15 * time.Second).
SetTransport(newRoundTripper(&m, nil)).
SetRedirectPolicy(resty.FlexibleRedirectPolicy(0)).
R().Get("http://03sec.com")
if err != nil && !strings.Contains(err.Error(), "stopped after ") {
fmt.Println(err)
}
reqMap, reqStatus := m.Load(r.RawResponse.Request.URL.String() + "_REQ")
if reqStatus {
ReqStr, _ := base64.StdEncoding.DecodeString(reqMap.(string))
fmt.Println(string(ReqStr))
}
respMap, respStatus := m.Load(r.RawResponse.Request.URL.String() + "_RESP")
if respStatus {
RespStr, _ := base64.StdEncoding.DecodeString(respMap.(string))
fmt.Println(string(RespStr))
}
}
// NewRoundTripper returns new RoundTripper instance for logging http request and response
func newRoundTripper(m *sync.Map, parent http.RoundTripper) http.RoundTripper {
return &rrLogTransport{
writer: m,
parent: parent,
}
}
type rrLogTransport struct {
writer *sync.Map
parent http.RoundTripper
}
func (lt *rrLogTransport) parentTransport() http.RoundTripper {
if lt.parent == nil {
return http.DefaultTransport
}
return lt.parent
}
func (lt *rrLogTransport) CancelRequest(req *http.Request) {
type canceler interface {
CancelRequest(*http.Request)
}
if cr, ok := lt.parentTransport().(canceler); ok {
cr.CancelRequest(req)
}
}
func (lt *rrLogTransport) RoundTrip(req *http.Request) (*http.Response, error) {
reqDump, _ := httputil.DumpRequest(req, true)
lt.writer.Store(req.URL.String()+"_REQ", base64.StdEncoding.EncodeToString(reqDump))
resp, _ := lt.parentTransport().RoundTrip(req)
respDump, err := httputil.DumpResponse(resp, true)
lt.writer.Store(req.URL.String()+"_RESP", base64.StdEncoding.EncodeToString(respDump))
return resp, err
}
其主要核心就是RoundTrip
方法,在这其中,使用httputil
包下的DumpRequest
方法以及DumpResponse
对请求和响应进行了记录,并且使用了其当前的URL地址作为Key进行保存,在后续需要的时候使用URL
将其取出来即可。