如何通过golang制作代理

I'm trying to make a proxy by golang.

The origin version is written by lua, nginx like this:

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

and lua file like this:

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)

I try to do the same thing by golang, and my code is like this:

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()
}

I put this go version to the same server where lua version locate, but it does not work as lua file do. I read the httputil document but found nothing that can help. What do I need to do?

I wrote together a simple proxy for GET requests. Hope this helps.

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)
}

You can try it as is. It will work and show the content of example.com.

It uses a single handler GetNews for all requests. It skips all of the request parameter parsing and building by simply using r.url.RawQuery and newsTargetURL to build the new url.

Then we make a request to the new url (the main part missing in your question). From the response we read resp.StatusCode and resp.body to use in our response to the original request.

The rest is error handling.

The sample does not forward any additional information like cookies, headers, etc. That can be added as needed.