简单原理:
最近需要用golang去实现一个http(s)的代理,简单的解释一下,实现过程就是首先启动一个http(s)的服务,这个很简单,demo一大堆,我就不多说了,接下来要实现代理,很简单的原理就是,通过你实现的http(s)服务接收到来自客户的请求,收到之后通过http.NewRequest建立一次新的请求,当然需要你在配置文件中写明确需要代理到的目标地址和端口,建立新请求到目标地址之后将目标地址返回的数据原封不动的返回给原来的客户端。
注意点:
1、客户端请求的所有头信息,都要原封不动的写入新请求,目标地址返回的头信息也要原封不动的返回给原来的客户端。
2、如果客户端使用https请求的,正常启用https服务能接收到,转发给后端的时候跳过证书就ok,能正常加密传输信息,如果客户端使用http请求的,但我们启动的https server,这样是收不到http发送过来的信息的(好像会给浏览器返回一个不支持http具体的大家可以测试一下),如果严谨一点我们需要自己再启动一个http server,收到请求之后返回302,这样的话一般浏览器都会自动再次转成https请求,再次发送到你的服务端。
3、如果是websocket请求,那就要升级协议了,具体看下一篇博客:
具体代码:
package main
import (
//"encoding/json"
"fmt"
"net/http"
"io/ioutil"
"strings"
"io"
)
type HttpProxyHandle struct {
}
func (h *HttpProxyHandle) ServeHTTP(w http.ResponseWriter, req *http.Request) {
fmt.Printf("Received request: %s %s %s %s\n", req.Method, req.Host, req.RemoteAddr, req.URL)
config := getConfig()
fmt.Printf("target server addr:%s\n", config.HttpProxy.Server)
body, err := ioutil.ReadAll(req.Body)
if err != nil {
fmt.Println("body = NULL:", err.Error())
//return,没有数据也是可以的,不需要直接结束
}
fmt.Println("req count:", len(body))
fmt.Println("send to target server:", config.HttpProxy.Server)
cli := &http.Client{}
//配置文件读取详见《golang配置文件与yaml》,http://一定要加
reqUrl := "http://" + config.HttpProxy.Server + req.URL.String()
proxy_req, err := http.NewRequest(req.Method, reqUrl, strings.NewReader(string(body)))
if err != nil {
fmt.Println("http.NewRequest(to target server addr):", err.Error())
return
}
for k, v := range req.Header {
proxy_req.Header.Set(k, v[0])
}
res, err := cli.Do(proxy_req)
if err != nil {
fmt.Println("cli.Do(req):", err.Error())
return
}
defer res.Body.Close()
for k, v := range res.Header {
w.Header().Set(k, v[0])
}
io.Copy(w, res.Body)
}
func startHttpProxy() {
c := getConfig()//配置文件读取详见《golang配置文件与yaml》
s := &http.Server{
Addr: c.HttpProxy.ListenAddr,//配置文件读取详见《golang配置文件与yaml》
Handler: &HttpProxyHandle{},
}
go func() {
mainLog.Fatal(s.ListenAndServeTLS(c.HttpApi.CrtFile, c.HttpApi.KeyFile))
}()
}