Goproxy是一个实现HTTP代理的Go包。我使用它来存储来自客户端的请求(例如: web浏览器),以供进一步处理。
在http.Request struct中,Body是io.ReadCloser,只能读取一次。
我面临的问题是,我需要读取主体来存储它,但仍然可以让goproxy访问它,这样就可以将它发送到服务器。
我用ioutil.ReadAll读取主体,然后用我得到的字节片创建一个ReadCloser来解决这个问题。然后,我将这个ReadCloser分配给请求Body。
package main
import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"regexp"
"sync"
"github.com/elazarl/goproxy"
)
var requests map[string][]Request
var mutex sync.Mutex
type Request struct {
Method string
URL url.URL
Proto string // "HTTP/1.0"
ProtoMajor int // 1
ProtoMinor int // 0
Header http.Header
Body []byte
ContentLength int64
Host string
PostForm url.Values
RequestURI string
}
type RequestBody struct {
*bytes.Reader
}
func (r RequestBody) Close() error {
return nil
}
func storeRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
// only print error for now
log.Println(err)
return r, nil
}
r.Body.Close()
r.Body = RequestBody{bytes.NewReader(body)}
fmt.Println(string(body))
mutex.Lock()
requests[r.URL.Host] = append(requests[r.URL.Host], Request{
Method: r.Method,
URL: *r.URL,
Proto: r.Proto,
ProtoMajor: r.ProtoMajor,
ProtoMinor: r.ProtoMinor,
Header: r.Header,
Body: body,
ContentLength: r.ContentLength,
Host: r.Host,
PostForm: r.PostForm,
RequestURI: r.RequestURI,
})
mutex.Unlock()
return r, nil
}
func main() {
proxy := goproxy.NewProxyHttpServer()
proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))).
HandleConnect(goproxy.AlwaysMitm)
proxy.OnRequest().DoFunc(storeRequest)
requests = make(map[string][]Request)
addr := flag.String("addr", ":8080", "proxy listen address")
flag.Parse()
log.Fatal(http.ListenAndServe(*addr, proxy))
}我想知道这是否解决这个问题的正确方法。
发布于 2019-12-20 10:11:37
阅读并替换它是很好的,不过请参阅ioutil.NopCloser。
然而,
这是我建议的修订版,
package main
import (
"bytes"
"flag"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"regexp"
"sync"
"github.com/elazarl/goproxy"
)
type store struct {
requests map[string][]http.Request
sync.Mutex
}
func (s *store) Add(r *http.Request) {
var c http.Request
c.Method = r.Method
c.Proto = r.Proto
c.ProtoMajor = r.ProtoMajor
c.ProtoMinor = r.ProtoMinor
c.Host = r.Host
c.RequestURI = r.RequestURI
c.ContentLength = r.ContentLength
c.URL = r.URL
c.Header = http.Header{}
for k, vv := range r.Header {
for _, v := range vv {
c.Header.Set(k, v)
}
}
c.PostForm = url.Values{}
for k, vv := range r.PostForm {
for _, v := range vv {
c.PostForm.Set(k, v)
}
}
c.Form = url.Values{}
for k, vv := range r.Form {
for _, v := range vv {
c.Form.Set(k, v)
}
}
var b bytes.Buffer
io.Copy(&b, r.Body)
r.Body.Close()
buf := b.Bytes()
var b1 bytes.Buffer
b1.Write(buf)
r.Body = ioutil.NopCloser(&b1)
c.Body = ioutil.NopCloser(bytes.NewBuffer(buf))
key := r.URL.Host
s.Lock()
s.requests[key] = append(s.requests[key], c)
s.Unlock()
}
type storeHandler struct {
requests *store
}
func (s storeHandler) Handle(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
s.requests.Add(r)
return r, nil
}
func main() {
h := storeHandler{requests: &store{requests: map[string][]http.Request{}}}
proxy := goproxy.NewProxyHttpServer()
proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))).
HandleConnect(goproxy.AlwaysMitm)
proxy.OnRequest().DoFunc(h.Handle)
addr := flag.String("addr", ":8080", "proxy listen address")
flag.Parse()
log.Fatal(http.ListenAndServe(*addr, proxy))
}发布于 2018-06-27 10:15:33
你问题中最难的部分是:
是否允许更改
Body结构的http.Request字段值?
http.Request文档的一部分:
// Body is the request's body.
//
// For client requests a nil body means the request has no
// body, such as a GET request. The HTTP Client's Transport
// is responsible for calling the Close method.
//
// For server requests the Request Body is always non-nil
// but will return EOF immediately when no body is present.
// The Server will close the request body. The ServeHTTP
// Handler does not need to.
Body io.ReadCloser文件上什么都没说。因此,我们需要在这方面找到一些最高级别的政策。据我所知,Go文档中没有人。
Body字段类型是io.ReadCloser接口,实际的结构类型隐藏在它后面。
Close方法结合使用,您可以查看当前的net/http资料来源以找到答案,但在下一个Go发行版中可能会发生变化。
因此,我想您最好的选择是询问Go社区,甚至建议对Body文档进行修改,这样就可以解决问题了。
另一种方法是包装原始Body并与请求处理程序一起读取它。
https://codereview.stackexchange.com/questions/148002
复制相似问题