如何读取NewSingleHostReverseProxy的响应

5
我已经创建了一个反向代理,类似于这样:

NewSingleHostReverseProxy

func ProxyFunc(w http.ResponseWriter, r *http.Request) {
    u, err := url.Parse("http://mynetworkserverhere:8957/some/path/here/")
    if err == nil {
        proxy := httputil.NewSingleHostReverseProxy(u)
        proxy.ServeHTTP(w, r)
        //????

    } else {
        w.Write([]byte(err.Error()))
    }
}

并从主函数中调用它:

func main() {
   log.Printf("Starting...")
   http.HandleFunc("/", ProxyFunc)
   log.Fatal(http.ListenAndServe(":6060", nil))
}

客户端可以正常工作,但我想从代理读取响应,我该怎么做?


你尝试过劫持连接吗?或者这个层次太低了? - Volker
你的意思是你想在你的Go服务器中读取代理的响应? - Daniel Williams
我正在执行操作,我想要的是读取代理的响应并将其保存在缓存中... :P - OscarRyz
1个回答

9

net/http包非常灵活。您可以使用自己的默认传输替换ReverseProxy的传输,该传输仅使用DefaultTransport。然后,您可以拦截RoundTrip调用并执行您想要的任何操作。

package main

import (
    "log"
    "net/http"
    "net/http/httputil"
    "net/url"
)

func main() {
    log.Printf("Starting...")
    http.HandleFunc("/", someFunc)
    log.Fatal(http.ListenAndServe(":6060", nil))
}

func someFunc(w http.ResponseWriter, r *http.Request) {

    // change the request host to match the target
    r.Host = "you.host.here:port"
    u, _ := url.Parse("http://you.host.here:port/some/path/")
    proxy := httputil.NewSingleHostReverseProxy(u)
    // You can optionally capture/wrap the transport if that's necessary (for
    // instance, if the transport has been replaced by middleware). Example:
    // proxy.Transport = &myTransport{proxy.Transport}
    proxy.Transport = &myTransport{}

    proxy.ServeHTTP(w, r)
}

type myTransport struct {
    // Uncomment this if you want to capture the transport
    // CapturedTransport http.RoundTripper
}

func (t *myTransport) RoundTrip(request *http.Request) (*http.Response, error) {
    response, err := http.DefaultTransport.RoundTrip(request)
    // or, if you captured the transport
    // response, err := t.CapturedTransport.RoundTrip(request)

    // The httputil package provides a DumpResponse() func that will copy the
    // contents of the body into a []byte and return it. It also wraps it in an
    // ioutil.NopCloser and sets up the response to be passed on to the client.
    body, err := httputil.DumpResponse(response, true)
    if err != nil {
        // copying the response body did not work
        return nil, err
    }

    // You may want to check the Content-Type header to decide how to deal with
    // the body. In this case, we're assuming it's text.
    log.Print(string(body))

    return response, err
}

它没有起作用。如果我消耗了主体,我的最终客户将一无所获。如果我注释掉ReadAll调用,它就可以工作(但我得不到输出)。请参见:http://play.golang.org/p/pu_hCabebY - OscarRyz
这是一个很好的观点。我修改了我的答案,从body字节中创建了一个新的缓冲区,并将其包装在NopCloser中(以便它实现ReadCloser接口),然后设置响应的Body属性。可能有更好的方法,但我认为这并不差。 - Tyson
2
请查看golang.org/pkg/net/http/httputil/#DumpResponse。它可以完成您所需的一切,或者提供如何复制响应的完整示例。 - deft_code
@deft_code 不错,好主意。看起来他们做了和我一样的事情:将其写入缓冲区,使用 NopCloser,并设置 Body 属性。 - Tyson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接