teeporxy.go
package main
import (
"bytes"
"crypto/tls"
"flag"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net"
"net/http"
"net/http/httputil"
"runtime"
"time"
)
// Console flags
//参数解析
var (
listen = flag.String("l", ":8888", "port to accept requests") //接收请求端口 默认渡口是8888
targetProduction = flag.String("a", "localhost:8080", "where production traffic goes. http://localhost:8080/production") //a代表产品机器 默认端口是8080
altTarget = flag.String("b", "localhost:8081", "where testing traffic goes. response are skipped. http://localhost:8081/test") //b 测试机器 端口是8081
debug = flag.Bool("debug", false, "more logging, showing ignored output") //日志开关
productionTimeout = flag.Int("a.timeout", 3, "timeout in seconds for production traffic")// 生产机器请求超时时间
alternateTimeout = flag.Int("b.timeout", 1, "timeout in seconds for alternate site traffic")//测试机器清酒超时时间
productionHostRewrite = flag.Bool("a.rewrite", false, "rewrite the host header when proxying production traffic") //生产机器是重定向开关
alternateHostRewrite = flag.Bool("b.rewrite", false, "rewrite the host header when proxying alternate site traffic")//测试机器是否重定向开关
percent = flag.Float64("p", 100.0, "float64 percentage of traffic to send to testing")// 生产数据发给测试机器数据的百分比 流量分割
tlsPrivateKey = flag.String("key.file", "", "path to the TLS private key file") //TSL 私钥证书
tlsCertificate = flag.String("cert.file", "", "path to the TLS certificate file")//Tsl 龚玥证书
)
// handler contains the address of the main Target and the one for the Alternative target
//handler 包含连个地址 其中一个是生产服务器 另个一是测试服务器
type handler struct {
Target string
Alternative string
Randomizer rand.Rand
}
// ServeHTTP duplicates the incoming request (req) and does the request to the Target and the Alternate target discading the Alternate response
//sereHttp 复制获取到的req 并且发送到生产服务器和测试服务器 测试服务器丢弃响应结果
func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var productionRequest, alternativeRequest *http.Request
if *percent == 100.0 || h.Randomizer.Float64()*100 < *percent {
alternativeRequest, productionRequest = DuplicateRequest(req) //复制数据到生产和测试请求中
go func() {
defer func() {
if r := recover(); r != nil && *debug {
fmt.Println("Recovered in f", r)
}
}()
// Open new TCP connection to the server
//获取客户端连接 带有超时时间
clientTcpConn, err := net.DialTimeout("tcp", h.Alternative, time.Duration(time.Duration(*alternateTimeout)*time.Second))
if err != nil {
if *debug {
fmt.Printf("Failed to connect to %s\n", h.Alternative)
}
return
}
clientHttpConn := httputil.NewClientConn(clientTcpConn, nil) // Start a new HTTP connection on it
defer clientHttpConn.Close() // Close the connection to the server
if *alternateHostRewrite {
alternativeRequest.Host = h.Alternative
}
err = clientHttpConn.Write(alternativeRequest) // Pass on the request
if err != nil {
if *debug {
fmt.Printf("Failed to send to %s: %v\n", h.Alternative, err)
}
return
}
_, err = clientHttpConn.Read(alternativeRequest) // Read back the reply
if err != nil {
if *debug {
fmt.Printf("Failed to receive from %s: %v\n", h.Alternative, err)
}
return
}
}()
} else {
productionRequest = req
}
defer func() {
if r := recover(); r != nil && *debug {
fmt.Println("Recovered in f", r)
}
}()
// Open new TCP connection to the server
//生产服务器
clientTcpConn, err := net.DialTimeout("tcp", h.Target, time.Duration(time.Duration(*productionTimeout)*time.Second))
if err != nil {
fmt.Printf("Failed to connect to %s\n", h.Target)
return
}
clientHttpConn := httputil.NewClientConn(clientTcpConn, nil) // Start a new HTTP connection on it
defer clientHttpConn.Close() // Close the connection to the server
if *productionHostRewrite {
productionRequest.Host = h.Target
}
err = clientHttpConn.Write(productionRequest) // Pass on the request
if err != nil {
fmt.Printf("Failed to send to %s: %v\n", h.Target, err)
return
}
resp, err := clientHttpConn.Read(productionRequest) // Read back the reply
if err != nil {
fmt.Printf("Failed to receive from %s: %v\n", h.Target, err)
return
}
defer resp.Body.Close()
for k, v := range resp.Header {
w.Header()[k] = v
}
w.WriteHeader(resp.StatusCode)
body, _ := ioutil.ReadAll(resp.Body)
w.Write(body)
}
func main() {
flag.Parse()
runtime.GOMAXPROCS(runtime.NumCPU())
var err error
var listener net.Listener
if len(*tlsPrivateKey) > 0 {
cer, err := tls.LoadX509KeyPair(*tlsCertificate, *tlsPrivateKey)
if err != nil {
fmt.Printf("Failed to load certficate: %s and private key: %s", *tlsCertificate, *tlsPrivateKey)
return
}
config := &tls.Config{Certificates: []tls.Certificate{cer}}
listener, err = tls.Listen("tcp", *listen, config)
if err != nil {
fmt.Printf("Failed to listen to %s: %s\n", *listen, err)
return
}
} else {
listener, err = net.Listen("tcp", *listen)
if err != nil {
fmt.Printf("Failed to listen to %s: %s\n", *listen, err)
return
}
}
h := handler{
Target: *targetProduction,
Alternative: *altTarget,
Randomizer: *rand.New(rand.NewSource(time.Now().UnixNano())),
}
http.Serve(listener, h)
}
type nopCloser struct {
io.Reader
}
func (nopCloser) Close() error { return nil }
//复制req到生茶服务器和测试服务器
func DuplicateRequest(request *http.Request) (request1 *http.Request, request2 *http.Request) {
b1 := new(bytes.Buffer)
b2 := new(bytes.Buffer)
w := io.MultiWriter(b1, b2) //同时向多个对象中写入数据
io.Copy(w, request.Body) //复制数据到 w中
defer request.Body.Close()
request1 = &http.Request{
Method: request.Method,
URL: request.URL,
Proto: request.Proto,
ProtoMajor: request.ProtoMajor,
ProtoMinor: request.ProtoMinor,
Header: request.Header,
Body: nopCloser{b1},
Host: request.Host,
ContentLength: request.ContentLength,
}
request2 = &http.Request{
Method: request.Method,
URL: request.URL,
Proto: request.Proto,
ProtoMajor: request.ProtoMajor,
ProtoMinor: request.ProtoMinor,
Header: request.Header,
Body: nopCloser{b2},
Host: request.Host,
ContentLength: request.ContentLength,
}
return
}
teeporxy.go的更多相关文章
随机推荐
- OS X 平台的 8 个实用终端工具
本文由 伯乐在线 - shinancao 翻译自 mitchchn.欢迎加入iOS小组.转载请参见文章末尾处的要求. OS X 终端对外开放了许多很强大的UNIX实用工具和脚本.如果你是从Linux转 ...
- ELF 文件 动态链接 - 地址无关代码(GOT)
Linux 系统中,ELF动态链接文件被称为 动态共享对象(DSO,Dynamic Shared Object),简称共享对象 文件拓展名为".so" 动态链接下 一个程序可以被分 ...
- ios项目开发汇总
UI界面 iOS和Android 界面设计尺寸规范 http://www.alibuybuy.com/posts/85486.html iPhone app界面设计尺寸规范 http://www. ...
- IOS Dev 需要常看的网站<转>
英文系列 网站 http://Raywenderlich.com 这个不多说了吧,iOS界的百科全书.iOS By tutorial系列书从iOS7到8全买的正版别说499刀了,999刀也入手. ob ...
- javaScript(3)---语法、关键保留字及变量
学习要点: 1.语法构成 2.关键字保留字 3.变量 任何语言的核心都必然会描述这门语言最基本的工作原理.而JavaScript的语言核心就是ECMAScript 一.语法构成 区分大小写:ECMAS ...
- 从__acrt_first_block == header 谈起,记录dll链接不一致的问题
最近写了一个postgresql的数据库连接池dll.写的比较随意,某个头文件如下: #pragma once #include "common.h"#include " ...
- AttributeError: module 'enum' has no attribute 'IntFlag'
Mac PyCharm新建以Python3.6.1为解释器的Django项目的时候出现以下错误提示: AttributeError: module 'enum' has no attribute 'I ...
- 各种代码版本控制工具下使用http代理的方法
原文:各种SCM工具下使用http代理下载源码:http://www.linuxeden.com/html/develop/20090723/66951.html SCM是软件配置管理的简称,常见的S ...
- DevOps之三 Git的安装与配置
Centos7 安装Git 一.卸载Centos7 自带的git # git --version git version 1.8.3.1# whereis gitgit: /usr/bin/git / ...
- JDK及JRE目录结构
JDK文件结构及目录: c:\jdk1.7.0: JDK安装根目录,包括版权.许可证和READEME文件,还包含ser.zip记录Java平台档案. c:\jdk1.7.0\bin 包含在Java开发 ...