From d1f5ec083a8fb89fec76f8534139e20670eb10c0 Mon Sep 17 00:00:00 2001 From: ambitioner Date: Fri, 19 May 2017 16:34:03 +0800 Subject: [PATCH] add http proxy add http proxy add proxy code add proxy --- models/plugin/http-proxy/config/config.json | 3 + models/plugin/http-proxy/proxy/config.go | 32 ++++ models/plugin/http-proxy/proxy/proxy.go | 201 ++++++++++++++++++++ 3 files changed, 236 insertions(+) create mode 100644 models/plugin/http-proxy/config/config.json create mode 100644 models/plugin/http-proxy/proxy/config.go create mode 100644 models/plugin/http-proxy/proxy/proxy.go diff --git a/models/plugin/http-proxy/config/config.json b/models/plugin/http-proxy/config/config.json new file mode 100644 index 0000000..3574ab2 --- /dev/null +++ b/models/plugin/http-proxy/config/config.json @@ -0,0 +1,3 @@ +{ + "port":":8080" +} diff --git a/models/plugin/http-proxy/proxy/config.go b/models/plugin/http-proxy/proxy/config.go new file mode 100644 index 0000000..4e8d0c1 --- /dev/null +++ b/models/plugin/http-proxy/proxy/config.go @@ -0,0 +1,32 @@ +package proxy + +import ( + "bufio" + "encoding/json" + "os" +) + +// Config 保存代理服务器的配置 +type Config struct { + Port string `json:"port"` + Auth bool `json:"auth"` + + User map[string]string `json:"user"` +} + +// 从指定json文件读取config配置 +func (c *Config) GetConfig(filename string) error { + + configFile, err := os.Open(filename) + if err != nil { + return err + } + defer configFile.Close() + + br := bufio.NewReader(configFile) + err = json.NewDecoder(br).Decode(c) + if err != nil { + return err + } + return nil +} diff --git a/models/plugin/http-proxy/proxy/proxy.go b/models/plugin/http-proxy/proxy/proxy.go new file mode 100644 index 0000000..11acb60 --- /dev/null +++ b/models/plugin/http-proxy/proxy/proxy.go @@ -0,0 +1,201 @@ +package proxy + +import ( + "fmt" + "io" + "net" + "net/http" + "os" + "time" + + "github.com/fatedier/frp/models/plugin" + "github.com/fatedier/frp/utils/log" + wrap "github.com/fatedier/frp/utils/net" +) + +var ( + HTTP_200 = []byte("HTTP/1.1 200 Connection Established\r\n\r\n") + ProxyName = "default-proxy" + cnfg Config +) + +func init() { + // 加载配置文件 + err := cnfg.GetConfig("../config/config.json") + if err != nil { + log.Error("can not load config file:%v\n", err) + os.Exit(-1) + } + plugin.Register(ProxyName, NewProxyPlugin) +} + +type ProxyServer struct { + Tr *http.Transport +} + +type Proxy struct { + Server *http.Server + Ln net.Listener +} + +func NewProxyPlugin(params map[string]string) (p plugin.Plugin, err error) { + + listen, err := net.Listen("tcp", cnfg.Port) + if err != nil { + log.Error("can not listen %v port", cnfg.Port) + return + } + + p = &Proxy{ + Server: NewProxyServer(), + Ln: listen, + } + return +} + +func (proxy *Proxy) Name() string { + return ProxyName +} + +// right?? +func (proxy *Proxy) Handle(conn io.ReadWriteCloser) { + wrapConn := wrap.WrapReadWriteCloserToConn(conn) + + remote, err := net.Dial("tcp", cnfg.Port) + if err != nil { + log.Error("dial tcp error:%v", err) + return + } + + // or tcp.Join(remote,wrapConn) + _, err = io.Copy(remote, wrapConn) + if err != nil && err != io.EOF { + log.Error("io copy data error:%v", err) + return + } + return +} + +func (proxy *Proxy) Close() error { + return proxy.Server.Close() +} + +func NewProxyServer() *http.Server { + + return &http.Server{ + Addr: cnfg.Port, + Handler: &ProxyServer{Tr: http.DefaultTransport.(*http.Transport)}, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, + } +} + +func (proxy *ProxyServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + defer func() { + if err := recover(); err != nil { + rw.WriteHeader(http.StatusInternalServerError) + log.Error("Panic: %v", err) + fmt.Fprintf(rw, fmt.Sprintln(err)) + } + }() + + if req.Method == "CONNECT" { // 是connect连接 + proxy.HttpsHandler(rw, req) + } else { + proxy.HttpHandler(rw, req) + } +} + +// 处理普通的http请求 +func (proxy *ProxyServer) HttpHandler(rw http.ResponseWriter, req *http.Request) { + log.Info("is sending request %v %v ", req.Method, req.URL.Host) + removeProxyHeaders(req) // 去除不必要的头 + + resp, err := proxy.Tr.RoundTrip(req) + if err != nil { + log.Error("transport RoundTrip error: %v", err) + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + defer resp.Body.Close() + + clearHeaders(rw.Header()) // 得到一个空的Header + copyHeaders(rw.Header(), resp.Header) + rw.WriteHeader(resp.StatusCode) + + nr, err := io.Copy(rw, resp.Body) + if err != nil && err != io.EOF { + log.Error("got an error when copy remote response to client.%v", err) + return + } + log.Info("copied %v bytes from remote host %v.", nr, req.URL.Host) +} + +// 处理https连接,主要用于CONNECT方法 +func (proxy *ProxyServer) HttpsHandler(rw http.ResponseWriter, req *http.Request) { + log.Info("[CONNECT] tried to connect to remote host %v", req.URL.Host) + + hj, _ := rw.(http.Hijacker) + client, _, err := hj.Hijack() //获取客户端与代理服务器的tcp连接 + if err != nil { + log.Error("failed to get Tcp connection of", req.RequestURI) + http.Error(rw, "Failed", http.StatusBadRequest) + return + } + + remote, err := net.Dial("tcp", req.URL.Host) //建立服务端和代理服务器的tcp连接 + if err != nil { + log.Error("failed to connect %v", req.RequestURI) + http.Error(rw, "Failed", http.StatusBadRequest) + client.Close() + return + } + + client.Write(HTTP_200) + + go copyRemoteToClient(remote, client) + go copyRemoteToClient(client, remote) +} + +// data copy between two socket +func copyRemoteToClient(remote, client net.Conn) { + defer func() { + remote.Close() + client.Close() + }() + + nr, err := io.Copy(remote, client) + if err != nil && err != io.EOF { + log.Error("got an error when handles CONNECT %v", err) + return + } + log.Info("[CONNECT] transported %v bytes betwwen %v and %v", nr, remote.RemoteAddr(), client.RemoteAddr()) +} + +func copyHeaders(dst, src http.Header) { + for key, values := range src { + for _, value := range values { + dst.Add(key, value) + } + } +} + +func clearHeaders(headers http.Header) { + for key, _ := range headers { + headers.Del(key) + } +} + +func removeProxyHeaders(req *http.Request) { + req.RequestURI = "" + req.Header.Del("Proxy-Connection") + req.Header.Del("Connection") + req.Header.Del("Keep-Alive") + req.Header.Del("Proxy-Authenticate") + req.Header.Del("Proxy-Authorization") + req.Header.Del("TE") + req.Header.Del("Trailers") + req.Header.Del("Transfer-Encoding") + req.Header.Del("Upgrade") +}