pu369com

golang 实现HTTP代理和反向代理

正向代理

package main

import (
    "fmt"
    "io"
    "net"
    "net/http"
    "strings"
)

type Pxy struct{}

func (p *Pxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
    fmt.Printf("Received request %s %s %s\n", req.Method, req.Host, req.RemoteAddr)

    transport := http.DefaultTransport

    // step 1
    outReq := new(http.Request)
    *outReq = *req // this only does shallow copies of maps

    if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
        if prior, ok := outReq.Header["X-Forwarded-For"]; ok {
            clientIP = strings.Join(prior, ", ") + ", " + clientIP
        }
        outReq.Header.Set("X-Forwarded-For", clientIP)
    }

    // step 2
    res, err := transport.RoundTrip(outReq)
    if err != nil {
        rw.WriteHeader(http.StatusBadGateway)
        return
    }

    // step 3
    for key, value := range res.Header {
        for _, v := range value {
            rw.Header().Add(key, v)
        }
    }

    rw.WriteHeader(res.StatusCode)
    io.Copy(rw, res.Body)
    res.Body.Close()
}

func main() {
    fmt.Println("Serve on :8080")
    http.Handle("/", &Pxy{})
    http.ListenAndServe("0.0.0.0:8080", nil)
}

上面的代码运行之后,会在本地的 8080 端口启动代理服务。修改浏览器的代理为 127.0.0.1::8080 再访问http网站,可以验证代理正常工作,也能看到它在终端打印出所有的请求信息。

如果了解 HTTPS 协议的话,你会明白这种模式下是无法完成 HTTPS 握手的,虽然代理可以和真正的服务器建立连接(知道了对方的公钥和证书),但是代理无法代表服务器和客户端建立连接,因为代理服务器无法知道真正服务器的私钥。

反向代理

编写反向代理按照上面的思路当然没有问题,只需要在第二步的时候,根据之前的配置修改 outReq 的 URL Host 地址可以了。不过 Golang 已经给我们提供了编写代理的框架: httputil.ReverseProxy 。我们可以用非常简短的代码来实现自己的代理,而且内部的细节问题都已经被很好地处理了。

package main

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

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        director := func(req *http.Request) {
            req = r
            req.URL.Scheme = "http"
            req.URL.Host = r.Host
        }
        proxy := &httputil.ReverseProxy{Director: director}
        proxy.ServeHTTP(w, r)
    })

    log.Fatal(http.ListenAndServe(":8888", nil))
}

感觉用法与正向代理没区别,也是设置浏览器代理地址后,用于访问http网站

 用代理访问HTTPS

可以在远程服务器上用http.Get 或client.Do获取http或https网页内容。然后用http.ListenAndServe向本地返回获取到的内容。

package main

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

func main() {
    resp, _ := http.Get("https://www.baidu.com")
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))

}

package main

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

func main() {
    client := &http.Client{}
    req, _ := http.NewRequest("GET", "https://www.baidu.com", nil)
    //req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    //req.Header.Set("Cookie", "name=any")
    resp, err := client.Do(req)
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        // handle error
    }
    fmt.Println(string(body))

}

其实也不必用这种土办法,直接在远程服务器上装ss就好了。(下面的参考中也有一个“用不到 100 行的 Golang 代码实现 HTTP(S) 代理”的代码) 

参考:

https://blog.csdn.net/mengxinghuiku/article/details/65448600

https://www.jianshu.com/p/f868c88b45e1

https://studygolang.com/articles/11967?fr=sidebar

https://studygolang.com/articles/12525?fr=sidebar

https://segmentfault.com/a/1190000003735562

https://www.cnblogs.com/freecast/p/9687733.html

https://studygolang.com/articles/14917?fr=sidebar

http://www.01happy.com/golang-http-client-get-and-post/

posted on 2019-04-05 12:46  pu369com  阅读(2460)  评论(0编辑  收藏  举报

导航