Golang: how to read response body of ReverseProxy?

前端 未结 4 692
一整个雨季
一整个雨季 2021-01-31 05:13
package main

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

func main() {
    target := &url.URL{Scheme: \"http\", Host: \"www.google.com\"}         


        
相关标签:
4条回答
  • 2021-01-31 05:18

    From source code httptest.ResponseRecorder is use for get the response from the handler

    func TestModifyResponseClosesBody(t *testing.T) {
        req, _ := http.NewRequest("GET", "http://foo.tld/", nil)
        req.RemoteAddr = "1.2.3.4:56789"
        closeCheck := new(checkCloser)
        logBuf := new(bytes.Buffer)
        outErr := errors.New("ModifyResponse error")
        rp := &ReverseProxy{
            Director: func(req *http.Request) {},
            Transport: &staticTransport{&http.Response{
                StatusCode: 200,
                Body:       closeCheck,
            }},
            ErrorLog: log.New(logBuf, "", 0),
            ModifyResponse: func(*http.Response) error {
                return outErr
            },
        }
        rec := httptest.NewRecorder()
        rp.ServeHTTP(rec, req)
        res := rec.Result()
        if g, e := res.StatusCode, http.StatusBadGateway; g != e {
            t.Errorf("got res.StatusCode %d; expected %d", g, e)
        }
        if !closeCheck.closed {
            t.Errorf("body should have been closed")
        }
        if g, e := logBuf.String(), outErr.Error(); !strings.Contains(g, e) {
            t.Errorf("ErrorLog %q does not contain %q", g, e)
        }
    }
    
    0 讨论(0)
  • 2021-01-31 05:19

    httputil.ReverseProxy has a Transport field. You can use it to modify the response. For example:

    type transport struct {
        http.RoundTripper
    }
    
    func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
        resp, err = t.RoundTripper.RoundTrip(req)
        if err != nil {
            return nil, err
        }
        b, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            return nil, err
        }
        err = resp.Body.Close()
        if err != nil {
            return nil, err
        }
        b = bytes.Replace(b, []byte("server"), []byte("schmerver"), -1)
        body := ioutil.NopCloser(bytes.NewReader(b))
        resp.Body = body
        resp.ContentLength = int64(len(b))
        resp.Header.Set("Content-Length", strconv.Itoa(len(b)))
        return resp, nil
    }
    
    // ...
    proxy := httputil.NewSingleHostReverseProxy(target)
    proxy.Transport = &transport{http.DefaultTransport}
    

    Playground example of the whole thing: http://play.golang.org/p/b0S5CbCMrI.

    0 讨论(0)
  • 2021-01-31 05:19

    now httputil/reverseproxy, support than, see source

     type ReverseProxy struct {
            ...
    
            // ModifyResponse is an optional function that
            // modifies the Response from the backend
            // If it returns an error, the proxy returns a StatusBadGateway error.
            ModifyResponse func(*http.Response) error
        }
    
    
    
    func rewriteBody(resp *http.Response) (err error) {
        b, err := ioutil.ReadAll(resp.Body) //Read html
        if err != nil {
            return  err
        }
        err = resp.Body.Close()
        if err != nil {
            return err
        }
        b = bytes.Replace(b, []byte("server"), []byte("schmerver"), -1) // replace html
        body := ioutil.NopCloser(bytes.NewReader(b))
        resp.Body = body
        resp.ContentLength = int64(len(b))
        resp.Header.Set("Content-Length", strconv.Itoa(len(b)))
        return nil
    }
    
    // ...
    target, _ := url.Parse("http://example.com")
    proxy := httputil.NewSingleHostReverseProxy(target)
    proxy.ModifyResponse = rewriteBody
    
    0 讨论(0)
  • 2021-01-31 05:23

    I don't know best solution. But you can do something like this:

    package main
    
    import (
        "fmt"
        "net/http"
        "net/http/httputil"
        "net/url"
    )
    
    func main() {
        target := &url.URL{Scheme: "http", Host: "www.google.com"}
        proxy := httputil.NewSingleHostReverseProxy(target)
    
        http.Handle("/google", CustomHandler(proxy))
        http.ListenAndServe(":8099", nil)
    }
    
    func CustomHandler(h http.Handler) http.HandlerFunc {
        return func(res http.ResponseWriter, req *http.Request) {
            h.ServeHTTP(NewCustomWriter(res), req)
        }
    }
    
    type customWriter struct {
        http.ResponseWriter
    }
    
    func NewCustomWriter(w http.ResponseWriter) *customWriter {
        return &customWriter{w}
    }
    
    func (c *customWriter) Header() http.Header {
        return c.ResponseWriter.Header()
    }
    
    func (c *customWriter) Write(data []byte) (int, error) {
        fmt.Println(string(data)) //get response here
        return c.ResponseWriter.Write(data)
    }
    
    func (c *customWriter) WriteHeader(i int) {
        c.ResponseWriter.WriteHeader(i)
    }
    
    0 讨论(0)
提交回复
热议问题