Skip to content

Commit

Permalink
add http proxy
Browse files Browse the repository at this point in the history
add http proxy

add proxy code

add proxy
  • Loading branch information
blizard863 committed May 26, 2017
1 parent 716ec28 commit d1f5ec0
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 0 deletions.
3 changes: 3 additions & 0 deletions models/plugin/http-proxy/config/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"port":":8080"
}
32 changes: 32 additions & 0 deletions models/plugin/http-proxy/proxy/config.go
Original file line number Diff line number Diff line change
@@ -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
}
201 changes: 201 additions & 0 deletions models/plugin/http-proxy/proxy/proxy.go
Original file line number Diff line number Diff line change
@@ -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")
}

0 comments on commit d1f5ec0

Please sign in to comment.