fiddler 一类的工具可以修改响应来做 mock 工具,支持 url 重写,本地文件替换响应和一些固定的状态码返回等,但想更新响应头或者进行请求,需要修改 fiddler Script。
所以这里萌生写 1 个 http(s) 专门来做这种代理型 mock 工具的想法。

http(s) 代理选用的是"github.com/elazarl/goproxy"这个包(其实这个包已经好久没更新了,且不支持 http/2(会降级),凑合着用吧)

主要修改点

proxy 开启中间人攻击:

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 页面直接提供下载证书

【二】配置上行(upstream)代理

在启动之前配置 upstreamProxyConfig.json 即可设置上行代理,结构如下:

upstreamProxyConfig.json
{
  "proxyActive": false,
  "proxyUrl":"http://192.168.16.67:8080",
  "proxyUser": "",
  "proxyPassword": ""
}

【三】代理规则(rules.json)说明

规则文件 rules.json 可随意更改,每隔 10s 自动更新一次(控制台中有提示)
有 2 种响应 mock,1 种是新建 http 响应,另 1 种是修改 http 响应。

新建 http 响应

这种不要求服务端可用,使用构造响应或转发 url 来返回相应,对应字段"newRespRules",它是个规则的列表。

【1】构造响应的规则的结构

构造响应 折叠源码

{
      "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"

      }
    }
【2】转发 url 的规则的结构

转发 url 折叠源码

{
      "active": false,
      "urlMatchRegexp": "(channelhub/api/v1/shoppingCart/list)",
      "reWriteUrl": "http://www.baidu.com/${1}",
      "respAction":null
 }

修改 http 响应

这种要求服务端可用,使用规则来更新服务端返回的响应的头及内容,对应字段"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.

【四】响应文件(存放 respFiles 中)说明

即存放需要替换响应的文件,比如 json、图片、html 等。
比如,通过 chrome 获取的接口响应内容 json,复制并保存下来,在 rules 中配置好路径,就可以用了。
内容随便改,响应随便变。

目前已经生成了 exe 文件,在 release 中
具体代码见:https://github.com/mao303mao/go_httpmock


↙↙↙阅读原文可查看相关链接,并与作者交流