如何通过golang创建代理

时间:2018-03-29 03:22:45

标签: go proxy

我正在尝试通过golang创建代理。

原始版本由lua,nginx编写,如下所示:

location / {
    keepalive_timeout  3600s;
    keepalive_requests 30000;
    rewrite_by_lua_file ./test.lua;
    proxy_pass http://www.example.com/bd/news/home;
}

和lua这样的文件:

local req_params = ngx.req.get_uri_args()
local args = {
    media = 24,
    submedia = 46,
    os = req_params.os,
    osv = req_params.osv,
    make = req_params.make,
    model = req_params.model,
    devicetype = req_params.devicetype,
    conn = req_params.conn,
    carrier = req_params.carrier,
    sw = req_params.w,
    sh = req_params.h,
}
if tonumber(req_params.os) == 1 then
    args.imei = req_params.imei
    args.adid = req_params.android_id
end
ngx.req.set_uri_args(args)

我尝试通过golang做同样的事情,我的代码是这样的:

const newsTargetURL = "http://www.example.com/bd/news/home"

func GetNews(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
        http.Error(w, "only get allowed", http.StatusMethodNotAllowed)
        return
    }

    // deal params
    rq := r.URL.Query()
    os := rq.Get("os")
    osv := rq.Get("osv")
    imei := rq.Get("imei")
    androidID := rq.Get("android_id")
    deviceMake := rq.Get("make")
    model := rq.Get("model")
    deviceType := rq.Get("devicetype")
    sw := rq.Get("w")
    sh := rq.Get("h")
    conn := rq.Get("conn")
    carrier := rq.Get("carrier")
    uv := make(url.Values)
    uv.Set("media", "24")
    uv.Set("submedia", "46")
    uv.Set("os", os)
    uv.Set("osv", osv)
    if os == "1" {
        uv.Set("imei", imei)
        uv.Set("anid", androidID)
    }
    uv.Set("make", deviceMake)
    uv.Set("model", model)
    uv.Set("sw", sw)
    uv.Set("sh", sh)
    uv.Set("devicetype", deviceType)
    uv.Set("ip", ip)
    uv.Set("ua", ua)
    uv.Set("conn", conn)
    uv.Set("carrier", carrier)
    t := newsTargetURL + "?" + uv.Encode()
    // make a director
    director := func(req *http.Request) {
        u, err := url.Parse(t)
        if err != nil {
            panic(err)
        }
        req.URL = u
    }

    // make a proxy
    proxy := &httputil.ReverseProxy{Director: director}
    proxy.ServeHTTP(w, r)
}

func main() {
    mux := http.NewServeMux()

    mux.Handle("/", http.HandlerFunc(GetNews))

    srv := &http.Server{
        Addr:    ":2222",
        Handler: mux,
    }
    srv.ListenAndServe()
}

我把这个版本放到lua版本所在的同一台服务器上,但它不能像lua文件那样工作。我阅读了httputil文档,但没有发现任何可以帮助的内容。我需要做什么?

1 个答案:

答案 0 :(得分:3)

我一起写了一个GET请求的简单代理。希望这会有所帮助。

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

const newsTargetURL = "http://www.example.com/bd/news/home"

func main() {
    mux := http.NewServeMux()

    mux.Handle("/", http.HandlerFunc(GetNews))

    srv := &http.Server{
        Addr:    ":2222",
        Handler: mux,
    }
    // output error and quit if ListenAndServe fails
    log.Fatal(srv.ListenAndServe())
}

func GetNews(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
        http.Error(w, "only get allowed", http.StatusMethodNotAllowed)
        return
    }

    // build proxy url
    urlstr := fmt.Sprintf("%s?%s", newsTargetURL, r.URL.RawQuery)

    // request the proxy url
    resp, err := http.Get(urlstr)
    if err != nil {
        http.Error(w, fmt.Sprintf("error creating request to %s", urlstr), http.StatusInternalServerError)
        return
    }
    // make sure body gets closed when this function exits
    defer resp.Body.Close()

    // read entire response body
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        http.Error(w, "error reading response body", http.StatusInternalServerError)
        return
    }

    // write status code and body from proxy request into the answer
    w.WriteHeader(resp.StatusCode)
    w.Write(body)
}

你可以按原样试试。它将起作用并显示example.com的内容。

它为所有请求使用单个处理程序GetNews。它只需使用r.url.RawQuerynewsTargetURL来构建新网址,就会跳过所有请求参数解析和构建。

然后我们向新网址发出请求(问题中缺少主要部分)。我们在回复时将resp.StatusCoderesp.body用于对原始请求的回复。

其余的是错误处理。

该示例不会转发任何其他信息,如Cookie,标题等。可以根据需要添加。