一个利用go反向代理解决api转发的例子(go反向代理)

实现的效果:

如果访问的url路径是类似 /163/  或 /163/debian 的形式,则转发到163开源镜像服务器

 

直接上代码:

package main

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

var fwdHost = "mirrors.163.com"  //http首部字段的HOST取值
var fwdTo = "http://" + fwdHost + "/"
var fwdPrefix = "/163/"

type forward struct {
    RProxy *httputil.ReverseProxy
}

func (f *forward) ServeHTTP(wr http.ResponseWriter, req *http.Request) {
    //fmt.Printf("http头部是:%+v\n", req.Header) //假设这是处理http头部的代码
    fmt.Printf(" #### REQ:%+v\n", req)

    //处理完后转发到网易163镜像
    req.URL.Path = req.URL.Path[len(fwdPrefix)-1:] //修改了这里,req.RequestURI会跟着变
    req.Host = fwdHost
    fmt.Printf(" *** REQ:%+v\n", req)
    f.RProxy.ServeHTTP(wr, req)
}

func main() {
    var fwd forward
    u, _ := url.Parse(fwdTo)
    fwd.RProxy = httputil.NewSingleHostReverseProxy(u)
    http.Handle(fwdPrefix, &fwd) //所有请求将转发到网易163的debian镜像
    http.HandleFunc("/", notForward)
    http.HandleFunc("/api/v1/", notForward)
    log.Fatal(http.ListenAndServe(":3000", nil))
}

func notForward(wr http.ResponseWriter, req *http.Request) {
    wr.Write([]byte(fmt.Sprintf(`<html>
<body>
      <em>Not forward!!</em>
      <br />
      <i>url = %s</i>
</body>
</html>
`,req.URL.String())))
}

 

类似的还有更简单的做法,关键在httputil.ReverseProxy的Director字段

func APIReverseProxy(host string) http.HandlerFunc {
    var rp = httputil.ReverseProxy{
        Director:func(req *http.Request) {
            req.URL.Scheme = "http"
req.URL.Host = host
req.Host = host //对于一个ip地址托管多个域名的情况下,必须要给req.Host赋值,如果一个ip地址只有一个域名,可以不写这一句 //req.URL.Path = //如果需要改path的话 }, } return func(wr http.ResponsWriter, req *http.Request){ rp.ServeHTTP(wr,req) } //这里是返回http.HandlerFunc的例子,其实也可以直接返回rp(因为*rp就是一个http.Handler, *rp实现了ServeHTTP(wr,req)方法) }

 

关于req.URL.Host和req.Host:

go http包中对request中Host的注释:

// 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

另外:
req.URL.Host是从URL中解析出来的,
req.Host是http请求头部"Host" , 这个头部用于实现虚拟主机(一个ip托管多个域名),http1.1规范中,Host头是必须存在的

对于下面的请求:
GET /index.html HTTP/1.1
Host: www.example.org:8080
req.URL.Host是空的
对于通过代理的请求,req.URL.Host是目标主机,req.Host是代理服务器。
对于不是走代理的请求,req.URL.Host是空的,req.Host是目标主机

 

posted on 2019-06-20 20:04  进取有乐  阅读(2678)  评论(0编辑  收藏  举报

导航