fiddler 一类的工具可以修改响应来做 mock 工具,支持 url 重写,本地文件替换响应和一些固定的状态码返回等,但想更新响应头或者进行请求,需要修改 fiddler Script。
所以这里萌生写 1 个 http(s) 专门来做这种代理型 mock 工具的想法。
http(s) 代理选用的是"github.com/elazarl/goproxy"这个包(其实这个包已经好久没更新了,且不支持 http/2(会降级),凑合着用吧)
proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
goproxyCa, err := tls.X509KeyPair(caCert, caKey)
if err != nil {
return err
}
// x509是Windows支持的cert格式
if goproxyCa.Leaf, err = x509.ParseCertificate(goproxyCa.Certificate[0]); err != nil {
return err
}
if goproxyCa.Leaf.NotAfter.Before(time.Now()){
return errors.New("current cert is expired, need using openssl to make a new one")
}
filePath:="./z.x509.cer"
if _,err:=os.Stat(filePath);err!=nil{
if os.IsNotExist(err){ // 如果不存在,则创建新的证书
certX509,err:=os.OpenFile(filePath,os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0766)
if err!=nil{
fmt.Println(err)
}
defer certX509.Close()
certX509.Write(goproxyCa.Leaf.Raw)
}
}
goproxy.GoproxyCa = goproxyCa
goproxy.OkConnect = &goproxy.ConnectAction{Action: goproxy.ConnectAccept, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)}
goproxy.MitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectMitm, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)}
goproxy.HTTPMitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectHTTPMitm, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)}
goproxy.RejectConnect = &goproxy.ConnectAction{Action: goproxy.ConnectReject, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)}
func updateResponse(resp *http.Response, r *respRule) {
resp.StatusCode = 200
resp.Header.Del("Location")
if r.RespAction.SetHeaders!=nil { // 设置请求头
for _,sh:=range r.RespAction.SetHeaders{
resp.Header.Set(sh.Header,sh.Value)
}
}
if r.RespAction.SetBody!= (setBody{}) {
switch r.RespAction.SetBody.BodyType {
case 0:
resp.Header.Set("Content-Type", ContentTypeJson)
case 1:
resp.Header.Set("Content-Type", ContentTypeText)
case 2:
resp.Header.Set("Content-Type", ContentTypeHtml)
case 3:
resp.Header.Set("Content-Type", ContentTypeJpeg)
default:
resp.Header.Set("Content-Type", ContentTypeText)
}
respFile,err:=os.Open(r.RespAction.SetBody.BodyFile)
if err!=nil{
log.Printf("文件%s无法打开\n",r.RespAction.SetBody.BodyFile)
return
}
defer respFile.Close()
rBytes,err:=ioutil.ReadAll(respFile)
if err!=nil{
log.Printf("文件%s打开内容报错请检查\n",r.RespAction.SetBody.BodyFile)
return
}
buf:=bytes.NewBuffer(rBytes)
resp.ContentLength = int64(buf.Len())
resp.Body=ioutil.NopCloser(buf)
}
}
windows 安装 z.x509.cer 证书(双击,不小心删除也没关系会重新生成),选择第三方根证书颁发机构,这样重启浏览器,使用 go_httpmock 的代理时的 https 请求就可信任了。
另外直接访问 127.0.0.1:8088 页面直接提供下载证书
在启动之前配置 upstreamProxyConfig.json 即可设置上行代理,结构如下:
upstreamProxyConfig.json
{
"proxyActive": false,
"proxyUrl":"http://192.168.16.67:8080",
"proxyUser": "",
"proxyPassword": ""
}
规则文件 rules.json 可随意更改,每隔 10s 自动更新一次(控制台中有提示)
有 2 种响应 mock,1 种是新建 http 响应,另 1 种是修改 http 响应。
这种不要求服务端可用,使用构造响应或转发 url 来返回相应,对应字段"newRespRules",它是个规则的列表。
构造响应 折叠源码
{
"active": true,
"urlMatchRegexp": "/api/v1/account/getUserInfo",
"respAction": {
"setHeaders": [
{"header": "Access-Control-Allow-Origin","value": "https://www.doba.com"},
{"header": "Set-Cookie","value": "lui=VjZnM1N0eGlYQnNZVlNjeTJHWjI0UT09;path=/;domain=.doba.com;HttpOnly"}
],
"bodyFile": "./respFiles/getUserInfo1.json"
}
}
转发 url 折叠源码
{
"active": false,
"urlMatchRegexp": "(channelhub/api/v1/shoppingCart/list)",
"reWriteUrl": "http://www.baidu.com/${1}",
"respAction":null
}
这种要求服务端可用,使用规则来更新服务端返回的响应的头及内容,对应字段"updateRespRules",它是个规则的列表。
更新响应的规则的结构
{
"active": true,
"urlMatchRegexp": "/api/v1/account/getUserInfo",
"respAction": {
"setHeaders": [
{"header": "Access-Control-Allow-Origin","value": "https://www.doba.com"},
{"header": "Set-Cookie","value": "lui=VjZnM1N0eGlYQnNZVlNjeTJHWjI0UT09;path=/;domain=.doba.com;HttpOnly"}
],
"bodyFile": "./respFiles/getUserInfo1.json"
}
}
与构造响应的规则结构与规则均一致,不多说了,但 setHeaders、setBody 都可为 null.
即存放需要替换响应的文件,比如 json、图片、html 等。
比如,通过 chrome 获取的接口响应内容 json,复制并保存下来,在 rules 中配置好路径,就可以用了。
内容随便改,响应随便变。
目前已经生成了 exe 文件,在 release 中
具体代码见:https://github.com/mao303mao/go_httpmock