From de2e0ebe93b65408fe1ea3296fad25da139656cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Mon, 16 Dec 2024 23:44:30 +0800 Subject: [PATCH 1/7] feat: add Listen method --- route.go | 35 ++++++++++++++ route_test.go | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) diff --git a/route.go b/route.go index 6586d9d..62c2afc 100644 --- a/route.go +++ b/route.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net" "net/http" "net/http/httptest" "time" @@ -88,6 +89,40 @@ func (r *Route) GlobalMiddleware(middlewares ...httpcontract.Middleware) { r.setMiddlewares(middlewares) } +func (r *Route) Listen(l net.Listener) error { + r.outputRoutes() + color.Green().Println(termlink.Link("[HTTP] Listening and serving HTTP on", "http://"+l.Addr().String())) + + r.server = &http.Server{ + Addr: l.Addr().String(), + Handler: http.AllowQuerySemicolons(r.instance), + MaxHeaderBytes: r.config.GetInt("http.drivers.gin.header_limit", 4096) << 10, + } + + if err := r.server.Serve(l); errors.Is(err, http.ErrServerClosed) { + return nil + } else { + return err + } +} + +func (r *Route) ListenTLS(l net.Listener, certFile, keyFile string) error { + r.outputRoutes() + color.Green().Println(termlink.Link("[HTTPS] Listening and serving HTTPS on", "https://"+l.Addr().String())) + + r.tlsServer = &http.Server{ + Addr: l.Addr().String(), + Handler: http.AllowQuerySemicolons(r.instance), + MaxHeaderBytes: r.config.GetInt("http.drivers.gin.header_limit", 4096) << 10, + } + + if err := r.tlsServer.ServeTLS(l, certFile, keyFile); errors.Is(err, http.ErrServerClosed) { + return nil + } else { + return err + } +} + func (r *Route) Run(host ...string) error { if len(host) == 0 { defaultHost := r.config.GetString("http.host") diff --git a/route_test.go b/route_test.go index 4311e48..851d8dc 100644 --- a/route_test.go +++ b/route_test.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "mime/multipart" + "net" "net/http" "net/http/httptest" "sync" @@ -43,6 +44,132 @@ func TestFallback(t *testing.T) { mockConfig.AssertExpectations(t) } +func TestListen(t *testing.T) { + var ( + err error + mockConfig *configmocks.Config + route *Route + ) + + tests := []struct { + name string + setup func(host string, port string) error + host string + port string + expectError error + }{ + { + name: "success listen", + setup: func(host string, port string) error { + mockConfig.EXPECT().GetBool("app.debug").Return(true).Once() + mockConfig.EXPECT().GetInt("http.drivers.gin.header_limit", 4096).Return(4096).Once() + go func() { + l, err := net.Listen("tcp", host) + assert.Nil(t, err) + assert.EqualError(t, route.Listen(l), "port can't be empty") + }() + time.Sleep(1 * time.Second) + + return errors.New("error") + }, + host: "127.0.0.1:3100", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + mockConfig = &configmocks.Config{} + mockConfig.EXPECT().GetBool("app.debug").Return(true).Once() + mockConfig.EXPECT().GetInt("http.drivers.gin.body_limit", 4096).Return(4096).Once() + + route, err = NewRoute(mockConfig, nil) + assert.Nil(t, err) + route.Get("/", func(ctx contractshttp.Context) contractshttp.Response { + return ctx.Response().Json(200, contractshttp.Json{ + "Hello": "Goravel", + }) + }) + if err := test.setup(test.host, test.port); err == nil { + time.Sleep(1 * time.Second) + hostUrl := "http://" + test.host + if test.port != "" { + hostUrl = hostUrl + ":" + test.port + } + resp, err := http.Get(hostUrl) + assert.Nil(t, err) + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + assert.Nil(t, err) + assert.Equal(t, "{\"Hello\":\"Goravel\"}", string(body)) + } + mockConfig.AssertExpectations(t) + }) + } +} + +func TestListenTLS(t *testing.T) { + var ( + err error + mockConfig *configmocks.Config + route *Route + ) + + tests := []struct { + name string + setup func(host string) error + host string + expectError error + }{ + { + name: "success listen", + setup: func(host string) error { + mockConfig.EXPECT().GetInt("http.drivers.gin.header_limit", 4096).Return(4096).Once() + mockConfig.EXPECT().GetBool("app.debug").Return(true).Once() + + go func() { + l, err := net.Listen("tcp", host) + assert.Nil(t, err) + assert.Nil(t, route.ListenTLS(l, "test_ca.crt", "test_ca.key")) + }() + + return nil + }, + host: "127.0.0.1:3101", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + mockConfig = configmocks.NewConfig(t) + mockConfig.EXPECT().GetBool("app.debug").Return(true).Once() + mockConfig.EXPECT().GetInt("http.drivers.gin.body_limit", 4096).Return(4096).Once() + + route, err = NewRoute(mockConfig, nil) + assert.Nil(t, err) + route.Get("/", func(ctx contractshttp.Context) contractshttp.Response { + return ctx.Response().Json(200, contractshttp.Json{ + "Hello": "Goravel", + }) + }) + if err := test.setup(test.host); err == nil { + time.Sleep(1 * time.Second) + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{Transport: tr} + resp, err := client.Get("https://" + test.host) + assert.Nil(t, err) + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + assert.Nil(t, err) + assert.Equal(t, "{\"Hello\":\"Goravel\"}", string(body)) + } + }) + } +} + func TestRun(t *testing.T) { var ( err error From 13ef389b74eb0fb824698064dafc68e9f7f99254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Mon, 16 Dec 2024 23:47:35 +0800 Subject: [PATCH 2/7] feat: optimize code --- route.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/route.go b/route.go index 62c2afc..785ac95 100644 --- a/route.go +++ b/route.go @@ -99,11 +99,11 @@ func (r *Route) Listen(l net.Listener) error { MaxHeaderBytes: r.config.GetInt("http.drivers.gin.header_limit", 4096) << 10, } - if err := r.server.Serve(l); errors.Is(err, http.ErrServerClosed) { - return nil - } else { + if err := r.server.Serve(l); !errors.Is(err, http.ErrServerClosed) { return err } + + return nil } func (r *Route) ListenTLS(l net.Listener, certFile, keyFile string) error { @@ -117,10 +117,10 @@ func (r *Route) ListenTLS(l net.Listener, certFile, keyFile string) error { } if err := r.tlsServer.ServeTLS(l, certFile, keyFile); errors.Is(err, http.ErrServerClosed) { - return nil - } else { return err } + + return nil } func (r *Route) Run(host ...string) error { @@ -144,10 +144,10 @@ func (r *Route) Run(host ...string) error { } if err := r.server.ListenAndServe(); errors.Is(err, http.ErrServerClosed) { - return nil - } else { return err } + + return nil } func (r *Route) RunTLS(host ...string) error { @@ -185,10 +185,10 @@ func (r *Route) RunTLSWithCert(host, certFile, keyFile string) error { } if err := r.tlsServer.ListenAndServeTLS(certFile, keyFile); errors.Is(err, http.ErrServerClosed) { - return nil - } else { return err } + + return nil } func (r *Route) ServeHTTP(writer http.ResponseWriter, request *http.Request) { From 34b7b7688dfe6c9e042f53b72e70447ebcc7d99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Tue, 17 Dec 2024 00:10:31 +0800 Subject: [PATCH 3/7] fix: test --- route_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/route_test.go b/route_test.go index 851d8dc..13c341e 100644 --- a/route_test.go +++ b/route_test.go @@ -66,7 +66,7 @@ func TestListen(t *testing.T) { go func() { l, err := net.Listen("tcp", host) assert.Nil(t, err) - assert.EqualError(t, route.Listen(l), "port can't be empty") + assert.Nil(t, route.Listen(l)) }() time.Sleep(1 * time.Second) From 6ad8e9dffe408ecd01612ba5e02fe983cdeb494c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Tue, 17 Dec 2024 00:16:09 +0800 Subject: [PATCH 4/7] fix: test --- route.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/route.go b/route.go index 785ac95..2373f75 100644 --- a/route.go +++ b/route.go @@ -116,7 +116,7 @@ func (r *Route) ListenTLS(l net.Listener, certFile, keyFile string) error { MaxHeaderBytes: r.config.GetInt("http.drivers.gin.header_limit", 4096) << 10, } - if err := r.tlsServer.ServeTLS(l, certFile, keyFile); errors.Is(err, http.ErrServerClosed) { + if err := r.tlsServer.ServeTLS(l, certFile, keyFile); !errors.Is(err, http.ErrServerClosed) { return err } @@ -143,7 +143,7 @@ func (r *Route) Run(host ...string) error { MaxHeaderBytes: r.config.GetInt("http.drivers.gin.header_limit", 4096) << 10, } - if err := r.server.ListenAndServe(); errors.Is(err, http.ErrServerClosed) { + if err := r.server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { return err } @@ -184,7 +184,7 @@ func (r *Route) RunTLSWithCert(host, certFile, keyFile string) error { MaxHeaderBytes: r.config.GetInt("http.drivers.gin.header_limit", 4096) << 10, } - if err := r.tlsServer.ListenAndServeTLS(certFile, keyFile); errors.Is(err, http.ErrServerClosed) { + if err := r.tlsServer.ListenAndServeTLS(certFile, keyFile); !errors.Is(err, http.ErrServerClosed) { return err } From 5cc3862b5ed68c388e09723ddf1b463d02aa0f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Tue, 17 Dec 2024 10:49:23 +0800 Subject: [PATCH 5/7] feat: add ListenTLSWithCert --- route.go | 6 ++++- route_test.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/route.go b/route.go index 2373f75..ceb093c 100644 --- a/route.go +++ b/route.go @@ -106,7 +106,11 @@ func (r *Route) Listen(l net.Listener) error { return nil } -func (r *Route) ListenTLS(l net.Listener, certFile, keyFile string) error { +func (r *Route) ListenTLS(l net.Listener) error { + return r.ListenTLSWithCert(l, r.config.GetString("http.tls.ssl.cert"), r.config.GetString("http.tls.ssl.key")) +} + +func (r *Route) ListenTLSWithCert(l net.Listener, certFile, keyFile string) error { r.outputRoutes() color.Green().Println(termlink.Link("[HTTPS] Listening and serving HTTPS on", "https://"+l.Addr().String())) diff --git a/route_test.go b/route_test.go index 13c341e..0d6c5de 100644 --- a/route_test.go +++ b/route_test.go @@ -115,6 +115,70 @@ func TestListenTLS(t *testing.T) { route *Route ) + tests := []struct { + name string + setup func(host string) error + host string + expectError error + }{ + { + name: "success listen", + setup: func(host string) error { + mockConfig.EXPECT().GetInt("http.drivers.gin.header_limit", 4096).Return(4096).Once() + mockConfig.EXPECT().GetString("http.tls.ssl.cert").Return("test_ca.crt").Once() + mockConfig.EXPECT().GetString("http.tls.ssl.key").Return("test_ca.key").Once() + mockConfig.EXPECT().GetBool("app.debug").Return(true).Once() + + go func() { + l, err := net.Listen("tcp", host) + assert.Nil(t, err) + assert.Nil(t, route.ListenTLS(l)) + }() + + return nil + }, + host: "127.0.0.1:3101", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + mockConfig = configmocks.NewConfig(t) + mockConfig.EXPECT().GetBool("app.debug").Return(true).Once() + mockConfig.EXPECT().GetInt("http.drivers.gin.body_limit", 4096).Return(4096).Once() + + route, err = NewRoute(mockConfig, nil) + assert.Nil(t, err) + route.Get("/", func(ctx contractshttp.Context) contractshttp.Response { + return ctx.Response().Json(200, contractshttp.Json{ + "Hello": "Goravel", + }) + }) + if err := test.setup(test.host); err == nil { + time.Sleep(1 * time.Second) + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{Transport: tr} + resp, err := client.Get("https://" + test.host) + assert.Nil(t, err) + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + assert.Nil(t, err) + assert.Equal(t, "{\"Hello\":\"Goravel\"}", string(body)) + } + }) + } +} + +func TestListenTLSWithCert(t *testing.T) { + var ( + err error + mockConfig *configmocks.Config + route *Route + ) + tests := []struct { name string setup func(host string) error @@ -130,7 +194,7 @@ func TestListenTLS(t *testing.T) { go func() { l, err := net.Listen("tcp", host) assert.Nil(t, err) - assert.Nil(t, route.ListenTLS(l, "test_ca.crt", "test_ca.key")) + assert.Nil(t, route.ListenTLSWithCert(l, "test_ca.crt", "test_ca.key")) }() return nil From 4cf74c8b63179fa66d5704f9d537208aaba17a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Tue, 17 Dec 2024 10:59:15 +0800 Subject: [PATCH 6/7] fix: test --- route_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/route_test.go b/route_test.go index 0d6c5de..07752b4 100644 --- a/route_test.go +++ b/route_test.go @@ -199,7 +199,7 @@ func TestListenTLSWithCert(t *testing.T) { return nil }, - host: "127.0.0.1:3101", + host: "127.0.0.1:3102", }, } From 593233b66ddd52deefa767997a81f24821c4a6f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Tue, 17 Dec 2024 12:14:40 +0800 Subject: [PATCH 7/7] feat: go mod tidy --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 141348f..ba808f3 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.23.4 require ( github.com/gin-gonic/gin v1.10.0 github.com/gookit/validate v1.5.3 - github.com/goravel/framework v1.14.1-0.20241214083012-9527d29ad584 + github.com/goravel/framework v1.14.1-0.20241217035357-003a28740efe github.com/rs/cors v1.11.1 github.com/savioxavier/termlink v1.4.1 github.com/spf13/cast v1.7.0 @@ -34,9 +34,9 @@ require ( github.com/catppuccin/go v0.2.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/bubbles v0.20.0 // indirect - github.com/charmbracelet/bubbletea v1.2.3 // indirect + github.com/charmbracelet/bubbletea v1.2.5-0.20241205214244-9306010a31ee // indirect github.com/charmbracelet/huh v0.6.0 // indirect - github.com/charmbracelet/huh/spinner v0.0.0-20241211235322-ceae3bbcfbb4 // indirect + github.com/charmbracelet/huh/spinner v0.0.0-20241216182847-438e4f741435 // indirect github.com/charmbracelet/lipgloss v1.0.0 // indirect github.com/charmbracelet/x/ansi v0.4.5 // indirect github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect @@ -154,7 +154,7 @@ require ( go.uber.org/multierr v1.9.0 // indirect golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.31.0 // indirect - golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect + golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e // indirect golang.org/x/net v0.31.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sync v0.10.0 // indirect diff --git a/go.sum b/go.sum index 8b3a4ea..5eb8374 100644 --- a/go.sum +++ b/go.sum @@ -132,12 +132,12 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= -github.com/charmbracelet/bubbletea v1.2.3 h1:d9MdMsANIYZB5pE1KkRqaUV6GfsiWm+/9z4fTuGVm9I= -github.com/charmbracelet/bubbletea v1.2.3/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM= +github.com/charmbracelet/bubbletea v1.2.5-0.20241205214244-9306010a31ee h1:xNijbIIsd6zADvvqrQj3kfKmLqJshZpCspKAfspXkFU= +github.com/charmbracelet/bubbletea v1.2.5-0.20241205214244-9306010a31ee/go.mod h1:Hbk5+oE4a7cDyjfdPi4sHZ42aGTMYcmHnVDhsRswn7A= github.com/charmbracelet/huh v0.6.0 h1:mZM8VvZGuE0hoDXq6XLxRtgfWyTI3b2jZNKh0xWmax8= github.com/charmbracelet/huh v0.6.0/go.mod h1:GGNKeWCeNzKpEOh/OJD8WBwTQjV3prFAtQPpLv+AVwU= -github.com/charmbracelet/huh/spinner v0.0.0-20241211235322-ceae3bbcfbb4 h1:FTJ/03WaUpEiZ5oK4/n22eqyAtj8Pi0Uu64oo9ZzBU8= -github.com/charmbracelet/huh/spinner v0.0.0-20241211235322-ceae3bbcfbb4/go.mod h1:3/xTBdgqRzAb+eUKRAGi9ix/K6QxsS0nGtd4zp+/tJs= +github.com/charmbracelet/huh/spinner v0.0.0-20241216182847-438e4f741435 h1:GnQvPBetPFyWaq4xVP4iia8UZAaLMVUk4UZ1O3Gdx44= +github.com/charmbracelet/huh/spinner v0.0.0-20241216182847-438e4f741435/go.mod h1:YqGqPo+vKnyTc0xppm1sv3Ir8FwG9bSW2H33LT++Xdg= github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM= @@ -370,8 +370,8 @@ github.com/gookit/validate v1.5.3 h1:czD1H+fcOJX+dfY0vzjHSMb+jrKTMeI8Fhu59ZMIB9o github.com/gookit/validate v1.5.3/go.mod h1:1OfvI1eGJqSODHERKdxvPGcconT4eTvVLmYt1A3QZlI= github.com/goravel/file-rotatelogs/v2 v2.4.2 h1:g68AzbePXcm0V2CpUMc9j4qVzcDn7+7aoWSjZ51C0m4= github.com/goravel/file-rotatelogs/v2 v2.4.2/go.mod h1:23VuSW8cBS4ax5cmbV+5AaiLpq25b8UJ96IhbAkdo8I= -github.com/goravel/framework v1.14.1-0.20241214083012-9527d29ad584 h1:fQAK2exHkhWX0EfBiqvmDiLAyCfHEKUXClF/AeR6sUU= -github.com/goravel/framework v1.14.1-0.20241214083012-9527d29ad584/go.mod h1:CNeStutJe+vR9tmCCuV09XicIPusG5lQCC1TVFuH+jA= +github.com/goravel/framework v1.14.1-0.20241217035357-003a28740efe h1:i8rVlwV4VeR5492mI8lRsxNLfIoLhRAtF8zfvPf3KZA= +github.com/goravel/framework v1.14.1-0.20241217035357-003a28740efe/go.mod h1:y4I8sLmuqf3jRKMvALwF3HB4ttKBFsZBLMBLkkfGoXg= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -694,8 +694,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= -golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4= +golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=