From 6ee5d7b0bd4402052cbf9b81c86abbc6efd3008f Mon Sep 17 00:00:00 2001 From: Yad Smood Date: Fri, 28 Aug 2020 10:29:24 +0900 Subject: [PATCH] reduce dependencies This will reduce the binary size. The translator example reduced from 12mb to 8mb. resolve #187 --- .github/CONTRIBUTING.md | 2 +- .github/workflows/check-issues.yml | 2 +- .github/workflows/test.yml | 10 +- .gitignore | 1 + browser.go | 10 +- browser_test.go | 67 +++- context.go | 8 +- dev_helpers.go | 69 ++-- element.go | 5 +- element_test.go | 11 +- examples_test.go | 4 +- fixtures/gen-fonts/main.go | 5 +- go.mod | 7 +- go.sum | 113 +----- hijack.go | 187 +++------- hijack_test.go | 95 +++-- lib/assets/assets.go | 2 +- lib/assets/generate/main.go | 26 +- lib/assets/monitor.html | 2 +- lib/cdp/{main.go => client.go} | 6 +- ...private_test.go => client_private_test.go} | 12 +- lib/cdp/{main_test.go => client_test.go} | 34 +- lib/cdp/example_test.go | 6 +- lib/cdp/utils.go | 15 +- lib/defaults/{main.go => defaults.go} | 0 .../{main_test.go => defaults_test.go} | 0 lib/devices/generate/main.go | 16 +- lib/devices/utils.go | 3 +- lib/docker/test.Dockerfile | 2 +- lib/examples/debug-deadlock/main.go | 16 +- lib/examples/remote-launch/main.go | 4 +- lib/launcher/basic_test.go | 20 +- lib/launcher/browser.go | 10 +- lib/launcher/launcher.go | 29 +- lib/launcher/private_test.go | 18 +- lib/launcher/proxy.go | 14 +- lib/launcher/revision/main.go | 10 +- lib/launcher/rod-launcher/main.go | 53 +-- lib/launcher/utils.go | 5 +- lib/proto/generate/main.go | 37 +- lib/proto/generate/utils.go | 8 +- lib/proto/utils.go | 4 +- lib/proto/utils_test.go | 5 +- lib/utils/check-issue/main.go | 43 ++- lib/utils/main.go | 26 -- lib/utils/main_test.go | 17 - lib/utils/utils.go | 352 ++++++++++++++++++ lib/utils/utils_test.go | 199 ++++++++++ must.go | 45 +-- page.go | 14 +- page_test.go | 72 ++-- query.go | 12 +- query_test.go | 1 + setup_test.go | 48 +-- utils.go | 14 +- 55 files changed, 1066 insertions(+), 730 deletions(-) rename lib/cdp/{main.go => client.go} (98%) rename lib/cdp/{main_private_test.go => client_private_test.go} (92%) rename lib/cdp/{main_test.go => client_test.go} (76%) rename lib/defaults/{main.go => defaults.go} (100%) rename lib/defaults/{main_test.go => defaults_test.go} (100%) delete mode 100644 lib/utils/main.go delete mode 100644 lib/utils/main_test.go create mode 100644 lib/utils/utils.go create mode 100644 lib/utils/utils_test.go diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 7d6a4305..7effe435 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -39,7 +39,7 @@ We also use it in the CI to enforce the minimum test coverage. ### To run inside docker -1. `docker build -t rod -f lib/docker/test.Dockerfile .` +1. `docker build -t rod -f lib/docker/Dockerfile .` 2. `docker run --name rod -itp 9273:9273 -v $(pwd):/t -w /t rod sh` diff --git a/.github/workflows/check-issues.yml b/.github/workflows/check-issues.yml index ef115b0f..a89bad5d 100644 --- a/.github/workflows/check-issues.yml +++ b/.github/workflows/check-issues.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/setup-go@v2 with: - go-version: 1.14 + go-version: 1.15 - uses: actions/checkout@v2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d3131ca4..36b4f163 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,13 +21,17 @@ jobs: - uses: actions/setup-go@v2 with: - go-version: 1.14 + go-version: 1.15 - uses: actions/checkout@v2 - name: setup run: | - (cd ~ && GO111MODULE=on go get github.com/ysmood/kit/cmd/godev) + (cd ~ && GO111MODULE=on go get \ + github.com/ysmood/kit/cmd/godev \ + golang.org/x/tools/cmd/goimports \ + github.com/client9/misspell/cmd/misspell \ + ) go generate - name: test @@ -49,7 +53,7 @@ jobs: - uses: actions/setup-go@v2 with: - go-version: 1.14 + go-version: 1.15 - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index dd12b9b7..a2bfee10 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /lib/assets/node_modules tmp/ coverage.txt +/rod.test diff --git a/browser.go b/browser.go index a47ec558..4e51c8c9 100644 --- a/browser.go +++ b/browser.go @@ -16,8 +16,8 @@ import ( "github.com/go-rod/rod/lib/defaults" "github.com/go-rod/rod/lib/launcher" "github.com/go-rod/rod/lib/proto" + "github.com/go-rod/rod/lib/utils" "github.com/ysmood/goob" - "github.com/ysmood/kit" ) // Browser implements the proto.Caller interface @@ -34,7 +34,7 @@ type Browser struct { ctx context.Context ctxCancel func() timeoutCancel func() - sleeper kit.Sleeper + sleeper utils.Sleeper // BrowserContextID is the id for incognito window BrowserContextID proto.BrowserBrowserContextID @@ -48,8 +48,6 @@ type Browser struct { defaultViewport *proto.EmulationSetDeviceMetricsOverride - monitorServer *kit.ServerContext - client *cdp.Client cdpCall CDPCall event *goob.Observable // all the browser events from cdp client @@ -173,7 +171,7 @@ func (b *Browser) Connect() (err error) { b.client.Context(b.ctx, b.ctxCancel).MustConnect() - b.monitorServer = b.ServeMonitor(defaults.Monitor, !defaults.Blind) + b.ServeMonitor(defaults.Monitor, !defaults.Blind) b.initEvents() @@ -372,7 +370,7 @@ func (b *Browser) PageFromTarget(targetID proto.TargetTargetID) (*Page, error) { executionIDs: map[proto.PageFrameID]proto.RuntimeExecutionContextID{}, }).Context(context.WithCancel(b.ctx)) - page.Mouse = &Mouse{lock: &sync.Mutex{}, page: page, id: kit.RandString(8)} + page.Mouse = &Mouse{lock: &sync.Mutex{}, page: page, id: utils.RandString(8)} page.Keyboard = &Keyboard{lock: &sync.Mutex{}, page: page} err := page.initSession() diff --git a/browser_test.go b/browser_test.go index b2b96209..5b39db14 100644 --- a/browser_test.go +++ b/browser_test.go @@ -3,24 +3,25 @@ package rod_test import ( "context" "errors" + "net/http" + "os" + "os/exec" "runtime" "strings" "sync" "testing" "time" - "github.com/gin-gonic/gin" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/launcher" "github.com/go-rod/rod/lib/proto" "github.com/go-rod/rod/lib/utils" "github.com/tidwall/sjson" - "github.com/ysmood/kit" ) func (s *S) TestIncognito() { file := srcFile("fixtures/click.html") - k := kit.RandString(8) + k := utils.RandString(8) b := s.browser.MustIncognito() page := b.MustPage(file) @@ -90,7 +91,7 @@ func (s *S) TestBrowserCrash() { wait := browser.WaitEvent(&proto.PageFrameNavigated{}) go func() { - kit.Sleep(0.3) + utils.Sleep(0.3) _ = proto.BrowserCrash{}.Call(browser) }() @@ -112,23 +113,25 @@ func (s *S) TestMonitor() { b := rod.New().Timeout(1 * time.Minute).MustConnect() defer b.MustClose() p := b.MustPage(srcFile("fixtures/click.html")).MustWaitLoad() - host := b.ServeMonitor("127.0.0.1:0", true).Listener.Addr().String() + host := b.ServeMonitor("127.0.0.1:0", true) - page := s.page.MustNavigate("http://" + host) + page := s.page.MustNavigate(host) s.Contains(page.MustElement("#targets a").MustParent().MustHTML(), string(p.TargetID)) - page.MustNavigate("http://" + host + "/page/" + string(p.TargetID)) + page.MustNavigate(host + "/page/" + string(p.TargetID)) s.Contains(page.MustEval(`document.title`).Str, p.TargetID) - s.Equal(400, kit.Req("http://"+host+"/api/page/test").MustResponse().StatusCode) + res, err := http.Get(host + "/api/page/test") + utils.E(err) + s.Equal(400, res.StatusCode) } func (s *S) TestRemoteLaunch() { - url, engine, close := serve() + url, mux, close := utils.Serve("") defer close() proxy := launcher.NewProxy() - engine.NoRoute(gin.WrapH(proxy)) + mux.Handle("/", proxy) ctx, cancel := context.WithCancel(context.Background()) l := launcher.NewRemote(strings.ReplaceAll(url, "http", "ws")) @@ -183,7 +186,7 @@ func (s *S) TestConcurrentOperations() { list = append(list, item) } - kit.All(func() { + utils.All(func() { add(p.MustEval(`new Promise(r => setTimeout(r, 100, 2))`).Int()) }, func() { add(p.MustEval(`1`).Int()) @@ -203,10 +206,10 @@ func (s *S) TestPromiseLeak() { p := s.page.MustNavigate(srcFile("fixtures/click.html")) var out string - kit.All(func() { + utils.All(func() { out = p.MustEval(`new Promise(r => setTimeout(() => r(location.href), 200))`).String() }, func() { - kit.Sleep(0.1) + utils.Sleep(0.1) p.MustNavigate(srcFile("fixtures/input.html")) })() @@ -233,15 +236,15 @@ func (s *S) TestBlockingNavigation() { If one page is blocked, other pages should still work. */ - url, engine, close := serve() + url, mux, close := utils.Serve("") defer close() pause, cancel := context.WithCancel(context.Background()) defer cancel() - engine.GET("/a", func(ctx kit.GinContext) { + mux.HandleFunc("/a", func(w http.ResponseWriter, r *http.Request) { <-pause.Done() }) - engine.GET("/b", ginHTML(`ok`)) + mux.HandleFunc("/b", httpHTML(`ok`)) blocked := s.browser.MustPage("") defer blocked.MustClose() @@ -252,20 +255,20 @@ func (s *S) TestBlockingNavigation() { }) }() - kit.Sleep(0.3) + utils.Sleep(0.3) p := s.browser.MustPage(url + "/b") defer p.MustClose() } func (s *S) TestResolveBlocking() { - url, engine, close := serve() + url, mux, close := utils.Serve("") defer close() pause, cancel := context.WithCancel(context.Background()) defer cancel() - engine.NoRoute(func(ctx kit.GinContext) { + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { <-pause.Done() }) @@ -273,7 +276,7 @@ func (s *S) TestResolveBlocking() { defer p.MustClose() go func() { - kit.Sleep(0.1) + utils.Sleep(0.1) p.MustStopLoading() }() @@ -303,6 +306,30 @@ func (s *S) TestBrowserOthers() { }) } +func (s *S) TestBinarySize() { + if runtime.GOOS == "windows" { + s.T().SkipNow() + } + + cmd := exec.Command("go", "build", + "-trimpath", + "-ldflags", "-w -s", + "-o", "tmp/translator", + "./lib/examples/translator") + + cmd.Env = append(os.Environ(), "GOOS=linux") + + out, err := cmd.CombinedOutput() + if err != nil { + s.T().Skip(err, string(out)) + } + + stat, err := os.Stat("tmp/translator") + utils.E(err) + + s.Less(float64(stat.Size())/1024/1024, 8.65) // mb +} + // It's obvious that, the v8 will take more time to parse long function. // For BenchmarkCache and BenchmarkNoCache, the difference is nearly 12% which is too much to ignore. func BenchmarkCacheOff(b *testing.B) { diff --git a/context.go b/context.go index 0d6b62bb..05c0235a 100644 --- a/context.go +++ b/context.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/ysmood/kit" + "github.com/go-rod/rod/lib/utils" ) // Context creates a clone with a context that inherits the previous one @@ -34,7 +34,7 @@ func (b *Browser) CancelTimeout() *Browser { } // Sleeper for chained sub-operations -func (b *Browser) Sleeper(sleeper kit.Sleeper) *Browser { +func (b *Browser) Sleeper(sleeper utils.Sleeper) *Browser { newObj := *b newObj.sleeper = sleeper return &newObj @@ -67,7 +67,7 @@ func (p *Page) CancelTimeout() *Page { } // Sleeper for chained sub-operations -func (p *Page) Sleeper(sleeper kit.Sleeper) *Page { +func (p *Page) Sleeper(sleeper utils.Sleeper) *Page { newObj := *p newObj.sleeper = sleeper return &newObj @@ -100,7 +100,7 @@ func (el *Element) CancelTimeout() *Element { } // Sleeper for chained sub-operations -func (el *Element) Sleeper(sleeper kit.Sleeper) *Element { +func (el *Element) Sleeper(sleeper utils.Sleeper) *Element { newObj := *el newObj.sleeper = sleeper return &newObj diff --git a/dev_helpers.go b/dev_helpers.go index e36b9ce0..62b9e658 100644 --- a/dev_helpers.go +++ b/dev_helpers.go @@ -19,71 +19,62 @@ import ( "github.com/go-rod/rod/lib/launcher" "github.com/go-rod/rod/lib/proto" "github.com/go-rod/rod/lib/utils" - "github.com/ysmood/kit" ) // ServeMonitor starts the monitor server. // If openBrowser is true, it will try to launcher a browser to play the screenshots. // The reason why not to use "chrome://inspect/#devices" is one target cannot be driven by multiple controllers. -func (b *Browser) ServeMonitor(host string, openBrowser bool) *kit.ServerContext { +func (b *Browser) ServeMonitor(host string, openBrowser bool) string { if host == "" { - return nil + return "" } - srv := kit.MustServer(host) - opts := &http.Server{} - opts.SetKeepAlivesEnabled(false) - srv.Set(opts) - - srv.Engine.Use(func(ctx kit.GinContext) { - defer func() { - if err := recover(); err != nil { - _ = ctx.AbortWithError(400, fmt.Errorf("%v", err)) - } - }() - ctx.Next() - }) - srv.Engine.GET("/", func(ctx kit.GinContext) { - ginHTML(ctx, assets.Monitor) + u, mux, close := utils.Serve(host) + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + httHTML(w, assets.Monitor) }) - srv.Engine.GET("/pages", func(ctx kit.GinContext) { + mux.HandleFunc("/api/pages", func(w http.ResponseWriter, r *http.Request) { res, err := proto.TargetGetTargets{}.Call(b) utils.E(err) - ctx.PureJSON(http.StatusOK, res.TargetInfos) + w.WriteHeader(http.StatusOK) + utils.E(w.Write(utils.MustToJSONBytes(res.TargetInfos))) }) - srv.Engine.GET("/page/:id", func(ctx kit.GinContext) { - ginHTML(ctx, assets.MonitorPage) + mux.HandleFunc("/page/", func(w http.ResponseWriter, r *http.Request) { + httHTML(w, assets.MonitorPage) }) - srv.Engine.GET("/api/page/:id", func(ctx kit.GinContext) { - info, err := b.pageInfo(proto.TargetTargetID(ctx.Param("id"))) + mux.HandleFunc("/api/page/", func(w http.ResponseWriter, r *http.Request) { + id := r.URL.Path[strings.LastIndex(r.URL.Path, "/")+1:] + info, err := b.pageInfo(proto.TargetTargetID(id)) utils.E(err) - ctx.PureJSON(http.StatusOK, info) + w.WriteHeader(http.StatusOK) + utils.E(w.Write(utils.MustToJSONBytes(info))) }) - srv.Engine.GET("/screenshot/:id", func(ctx kit.GinContext) { - id := proto.TargetTargetID(ctx.Param("id")) - p := b.MustPageFromTargetID(id) + mux.HandleFunc("/screenshot/", func(w http.ResponseWriter, r *http.Request) { + id := r.URL.Path[strings.LastIndex(r.URL.Path, "/")+1:] + target := proto.TargetTargetID(id) + p := b.MustPageFromTargetID(target) - ctx.Header("Content-Type", "image/png;") - _, _ = ctx.Writer.Write(p.MustScreenshot()) + w.Header().Add("Content-Type", "image/png;") + utils.E(w.Write(p.MustScreenshot())) }) - go func() { _ = srv.Do() }() go func() { <-b.ctx.Done() - _ = srv.Listener.Close() + close() }() if openBrowser { - launcher.NewBrowser().Open("http://" + srv.Listener.Addr().String()) + launcher.NewBrowser().Open(u) } - return srv + return u } // Overlay a rectangle on the main frame with specified message func (p *Page) Overlay(left, top, width, height float64, msg string) (remove func()) { root := p.Root() - id := kit.RandString(8) + id := utils.RandString(8) _, err := root.EvalWithOptions(jsHelper(js.Overlay, Array{ id, @@ -113,7 +104,7 @@ func (p *Page) ExposeJSHelper() *Page { // Trace with an overlay on the element func (el *Element) Trace(msg string) (removeOverlay func()) { - id := kit.RandString(8) + id := utils.RandString(8) _, err := el.EvalWithOptions(jsHelper(js.ElementOverlay, Array{ id, @@ -174,7 +165,7 @@ func (p *Page) tryTraceFn(js string, params Array) func() { } func defaultTraceLogAct(msg string) { - log.Println(utils.C("act", "cyan"), msg) + log.Println("act", msg) } func defaultTraceLogJS(js string, params Array) { @@ -183,12 +174,12 @@ func defaultTraceLogJS(js string, params Array) { paramsStr = strings.Trim(mustToJSONForDev(params), "[]\r\n") } msg := fmt.Sprintf("%s(%s)", js, paramsStr) - log.Println(utils.C("js", "yellow"), msg) + log.Println("js", msg) } func defaultTraceLogErr(err error) { if err != context.Canceled && err != context.DeadlineExceeded { - log.Println(utils.C("[rod trace err]", "yellow"), err) + log.Println("[rod trace err]", err) } } diff --git a/element.go b/element.go index ad16d865..a768c7ae 100644 --- a/element.go +++ b/element.go @@ -14,7 +14,6 @@ import ( "github.com/go-rod/rod/lib/assets/js" "github.com/go-rod/rod/lib/proto" "github.com/go-rod/rod/lib/utils" - "github.com/ysmood/kit" ) // Element represents the DOM element @@ -22,7 +21,7 @@ type Element struct { ctx context.Context ctxCancel func() timeoutCancel func() - sleeper kit.Sleeper + sleeper utils.Sleeper page *Page @@ -406,7 +405,7 @@ func (el *Element) WaitStable(interval time.Duration) error { // Wait doc is similar to the method MustWait func (el *Element) Wait(js string, params ...interface{}) error { - return kit.Retry(el.ctx, el.sleeper, func() (bool, error) { + return utils.Retry(el.ctx, el.sleeper, func() (bool, error) { res, err := el.Eval(js, params...) if err != nil { return true, err diff --git a/element_test.go b/element_test.go index 8305c5f6..4b996e0c 100644 --- a/element_test.go +++ b/element_test.go @@ -16,7 +16,6 @@ import ( "github.com/go-rod/rod/lib/utils" "github.com/tidwall/gjson" "github.com/tidwall/sjson" - "github.com/ysmood/kit" ) func (s *S) TestClick() { @@ -368,9 +367,9 @@ func (s *S) TestWaitInvisible() { h4t.CancelTimeout() go func() { - kit.Sleep(0.03) + utils.Sleep(0.03) h4.MustEval(`this.remove()`) - kit.Sleep(0.03) + utils.Sleep(0.03) btn.MustEval(`this.style.visibility = 'hidden'`) }() @@ -389,7 +388,7 @@ func (s *S) TestWaitStable() { ctx, cancel := context.WithCancel(context.Background()) go func() { - kit.Sleep(0.2) + utils.Sleep(0.2) cancel() }() s.Error(el.Context(ctx, cancel).WaitStable(time.Minute)) @@ -409,7 +408,7 @@ func (s *S) TestResource() { func() { defer s.at(3, func(res []byte, err error) ([]byte, error) { - return kit.MustToJSONBytes(proto.PageGetResourceContentResult{ + return utils.MustToJSONBytes(proto.PageGetResourceContentResult{ Content: "ok", Base64Encoded: false, }), nil @@ -428,7 +427,7 @@ func (s *S) TestResource() { } func (s *S) TestElementScreenshot() { - f := filepath.Join("tmp", kit.RandString(8)+".png") + f := filepath.Join("tmp", utils.RandString(8)+".png") p := s.page.MustNavigate(srcFile("fixtures/click.html")) el := p.MustElement("h4") diff --git a/examples_test.go b/examples_test.go index 924bb06f..190c5571 100644 --- a/examples_test.go +++ b/examples_test.go @@ -347,7 +347,7 @@ func Example_hijack_requests() { // Here we update the request's header. Rod gives functionality to // change or update all parts of the request. Refer to the documentation // for more information. - ctx.Request.SetHeader("My-Header", "test") + ctx.Request.Req().Header.Set("My-Header", "test") // LoadResponse runs the default request to the destination of the request. // Not calling this will require you to mock the entire response. @@ -357,7 +357,7 @@ func Example_hijack_requests() { // Here we append some code to every js file. // The code will update the document title to "hi" - ctx.Response.SetBody(ctx.Response.StringBody() + "\n document.title = 'hi' ") + ctx.Response.SetBody(ctx.Response.Body() + "\n document.title = 'hi' ") }) go router.Run() diff --git a/fixtures/gen-fonts/main.go b/fixtures/gen-fonts/main.go index 62c476a4..13868f85 100644 --- a/fixtures/gen-fonts/main.go +++ b/fixtures/gen-fonts/main.go @@ -12,7 +12,6 @@ import ( "github.com/go-rod/rod" "github.com/go-rod/rod/lib/launcher" "github.com/go-rod/rod/lib/utils" - "github.com/ysmood/kit" ) func main() { @@ -41,7 +40,7 @@ func main() { name := lang.MustText() result := p.MustElement(".tlid-translation").MustText() for strings.Contains(result, "...") { - kit.Sleep(0.1) + utils.Sleep(0.1) result = p.MustElement(".tlid-translation").MustText() } log.Println(name, result) @@ -59,5 +58,5 @@ func main() { html, ) - utils.E(kit.OutputFile("fixtures/fonts.html", html, nil)) + utils.E(utils.OutputFile("fixtures/fonts.html", html, nil)) } diff --git a/go.mod b/go.mod index 5623913e..a8c3828e 100644 --- a/go.mod +++ b/go.mod @@ -1,19 +1,16 @@ module github.com/go-rod/rod -go 1.14 +go 1.15 require ( - github.com/gin-gonic/gin v1.6.3 github.com/gorilla/websocket v1.4.2 - github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/pkg/errors v0.9.1 github.com/ramr/go-reaper v0.2.0 github.com/stretchr/testify v1.6.1 github.com/tidwall/gjson v1.6.0 github.com/tidwall/sjson v1.1.1 github.com/ysmood/goob v0.2.2 - github.com/ysmood/kit v0.24.7 - github.com/ysmood/leakless v0.5.5 + github.com/ysmood/leakless v0.5.6 github.com/ysmood/lookpath v1.1.0 go.uber.org/goleak v1.1.10 ) diff --git a/go.sum b/go.sum index a1185208..dfc2da17 100644 --- a/go.sum +++ b/go.sum @@ -1,83 +1,16 @@ -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alessio/shellescape v1.2.2 h1:8LnL+ncxhWT2TR00dfJRT25JWWrhkMZXneHVWnetDZg= -github.com/alessio/shellescape v1.2.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bmatcuk/doublestar v1.3.1 h1:rT8rxDPsavp9G+4ZULzqhhUSaI/OPsTZNG88Z3i0xvY= -github.com/bmatcuk/doublestar v1.3.1/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= -github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/derekstavis/go-qs v0.0.0-20180720192143-9eef69e6c4e7 h1:zmAiXR9h1TCVN/0yCMRYQNE91dNRORpSzMFiqfTTPOs= -github.com/derekstavis/go-qs v0.0.0-20180720192143-9eef69e6c4e7/go.mod h1:Vgz4nKcG6+B7QcALsWZpmhyQTLSl7nwFGKSrbq2LxEo= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= -github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= -github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= -github.com/karrick/godirwalk v1.15.6 h1:Yf2mmR8TJy+8Fa0SuQVto5SYap6IF7lNVX4Jdl8G1qA= -github.com/karrick/godirwalk v1.15.6/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= -github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= -github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI= -github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= -github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc= -github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE= -github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg= github.com/ramr/go-reaper v0.2.0 h1:hhGZ1SRZ9fJfSEf9e14hRB4O0MafRwHK5O33j70qTNI= github.com/ramr/go-reaper v0.2.0/go.mod h1:DFg2AhfQCvkJwRKUfsycOSSZELGBA9gt46ne3SOecJM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -94,69 +27,33 @@ github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8= github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/sjson v1.1.1 h1:7h1vk049Jnd5EH9NyzNiEuwYW4b5qgreBbqRC19AS3U= github.com/tidwall/sjson v1.1.1/go.mod h1:yvVuSnpEQv5cYIrO+AT6kw4QVfd5SDZoGIS7/5+fZFs= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ysmood/byframe/v2 v2.0.2 h1:6/vQKeuE/DcP4C6rVvR3POVw0nF4rjpMUvcVm312BmE= github.com/ysmood/byframe/v2 v2.0.2/go.mod h1:FKUO9W0UUF29zC2oLCYtxoz3UBkFnV16sxKzdGsJ37k= github.com/ysmood/goob v0.2.2 h1:3WDGn6xZRB4riAyioATQJBi1eqcRIXsp6vGN3xbPAEk= github.com/ysmood/goob v0.2.2/go.mod h1:ilZKgv9nYmX7p8t1i9LVTzqHtLWNg0YOUBJhVb9xTIk= -github.com/ysmood/kit v0.24.7 h1:cYAAOINmWKu7Tjmm+geO1xFbeqsuv4KCh5TJMwteKH8= -github.com/ysmood/kit v0.24.7/go.mod h1:pCtRJuhw3s/v8GuNRn6IH0UirKSP8EOOL1KdxeCexqE= -github.com/ysmood/leakless v0.5.5 h1:4X5CAw/IpngYYhcesJimeWpZw9PSaZx0WWP8nfZX2Qw= -github.com/ysmood/leakless v0.5.5/go.mod h1:/PFSK7HDV1rxUx0/8ZaUawTFk4ukb2Cw5LHO5bq/8vE= +github.com/ysmood/leakless v0.5.6 h1:K3UAVKAavoXHEyrLel9hIkISfMEJMkIimU7DSQu51Wg= +github.com/ysmood/leakless v0.5.6/go.mod h1:ScLNHfq7wzNMaArZHzjL9P2zjMAL5kj+C17asiBlpVg= github.com/ysmood/lookpath v1.1.0 h1:heliJRj3thM8qw7236g5qDeI+vKELGndm+SWzwjxHqI= github.com/ysmood/lookpath v1.1.0/go.mod h1:QQh4rXcDdYAacpl7Q8cgZqkf+NRMJ4wc+lpQp0FgW+0= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= -golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200802091954-4b90ce9b60b3 h1:qDJKu1y/1SjhWac4BQZjLljqvqiWUhjmDMnonmVGDAU= -golang.org/x/sys v0.0.0-20200802091954-4b90ce9b60b3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200711155855-7342f9734a7d h1:F3OmlXCzYtG9YE6tXDnUOlJBzVzHF8EcmZ1yTJlcgIk= -golang.org/x/tools v0.0.0-20200711155855-7342f9734a7d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hijack.go b/hijack.go index e6bc6deb..06f3a23f 100644 --- a/hijack.go +++ b/hijack.go @@ -3,8 +3,7 @@ package rod import ( "bytes" "context" - "encoding/json" - "io" + "io/ioutil" "log" "net/http" "net/url" @@ -16,7 +15,6 @@ import ( "github.com/go-rod/rod/lib/proto" "github.com/go-rod/rod/lib/utils" "github.com/tidwall/gjson" - "github.com/ysmood/kit" ) // HijackRequests creates a new router instance for requests hijacking. @@ -141,19 +139,21 @@ func (r *HijackRouter) new(ctx context.Context, e *proto.FetchRequestPaused) *Hi headers[k] = []string{v.String()} } - req := kit.Req(e.Request.URL). - Method(e.Request.Method). - Headers(headers). - StringBody(e.Request.PostData). - Context(ctx) + u, _ := url.Parse(e.Request.URL) + + req := &http.Request{ + Method: e.Request.Method, + URL: u, + Body: ioutil.NopCloser(strings.NewReader(e.Request.PostData)), + Header: headers, + } return &Hijack{ Request: &HijackRequest{ event: e, - req: req, + req: req.WithContext(ctx), }, Response: &HijackResponse{ - req: req, payload: &proto.FetchFulfillRequest{ ResponseCode: 200, RequestID: e.RequestID, @@ -164,7 +164,7 @@ func (r *HijackRouter) new(ctx context.Context, e *proto.FetchRequestPaused) *Hi }, OnError: func(err error) { if err != context.Canceled { - log.Println(utils.C("[rod hijack err]", "yellow"), err) + log.Println("[rod hijack err]", err) } }, } @@ -206,19 +206,18 @@ func (h *Hijack) ContinueRequest(cq *proto.FetchContinueRequest) { } // LoadResponse will send request to the real destination and load the response as default response to override. -func (h *Hijack) LoadResponse(loadBody bool) error { - code, err := h.Response.StatusCode() +func (h *Hijack) LoadResponse(client *http.Client, loadBody bool) error { + res, err := client.Do(h.Request.req) if err != nil { return err } - h.Response.SetStatusCode(code) - headers, err := h.Response.Headers() - if err != nil { - return err - } + defer func() { _ = res.Body.Close() }() + + h.Response.payload.ResponseCode = int64(res.StatusCode) + list := []string{} - for k, vs := range headers { + for k, vs := range res.Header { for _, v := range vs { list = append(list, k, v) } @@ -226,11 +225,11 @@ func (h *Hijack) LoadResponse(loadBody bool) error { h.Response.SetHeader(list...) if loadBody { - body, err := h.Response.Body() + b, err := ioutil.ReadAll(res.Body) if err != nil { return err } - h.Response.SetBody(body) + h.Response.payload.Body = b } return nil @@ -239,7 +238,7 @@ func (h *Hijack) LoadResponse(loadBody bool) error { // HijackRequest context type HijackRequest struct { event *proto.FetchRequestPaused - req *kit.ReqContext + req *http.Request } // Type of the resource @@ -254,8 +253,7 @@ func (ctx *HijackRequest) Method() string { // URL of the request func (ctx *HijackRequest) URL() *url.URL { - u, err := url.Parse(ctx.event.Request.URL) - utils.E(err) // no way this will happen, if it happens it's fatal + u, _ := url.Parse(ctx.event.Request.URL) return u } @@ -279,136 +277,68 @@ func (ctx *HijackRequest) JSONBody() gjson.Result { return gjson.Parse(ctx.Body()) } -// SetMethod of request -func (ctx *HijackRequest) SetMethod(name string) *HijackRequest { - ctx.req.Method(name) - return ctx -} - -// SetHeader via key-value pairs -func (ctx *HijackRequest) SetHeader(pairs ...string) *HijackRequest { - ctx.req.Header(pairs...) - return ctx -} - -// SetQuery of the request, example Query(k, v, k, v ...) -func (ctx *HijackRequest) SetQuery(pairs ...interface{}) *HijackRequest { - ctx.req.Query(pairs...) - return ctx -} - -// SetURL of the request -func (ctx *HijackRequest) SetURL(url string) *HijackRequest { - ctx.req.URL(url) - return ctx +// Req returns the underlaying http.Request instance that will be used to send the request. +func (ctx *HijackRequest) Req() *http.Request { + return ctx.req } // SetBody of the request, if obj is []byte or string, raw body will be used, else it will be encoded as json. func (ctx *HijackRequest) SetBody(obj interface{}) *HijackRequest { - // reset to empty - ctx.req.StringBody("") - ctx.req.JSONBody(nil) + var b []byte switch body := obj.(type) { case []byte: - buf := bytes.NewBuffer(body) - ctx.req.Body(buf) + b = body case string: - ctx.req.StringBody(body) + b = []byte(body) default: - ctx.req.JSONBody(obj) + b = utils.MustToJSONBytes(body) } - return ctx -} -// SetClient for http -func (ctx *HijackRequest) SetClient(client *http.Client) *HijackRequest { - ctx.req.Client(client) + ctx.req.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + return ctx } // HijackResponse context type HijackResponse struct { - req *kit.ReqContext payload *proto.FetchFulfillRequest fail *proto.FetchFailRequest } -// StatusCode of response -func (ctx *HijackResponse) StatusCode() (int, error) { - res, err := ctx.req.Response() - if err != nil { - return 0, err - } - - return res.StatusCode, nil +// Payload to respond the request from the browser. +func (ctx *HijackResponse) Payload() *proto.FetchFulfillRequest { + return ctx.payload } -// SetStatusCode of response -func (ctx *HijackResponse) SetStatusCode(code int) { - ctx.payload.ResponseCode = int64(code) +// Body of the payload +func (ctx *HijackResponse) Body() string { + return string(ctx.payload.Body) } -// Header via key -func (ctx *HijackResponse) Header(key string) (string, error) { - res, err := ctx.req.Response() - if err != nil { - return "", err - } - - return res.Header.Get(key), nil -} +// Headers of the payload +func (ctx *HijackResponse) Headers() http.Header { + header := http.Header{} -// Headers of request -func (ctx *HijackResponse) Headers() (http.Header, error) { - res, err := ctx.req.Response() - if err != nil { - return nil, err + for _, h := range ctx.payload.ResponseHeaders { + header.Add(h.Name, h.Value) } - return res.Header, nil + return header } -// SetHeader via key-value pairs -func (ctx *HijackResponse) SetHeader(pairs ...string) { +// SetHeader of the payload via key-value pairs +func (ctx *HijackResponse) SetHeader(pairs ...string) *HijackResponse { for i := 0; i < len(pairs); i += 2 { ctx.payload.ResponseHeaders = append(ctx.payload.ResponseHeaders, &proto.FetchHeaderEntry{ Name: pairs[i], Value: pairs[i+1], }) } + return ctx } -// Body of response -func (ctx *HijackResponse) Body() ([]byte, error) { - b, err := ctx.req.Bytes() - if err != nil { - return nil, err - } - - return b, nil -} - -// BodyStream returns the stream of the body -func (ctx *HijackResponse) BodyStream() (io.Reader, error) { - res, err := ctx.req.Response() - if err != nil { - return nil, err - } - return res.Body, nil -} - -// StringBody of response -func (ctx *HijackResponse) StringBody() string { - return string(ctx.MustBody()) -} - -// JSONBody of response -func (ctx *HijackResponse) JSONBody() gjson.Result { - return gjson.ParseBytes(ctx.MustBody()) -} - -// SetBody of response, if obj is []byte, raw body will be used, else it will be encoded as json +// SetBody of the payload, if obj is []byte or string, raw body will be used, else it will be encoded as json. func (ctx *HijackResponse) SetBody(obj interface{}) *HijackResponse { switch body := obj.(type) { case []byte: @@ -416,10 +346,7 @@ func (ctx *HijackResponse) SetBody(obj interface{}) *HijackResponse { case string: ctx.payload.Body = []byte(body) default: - ctx.SetHeader("Content-Type", "application/json; charset=utf-8") - var err error - ctx.payload.Body, err = json.Marshal(obj) - utils.E(err) + ctx.payload.Body = utils.MustToJSONBytes(body) } return ctx } @@ -432,7 +359,7 @@ func (ctx *HijackResponse) Fail(reason proto.NetworkErrorReason) *HijackResponse // GetDownloadFile of the next download url that matches the pattern, returns the file content. // The handler will be used once and removed. -func (p *Page) GetDownloadFile(pattern string, resourceType proto.NetworkResourceType) func() (http.Header, io.Reader, error) { +func (p *Page) GetDownloadFile(pattern string, resourceType proto.NetworkResourceType, client *http.Client) func() (http.Header, []byte, error) { enable := p.DisableDomain(&proto.FetchEnable{}) _ = proto.BrowserSetDownloadBehavior{ @@ -446,7 +373,7 @@ func (p *Page) GetDownloadFile(pattern string, resourceType proto.NetworkResourc downloading := &proto.PageDownloadWillBegin{} waitDownload := p.Context(ctx, cancel).WaitEvent(downloading) - return func() (http.Header, io.Reader, error) { + return func() (http.Header, []byte, error) { defer enable() defer cancel() @@ -457,7 +384,7 @@ func (p *Page) GetDownloadFile(pattern string, resourceType proto.NetworkResourc }.Call(r.caller) }() - var body io.Reader + var body []byte var header http.Header wg := &sync.WaitGroup{} wg.Add(1) @@ -472,17 +399,13 @@ func (p *Page) GetDownloadFile(pattern string, resourceType proto.NetworkResourc err = e } - err = ctx.LoadResponse(false) - if err != nil { - return - } - - header, err = ctx.Response.Headers() + err = ctx.LoadResponse(client, true) if err != nil { return } - body, err = ctx.Response.BodyStream() + header = ctx.Response.Headers() + body = ctx.Response.payload.Body }) if err != nil { return nil, nil, err @@ -506,7 +429,7 @@ func (p *Page) GetDownloadFile(pattern string, resourceType proto.NetworkResourc if strings.HasPrefix(u, "data:") { t, d := parseDataURI(u) header = http.Header{"Content-Type": []string{t}} - body = bytes.NewBuffer(d) + body = d } else { return } diff --git a/hijack_test.go b/hijack_test.go index 64fe74ca..4de89593 100644 --- a/hijack_test.go +++ b/hijack_test.go @@ -8,43 +8,37 @@ import ( "github.com/go-rod/rod" "github.com/go-rod/rod/lib/proto" "github.com/go-rod/rod/lib/utils" - "github.com/ysmood/kit" ) func (s *S) TestHijack() { - url, engine, close := serve() + url, mux, close := utils.Serve("") defer close() // to simulate a backend server - engine.GET("/", ginHTMLFile("fixtures/fetch.html")) - engine.POST("/a", func(ctx kit.GinContext) { - s.Equal("header", ctx.GetHeader("Test")) + mux.HandleFunc("/", httpHTMLFile("fixtures/fetch.html")) + mux.HandleFunc("/a", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + panic("wrong http method") + } + + s.Equal("header", r.Header.Get("Test")) - b, err := ioutil.ReadAll(ctx.Request.Body) + b, err := ioutil.ReadAll(r.Body) utils.E(err) s.Equal("a", string(b)) - ginString("test")(ctx) + httpString("test")(w, r) }) - engine.GET("/b", ginString("b")) + mux.HandleFunc("/b", httpString("b")) router := s.page.HijackRequests() defer router.MustStop() router.MustAdd(url+"/a", func(ctx *rod.Hijack) { - ctx.Request. - SetClient(&http.Client{ - Transport: &http.Transport{ - DisableKeepAlives: true, - }, - }). // customize http client - SetMethod(ctx.Request.Method()). // override request method - SetURL(ctx.Request.URL().String()). // override request url - SetQuery("a", "b"). - SetHeader("Test", "header"). // override request header - SetBody(0). - SetBody([]byte("")). - SetBody(ctx.Request.Body()) // override request body + r := ctx.Request + r.Req().URL = r.Req().URL // override request url + r.Req().Header.Set("Test", "header") // override request header + r.SetBody(r.Body()) // override request body s.Equal(proto.NetworkResourceTypeXHR, ctx.Request.Type()) s.Contains(ctx.Request.Header("Origin"), url) @@ -54,24 +48,23 @@ func (s *S) TestHijack() { // send request load response from real destination as the default value to hijack ctx.MustLoadResponse() - s.Equal(200, ctx.Response.MustStatusCode()) + s.EqualValues(200, ctx.Response.Payload().ResponseCode) // override status code - ctx.Response.SetStatusCode(201) + ctx.Response.Payload().ResponseCode = http.StatusCreated - s.Equal("4", ctx.Response.MustHeader("Content-Length")) - s.Equal("text/plain; charset=utf-8", ctx.Response.MustHeaders().Get("Content-Type")) + s.Equal("4", ctx.Response.Headers().Get("Content-Length")) + s.Equal("text/plain; charset=utf-8", ctx.Response.Headers().Get("Content-Type")) // override response header ctx.Response.SetHeader("Set-Cookie", "key=val") // override response body - ctx.Response.SetBody("").SetBody(map[string]string{ - "text": ctx.Response.StringBody(), - }) + ctx.Response.SetBody(utils.MustToJSON(map[string]string{ + "text": ctx.Response.Body(), + })) - s.NotNil(ctx.Response.MustBodyStream()) - s.Equal("true", ctx.Response.JSONBody().String()) + s.Equal("{\"text\":\"test\"}", ctx.Response.Body()) }) router.MustAdd(url+"/b", func(ctx *rod.Hijack) { @@ -93,18 +86,18 @@ func (s *S) TestHijack() { } func (s *S) TestHijackContinue() { - url, engine, close := serve() + url, mux, close := utils.Serve("") defer close() // to simulate a backend server - engine.GET("/", ginHTML(` + mux.HandleFunc("/", httpHTML(` `)) - engine.GET("/a", ginString(`ok`)) + mux.HandleFunc("/a", httpString(`ok`)) router := s.page.HijackRequests() defer router.MustStop() @@ -121,11 +114,11 @@ func (s *S) TestHijackContinue() { } func (s *S) TestHijackFailRequest() { - url, engine, close := serve() + url, mux, close := utils.Serve("") defer close() // to simulate a backend server - engine.GET("/", ginHTML(` + mux.HandleFunc("/", httpHTML(`