1. package main

  1. import (
  1.     "bytes"
  1.     "crypto/tls"
  1.     "flag"
  1.     "fmt"
  1.     "io"
  1.     "io/ioutil"
  1.     "math/rand"
  1.     "net"
  1.     "net/http"
  1.     "net/http/httputil"
  1.     "runtime"
  1.     "time"
  1. )

  1. // Console flags
  1. //参数解析
  1. var (
  1.     listen = flag.String("l", ":8888", "port to accept requests") //接收请求端口 默认渡口是8888
  1.     targetProduction = flag.String("a", "localhost:8080", "where production traffic goes. http://localhost:8080/production") //a代表产品机器 默认端口是8080
  1.     altTarget = flag.String("b", "localhost:8081", "where testing traffic goes. response are skipped. http://localhost:8081/test") //b 测试机器 端口是8081
  1.     debug = flag.Bool("debug", false, "more logging, showing ignored output") //日志开关
  1.     productionTimeout = flag.Int("a.timeout", 3, "timeout in seconds for production traffic")// 生产机器请求超时时间
  1.     alternateTimeout = flag.Int("b.timeout", 1, "timeout in seconds for alternate site traffic")//测试机器清酒超时时间
  1.     productionHostRewrite = flag.Bool("a.rewrite", false, "rewrite the host header when proxying production traffic") //生产机器是重定向开关
  1.     alternateHostRewrite = flag.Bool("b.rewrite", false, "rewrite the host header when proxying alternate site traffic")//测试机器是否重定向开关
  1.     percent = flag.Float64("p", 100.0, "float64 percentage of traffic to send to testing")// 生产数据发给测试机器数据的百分比 流量分割
  1.     tlsPrivateKey = flag.String("key.file", "", "path to the TLS private key file") //TSL 私钥证书
  1.     tlsCertificate = flag.String("cert.file", "", "path to the TLS certificate file")//Tsl 龚玥证书
  1. )

  1. // handler contains the address of the main Target and the one for the Alternative target
  1. //handler 包含连个地址 其中一个是生产服务器 另个一是测试服务器
  1. type handler struct {
  1.     Target string
  1.     Alternative string
  1.     Randomizer rand.Rand
  1. }

  1. // ServeHTTP duplicates the incoming request (req) and does the request to the Target and the Alternate target discading the Alternate response
  1. //sereHttp 复制获取到的req 并且发送到生产服务器和测试服务器 测试服务器丢弃响应结果
  1. func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  1.     var productionRequest, alternativeRequest *http.Request
  1.     if *percent == 100.0 || h.Randomizer.Float64()*100 < *percent {
  1.         alternativeRequest, productionRequest = DuplicateRequest(req) //复制数据到生产和测试请求中
  1.         go func() {
  1.             defer func() {
  1.                 if r := recover(); r != nil && *debug {
  1.                     fmt.Println("Recovered in f", r)
  1.                 }
  1.             }()
  1.             // Open new TCP connection to the server
  1. //获取客户端连接 带有超时时间
  1.             clientTcpConn, err := net.DialTimeout("tcp", h.Alternative, time.Duration(time.Duration(*alternateTimeout)*time.Second))
  1.             if err != nil {
  1.                 if *debug {
  1.                     fmt.Printf("Failed to connect to %s\n", h.Alternative)
  1.                 }
  1.                 return
  1.             }
  1.             clientHttpConn := httputil.NewClientConn(clientTcpConn, nil) // Start a new HTTP connection on it
  1.             defer clientHttpConn.Close() // Close the connection to the server
  1.             if *alternateHostRewrite {
  1.                 alternativeRequest.Host = h.Alternative
  1.             }
  1.             err = clientHttpConn.Write(alternativeRequest) // Pass on the request
  1.             if err != nil {
  1.                 if *debug {
  1.                     fmt.Printf("Failed to send to %s: %v\n", h.Alternative, err)
  1.                 }
  1.                 return
  1.             }
  1.             _, err = clientHttpConn.Read(alternativeRequest) // Read back the reply
  1.             if err != nil {
  1.                 if *debug {
  1.                     fmt.Printf("Failed to receive from %s: %v\n", h.Alternative, err)
  1.                 }
  1.                 return
  1.             }
  1.         }()
  1.     } else {
  1.         productionRequest = req
  1.     }
  1.     defer func() {
  1.         if r := recover(); r != nil && *debug {
  1.             fmt.Println("Recovered in f", r)
  1.         }
  1.     }()

  1.     // Open new TCP connection to the server
  1. //生产服务器
  1.     clientTcpConn, err := net.DialTimeout("tcp", h.Target, time.Duration(time.Duration(*productionTimeout)*time.Second))
  1.     if err != nil {
  1.         fmt.Printf("Failed to connect to %s\n", h.Target)
  1.         return
  1.     }
  1.     clientHttpConn := httputil.NewClientConn(clientTcpConn, nil) // Start a new HTTP connection on it
  1.     defer clientHttpConn.Close() // Close the connection to the server
  1.     if *productionHostRewrite {
  1.         productionRequest.Host = h.Target
  1.     }
  1.     err = clientHttpConn.Write(productionRequest) // Pass on the request
  1.     if err != nil {
  1.         fmt.Printf("Failed to send to %s: %v\n", h.Target, err)
  1.         return
  1.     }
  1.     resp, err := clientHttpConn.Read(productionRequest) // Read back the reply
  1.     if err != nil {
  1.         fmt.Printf("Failed to receive from %s: %v\n", h.Target, err)
  1.         return
  1.     }
  1.     defer resp.Body.Close()
  1.     for k, v := range resp.Header {
  1.         w.Header()[k] = v
  1.     }
  1.     w.WriteHeader(resp.StatusCode)
  1.     body, _ := ioutil.ReadAll(resp.Body)
  1.     w.Write(body)
  1. }

  1. func main() {
  1.     flag.Parse()

  1.     runtime.GOMAXPROCS(runtime.NumCPU())

  1.     var err error

  1.     var listener net.Listener

  1.     if len(*tlsPrivateKey) > 0 {
  1.         cer, err := tls.LoadX509KeyPair(*tlsCertificate, *tlsPrivateKey)
  1.         if err != nil {
  1.             fmt.Printf("Failed to load certficate: %s and private key: %s", *tlsCertificate, *tlsPrivateKey)
  1.             return
  1.         }

  1.         config := &tls.Config{Certificates: []tls.Certificate{cer}}
  1.         listener, err = tls.Listen("tcp", *listen, config)
  1.         if err != nil {
  1.             fmt.Printf("Failed to listen to %s: %s\n", *listen, err)
  1.             return
  1.         }
  1.     } else {
  1.         listener, err = net.Listen("tcp", *listen)
  1.         if err != nil {
  1.             fmt.Printf("Failed to listen to %s: %s\n", *listen, err)
  1.             return
  1.         }
  1.     }

  1.     h := handler{
  1.         Target: *targetProduction,
  1.         Alternative: *altTarget,
  1.         Randomizer: *rand.New(rand.NewSource(time.Now().UnixNano())),
  1.     }
  1.     http.Serve(listener, h)
  1. }

  1. type nopCloser struct {
  1.     io.Reader
  1. }

  1. func (nopCloser) Close() error { return nil }
  1. //复制req到生茶服务器和测试服务器
  1. func DuplicateRequest(request *http.Request) (request1 *http.Request, request2 *http.Request) {
  1.     b1 := new(bytes.Buffer)
  1.     b2 := new(bytes.Buffer)
  1.     w := io.MultiWriter(b1, b2) //同时向多个对象中写入数据
  1.     io.Copy(w, request.Body) //复制数据到 w中
  1.     defer request.Body.Close()
  1.     request1 = &http.Request{
  1.         Method: request.Method,
  1.         URL: request.URL,
  1.         Proto: request.Proto,
  1.         ProtoMajor: request.ProtoMajor,
  1.         ProtoMinor: request.ProtoMinor,
  1.         Header: request.Header,
  1.         Body: nopCloser{b1},
  1.         Host: request.Host,
  1.         ContentLength: request.ContentLength,
  1.     }
  1.     request2 = &http.Request{
  1.         Method: request.Method,
  1.         URL: request.URL,
  1.         Proto: request.Proto,
  1.         ProtoMajor: request.ProtoMajor,
  1.         ProtoMinor: request.ProtoMinor,
  1.         Header: request.Header,
  1.         Body: nopCloser{b2},
  1.         Host: request.Host,
  1.         ContentLength: request.ContentLength,
  1.     }
  1.     return
  1. }

