golang ReverseProxy无法正常工作

Seb*_*oli 10 proxy go

我不知道为什么这个反向代理不起作用.我见过几个例子,我发现它没有任何问题.

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

它记录:

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 ""
Run Code Online (Sandbox Code Playgroud)

如果我取消注释重新定义Schema的行,则错误消息变为:

2014/04/18 21:38:05 http: proxy error: http: no Host in request URL
Run Code Online (Sandbox Code Playgroud)

如果我取消注释重新定义主机的行,那么目标服务器将变为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
Run Code Online (Sandbox Code Playgroud)

它不会自动翻译主机头.

如果那时我取消注释设置(和另一个打印)标题"主机"的行.然后我可以在日志中读取"stackoverflow.com",但我仍然得到相同的404页面报告,我试图访问"mylocalhost.com".

我正在使用 go1.2.1 linux/amd64

我应该如何让程序作为代理工作呢?

Seb*_*oli 11

感谢来自Golang-nuts的Alex,我现在有了答案.

亚历克斯说这是:

只需要在Director中设置http.Request.Host [和scheme]即可实现:http://play.golang.org/p/I17ZSM6LQb

如果您阅读SingleHostReverseProxy的源代码(http://golang.org/src/pkg/net/http/httputil/reverseproxy.go#L61),它会设置自己的Director,您将覆盖它.因此,您需要重新实现它已经完成的工作以及额外的主机更改.

无论如何,这没有解决问题的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)
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在我能够看到StackOverflow或任何其他网站应该如何.
不过,我还在研究POST调用,所以这是一项正在进行中的工作.


Bai*_*ker 8

聚会有点晚了,但ReverseProxy没有坏,只是有点混乱,因为它不像你期望的那样工作(至少,我预计它会像你那样工作,所以我们两个人)。

文档

// 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
Run Code Online (Sandbox Code Playgroud)

由于在幕后ReverseProxy使用它Request来发出客户端请求(在ReverseProxy.Director可选地修改它之后),如果Host设置了它,它将覆盖Host标头。这将始终被设置,因为正如文档注释的第一部分所述“对于服务器请求,主机指定了在其上寻找 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"
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以设置req.Host"",它将使用req.URL.Host.

我通过阅读了解了这一点:https : //github.com/golang/go/issues/14413