golang反向代理无法访问

golang原生提供的httputil库可以非常方便的实现反向代理。那么到底有多简单呢?如果只实现一个简单的定向代理,main函数里只需要写3行代码!

package main

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

func main() {
	u, _ := url.Parse("http://localhost:8888")
	http.Handle("/", httputil.NewSingleHostReverseProxy(u))
	http.ListenAndServe(":9999", nil)
}

上面的程序将本地:9999端口代理到:8888,当然你需要在:8888端口起一个服务。

如果只是实现本地代理,上面的程序是完全没有问题的,但是如果你想代理到本机以外的域名,就会得到一个404的响应,比如你想代理到www.baidu.com,发现根本代理不过去。原因出在NewSingleHostReverseProxy函数上。首先我们来看一下反向代理的真面目。

type ReverseProxy struct {
	Director func(*http.Request)
	Transport http.RoundTripper
	FlushInterval time.Duration
	ErrorLog *log.Logger
	BufferPool BufferPool
	ModifyResponse func(*http.Response) error
	ErrorHandler func(http.ResponseWriter, *http.Request, error)
}

并无神奇之处,反向代理就是一个结构体!

从后往前说,ErrorHandler是错误处理器,看到这个函数签名你应该感到熟悉。代理发生错误时,返回给你什么响应由它说了算,默认是一个404的处理器。

ModifyResponse用来修改响应,如果你想自定义代理的响应,不妨自己实现这个函数。

BufferPool故名思意就是缓存池,代理需要拷贝响应内容,一般不需要自定义它。

Errorlog日志,没啥好说的。

FlushInterval拷贝响应时,将内容刷新到客户端的时间间隔,0代表不刷新,负值代表立即刷新;如果是流式响应,会忽略这个值,并总是立即刷新。

Transport用来执行代理的请求,得到响应。

Director正是问题的关键,它会重写请求,反向代理代理到哪里去就是由它说了算。

接下来就来看看我们是如何得到一个反向代理结构体的。

func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy {
	targetQuery := target.RawQuery
	director := func(req *http.Request) {
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
		if _, ok := req.Header["User-Agent"]; !ok {
			req.Header.Set("User-Agent", "")
		}
	}
	return &ReverseProxy{Director: director}
}

NewSingleHostReverseProxy用了一个闭包作为Director新建了一个ReverseProxy结构体,director就是它为我们默认实现的代理函数。在这个函数中重写了请求URL的协议、HostPath等。但是它没有重写req.Host,这也就导致了我们的反向代理只能在本地进行代理。所以我们只要对这个函数进行修改就可以了,有两种方式可以修改。

一是我们可以将NewSingleHostReverseProxy函数拷贝出来进行修改,反正这个函数也不复杂,然后在需要用到反向代理结构体的地方用我们自定义的函数新建代理器。

func NewProxy(target *url.URL) *httputil.ReverseProxy {
	targetQuery := target.RawQuery
	director := func(req *http.Request) {
		req.Host = target.Host // -- 加入这句 --
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
		if _, ok := req.Header["User-Agent"]; !ok {
			// explicitly disable User-Agent so it's not set to default value
			req.Header.Set("User-Agent", "")
		}
	}
	return &httputil.ReverseProxy{Director: director}
}

然后将httputil.NewSingleHostReverseProxy(u)替换成NewProxy(u)就可以代理到其他域名了。

二是自定义Director函数。我们还是用NewSingleHostReverseProxy函数新建代理器,然后自定义一个Director函数给它。

p := httputil.NewSingleHostReverseProxy(u)
d := p.Director
p.Director = func(r *http.Request) {
	d(r)
	r.Host = u.Host
}

最后附图一张。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值