teeporxy.go的更多相关文章

随机推荐

  1. rails应用ajax之一:使用纯js方法

    考虑如下需求: 1. 用户输入一个用户名,当焦点跳出文本框时,检查该用户名是否有效 2. 动态更新检查的结果 我们使用ajax的方式来实现这个简单的功能,首先建立view:check.html.erb ...

  2. How--to-deploy-smart-contracts-on

    The following smart contract code is only an example and is NOT to be used in Production systems. pr ...

  3. form表单提交转为可被 getModel(PROJECT.class ,null);接收

    var form = new mini.Form("#editForm"+id); form.validate();if (!form.isValid()) { alert('信息 ...

  4. intersection of two linked lists.(两个链表交叉的地方)

    Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...

  5. 什么是shell? bash和shell有什么关系?

    什么是shell? bash和shell有什么关系? 博客分类: Linux   什么是Shell?      shell是你(用户)和Linux(或者更准确的说,是你和Linux内核)之间的接口程序 ...

  6. 任务调度利器:Celery

    http://www.liaoxuefeng.com/article/00137760323922531a8582c08814fb09e9930cede45e3cc000 Celery是Python开 ...

  7. ubuntu 18.04安装docker以及docker内配置neo4j

    如题 切换到root用户下 apt install docker.io 等啊等,很快,就好了.. 如图 即可使用 如果出现Cannot connect to the Docker daemon at ...

  8. laravel 5.5 安装

    PHP要求 PHP> = 7.0.0 OpenSSL PHP扩展 PDO PHP扩展 Mbstring PHP扩展 Tokenizer PHP扩展 XML PHP扩展 通过Composer创建项 ...

  9. myEclipse 配置tomcat清除缓存

    -Xms256m -Xmx512m -XX:MaxNewSize=64m -XX:MaxPermSize=128m

  10. day12 EL 表达式和国际化开发

    day12 EL 表达式和国际化开发 1. EL(Expression Language) 表达式简介 1.1 执行运算 1.2 获取web开发常用对象(el 中定义了11个隐式对象) 1.3 使用 ...