Răsfoiți Sursa

wip:提交代理外部地址

wangkaiyue 2 ani în urmă
părinte
comite
5e2090598f

+ 30 - 0
core/proxy/broker/broker.go

@@ -4,6 +4,7 @@ import (
 	. "bp.jydev.jianyu360.cn/BaseService/gateway/common/gatecode"
 	"bp.jydev.jianyu360.cn/BaseService/gateway/core/proxy/loadmodule"
 	"fmt"
+	"github.com/gogf/gf/v2/net/ghttp"
 	"github.com/gogf/gf/v2/os/gcfg"
 	"github.com/gogf/gf/v2/os/gctx"
 	"log"
@@ -14,14 +15,21 @@ import (
 type broker struct {
 	sync.RWMutex
 	addresses map[string]loadmodule.ProxyLoadModule
+	outServer map[string]OutServerInterface
 }
 
 func InitBroker() *broker {
 	b := new(broker)
 	b.addresses = make(map[string]loadmodule.ProxyLoadModule)
+	b.outServer = make(map[string]OutServerInterface)
 	return b
 }
 
+type OutServerInterface interface {
+	Login() error                        //自动登录
+	RequestLogin(r *ghttp.Request) error //身份状态
+}
+
 // AddNode 发现注册新节点
 func (r *broker) AddNode(code, address string) error {
 	r.Lock()
@@ -59,3 +67,25 @@ func (r *broker) GetServerAddr(code, ip string) (*url.URL, error) {
 	}
 	return module.Get(ip)
 }
+
+// RegisterOutServer 注册外部服务
+func (b *broker) RegisterOutServer(u *url.URL, outServer OutServerInterface) {
+	if u != nil && outServer != nil {
+		b.outServer[u.String()] = outServer
+	}
+}
+
+// GetOutSeverAutoLogin 获取代理节点
+func (b *broker) GetOutSeverAutoLogin(address string, r *ghttp.Request) (serverUrl *url.URL, err error) {
+	serverUrl, err = url.Parse(address)
+	if err != nil {
+		return nil, err
+	}
+	server, ok := b.outServer[address]
+	if !ok {
+		return nil, fmt.Errorf("未知外部服务")
+	}
+	//装配登录身份
+	err = server.RequestLogin(r)
+	return
+}

+ 59 - 0
core/proxy/broker/outServer/SussBi.go

@@ -0,0 +1,59 @@
+package outServer
+
+import (
+	"fmt"
+	"github.com/gogf/gf/v2/net/ghttp"
+	"golang.org/x/net/publicsuffix"
+	"net/http"
+	"net/http/cookiejar"
+	"net/url"
+)
+
+type sussBi struct {
+	addr string
+	user string
+	pwd  string
+	Url  *url.URL
+	jar  *cookiejar.Jar
+}
+
+func InitSussBi(address, user, password string) *sussBi {
+	sussCookie, _ := cookiejar.New(&cookiejar.Options{
+		PublicSuffixList: publicsuffix.List,
+	})
+	u, _ := url.Parse(address)
+	ss := &sussBi{
+		addr: address,
+		user: user,
+		Url:  u,
+		pwd:  password,
+		jar:  sussCookie,
+	}
+	ss.Login()
+	return ss
+}
+
+// Login 自动登录
+func (s *sussBi) Login() error {
+	client := &http.Client{
+		Jar: s.jar,
+	}
+	resp, err := client.Get(fmt.Sprintf("%s/?:user=%s&:password=%s", s.addr, s.user, s.pwd))
+	if err != nil {
+		return err
+	}
+	if resp.StatusCode != 302 {
+		return fmt.Errorf("自动登录异常")
+	}
+	return nil
+}
+
+// RequestLogin 装配登录状态
+func (s *sussBi) RequestLogin(r *ghttp.Request) error {
+	if cookies := s.jar.Cookies(s.Url); len(cookies) > 0 {
+		r.Request.AddCookie(cookies[0])
+	} else {
+		return fmt.Errorf("装配登录身份异常")
+	}
+	return nil
+}

+ 29 - 2
core/proxy/proxyServer.go

@@ -4,6 +4,7 @@ import (
 	. "bp.jydev.jianyu360.cn/BaseService/gateway/common/gatecode"
 	"bp.jydev.jianyu360.cn/BaseService/gateway/core/node"
 	"bp.jydev.jianyu360.cn/BaseService/gateway/core/proxy/broker"
+	"bp.jydev.jianyu360.cn/BaseService/gateway/core/proxy/broker/outServer"
 	"bp.jydev.jianyu360.cn/BaseService/gateway/core/proxy/middleware"
 	"bp.jydev.jianyu360.cn/BaseService/gateway/core/proxy/proxyClient"
 	"bp.jydev.jianyu360.cn/BaseService/gateway/core/router"
@@ -12,7 +13,9 @@ import (
 	"github.com/gogf/gf/v2/net/ghttp"
 	"github.com/gogf/gf/v2/os/gcfg"
 	"github.com/gogf/gf/v2/os/gctx"
+	"log"
 	"net/http"
+	"net/url"
 )
 
 var bManager = broker.InitBroker()
@@ -33,6 +36,11 @@ func InitGateWayServer() *ghttp.Server {
 	if err != nil {
 		g.Log().Error(initCtx, err)
 	}
+	//初始化外部服务
+	sussBiServer := outServer.InitSussBi(gcfg.Instance().MustGet(initCtx, "outServer.sussbi.addr", nil).String(),
+		gcfg.Instance().MustGet(initCtx, "outServer.sussbi.user", nil).String(),
+		gcfg.Instance().MustGet(initCtx, "outServer.sussbi.password", nil).String())
+	bManager.RegisterOutServer(sussBiServer.Url, sussBiServer)
 
 	gateWayServer := g.Server()
 	//关闭系统自带请求日志
@@ -52,6 +60,7 @@ func InitGateWayServer() *ghttp.Server {
 	proxyClient.ReLoadClient()
 	//注册代理
 	gateWayServer.BindHandler("POST:/*", proxyHandler)
+	gateWayServer.BindHandler("GET:/*", proxyHandler)
 	return gateWayServer
 }
 
@@ -65,12 +74,20 @@ var proxyHandler = func(r *ghttp.Request) {
 	gCtx := router.GetGContext(r.GetCtx())
 	// 请求重试,防止某个服务中断不可用,导致接口不可用。
 	for i := 0; i < errTryTime; i++ {
-		// 根据负载规则获取服务地址
-		proxyAddr, err := bManager.GetServerAddr(gCtx.RouterRule.MiddleCode, r.GetClientIp())
+		var proxyAddr *url.URL
+		var err error
+		if gCtx.RouterRule.IsOutServer {
+			//外部服务直接获取
+			proxyAddr, err = bManager.GetOutSeverAutoLogin(gCtx.RouterRule.MiddleCode, r)
+		} else {
+			// 从etcd注册的地址,根据负载规则获取服务地址
+			proxyAddr, err = bManager.GetServerAddr(gCtx.RouterRule.MiddleCode, r.GetClientIp())
+		}
 		if err != nil {
 			r.SetError(err)
 			return
 		}
+
 		// 代理地址存入上下文ctx中
 		gCtx.ServerAddr = proxyAddr.String()
 		router.UpdateGContext(r, gCtx)
@@ -86,9 +103,19 @@ var proxyHandler = func(r *ghttp.Request) {
 			}
 			g.Log().Error(r.Context(), "ErrorHandler ", err)
 		}
+		log.Println("xxxx", r.Request.Header.Get("Cookie"))
 		// 代理请求
 		proxyClient.CreateCustomProxyClient(proxyAddr, errHandel).ServeHTTP(r.Response.ResponseWriter, r.Request)
 
+		//if gCtx.RouterRule.IsOutServer {
+		//	if r.Response.Status == 401 {
+		//		hasErr = true
+		//		log.Println("do login Again")
+		//		r.Request.Header.Set("Cookie", "JSESSIONID=A4745CB36FA67EE15BA162033B0343F5")
+		//		r.Response.Flush()
+		//	}
+		//}
+		//log.Println("qqqqq", r.Request.RequestURI, r.Response.Status)
 		// 未捕获到请求,标识请求成功
 		if !hasErr {
 			return

+ 4 - 1
core/router/manager.go

@@ -11,6 +11,7 @@ import (
 	"github.com/gogf/gf/v2/util/gconv"
 	"log"
 	"regexp"
+	"strings"
 )
 
 type Manager struct {
@@ -36,6 +37,7 @@ func InitRouterManager() (*Manager, error) {
 	for _, row := range *res {
 		// 获取路由信息
 		router := gconv.String(row["url"])
+		signCode := gconv.String(row["middleground_code"])
 		routerRule := &Router{
 			Status:       gconv.Int(row["status"]),
 			PowerCheck:   gconv.Int(row["check_power"]),
@@ -46,9 +48,10 @@ func InitRouterManager() (*Manager, error) {
 			LimitPloy:    gconv.String(row["ploy_code"]), //策略
 			Deduct:       gconv.Int(row["deduct_source"]),
 			FuncCode:     gconv.String(row["function_code"]),
-			MiddleCode:   gconv.String(row["middleground_code"]),
+			MiddleCode:   signCode,
 			AppId:        gconv.String(row["appid"]),
 			ReqUrl:       router,
+			IsOutServer:  strings.HasPrefix(signCode, "http://") || strings.HasPrefix(signCode, "https://"),
 			TimeOut:      gconv.Int64(row["timeout"]),
 			Remark:       gconv.String(row["remark"]),
 			PowerType:    gconv.Int(row["power_type"]),

+ 2 - 1
core/router/router.go

@@ -16,7 +16,8 @@ type Router struct {
 	BlackCheck   bool               // 是否校验黑名单 【是否校验黑名单; 0:不需要 1:需要】
 	LimitPloy    string             // 策略
 	FuncCode     string             // 功能代码
-	MiddleCode   string             // 中台代码
+	MiddleCode   string             // 中台代码 || 外部服务地址
+	IsOutServer  bool               //是否是外部服务
 	ReqUrl       string             // 请求地址
 	Remark       string             // 路由备注信息
 	PowerType    int                // 权益判断方式、0走资源中台  1前置代理判断,是否是付费用户  2前置代理判断,是否是超级订阅  3前置代理判断,是否是大会员  4前置代理判断,是否是商机管理

+ 5 - 0
etc/config.yaml

@@ -122,3 +122,8 @@ logger:
     id: pre_alert                       # id
     title: 你有新的告警消息处理            # 消息标题
     text: 前置代理告警请查看               # 消息正文
+outServer:
+  sussbi:
+    addr: http://192.168.3.10:8089
+    user: jianyuweb
+    password: 111111

+ 1 - 0
go.mod

@@ -13,6 +13,7 @@ require (
 	github.com/golang/freetype v0.0.0
 	github.com/zeromicro/go-zero v1.3.5
 	go.etcd.io/etcd/client/v3 v3.5.4
+	golang.org/x/net v0.1.0 // indirect
 )
 
 replace github.com/golang/freetype v0.0.0 => github.com/Kusers/freetype v0.0.0-20220714074437-04188d6f4e2c

+ 17 - 0
go.sum

@@ -573,6 +573,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
 github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw=
 github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
@@ -650,6 +651,8 @@ golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWP
 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/crypto v0.0.0-20210920023735-84f357641f63 h1:kETrAMYZq6WVGPa8IIixL0CaEcIUNi+1WX7grUoi3y8=
 golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -684,6 +687,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -728,6 +732,9 @@ golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA=
 golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -749,6 +756,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -818,11 +827,16 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
+golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -833,6 +847,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
 golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -893,6 +909,7 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=