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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM