node/cmd: add support for grpcweb and tls auto-provisioning
Change-Id: I41d76216fe4095772740e127adb0d62b05964d94
This commit is contained in:
parent
2437a76ad7
commit
a587316e7d
|
@ -81,6 +81,9 @@ var (
|
|||
publicRPC *string
|
||||
publicREST *string
|
||||
|
||||
tlsHostname *string
|
||||
tlsProdEnv *bool
|
||||
|
||||
disableHeartbeatVerify *bool
|
||||
|
||||
bigTablePersistenceEnabled *bool
|
||||
|
@ -127,7 +130,11 @@ func init() {
|
|||
nodeName = BridgeCmd.Flags().String("nodeName", "", "Node name to announce in gossip heartbeats")
|
||||
|
||||
publicRPC = BridgeCmd.Flags().String("publicRPC", "", "Listen address for public gRPC interface")
|
||||
publicREST = BridgeCmd.Flags().String("publicREST", "", "Listen address for public REST interface")
|
||||
publicREST = BridgeCmd.Flags().String("publicREST", "", "Listen address for public REST and gRPC Web interface")
|
||||
|
||||
tlsHostname = BridgeCmd.Flags().String("tlsHostname", "", "If set, serve publicREST as TLS with this hostname using Let's Encrypt")
|
||||
tlsProdEnv = BridgeCmd.Flags().Bool("tlsProdEnv", false,
|
||||
"Use the production Let's Encrypt environment instead of staging")
|
||||
|
||||
disableHeartbeatVerify = BridgeCmd.Flags().Bool("disableHeartbeatVerify", false,
|
||||
"Disable heartbeat signature verification (useful during network startup)")
|
||||
|
@ -427,7 +434,7 @@ func runBridge(cmd *cobra.Command, args []string) {
|
|||
|
||||
// subscriber channel multiplexing for public gPRC streams
|
||||
rawHeartbeatListeners := publicrpc.HeartbeatStreamMultiplexer(logger)
|
||||
publicrpcService, err := publicrpcServiceRunnable(logger, *publicRPC, rawHeartbeatListeners, db, gst)
|
||||
publicrpcService, publicrpcServer, err := publicrpcServiceRunnable(logger, *publicRPC, rawHeartbeatListeners, db, gst)
|
||||
if err != nil {
|
||||
log.Fatal("failed to create publicrpc service socket", zap.Error(err))
|
||||
}
|
||||
|
@ -438,7 +445,8 @@ func runBridge(cmd *cobra.Command, args []string) {
|
|||
logger.Fatal("failed to create admin service socket", zap.Error(err))
|
||||
}
|
||||
|
||||
publicrestService, err := publicrestServiceRunnable(logger, *publicREST, *adminSocketPath)
|
||||
publicrestService, err := publicrestServiceRunnable(logger, *publicREST, *adminSocketPath, publicrpcServer,
|
||||
*tlsHostname, *tlsProdEnv, path.Join(*dataDir, "autocert"))
|
||||
if err != nil {
|
||||
log.Fatal("failed to create publicrpc service socket", zap.Error(err))
|
||||
}
|
||||
|
|
|
@ -6,15 +6,52 @@ import (
|
|||
"github.com/certusone/wormhole/bridge/pkg/proto/publicrpc/v1"
|
||||
"github.com/certusone/wormhole/bridge/pkg/supervisor"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"github.com/improbable-eng/grpc-web/go/grpcweb"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/acme"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
"google.golang.org/grpc"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func allowCORSWrapper(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if origin := r.Header.Get("Origin"); origin != "" {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
if r.Method == "OPTIONS" && r.Header.Get("Access-Control-Request-Method") != "" {
|
||||
corsPreflightHandler(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func corsPreflightHandler(w http.ResponseWriter, r *http.Request) {
|
||||
headers := []string{
|
||||
"content-type",
|
||||
"accept",
|
||||
"x-user-agent",
|
||||
"x-grpc-web",
|
||||
"grpc-status",
|
||||
"grpc-message",
|
||||
"authorization",
|
||||
}
|
||||
w.Header().Set("Access-Control-Allow-Headers", strings.Join(headers, ","))
|
||||
methods := []string{"GET", "HEAD", "POST", "PUT", "DELETE"}
|
||||
w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ","))
|
||||
}
|
||||
|
||||
func publicrestServiceRunnable(
|
||||
logger *zap.Logger,
|
||||
listenAddr string,
|
||||
upstreamAddr string,
|
||||
grpcServer *grpc.Server,
|
||||
tlsHostname string,
|
||||
tlsProd bool,
|
||||
tlsCacheDir string,
|
||||
) (supervisor.Runnable, error) {
|
||||
return func(ctx context.Context) error {
|
||||
conn, err := grpc.DialContext(
|
||||
|
@ -31,16 +68,56 @@ func publicrestServiceRunnable(
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
grpcWebServer := grpcweb.WrapServer(grpcServer)
|
||||
mux.Handle("/", allowCORSWrapper(http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
if grpcWebServer.IsGrpcWebRequest(req) {
|
||||
grpcWebServer.ServeHTTP(resp, req)
|
||||
} else {
|
||||
gwmux.ServeHTTP(resp, req)
|
||||
}
|
||||
})))
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: listenAddr,
|
||||
Handler: gwmux,
|
||||
Addr: listenAddr,
|
||||
ErrorLog: log.Default(),
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
// TLS setup
|
||||
if tlsHostname != "" {
|
||||
logger.Info("provisioning Let's Encrypt certificate", zap.String("hostname", tlsHostname))
|
||||
|
||||
var acmeApi string
|
||||
if tlsProd {
|
||||
logger.Info("using production Let's Encrypt server")
|
||||
acmeApi = autocert.DefaultACMEDirectory
|
||||
} else {
|
||||
logger.Info("using staging Let's Encrypt server")
|
||||
acmeApi = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
}
|
||||
|
||||
certManager := autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: autocert.HostWhitelist(tlsHostname),
|
||||
Cache: autocert.DirCache(tlsCacheDir),
|
||||
Client: &acme.Client{DirectoryURL: acmeApi},
|
||||
}
|
||||
|
||||
srv.TLSConfig = certManager.TLSConfig()
|
||||
logger.Info("certificate provisioning configured")
|
||||
}
|
||||
|
||||
supervisor.Signal(ctx, supervisor.SignalHealthy)
|
||||
errC := make(chan error)
|
||||
go func() {
|
||||
logger.Info("publicrest server listening", zap.String("addr", srv.Addr))
|
||||
errC <- srv.ListenAndServe()
|
||||
if tlsHostname != "" {
|
||||
errC <- srv.ListenAndServeTLS("", "")
|
||||
} else {
|
||||
errC <- srv.ListenAndServe()
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
|
|
@ -12,10 +12,10 @@ import (
|
|||
"net"
|
||||
)
|
||||
|
||||
func publicrpcServiceRunnable(logger *zap.Logger, listenAddr string, hl *publicrpc.RawHeartbeatConns, db *db.Database, gst *common.GuardianSetState) (supervisor.Runnable, error) {
|
||||
func publicrpcServiceRunnable(logger *zap.Logger, listenAddr string, hl *publicrpc.RawHeartbeatConns, db *db.Database, gst *common.GuardianSetState) (supervisor.Runnable, *grpc.Server, error) {
|
||||
l, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to listen: %w", err)
|
||||
return nil, nil, fmt.Errorf("failed to listen: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("publicrpc server listening", zap.String("addr", l.Addr().String()))
|
||||
|
@ -24,5 +24,5 @@ func publicrpcServiceRunnable(logger *zap.Logger, listenAddr string, hl *publicr
|
|||
grpcServer := grpc.NewServer()
|
||||
publicrpcv1.RegisterPublicrpcServer(grpcServer, rpcServer)
|
||||
|
||||
return supervisor.GRPCServer(grpcServer, l, false), nil
|
||||
return supervisor.GRPCServer(grpcServer, l, false), grpcServer, nil
|
||||
}
|
||||
|
|
|
@ -5,12 +5,14 @@ go 1.16
|
|||
require (
|
||||
github.com/cenkalti/backoff/v4 v4.1.1
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgraph-io/badger/v3 v3.2103.1
|
||||
github.com/ethereum/go-ethereum v1.10.6
|
||||
github.com/gagliardetto/solana-go v0.3.5-0.20210727215348-0cf016734976
|
||||
github.com/gorilla/mux v1.7.4
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0
|
||||
github.com/improbable-eng/grpc-web v0.14.0 // indirect
|
||||
github.com/ipfs/go-log/v2 v2.3.0
|
||||
github.com/libp2p/go-libp2p v0.14.4
|
||||
github.com/libp2p/go-libp2p-connmgr v0.2.4
|
||||
|
@ -38,6 +40,7 @@ require (
|
|||
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced
|
||||
google.golang.org/grpc v1.38.0
|
||||
google.golang.org/protobuf v1.26.0
|
||||
nhooyr.io/websocket v1.8.7 // indirect
|
||||
)
|
||||
|
||||
// Temporary fork that adds GetConfirmedTransactionWithOpts. Can be removed
|
||||
|
|
|
@ -233,6 +233,8 @@ github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6Uh
|
|||
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0=
|
||||
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
|
||||
github.com/dfuse-io/binary v0.0.0-20210216024852-4ae6830a495d h1:EvEAQZ38olo+sALrqqCo345IeB2HH4im1deNOcjbi0s=
|
||||
github.com/dfuse-io/binary v0.0.0-20210216024852-4ae6830a495d/go.mod h1:GDFX6qH3BQZPWTeYaA4ZW98T94zs2skRoG3oMz/0jw0=
|
||||
github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI=
|
||||
|
@ -303,6 +305,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
|
|||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
|
@ -321,6 +325,10 @@ github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih
|
|||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
|
@ -328,6 +336,9 @@ github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
|||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
|
@ -509,6 +520,8 @@ github.com/huin/goupnp v1.0.1-0.20210626160114-33cdcbb30dda h1:Vofqyy/Ysqit++X33
|
|||
github.com/huin/goupnp v1.0.1-0.20210626160114-33cdcbb30dda/go.mod h1:0dxJBVBHqTMjIUMkESDTNgOOx/Mw5wYIfyFmdzSamkM=
|
||||
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/improbable-eng/grpc-web v0.14.0 h1:GdoK+cXABdB+1keuqsV1drSFO2XLYIxqt/4Rj8SWGBk=
|
||||
github.com/improbable-eng/grpc-web v0.14.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY=
|
||||
|
@ -620,6 +633,7 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
|
|||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.13.1 h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ=
|
||||
|
@ -645,6 +659,7 @@ 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/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ=
|
||||
github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU=
|
||||
|
@ -1303,7 +1318,9 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1
|
|||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4=
|
||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
|
@ -1852,6 +1869,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
|||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o=
|
||||
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
|
||||
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
|
|
Loading…
Reference in New Issue