当前位置: 首页 > 知识库问答 >
问题:

ReverseProxy取决于请求。戈朗的尸体

金健
2023-03-14

我想构建一个http反向代理,它检查http主体,然后向其上游服务器发送http请求。你怎么能在围棋中做到这一点?

初始尝试(随后)失败,因为ReverseProxy复制传入请求,修改并发送请求,但正文已被读取。

func main() {
    backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        b, err := ioutil.ReadAll(r.Body)
        if err != nil {
            http.Error(w, fmt.Sprintf("ioutil.ReadAll: %s", err), 500)
            return
        }
        // expecting to see hoge=fuga
        fmt.Fprintf(w, "this call was relayed by the reverse proxy, body: %s", string(b))
    }))
    defer backendServer.Close()

    rpURL, err := url.Parse(backendServer.URL)
    if err != nil {
        log.Fatal(err)
    }

    proxy := func(u *url.URL) http.Handler {
        p := httputil.NewSingleHostReverseProxy(u)
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            if err := r.ParseForm(); err != nil {
                http.Error(w, fmt.Sprintf("ParseForm: %s", err), 500)
                return
            }
            p.ServeHTTP(w, r)
        })
    }(rpURL)
    frontendProxy := httptest.NewServer(proxy)
    defer frontendProxy.Close()

    resp, err := http.Post(frontendProxy.URL, "application/x-www-form-urlencoded", bytes.NewBufferString("hoge=fuga"))
    if err != nil {
        log.Fatalf("http.Post: %s", err)
    }

    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatalf("ioutil.ReadAll: %s", err)
    }

    fmt.Printf("%s", b)
}
// shows: "http: proxy error: http: ContentLength=9 with Body length 0"

然后,我的下一个尝试是将整个正文读入字节。读卡器,并使用它检查正文内容,在发送到上游服务器之前查找开头部分。但是接下来我必须重新实现我想要避免的ReverseProxy。还有其他优雅的方式吗?

共有2个答案

祁修平
2023-03-14

编辑:

如上所述,在这种情况下,解析的表单将为空。您需要从主体中手动解析表单。

请求。Body是一个io。ReaderCloser,因为它描述了tcp连接的rx部分。但是在您的用例中,您需要阅读所有内容,因为您正在将主体解析为表单。这里的技巧是使用io重新分配r.Body。ReaderCloser从已读数据派生的对象。下面是我要做的:

  // before calling r.ParseForm(), get the body
  // as a byte slice
  body, err := ioutil.ReadAll(r.Body)
  // after calling r.ParseForm(), reassign body
  r.Body = ioutil.NopCloser(bytes.NewBuffer(body))

字节。NewBuffer(body)将主体字节片转换为io。Readerioutil。NopCler转换一个io。读取io。带有nop方法的ReaderCloser方法。

  package main

  import "net/http"
  import "net/http/httputil"
  import "net/url"
  import "net/http/httptest"
  import "fmt"
  import "log"
  import "bytes"
  import "io/ioutil"

  func main() {
    backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        b, err := ioutil.ReadAll(r.Body)
        if err != nil {
            http.Error(w, fmt.Sprintf("ioutil.ReadAll: %s", err), 500)
            return
        }
        // expecting to see hoge=fuga
        fmt.Fprintf(w, "this call was relayed by the reverse proxy, body: %s", string(b))
    }))
    defer backendServer.Close()

    rpURL, err := url.Parse(backendServer.URL)
    if err != nil {
        log.Fatal(err)
    }

    proxy := func(u *url.URL) http.Handler {
        p := httputil.NewSingleHostReverseProxy(u)
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // read out body into a slice
            body, err := ioutil.ReadAll(r.Body)
            if err != nil {
                http.Error(w, fmt.Sprintf("Error reading body: %s", err), 500)
                return
            }

            // inspect current body here
            if err := r.ParseForm(); err != nil {
                http.Error(w, fmt.Sprintf("ParseForm: %s", err), 500)
                return
            }

            // assign a new body with previous byte slice
            r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
            p.ServeHTTP(w, r)
        })
    }(rpURL)
    frontendProxy := httptest.NewServer(proxy)
    defer frontendProxy.Close()

    resp, err := http.Post(
        frontendProxy.URL,
        "application/x-www-form-urlencoded",
        bytes.NewBufferString("hoge=fuga"))
    if err != nil {
        log.Fatalf("http.Post: %s", err)
    }

    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatalf("ioutil.ReadAll: %s", err)
    }

    fmt.Printf("%s", b)
  }

富念
2023-03-14

您可以将导演处理程序设置为http putil。ReverseProxy文档:https://golang.org/pkg/net/http/httputil/#ReverseProxy

下面是一个示例代码,它从请求中读取内容正文,并从localhost:8080localhost:3333

package main

import (
    "bytes"
    "io/ioutil"
    "log"
    "net/http"
    "net/http/httputil"
)

func main() {
    director := func(req *http.Request) {
        if req.Body != nil {
            // read all bytes from content body and create new stream using it.
            bodyBytes, _ := ioutil.ReadAll(req.Body)
            req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

            // create new request for parsing the body
            req2, _ := http.NewRequest(req.Method, req.URL.String(), bytes.NewReader(bodyBytes))
            req2.Header = req.Header
            req2.ParseForm()
            log.Println(req2.Form)
        }

        req.URL.Host = "localhost:3333"
        req.URL.Scheme = "http"
    }
    proxy := &httputil.ReverseProxy{Director: director}
    log.Fatalln(http.ListenAndServe(":8080", proxy))
}
 类似资料:
  • 问题内容: 我正在将算法从C移植到Go。我有些困惑。这是C函数: 在for循环中,将值s分配给元素cd数组x。这怎么可能?据我所知,长双精度数是float64(在Go上下文中)。因此,我不应该编译C代码,因为我正在向只包含uint64元素的数组分配一个long double。但是C代码可以正常工作。 那么有人可以解释为什么这行得通吗? 非常感谢你。 更新: 该函数的原始C代码可以在以下位置找到:h

  • 有什么方法可以重写panic功能来使用我们的记录器吗?

  • 用围棋的语言, 是一个字符串数组 我们还使用作为参数。 有什么区别? 功能定义: 我可以像下面那样调用这个函数吗?

  • 问题内容: Go语言规范的“地图类型”部分描述了地图类型的界面和常规用法,The Go Blog上 的“ Go maps in action”一文中 偶然提到了哈希表和“快速查找,添加和删除”。 的电流源代码描述了其作为散列表实施方案(其通常摊销); 但是,在语言规范或其他材料中我看不到任何性能特征(例如Big O性能)的保证。 go语言是否为地图类型或仅 接口 保证了任何 性能 保证(例如,恒定

  • 问题内容: 我正在使用Java 7和。我想读取一个请求(例如),但如果它大于我正在使用的大小,则我正在努力想出一个很好的解决方案来读取完整的请求。例如,如果是4048字节,并且HTTP POST包含大于4kB的图像。 有没有很好的递归解决方案或循环呢? 这是我的阅读请求代码: 在我写过的地方: 它看起来像是代码重用的好地方,但是我无法提出一个好的解决方案。有什么办法可以在这里重用Completio