首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在http.Requests (政府代理)中重用主体

在http.Requests (政府代理)中重用主体
EN

Code Review用户
提问于 2016-11-24 15:11:42
回答 2查看 2.5K关注 0票数 5

Goproxy是一个实现HTTP代理的Go包。我使用它来存储来自客户端的请求(例如: web浏览器),以供进一步处理。

http.Request struct中,Bodyio.ReadCloser,只能读取一次。

我面临的问题是,我需要读取主体来存储它,但仍然可以让goproxy访问它,这样就可以将它发送到服务器。

我用ioutil.ReadAll读取主体,然后用我得到的字节片创建一个ReadCloser来解决这个问题。然后,我将这个ReadCloser分配给请求Body

代码语言:javascript
复制
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))
}

我想知道这是否解决这个问题的正确方法。

EN

回答 2

Code Review用户

发布于 2019-12-20 10:11:37

阅读并替换它是很好的,不过请参阅ioutil.NopCloser

然而,

  • 复制http.Request类型是没有意义的。我们用它代替。如果您需要更精细的控制,嵌入它。
  • 在传递切片、地图和指针时,要小心制作真正的副本。
  • 代码缺乏结构化因式分解,使用类型定义

这是我建议的修订版,

代码语言:javascript
复制
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))
}
票数 1
EN

Code Review用户

发布于 2018-06-27 10:15:33

你问题中最难的部分是:

是否允许更改Body结构的http.Request字段值?

http.Request文档的一部分:

代码语言:javascript
复制
// 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方法结合使用,
  • 隐藏的结构可能是某个可重用池的一部分,并且会丢失,
  • HTTP拖车标头可能在EOF的后面。

您可以查看当前的net/http资料来源以找到答案,但在下一个Go发行版中可能会发生变化。

因此,我想您最好的选择是询问Go社区,甚至建议对Body文档进行修改,这样就可以解决问题了。

另一种方法是包装原始Body并与请求处理程序一起读取它。

票数 0
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/148002

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档