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的更多相关文章
随机推荐
- LeetCode(29)-Plus One
题目: Given a non-negative number represented as an array of digits, plus one to the number. The digit ...
- IndexedDB,FileSystem- 前端数据库,文件管理系统
"我们不再需要下载并且安装软件.一个简单的web浏览器和一个可供使用的互联网就足以让我们在任何时间, 任何地点, 还有任何平台上使用任何web应用程序." web应用很酷, 但是相 ...
- Struts2数据传输的背后机制:ValueStack(值栈)
1. 数据传输背后机制:ValueStack(值栈) 在这一切的背后,是因为有了ValueStack(值栈)! ValueStack基础:OGNL 要了解ValueStack,必须先理解OGN ...
- ffmpeg 时间戳处理
视频的显示和存放原理 对于一个电影,帧是这样来显示的:I B B P.现在我们需要在显示B帧之前知道P帧中的信息.因此,帧可能会按照这样的方式来存储:IPBB.这就是为什么我们会有一个解码时间戳和一个 ...
- nginx防盗链
盗链是指一个网站的资源(图片或附件)未经允许在其它网站提供浏览和下载.尤其热门资源的盗链,对网站带宽的消耗非常大,本文通过nginx的配置指令location来实现简单的图片和其它类型文件的防盗链. ...
- 与班尼特·胡迪一起找简单规律(HZOJ-2262)
与班尼特·胡迪一起找简单规律 Time Limit: 1 s Memory Limit: 256 MB Description 班尼特·胡迪发现了一个简单规律 给定一个数列,1 , 1 ...
- Webpack的配置与使用
一.什么是Webpack? WebPack可以看做是模块打包机.用于分析项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),将 ...
- DDGScreenShot — 复杂屏幕截屏(如view ScrollView webView wkwebView)
写在前面 最近有这么一个需求,分享页面,分享的是web订单截图,既然是web 就会有超出屏幕的部分, 生成的图片还要加上我们的二维码,这就涉及到图片的合成了. 有了这样的需求,就是各种google.也 ...
- Python Tips阅读摘要
发现了一本关于Python精通知识点的好书<Python Tips>,关于Python的进阶的技巧.摘录一些比较有价值的内容作为分享. *args and **kwargs 在函数定义的时 ...
- 第三方支付设计——账户体系
第三方支付架构设计之-帐户体系 一, 什么是第三方支付? 什么是第三方支付?相信很多人对这个名字很熟悉,不管是从各种媒体等都经常听到,可以说是耳熟能熟.但,如果非得给这个名词 ...