我建立了一个小的反向代理,将请求转发到同一台计算机上的另一个端口,并将结果传递回调用方。
就我而言,服务的结果是一个包含链接的简单html页面。由于反向代理在与服务不同的端口上运行,因此我必须调整链接。我尝试使用Transport的自定义实现来做到这一点:
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
}
// This works
b = bytes.Replace(b, []byte("http://localhost:5000"), []byte("http://localhost:8080"), -1)
// This doesn't
b = bytes.Replace(b, []byte("api"), []byte("auth-api"), -1)
resp.Body = ioutil.NopCloser(bytes.NewReader(b))
resp.ContentLength = int64(len(b))
return resp, nil
}
这是我在请求处理程序中初始化反向代理的方式:
proxy := httputil.NewSingleHostReverseProxy(target)
proxy.Transport = &transport{http.DefaultTransport}
proxy.ServeHTTP(res, req)
我的代码中的第一个替换有效。但是秒数会导致客户端再调用几次(通过多次击中断点来识别),但没有结果:
curl: (52) Empty reply from server
如果我取消评论
b = bytes.Replace(b, []byte("api"), []byte("auth-api"), -1)
一切正常,主机名将按预期替换。我尝试调试非常努力,但无法找出问题所在。如果在替换完成后将正文保存到文件中,那么一切似乎都很好,并且前缀也得到了交换。它只是无法通过响应传递它。
绝对不是我的代码有问题。我创建了一个最小的工作示例,该示例是完全独立的,可以由您中的任何人进行测试。如果您在第37行(第二个替换)中注释,则端口8080(反向代理)将不再响应。
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"net/url"
)
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
}
// This works
b = bytes.Replace(b, []byte("localhost:5000"), []byte("localhost:8080"), -1)
// This doesn't
// b = bytes.Replace(b, []byte("api"), []byte("auth-api"), -1)
reader := bytes.NewReader(b)
closer := ioutil.NopCloser(reader)
body := closer
resp.Body = body
resp.ContentLength = int64(len(b))
return resp, nil
}
func main() {
infoPageServer := http.NewServeMux()
infoPageServer.HandleFunc("/", serveHTML)
reverseProxy := http.NewServeMux()
reverseProxy.HandleFunc("/", serveProxy)
// Running Info Page Server
go func() {
log.Println("Starting Info Page service on port 5000")
http.ListenAndServe(":5000", infoPageServer)
}()
// Running Reverse Proxy Server
log.Println("Starting Reverse Proxy service on port 8080")
http.ListenAndServe(":8080", reverseProxy)
}
func serveHTML(resp http.ResponseWriter, req *http.Request) {
const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Info Page</title>
</head>
<body>
<pre>http://localhost:5000/api/</pre>
</body>
</html>`
fmt.Fprint(resp, html)
}
func serveProxy(resp http.ResponseWriter, req *http.Request) {
target, err := url.Parse("http://localhost:5000")
if err != nil {
panic(err)
}
proxy := httputil.NewSingleHostReverseProxy(target)
proxy.Transport = &transport{http.DefaultTransport}
proxy.ServeHTTP(resp, req)
}