├── .gitignore ├── go.mod ├── README.md ├── LICENSE ├── example ├── client │ └── main.go └── server │ └── main.go ├── tcpserver.go └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | example/out/ 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/x-mod/tcpserver 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/x-mod/event v0.0.2 7 | github.com/x-mod/glog v0.1.1 8 | github.com/x-mod/routine v1.3.3 9 | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 10 | ) 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tcpserver 2 | === 3 | 4 | ## Installation 5 | 6 | ```` 7 | $: go get github.com/x-mod/tcpserver 8 | ```` 9 | 10 | ## Quick Start 11 | 12 | ```` 13 | import ( 14 | "net" 15 | "log" 16 | "context" 17 | "github.com/x-mod/tcpserver" 18 | ) 19 | 20 | func EchoHandler(ctx context.Context, con net.Conn) error { 21 | //TODO LOGIC 22 | return nil 23 | } 24 | 25 | func main() { 26 | srv := tcpserver.New( 27 | tcpserver.Address(":8080"), 28 | //tcpserver.TLSConfig(tlsconfig), 29 | tcpserver.TCPHandler(EchoHandler), 30 | ) 31 | if err := srv.Serve(context.TODO()); err != nil { 32 | log.Println("tcpserver failed:", err) 33 | } 34 | } 35 | ```` 36 | 37 | More Details, Pls check the [example](example/server.go). 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 JayL gitdig.com@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "fmt" 7 | "io" 8 | "log" 9 | ) 10 | 11 | func main() { 12 | config := tls.Config{Certificates: []tls.Certificate{}, InsecureSkipVerify: true} 13 | conn, err := tls.Dial("tcp", "127.0.0.1:8080", &config) 14 | if err != nil { 15 | log.Fatalf("client: dial: %s", err) 16 | } 17 | defer conn.Close() 18 | log.Println("client: connected to: ", conn.RemoteAddr()) 19 | 20 | state := conn.ConnectionState() 21 | for _, v := range state.PeerCertificates { 22 | fmt.Println(x509.MarshalPKIXPublicKey(v.PublicKey)) 23 | fmt.Println(v.Subject) 24 | } 25 | log.Println("client: handshake: ", state.HandshakeComplete) 26 | log.Println("client: mutual: ", state.NegotiatedProtocolIsMutual) 27 | 28 | reply := make([]byte, 256) 29 | n, err := conn.Read(reply) 30 | log.Printf("client: read %q (%d bytes)", string(reply[:n]), n) 31 | 32 | message := "Hello\n" 33 | n, err = io.WriteString(conn, message) 34 | if err != nil { 35 | log.Fatalf("client: write: %s", err) 36 | } 37 | log.Printf("client: wrote %q (%d bytes)", message, n) 38 | 39 | //reply := make([]byte, 256) 40 | n, err = conn.Read(reply) 41 | log.Printf("client: read %q (%d bytes)", string(reply[:n]), n) 42 | log.Print("client: exiting") 43 | 44 | } 45 | -------------------------------------------------------------------------------- /example/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net" 7 | "net/textproto" 8 | "strings" 9 | "syscall" 10 | 11 | "github.com/x-mod/glog" 12 | "github.com/x-mod/routine" 13 | "github.com/x-mod/tcpserver" 14 | "golang.org/x/net/trace" 15 | ) 16 | 17 | func main() { 18 | glog.Open( 19 | glog.LogToStderr(true), 20 | glog.Verbosity(5), 21 | ) 22 | defer glog.Close() 23 | srv := tcpserver.New( 24 | tcpserver.Network("tcp"), 25 | tcpserver.Address("127.0.0.1:8080"), 26 | tcpserver.TCPHandler(EchoHandler), 27 | // tcpserver.TLSConfig(tlsconfig.New( 28 | // tlsconfig.CertKeyPair("out/server.crt", "out/server.key"), 29 | // )), 30 | ) 31 | 32 | ctx, cancel := context.WithCancel(context.TODO()) 33 | defer cancel() 34 | if err := routine.Main( 35 | ctx, 36 | routine.ExecutorFunc(func(ctx context.Context) error { 37 | <-srv.Serving() 38 | log.Println("serving ... now") 39 | <-ctx.Done() 40 | return ctx.Err() 41 | }), 42 | routine.Go(routine.ExecutorFunc(srv.Serve)), 43 | routine.Signal(syscall.SIGINT, routine.SigHandler(func() { 44 | cancel() 45 | log.Println("closing ... now") 46 | <-srv.Close() 47 | log.Println("closed") 48 | })), 49 | ); err != nil { 50 | log.Println("tcpserver failed:", err) 51 | } 52 | } 53 | 54 | func EchoHandler(ctx context.Context, con net.Conn) error { 55 | defer con.Close() 56 | c := textproto.NewConn(con) 57 | 58 | c.PrintfLine("HELLO From Server") 59 | for { 60 | select { 61 | case <-ctx.Done(): 62 | return ctx.Err() 63 | default: 64 | line, err := c.ReadLine() 65 | if err != nil { 66 | return err 67 | } 68 | if strings.HasPrefix(line, "quit") { 69 | return nil 70 | } 71 | if err := c.PrintfLine(line); err != nil { 72 | return err 73 | } 74 | if tr, ok := trace.FromContext(ctx); ok { 75 | tr.LazyPrintf("echo request: %s", line) 76 | } 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /tcpserver.go: -------------------------------------------------------------------------------- 1 | package tcpserver 2 | 3 | import ( 4 | "context" 5 | "crypto/tls" 6 | "fmt" 7 | "net" 8 | "runtime" 9 | "sync" 10 | 11 | "github.com/x-mod/event" 12 | "github.com/x-mod/glog" 13 | "golang.org/x/net/trace" 14 | ) 15 | 16 | // Handler connection handler definition 17 | type Handler func(ctx context.Context, conn net.Conn) error 18 | 19 | // Server represent tcpserver 20 | type Server struct { 21 | name string 22 | network string 23 | address string 24 | handler Handler 25 | traced bool 26 | mu sync.Mutex 27 | events trace.EventLog 28 | listener net.Listener 29 | tls *tls.Config 30 | openned *event.Event 31 | stopped *event.Event 32 | serving *event.Event 33 | wgroup sync.WaitGroup 34 | ctxCancel context.CancelFunc 35 | } 36 | 37 | // Name option for tcpserver 38 | func Name(name string) ServerOpt { 39 | return func(srv *Server) { 40 | srv.name = name 41 | } 42 | } 43 | 44 | // Network option for listener 45 | func Network(inet string) ServerOpt { 46 | return func(srv *Server) { 47 | if len(inet) != 0 { 48 | srv.network = inet 49 | } 50 | } 51 | } 52 | 53 | // Address option for listener 54 | func Address(addr string) ServerOpt { 55 | return func(srv *Server) { 56 | if len(addr) != 0 { 57 | srv.address = addr 58 | } 59 | } 60 | } 61 | 62 | // TLSConfig option 63 | func TLSConfig(tls *tls.Config) ServerOpt { 64 | return func(srv *Server) { 65 | srv.tls = tls 66 | } 67 | } 68 | 69 | // Listener option for listener 70 | func Listener(ln net.Listener) ServerOpt { 71 | return func(srv *Server) { 72 | if ln != nil { 73 | srv.listener = ln 74 | } 75 | } 76 | } 77 | 78 | // TCPHandler option for Connection Handler 79 | func TCPHandler(h Handler) ServerOpt { 80 | return func(srv *Server) { 81 | if h != nil { 82 | srv.handler = h 83 | } 84 | } 85 | } 86 | 87 | func NetTrace(flag bool) ServerOpt { 88 | return func(srv *Server) { 89 | srv.traced = flag 90 | } 91 | } 92 | 93 | // ServerOpt typedef 94 | type ServerOpt func(*Server) 95 | 96 | // NewServer create a new tcpserver 97 | func New(opts ...ServerOpt) *Server { 98 | serv := &Server{ 99 | name: "tcpserver", 100 | network: "tcp", 101 | openned: event.New(), 102 | serving: event.New(), 103 | stopped: event.New(), 104 | } 105 | for _, opt := range opts { 106 | opt(serv) 107 | } 108 | if serv.traced { 109 | _, file, line, _ := runtime.Caller(1) 110 | serv.events = trace.NewEventLog(serv.name, fmt.Sprintf("%s:%d", file, line)) 111 | } 112 | return serv 113 | } 114 | 115 | func (srv *Server) printf(format string, a ...interface{}) { 116 | srv.mu.Lock() 117 | defer srv.mu.Unlock() 118 | if srv.events != nil { 119 | srv.events.Printf(format, a...) 120 | } 121 | glog.V(2).Infof(format, a...) 122 | } 123 | 124 | func (srv *Server) errorf(format string, a ...interface{}) { 125 | srv.mu.Lock() 126 | defer srv.mu.Unlock() 127 | if srv.events != nil { 128 | srv.events.Errorf(format, a...) 129 | } 130 | glog.Errorf(format, a...) 131 | } 132 | 133 | func (srv *Server) Open() error { 134 | if srv.openned.HasFired() { 135 | return nil 136 | } 137 | if srv.handler == nil { 138 | return fmt.Errorf("tcpserver.Handler required") 139 | } 140 | if srv.listener == nil { 141 | ln, err := net.Listen(srv.network, srv.address) 142 | if err != nil { 143 | return err 144 | } 145 | // srv.printf("%s serving at %s:%s", srv.name, srv.network, srv.address) 146 | glog.Infof("%s serving at %s:%s", srv.name, srv.network, srv.address) 147 | srv.listener = ln 148 | 149 | } 150 | if srv.tls != nil { 151 | srv.listener = tls.NewListener(srv.listener, srv.tls) 152 | } 153 | 154 | srv.openned.Fire() 155 | return nil 156 | } 157 | 158 | // Serve tcpserver serving 159 | func (srv *Server) Serve(ctx context.Context) error { 160 | if err := srv.Open(); err != nil { 161 | return err 162 | } 163 | defer srv.listener.Close() 164 | //flags 165 | defer srv.stopped.Fire() 166 | srv.serving.Fire() 167 | //add context cancel 168 | ctx, cancel := context.WithCancel(ctx) 169 | srv.ctxCancel = cancel 170 | for { 171 | select { 172 | case <-ctx.Done(): 173 | srv.errorf("%s stopped: %v", srv.name, ctx.Err()) 174 | return ctx.Err() 175 | default: 176 | con, err := srv.listener.Accept() 177 | if err != nil { 178 | if ne, ok := err.(net.Error); ok && ne.Temporary() { 179 | srv.errorf("%s accept temp err: %v", srv.name, ne) 180 | continue 181 | } 182 | srv.errorf("%s accept failed: %v", srv.name, err) 183 | return err 184 | } 185 | 186 | srv.wgroup.Add(1) 187 | go func() { 188 | defer srv.wgroup.Done() 189 | if srv.traced { 190 | tr := trace.New("client", con.RemoteAddr().String()) 191 | ctx = trace.NewContext(ctx, tr) 192 | } 193 | if err := srv.handler(ctx, con); err != nil { 194 | srv.errorf("client (%s) failed: %v", con.RemoteAddr().String(), err) 195 | } 196 | if tr, ok := trace.FromContext(ctx); ok { 197 | tr.Finish() 198 | } 199 | }() 200 | } 201 | } 202 | } 203 | 204 | // Serving check 205 | func (srv *Server) Serving() <-chan struct{} { 206 | return srv.serving.Done() 207 | } 208 | 209 | func (srv *Server) IsServing() bool { 210 | return srv.serving.HasFired() 211 | } 212 | 213 | func (srv *Server) Stopped() <-chan struct{} { 214 | return srv.stopped.Done() 215 | } 216 | 217 | func (srv *Server) IsStopped() bool { 218 | return srv.stopped.HasFired() 219 | } 220 | 221 | // Close tcpserver waiting all connections finished 222 | func (srv *Server) Close() <-chan struct{} { 223 | if srv.serving.HasFired() { 224 | srv.listener.Close() 225 | srv.ctxCancel() 226 | srv.wgroup.Wait() 227 | return srv.stopped.Done() 228 | } 229 | return event.Done() 230 | } 231 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 4 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 7 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 8 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 9 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 10 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 11 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 12 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 13 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 14 | github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY= 15 | github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= 16 | github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= 17 | github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= 18 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 19 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 20 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 21 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 22 | github.com/x-mod/errors v0.2.3 h1:vLoKfXC1H25HcfsDFEyZNM5aBRqEL6veh1XdnHQ71n4= 23 | github.com/x-mod/errors v0.2.3/go.mod h1:Py3a5cvFx2w8QfqPFu1w6bt0czGdkzkfWzAbSbRQpTg= 24 | github.com/x-mod/event v0.0.2 h1:BLbHtzpwa1tBPi1VH9kB5ieP8xHgwKmrQRS/UUHnMFQ= 25 | github.com/x-mod/event v0.0.2/go.mod h1:zyojdq5vj6hPRXVHvqb1ka7UZ4rP8W10gfsHIBDAu9s= 26 | github.com/x-mod/glog v0.1.1 h1:GIyw6ncObjKFr8CVrXc70mXwvBOqxmVizif74ZlsLjI= 27 | github.com/x-mod/glog v0.1.1/go.mod h1:xSc+NEYXji4yUBSNPr7rkZNyP0YZMS2PwlR6ccn8beM= 28 | github.com/x-mod/routine v1.3.3 h1:ItHrQD7QKhpX+JPe3KoCEm8ONXBLk+CVzxW6GyBzErY= 29 | github.com/x-mod/routine v1.3.3/go.mod h1:RvKi6Hw6+0fz4AATOPSV1gmXU5dn73F5dDoqkVuStSc= 30 | github.com/x-mod/sigtrap v0.1.1 h1:sz48cJDqAW7EvbLNLJVbgp/gctddAfMaULVRHMEzbeI= 31 | github.com/x-mod/sigtrap v0.1.1/go.mod h1:rvv62V3LLxltc5la0XX0h/DFYGIrMi9usudVSMXGVh8= 32 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 33 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 34 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 35 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 36 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 37 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 38 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 39 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 40 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 41 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 42 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 43 | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M= 44 | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 45 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 46 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 47 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 48 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 49 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 50 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 51 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 52 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 53 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 54 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 55 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 56 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 57 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 58 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 59 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 60 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 61 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 62 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 63 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= 64 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 65 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 66 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 67 | google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= 68 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 69 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 70 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 71 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 72 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 73 | --------------------------------------------------------------------------------