我不知道为什么这个反向代理不起作用。我已经看过几个例子,我发现它没有任何问题。
package main
import (
"log"
"net/url"
"net/http"
"net/http/httputil"
)
func report(r *http.Request){
log.Print("URL: " + r.URL.Path)
log.Print("Scheme: " + r.URL.Scheme)
log.Print("Host: " + r.URL.Host)
//r.URL.Scheme = "http"
//r.URL.Host = "stackoverflow.com"
//r.Header.Set("Host", "stackoverflow.com")
//log.Print("Header Host: " + r.Header.Get("Host"))
}
func main() {
proxy := httputil.NewSingleHostReverseProxy( &url.URL{Scheme:"http",Host:"myrealserver.com"})
proxy.Director = report
// http.Handle("/", proxy)
error := http.ListenAndServe("mylocalhost.com:8080", proxy)
if(error != nil) {
log.Fatal(error)
}
}
记录:
2014/04/18 21:32:50 URL: /arg/es
2014/04/18 21:32:50 Scheme:
2014/04/18 21:32:50 Host:
2014/04/18 21:32:50 http: proxy error: unsupported protocol scheme ""
2014/04/18 21:32:51 URL: /favicon.ico
2014/04/18 21:32:51 Scheme:
2014/04/18 21:32:51 Host:
2014/04/18 21:32:51 http: proxy error: unsupported protocol scheme ""
如果我取消注释重新定义Schema的行,则错误消息变为:
2014/04/18 21:38:05 http: proxy error: http: no Host in request URL
如果我取消注释重新定义主机的行,那么目标服务器将变为stackoverflow.com(我的意思是,它永远不会使用" myrealserver.com")。
如果我要求mylocalhost.com:8080/somepath(甚至/),那么无论stackoverflow.com/somepath是否存在,我都会从Stackoverflow获得404。它说:
Couldn't find mylocalhost.com:8080
The Q&A site mylocalhost.com:8080 doesn't seem to exist... yet
它不会自动翻译主机标头。
如果那时我取消注释设置的行(以及打印的另一行)Header" Host"。然后我就可以阅读" stackoverflow.com"在日志中,但我仍然得到我正在尝试访问的相同的404页面报告" mylocalhost.com"。
我正在使用go1.2.1 linux/amd64
我应该如何让程序作为代理工作?
答案 0 :(得分:10)
感谢Golang-nuts的Alex,我现在有了答案。
这就是Alex所说的:
只需要在Director中设置http.Request.Host [和scheme]即可 工作:http://play.golang.org/p/I17ZSM6LQb
如果您阅读了SingleHostReverseProxy的来源 (http://golang.org/src/pkg/net/http/httputil/reverseproxy.go#L61),它 设置自己的主管,你要重写。所以你需要 重新实现它已经做的以及额外的主机更改。
无论如何,这并没有解决问题的de Header部分:目标服务器仍在接收" localhost:8080"作为HTTP主机名,所以我没有ReverseProxy包,只使用http和RoundTripper,以及复制所有标题的辅助函数:
package main
import (
"flag"
"fmt"
"os"
"log"
"net/http"
"io/ioutil"
)
var target *string
func main() {
target = flag.String("target", "http://stackoverflow.com", "target URL for reverse proxy")
flag.Parse()
http.HandleFunc("/", report)
log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))
}
func report(w http.ResponseWriter, r *http.Request){
uri := *target+r.RequestURI
fmt.Println(r.Method + ": " + uri)
if r.Method == "POST" {
body, err := ioutil.ReadAll(r.Body)
fatal(err)
fmt.Printf("Body: %v\n", string(body));
}
rr, err := http.NewRequest(r.Method, uri, r.Body)
fatal(err)
copyHeader(r.Header, &rr.Header)
// Create a client and query the target
var transport http.Transport
resp, err := transport.RoundTrip(rr)
fatal(err)
fmt.Printf("Resp-Headers: %v\n", resp.Header);
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fatal(err)
dH := w.Header()
copyHeader(resp.Header, &dH)
dH.Add("Requested-Host", rr.Host)
w.Write(body)
}
func fatal(err error) {
if err != nil {
log.Fatal(err)
os.Exit(1)
}
}
func copyHeader(source http.Header, dest *http.Header){
for n, v := range source {
for _, vv := range v {
dest.Add(n, vv)
}
}
}
现在,我能够看到StackOverflow或其他任何网站应该如何。
不过,我仍然在进行POST调用,所以这是一项正在进行中的工作。
答案 1 :(得分:1)
派对有点晚了,但是ReverseProxy
并没有被打破,只是有点混乱,因为它没有达到预期的效果(至少,我希望它能像你一样工作做了,所以这让我们两个人。)
来自the docs:
// For server requests Host specifies the host on which the // URL is sought. Per RFC 2616, this is either the value of // the "Host" header or the host name given in the URL itself. // It may be of the form "host:port". For international domain // names, Host may be in Punycode or Unicode form. Use // golang.org/x/net/idna to convert it to either format if // needed. // // For client requests Host optionally overrides the Host // header to send. If empty, the Request.Write method uses // the value of URL.Host. Host may contain an international // domain name. Host string
由于引擎ReverseProxy
正在使用此Request
发出客户端请求(在ReverseProxy.Director
可选地修改它之后),如果Host
已设置,它将覆盖Host
标题。这将始终设置,因为doc注释的第一部分指出“对于服务器请求,主机指定寻求URL的主机”。
因此,除了Sebastián的回答,您还需要设置req.Host
。例如,代理example.com
:
proxy := ReverseProxy{
Director: func(req *http.Request) {
req.URL.Scheme = "http"
req.URL.Host = "example.com"
req.Host = "example.com"
}
}
或者,您可以将req.Host
设置为""
,并使用req.URL.Host
的值。