mirror of
https://github.com/linka-cloud/grpc.git
synced 2025-04-29 15:17:40 +00:00
Compare commits
62 Commits
Author | SHA1 | Date | |
---|---|---|---|
533a0ea43a | |||
8999dedd32 | |||
82e4d9a944 | |||
8eb63902b0 | |||
67e1c9be8e | |||
939c060513 | |||
174aa3a497 | |||
abe69f1c80 | |||
3a3d77169c | |||
9591a64e09 | |||
ccf44285f9 | |||
fd51ed5961 | |||
198bd2bd59 | |||
|
efaa4bd14f | ||
2380a4386c | |||
5d6b16a2c2 | |||
0b4d636ec2 | |||
8fd170c0a8 | |||
37b09f9f54 | |||
c0c19683cf | |||
f455c9994c | |||
97f48d30c0 | |||
b52ae2c670 | |||
724d6103c6 | |||
ef3af1e4d9 | |||
2f163ab7d1 | |||
0e1fe17b97 | |||
df505b58d7 | |||
1fa30d9706 | |||
36f3c5bc81 | |||
9baceef381 | |||
3af87d65d6 | |||
e295da1988 | |||
82d04d63b6 | |||
291c4f6361 | |||
19787f85ca | |||
e70369a902 | |||
2bb79e6c11 | |||
8f75b6aca4 | |||
926af303e8 | |||
ef9a12d89e | |||
01b37a0d91 | |||
dcd2f18f65 | |||
1d3d5315a4 | |||
23f1b78389 | |||
c7096975b1 | |||
9bf4e691ce | |||
b230278441 | |||
23370fd04d | |||
7c3a338748 | |||
d5210f8db5 | |||
8e6cfd2daa | |||
9729fb8b8a | |||
4de0ec6a3b | |||
6e86120943 | |||
bb7e4b124b | |||
d8443ee470 | |||
3fb566cb80 | |||
79771e58c1 | |||
5662486b3b | |||
ec06b7c4a2 | |||
9174446b2c |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,4 @@
|
|||||||
.idea
|
.idea
|
||||||
.bin
|
.bin
|
||||||
|
/tmp
|
||||||
|
diff
|
||||||
|
2
Makefile
2
Makefile
@ -1,4 +1,4 @@
|
|||||||
MODULE = go.linka.cloud/grpc
|
MODULE = go.linka.cloud/grpc-toolkit
|
||||||
|
|
||||||
|
|
||||||
PROTO_BASE_PATH = $(PWD)
|
PROTO_BASE_PATH = $(PWD)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# gRPC
|
# gRPC-toolkit
|
||||||
|
|
||||||
A utility module, largely taken from the [go-micro](https://github.com/micro/go-micro) patterns (and a good amount of code too...)
|
A utility module, largely taken from the [go-micro](https://github.com/micro/go-micro) patterns (and a good amount of code too...)
|
||||||
with pure gRPC ecosystem modules.
|
with pure gRPC ecosystem modules.
|
||||||
@ -9,7 +9,6 @@ Principles:
|
|||||||
|
|
||||||
Features:
|
Features:
|
||||||
- [x] simple configuration with options
|
- [x] simple configuration with options
|
||||||
- [x] embedded gorm database with options (branch db)
|
|
||||||
- [x] simple TLS configuration
|
- [x] simple TLS configuration
|
||||||
- [ ] TLS auth
|
- [ ] TLS auth
|
||||||
- [ ] client connection pool
|
- [ ] client connection pool
|
||||||
@ -24,7 +23,7 @@ Features:
|
|||||||
- [ ] context logger
|
- [ ] context logger
|
||||||
- [x] sentry
|
- [x] sentry
|
||||||
- [ ] rate-limiting
|
- [ ] rate-limiting
|
||||||
- [ ] ban
|
- [x] ban
|
||||||
- [ ] auth claim in context
|
- [ ] auth claim in context
|
||||||
- [x] recovery (server side only)
|
- [x] recovery (server side only)
|
||||||
- [x] tracing (open-tracing)
|
- [x] tracing (open-tracing)
|
||||||
|
@ -2,6 +2,7 @@ package certs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
@ -9,9 +10,17 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/config"
|
||||||
|
"go.linka.cloud/grpc-toolkit/config/file"
|
||||||
|
"go.linka.cloud/grpc-toolkit/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(host ...string) (tls.Certificate, error) {
|
func New(host ...string) (tls.Certificate, error) {
|
||||||
@ -72,3 +81,83 @@ func New(host ...string) (tls.Certificate, error) {
|
|||||||
|
|
||||||
return tls.X509KeyPair(certOut.Bytes(), keyOut.Bytes())
|
return tls.X509KeyPair(certOut.Bytes(), keyOut.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TLSConfig(ctx context.Context, cert, key string) (*tls.Config, error) {
|
||||||
|
c, err := Load(ctx, cert, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &tls.Config{
|
||||||
|
GetCertificate: c,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load(ctx context.Context, cert, key string) (func(info *tls.ClientHelloInfo) (*tls.Certificate, error), error) {
|
||||||
|
c, err := file.NewConfig(cert)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load cert: %v", err)
|
||||||
|
}
|
||||||
|
k, err := file.NewConfig(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load key: %v", err)
|
||||||
|
}
|
||||||
|
crt, err := load(c, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load cert: %v", err)
|
||||||
|
}
|
||||||
|
var mu sync.RWMutex
|
||||||
|
kch := make(chan []byte)
|
||||||
|
if err := k.Watch(ctx, kch); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to watch key: %v", err)
|
||||||
|
}
|
||||||
|
cch := make(chan []byte)
|
||||||
|
if err := c.Watch(ctx, cch); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to watch cert: %v", err)
|
||||||
|
}
|
||||||
|
reload := func() {
|
||||||
|
c, err := load(c, key)
|
||||||
|
// ignore errors due to cert and key not matching as this is expected
|
||||||
|
// when the cert is being reloaded and the key is not yet updated or vice versa
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "does not match") {
|
||||||
|
logger.C(ctx).Errorf("failed to reload cert: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
crt = c
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-kch:
|
||||||
|
reload()
|
||||||
|
case <-cch:
|
||||||
|
reload()
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
|
mu.RLock()
|
||||||
|
defer mu.RUnlock()
|
||||||
|
return crt, nil
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func load(cert config.Config, key string) (*tls.Certificate, error) {
|
||||||
|
cb, err := cert.Read()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read cert: %v", err)
|
||||||
|
}
|
||||||
|
kb, err := os.ReadFile(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read key: %v", err)
|
||||||
|
}
|
||||||
|
c, err := tls.X509KeyPair(cb, kb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &c, nil
|
||||||
|
}
|
||||||
|
96
certs/certs_test.go
Normal file
96
certs/certs_test.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package certs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoad(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
t.Run("missing", func(t *testing.T) {
|
||||||
|
fn, err := Load(ctx, "missing", "missing")
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Nil(t, fn)
|
||||||
|
})
|
||||||
|
dir, err := os.MkdirTemp("", "certs")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
var (
|
||||||
|
want tls.Certificate
|
||||||
|
fn func(*tls.ClientHelloInfo) (*tls.Certificate, error)
|
||||||
|
)
|
||||||
|
t.Run("load", func(t *testing.T) {
|
||||||
|
want, err = New("acme.org")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, want.PrivateKey)
|
||||||
|
require.NotEmpty(t, want.Certificate)
|
||||||
|
write(t, dir, want)
|
||||||
|
fn, err = Load(ctx, filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, fn)
|
||||||
|
got, err := fn(nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, got)
|
||||||
|
require.Equal(t, want.Certificate, got.Certificate)
|
||||||
|
require.Equal(t, want.PrivateKey, got.PrivateKey)
|
||||||
|
})
|
||||||
|
t.Run("reload", func(t *testing.T) {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
want, err = New("acme.org")
|
||||||
|
require.NoError(t, err)
|
||||||
|
write(t, dir, want)
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
got, err := fn(nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, got)
|
||||||
|
require.Equal(t, want.Certificate, got.Certificate)
|
||||||
|
require.Equal(t, want.PrivateKey, got.PrivateKey)
|
||||||
|
require.Equal(t, want.Leaf, got.Leaf)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("removed", func(t *testing.T) {
|
||||||
|
require.NoError(t, os.Remove(filepath.Join(dir, "cert.pem")))
|
||||||
|
require.NoError(t, os.Remove(filepath.Join(dir, "key.pem")))
|
||||||
|
got, err := fn(nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, got)
|
||||||
|
require.Equal(t, want.Certificate, got.Certificate)
|
||||||
|
require.Equal(t, want.PrivateKey, got.PrivateKey)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func write(t *testing.T, dir string, cert tls.Certificate) {
|
||||||
|
crt, err := os.Create(filepath.Join(dir, "cert.pem"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer crt.Close()
|
||||||
|
require.NoError(t, pem.Encode(crt, &pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: cert.Certificate[0],
|
||||||
|
}))
|
||||||
|
if err := crt.Sync(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
key, err := os.Create(filepath.Join(dir, "key.pem"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer key.Close()
|
||||||
|
b, err := x509.MarshalECPrivateKey(cert.PrivateKey.(*ecdsa.PrivateKey))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, pem.Encode(key, &pem.Block{
|
||||||
|
Type: "RSA PRIVATE KEY",
|
||||||
|
Bytes: b,
|
||||||
|
}))
|
||||||
|
if err := key.Sync(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
84
cli/clifmt/formatter.go
Normal file
84
cli/clifmt/formatter.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2023 Linka Cloud All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package clifmt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TimeFormat string
|
||||||
|
|
||||||
|
const (
|
||||||
|
NoneTimeFormat TimeFormat = "none"
|
||||||
|
FullTimeFormat TimeFormat = "full"
|
||||||
|
RelativeTimeFormat TimeFormat = "relative"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
red = 31
|
||||||
|
yellow = 33
|
||||||
|
blue = 36
|
||||||
|
white = 39
|
||||||
|
gray = 90
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(f TimeFormat) logrus.Formatter {
|
||||||
|
return &clifmt{start: time.Now(), format: f}
|
||||||
|
}
|
||||||
|
|
||||||
|
type clifmt struct {
|
||||||
|
start time.Time
|
||||||
|
format TimeFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *clifmt) Format(entry *logrus.Entry) ([]byte, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
var c *color.Color
|
||||||
|
switch entry.Level {
|
||||||
|
case logrus.DebugLevel, logrus.TraceLevel:
|
||||||
|
c = color.New(gray)
|
||||||
|
case logrus.WarnLevel:
|
||||||
|
c = color.New(yellow)
|
||||||
|
case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
|
||||||
|
c = color.New(red)
|
||||||
|
default:
|
||||||
|
c = color.New(white)
|
||||||
|
}
|
||||||
|
msg := entry.Message
|
||||||
|
if len(entry.Message) > 0 && entry.Level < logrus.DebugLevel {
|
||||||
|
msg = strings.ToTitle(string(msg[0])) + msg[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
switch f.format {
|
||||||
|
case FullTimeFormat:
|
||||||
|
_, err = c.Fprintf(&b, "[%s] %s\n", entry.Time.Format("2006-01-02 15:04:05"), entry.Message)
|
||||||
|
case RelativeTimeFormat:
|
||||||
|
_, err = c.Fprintf(&b, "[%5v] %s\n", entry.Time.Sub(f.start).Truncate(time.Second).String(), msg)
|
||||||
|
case NoneTimeFormat:
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
_, err = c.Fprintln(&b, msg)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
297
cli/command.go
Normal file
297
cli/command.go
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
// Package cli is adapted from https://github.com/rancher/wrangler-cli
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/logger"
|
||||||
|
"go.linka.cloud/grpc-toolkit/signals"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
caseRegexp = regexp.MustCompile("([a-z])([A-Z])")
|
||||||
|
)
|
||||||
|
|
||||||
|
type PersistentPreRunnable interface {
|
||||||
|
PersistentPre(cmd *cobra.Command, args []string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type PreRunnable interface {
|
||||||
|
Pre(cmd *cobra.Command, args []string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Runnable interface {
|
||||||
|
Run(cmd *cobra.Command, args []string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type customizer interface {
|
||||||
|
Customize(cmd *cobra.Command)
|
||||||
|
}
|
||||||
|
|
||||||
|
type fieldInfo struct {
|
||||||
|
FieldType reflect.StructField
|
||||||
|
FieldValue reflect.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func fields(obj interface{}) []fieldInfo {
|
||||||
|
ptrValue := reflect.ValueOf(obj)
|
||||||
|
objValue := ptrValue.Elem()
|
||||||
|
|
||||||
|
var result []fieldInfo
|
||||||
|
|
||||||
|
for i := 0; i < objValue.NumField(); i++ {
|
||||||
|
fieldType := objValue.Type().Field(i)
|
||||||
|
if !fieldType.IsExported() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fieldType.Anonymous && fieldType.Type.Kind() == reflect.Struct {
|
||||||
|
result = append(result, fields(objValue.Field(i).Addr().Interface())...)
|
||||||
|
} else if !fieldType.Anonymous {
|
||||||
|
result = append(result, fieldInfo{
|
||||||
|
FieldValue: objValue.Field(i),
|
||||||
|
FieldType: objValue.Type().Field(i),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func Name(obj interface{}) string {
|
||||||
|
ptrValue := reflect.ValueOf(obj)
|
||||||
|
objValue := ptrValue.Elem()
|
||||||
|
commandName := strings.Replace(objValue.Type().Name(), "Command", "", 1)
|
||||||
|
commandName, _ = name(commandName, "", "")
|
||||||
|
return commandName
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main(cmd *cobra.Command) {
|
||||||
|
ctx := signals.SetupSignalHandler()
|
||||||
|
if err := cmd.ExecuteContext(ctx); err != nil {
|
||||||
|
logger.C(ctx).Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeEnvVar[T comparable](to []func(), name string, vars []string, defValue T, flags *pflag.FlagSet, fn func(flag string) (T, error)) []func() {
|
||||||
|
for _, v := range vars {
|
||||||
|
to = append(to, func() {
|
||||||
|
v := os.Getenv(v)
|
||||||
|
if v == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fv, err := fn(name)
|
||||||
|
if err == nil && fv == defValue {
|
||||||
|
flags.Set(name, v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command populates a obj.Command() object by extracting args from struct tags of the
|
||||||
|
// Runnable obj passed. Also the Run method is assigned to the RunE of the cli.
|
||||||
|
// name = Override the struct field with
|
||||||
|
func Command(obj Runnable, c *cobra.Command) *cobra.Command {
|
||||||
|
var (
|
||||||
|
envs []func()
|
||||||
|
arrays = map[string]reflect.Value{}
|
||||||
|
slices = map[string]reflect.Value{}
|
||||||
|
maps = map[string]reflect.Value{}
|
||||||
|
ptrValue = reflect.ValueOf(obj)
|
||||||
|
objValue = ptrValue.Elem()
|
||||||
|
)
|
||||||
|
|
||||||
|
if c.Use == "" {
|
||||||
|
c.Use = Name(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, info := range fields(obj) {
|
||||||
|
fieldType := info.FieldType
|
||||||
|
v := info.FieldValue
|
||||||
|
|
||||||
|
name, alias := name(fieldType.Name, fieldType.Tag.Get("name"), fieldType.Tag.Get("short"))
|
||||||
|
usage := fieldType.Tag.Get("usage")
|
||||||
|
envVars := strings.Split(fieldType.Tag.Get("env"), ",")
|
||||||
|
defValue := fieldType.Tag.Get("default")
|
||||||
|
if len(envVars) == 1 && envVars[0] == "" {
|
||||||
|
envVars = nil
|
||||||
|
}
|
||||||
|
for _, v := range envVars {
|
||||||
|
if v == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
usage += fmt.Sprintf(" [$%s]", v)
|
||||||
|
}
|
||||||
|
defInt, err := strconv.Atoi(defValue)
|
||||||
|
if err != nil {
|
||||||
|
defInt = 0
|
||||||
|
}
|
||||||
|
defValueLower := strings.ToLower(defValue)
|
||||||
|
defBool := defValueLower == "true" || defValueLower == "1" || defValueLower == "yes" || defValueLower == "y"
|
||||||
|
|
||||||
|
flags := c.PersistentFlags()
|
||||||
|
switch fieldType.Type.Kind() {
|
||||||
|
case reflect.Int:
|
||||||
|
flags.IntVarP((*int)(unsafe.Pointer(v.Addr().Pointer())), name, alias, defInt, usage)
|
||||||
|
envs = append(envs, makeEnvVar(envs, name, envVars, defInt, flags, flags.GetInt)...)
|
||||||
|
case reflect.String:
|
||||||
|
flags.StringVarP((*string)(unsafe.Pointer(v.Addr().Pointer())), name, alias, defValue, usage)
|
||||||
|
envs = append(envs, makeEnvVar(envs, name, envVars, defValue, flags, flags.GetString)...)
|
||||||
|
case reflect.Slice:
|
||||||
|
// env is not supported for slices
|
||||||
|
switch fieldType.Tag.Get("split") {
|
||||||
|
case "false":
|
||||||
|
arrays[name] = v
|
||||||
|
flags.StringArrayP(name, alias, nil, usage)
|
||||||
|
default:
|
||||||
|
slices[name] = v
|
||||||
|
flags.StringSliceP(name, alias, nil, usage)
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
maps[name] = v
|
||||||
|
flags.StringSliceP(name, alias, nil, usage)
|
||||||
|
case reflect.Bool:
|
||||||
|
flags.BoolVarP((*bool)(unsafe.Pointer(v.Addr().Pointer())), name, alias, defBool, usage)
|
||||||
|
envs = append(envs, makeEnvVar(envs, name, envVars, defBool, flags, flags.GetBool)...)
|
||||||
|
default:
|
||||||
|
panic("Unknown kind on field " + fieldType.Name + " on " + objValue.Type().Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(envVars) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if p, ok := obj.(PersistentPreRunnable); ok {
|
||||||
|
c.PersistentPreRunE = p.PersistentPre
|
||||||
|
}
|
||||||
|
|
||||||
|
if p, ok := obj.(PreRunnable); ok {
|
||||||
|
c.PreRunE = p.Pre
|
||||||
|
}
|
||||||
|
|
||||||
|
c.RunE = obj.Run
|
||||||
|
c.PersistentPreRunE = bind(c.PersistentPreRunE, arrays, slices, maps, envs)
|
||||||
|
c.PreRunE = bind(c.PreRunE, arrays, slices, maps, envs)
|
||||||
|
c.RunE = bind(c.RunE, arrays, slices, maps, envs)
|
||||||
|
|
||||||
|
cust, ok := obj.(customizer)
|
||||||
|
if ok {
|
||||||
|
cust.Customize(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func assignMaps(app *cobra.Command, maps map[string]reflect.Value) error {
|
||||||
|
for k, v := range maps {
|
||||||
|
k = contextKey(k)
|
||||||
|
s, err := app.Flags().GetStringSlice(k)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s != nil {
|
||||||
|
values := map[string]string{}
|
||||||
|
for _, part := range s {
|
||||||
|
parts := strings.SplitN(part, "=", 2)
|
||||||
|
if len(parts) == 1 {
|
||||||
|
values[parts[0]] = ""
|
||||||
|
} else {
|
||||||
|
values[parts[0]] = parts[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.Set(reflect.ValueOf(values))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func assignSlices(app *cobra.Command, slices map[string]reflect.Value) error {
|
||||||
|
for k, v := range slices {
|
||||||
|
k = contextKey(k)
|
||||||
|
s, err := app.Flags().GetStringSlice(k)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s != nil {
|
||||||
|
v.Set(reflect.ValueOf(s[:]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func assignArrays(app *cobra.Command, arrays map[string]reflect.Value) error {
|
||||||
|
for k, v := range arrays {
|
||||||
|
k = contextKey(k)
|
||||||
|
s, err := app.Flags().GetStringArray(k)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s != nil {
|
||||||
|
v.Set(reflect.ValueOf(s[:]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func contextKey(name string) string {
|
||||||
|
parts := strings.Split(name, ",")
|
||||||
|
return parts[len(parts)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func name(name, setName, short string) (string, string) {
|
||||||
|
if setName != "" {
|
||||||
|
return setName, short
|
||||||
|
}
|
||||||
|
parts := strings.Split(name, "_")
|
||||||
|
i := len(parts) - 1
|
||||||
|
name = caseRegexp.ReplaceAllString(parts[i], "$1-$2")
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
result := append([]string{name}, parts[0:i]...)
|
||||||
|
for i := 0; i < len(result); i++ {
|
||||||
|
result[i] = strings.ToLower(result[i])
|
||||||
|
}
|
||||||
|
if short == "" && len(result) > 1 {
|
||||||
|
short = result[1]
|
||||||
|
}
|
||||||
|
return result[0], short
|
||||||
|
}
|
||||||
|
|
||||||
|
func bind(next func(*cobra.Command, []string) error,
|
||||||
|
arrays map[string]reflect.Value,
|
||||||
|
slices map[string]reflect.Value,
|
||||||
|
maps map[string]reflect.Value,
|
||||||
|
envs []func()) func(*cobra.Command, []string) error {
|
||||||
|
if next == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return func(cmd *cobra.Command, args []string) error {
|
||||||
|
for _, envCallback := range envs {
|
||||||
|
envCallback()
|
||||||
|
}
|
||||||
|
if err := assignArrays(cmd, arrays); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := assignSlices(cmd, slices); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := assignMaps(cmd, maps); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if next != nil {
|
||||||
|
return next(cmd, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
49
cli/command_test.go
Normal file
49
cli/command_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testCmd struct {
|
||||||
|
String string `name:"string" short:"s" usage:"string flag" env:"STRING" default:"string"`
|
||||||
|
Int int `name:"int" short:"i" usage:"int flag" env:"INT" default:"1"`
|
||||||
|
Bool bool `name:"bool" short:"b" usage:"bool flag" env:"BOOL" default:"true"`
|
||||||
|
StringSlice []string `name:"string-slice" short:"S" usage:"string slice flag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testCmd) Run(cmd *cobra.Command, args []string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommand(t *testing.T) {
|
||||||
|
var c testCmd
|
||||||
|
cmd := Command(&c, &cobra.Command{
|
||||||
|
Short: "test",
|
||||||
|
})
|
||||||
|
require.NoError(t, cmd.Execute())
|
||||||
|
assert.Equal(t, "string", c.String)
|
||||||
|
assert.Equal(t, 1, c.Int)
|
||||||
|
assert.Equal(t, true, c.Bool)
|
||||||
|
assert.Equal(t, []string{}, c.StringSlice)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandEnv(t *testing.T) {
|
||||||
|
require.NoError(t, os.Setenv("STRING", "env-string"))
|
||||||
|
require.NoError(t, os.Setenv("INT", "2"))
|
||||||
|
require.NoError(t, os.Setenv("BOOL", "false"))
|
||||||
|
require.NoError(t, os.Setenv("STRING_SLICE", "env-string1,env-string2"))
|
||||||
|
var c testCmd
|
||||||
|
cmd := Command(&c, &cobra.Command{
|
||||||
|
Short: "test",
|
||||||
|
})
|
||||||
|
require.NoError(t, cmd.Execute())
|
||||||
|
assert.Equal(t, "env-string", c.String)
|
||||||
|
assert.Equal(t, 2, c.Int)
|
||||||
|
assert.Equal(t, false, c.Bool)
|
||||||
|
assert.Equal(t, []string{}, c.StringSlice)
|
||||||
|
}
|
@ -2,16 +2,16 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
"google.golang.org/grpc/resolver"
|
"google.golang.org/grpc/resolver"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/registry/noop"
|
"go.linka.cloud/grpc-toolkit/registry/noop"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client interface {
|
type Client interface {
|
||||||
@ -27,15 +27,14 @@ func New(opts ...Option) (Client, error) {
|
|||||||
c.opts.registry = noop.New()
|
c.opts.registry = noop.New()
|
||||||
}
|
}
|
||||||
resolver.Register(c.opts.registry.ResolverBuilder())
|
resolver.Register(c.opts.registry.ResolverBuilder())
|
||||||
c.pool = newPool(DefaultPoolSize, DefaultPoolTTL, DefaultPoolMaxIdle, DefaultPoolMaxStreams)
|
if err := c.opts.parseTLSConfig(); err != nil {
|
||||||
if c.opts.tlsConfig == nil && c.opts.Secure() {
|
return nil, err
|
||||||
c.opts.tlsConfig = &tls.Config{InsecureSkipVerify: true}
|
|
||||||
}
|
}
|
||||||
if c.opts.tlsConfig != nil {
|
if c.opts.tlsConfig != nil {
|
||||||
c.opts.dialOptions = append(c.opts.dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(c.opts.tlsConfig)))
|
c.opts.dialOptions = append(c.opts.dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(c.opts.tlsConfig)))
|
||||||
}
|
}
|
||||||
if !c.opts.secure {
|
if !c.opts.secure && c.opts.tlsConfig == nil {
|
||||||
c.opts.dialOptions = append(c.opts.dialOptions, grpc.WithInsecure())
|
c.opts.dialOptions = append(c.opts.dialOptions, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
}
|
}
|
||||||
if len(c.opts.unaryInterceptors) > 0 {
|
if len(c.opts.unaryInterceptors) > 0 {
|
||||||
c.opts.dialOptions = append(c.opts.dialOptions, grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(c.opts.unaryInterceptors...)))
|
c.opts.dialOptions = append(c.opts.dialOptions, grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(c.opts.unaryInterceptors...)))
|
||||||
@ -43,37 +42,39 @@ func New(opts ...Option) (Client, error) {
|
|||||||
if len(c.opts.streamInterceptors) > 0 {
|
if len(c.opts.streamInterceptors) > 0 {
|
||||||
c.opts.dialOptions = append(c.opts.dialOptions, grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(c.opts.streamInterceptors...)))
|
c.opts.dialOptions = append(c.opts.dialOptions, grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(c.opts.streamInterceptors...)))
|
||||||
}
|
}
|
||||||
if c.opts.addr == "" {
|
switch {
|
||||||
|
case c.opts.addr == "":
|
||||||
c.addr = fmt.Sprintf("%s:///%s", c.opts.registry.String(), c.opts.name)
|
c.addr = fmt.Sprintf("%s:///%s", c.opts.registry.String(), c.opts.name)
|
||||||
} else if strings.HasPrefix(c.opts.addr, "tcp://") {
|
case strings.HasPrefix(c.opts.addr, "tcp://"):
|
||||||
c.addr = strings.Replace(c.opts.addr, "tcp://", "", 1)
|
c.addr = strings.Replace(c.opts.addr, "tcp://", "", 1)
|
||||||
} else {
|
case strings.HasPrefix(c.opts.addr, "unix:///"):
|
||||||
|
c.addr = c.opts.addr
|
||||||
|
case strings.HasPrefix(c.opts.addr, "unix://"):
|
||||||
|
c.addr = strings.Replace(c.opts.addr, "unix://", "unix:", 1)
|
||||||
|
default:
|
||||||
c.addr = c.opts.addr
|
c.addr = c.opts.addr
|
||||||
}
|
}
|
||||||
if c.opts.version != "" && c.opts.addr == "" {
|
if c.opts.version != "" && c.opts.addr == "" {
|
||||||
c.addr = c.addr + ":" + strings.TrimSpace(c.opts.version)
|
c.addr = c.addr + ":" + strings.TrimSpace(c.opts.version)
|
||||||
}
|
}
|
||||||
|
cc, err := grpc.Dial(c.addr, c.opts.dialOptions...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.cc = cc
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
addr string
|
addr string
|
||||||
pool *pool
|
|
||||||
opts *options
|
opts *options
|
||||||
|
cc *grpc.ClientConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Invoke(ctx context.Context, method string, args interface{}, reply interface{}, opts ...grpc.CallOption) error {
|
func (c *client) Invoke(ctx context.Context, method string, args interface{}, reply interface{}, opts ...grpc.CallOption) error {
|
||||||
pc, err := c.pool.getConn(c.addr, c.opts.dialOptions...)
|
return c.cc.Invoke(ctx, method, args, reply, opts...)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return pc.Invoke(ctx, method, args, reply, opts...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) NewStream(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
func (c *client) NewStream(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||||
pc, err := c.pool.getConn(c.addr, c.opts.dialOptions...)
|
return c.cc.NewStream(ctx, desc, method, opts...)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return pc.NewStream(ctx, desc, method, opts...)
|
|
||||||
}
|
}
|
||||||
|
@ -12,29 +12,31 @@ var u = strings.ToUpper
|
|||||||
|
|
||||||
func NewFlagSet() (*pflag.FlagSet, Option) {
|
func NewFlagSet() (*pflag.FlagSet, Option) {
|
||||||
const (
|
const (
|
||||||
addr = "address"
|
addr = "address"
|
||||||
secure = "secure"
|
secure = "secure"
|
||||||
// caCert = "ca-cert"
|
caCert = "ca-cert"
|
||||||
// clientCert = "client-cert"
|
clientCert = "client-cert"
|
||||||
// clientKey = "client-key"
|
clientKey = "client-key"
|
||||||
)
|
)
|
||||||
var (
|
var (
|
||||||
optAddress string
|
optAddress string
|
||||||
optSecure bool
|
optSecure bool
|
||||||
// optCACert string
|
optCACert string
|
||||||
// optCert string
|
optCert string
|
||||||
// optKey string
|
optKey string
|
||||||
)
|
)
|
||||||
flags := pflag.NewFlagSet("gRPC", pflag.ContinueOnError)
|
flags := pflag.NewFlagSet("gRPC", pflag.ContinueOnError)
|
||||||
flags.StringVar(&optAddress, addr, env.GetDefault(u(addr), "0.0.0.0:0"), "Bind address for the server. 127.0.0.1:9090"+flagEnv(addr))
|
flags.StringVar(&optAddress, addr, env.GetDefault(u(addr), "0.0.0.0:0"), "Bind address for the server. 127.0.0.1:9090"+flagEnv(addr))
|
||||||
flags.BoolVar(&optSecure, secure, env.GetBoolDefault(u(secure), true), "Generate self signed certificate if none provided"+flagEnv(secure))
|
flags.BoolVar(&optSecure, secure, env.GetBoolDefault(u(secure), true), "Generate self signed certificate if none provided"+flagEnv(secure))
|
||||||
// flags.StringVar(&optCACert, caCert, "", "Path to Root CA certificate"+flagEnv(optCACert))
|
flags.StringVar(&optCACert, caCert, "", "Path to Root CA certificate"+flagEnv(optCACert))
|
||||||
// flags.StringVar(&optCert, clientCert, "", "Path to Server certificate"+flagEnv(clientCert))
|
flags.StringVar(&optCert, clientCert, "", "Path to Server certificate"+flagEnv(clientCert))
|
||||||
// flags.StringVar(&optKey, clientKey, "", "Path to Server key"+flagEnv(clientKey))
|
flags.StringVar(&optKey, clientKey, "", "Path to Server key"+flagEnv(clientKey))
|
||||||
return flags, func(o *options) {
|
return flags, func(o *options) {
|
||||||
o.addr = optAddress
|
o.addr = optAddress
|
||||||
o.secure = optSecure
|
o.secure = optSecure
|
||||||
|
o.caCert = optCACert
|
||||||
|
o.cert = optCert
|
||||||
|
o.key = optKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,14 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/interceptors"
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
"go.linka.cloud/grpc/registry"
|
"go.linka.cloud/grpc-toolkit/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options interface {
|
type Options interface {
|
||||||
@ -15,6 +18,9 @@ type Options interface {
|
|||||||
Address() string
|
Address() string
|
||||||
Secure() bool
|
Secure() bool
|
||||||
Registry() registry.Registry
|
Registry() registry.Registry
|
||||||
|
CA() string
|
||||||
|
Cert() string
|
||||||
|
Key() string
|
||||||
TLSConfig() *tls.Config
|
TLSConfig() *tls.Config
|
||||||
DialOptions() []grpc.DialOption
|
DialOptions() []grpc.DialOption
|
||||||
UnaryInterceptors() []grpc.UnaryClientInterceptor
|
UnaryInterceptors() []grpc.UnaryClientInterceptor
|
||||||
@ -47,6 +53,24 @@ func WithAddress(address string) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithCA(ca string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.caCert = ca
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithCert(cert string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.cert = cert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithKey(key string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.key = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WithTLSConfig(conf *tls.Config) Option {
|
func WithTLSConfig(conf *tls.Config) Option {
|
||||||
return func(o *options) {
|
return func(o *options) {
|
||||||
o.tlsConfig = conf
|
o.tlsConfig = conf
|
||||||
@ -87,10 +111,14 @@ func WithStreamInterceptors(i ...grpc.StreamClientInterceptor) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
registry registry.Registry
|
registry registry.Registry
|
||||||
name string
|
name string
|
||||||
version string
|
version string
|
||||||
addr string
|
addr string
|
||||||
|
|
||||||
|
caCert string
|
||||||
|
cert string
|
||||||
|
key string
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
secure bool
|
secure bool
|
||||||
dialOptions []grpc.DialOption
|
dialOptions []grpc.DialOption
|
||||||
@ -115,6 +143,18 @@ func (o *options) Registry() registry.Registry {
|
|||||||
return o.registry
|
return o.registry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *options) CA() string {
|
||||||
|
return o.caCert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *options) Cert() string {
|
||||||
|
return o.cert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *options) Key() string {
|
||||||
|
return o.key
|
||||||
|
}
|
||||||
|
|
||||||
func (o *options) TLSConfig() *tls.Config {
|
func (o *options) TLSConfig() *tls.Config {
|
||||||
return o.tlsConfig
|
return o.tlsConfig
|
||||||
}
|
}
|
||||||
@ -134,3 +174,38 @@ func (o *options) UnaryInterceptors() []grpc.UnaryClientInterceptor {
|
|||||||
func (o *options) StreamInterceptors() []grpc.StreamClientInterceptor {
|
func (o *options) StreamInterceptors() []grpc.StreamClientInterceptor {
|
||||||
return o.streamInterceptors
|
return o.streamInterceptors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *options) hasTLSConfig() bool {
|
||||||
|
return o.caCert != "" && o.cert != "" && o.key != "" && o.tlsConfig == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *options) parseTLSConfig() error {
|
||||||
|
if o.tlsConfig != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !o.hasTLSConfig() {
|
||||||
|
if !o.secure {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
o.tlsConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
caCert, err := os.ReadFile(o.caCert)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
caCertPool := x509.NewCertPool()
|
||||||
|
ok := caCertPool.AppendCertsFromPEM(caCert)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("failed to load CA Cert from %s", o.caCert)
|
||||||
|
}
|
||||||
|
cert, err := tls.LoadX509KeyPair(o.cert, o.key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
o.tlsConfig = &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
RootCAs: caCertPool,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
244
client/pool.go
244
client/pool.go
@ -1,244 +0,0 @@
|
|||||||
/*
|
|
||||||
Taken from the https://github.com/micro/go-micro/client/grpc
|
|
||||||
*/
|
|
||||||
|
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/connectivity"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// DefaultPoolSize sets the connection pool size
|
|
||||||
DefaultPoolSize = 100
|
|
||||||
// DefaultPoolTTL sets the connection pool ttl
|
|
||||||
DefaultPoolTTL = time.Minute
|
|
||||||
// DefaultPoolMaxStreams maximum streams on a connectioin
|
|
||||||
// (20)
|
|
||||||
DefaultPoolMaxStreams = 20
|
|
||||||
|
|
||||||
// DefaultPoolMaxIdle maximum idle conns of a pool
|
|
||||||
// (50)
|
|
||||||
DefaultPoolMaxIdle = 50
|
|
||||||
|
|
||||||
// DefaultMaxRecvMsgSize maximum message that client can receive
|
|
||||||
// (4 MB).
|
|
||||||
DefaultMaxRecvMsgSize = 1024 * 1024 * 4
|
|
||||||
|
|
||||||
// DefaultMaxSendMsgSize maximum message that client can send
|
|
||||||
// (4 MB).
|
|
||||||
DefaultMaxSendMsgSize = 1024 * 1024 * 4
|
|
||||||
)
|
|
||||||
|
|
||||||
type pool struct {
|
|
||||||
size int
|
|
||||||
ttl int64
|
|
||||||
|
|
||||||
// max streams on a *poolConn
|
|
||||||
maxStreams int
|
|
||||||
// max idle conns
|
|
||||||
maxIdle int
|
|
||||||
|
|
||||||
sync.Mutex
|
|
||||||
conns map[string]*streamsPool
|
|
||||||
}
|
|
||||||
|
|
||||||
type streamsPool struct {
|
|
||||||
// head of list
|
|
||||||
head *poolConn
|
|
||||||
// busy conns list
|
|
||||||
busy *poolConn
|
|
||||||
// the siza of list
|
|
||||||
count int
|
|
||||||
// idle conn
|
|
||||||
idle int
|
|
||||||
}
|
|
||||||
|
|
||||||
type poolConn struct {
|
|
||||||
// grpc conn
|
|
||||||
*grpc.ClientConn
|
|
||||||
err error
|
|
||||||
addr string
|
|
||||||
|
|
||||||
// pool and streams pool
|
|
||||||
pool *pool
|
|
||||||
sp *streamsPool
|
|
||||||
streams int
|
|
||||||
created int64
|
|
||||||
|
|
||||||
// list
|
|
||||||
pre *poolConn
|
|
||||||
next *poolConn
|
|
||||||
in bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPool(size int, ttl time.Duration, idle int, ms int) *pool {
|
|
||||||
if ms <= 0 {
|
|
||||||
ms = 1
|
|
||||||
}
|
|
||||||
if idle < 0 {
|
|
||||||
idle = 0
|
|
||||||
}
|
|
||||||
return &pool{
|
|
||||||
size: size,
|
|
||||||
ttl: int64(ttl.Seconds()),
|
|
||||||
maxStreams: ms,
|
|
||||||
maxIdle: idle,
|
|
||||||
conns: make(map[string]*streamsPool),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pool) getConn(addr string, opts ...grpc.DialOption) (*poolConn, error) {
|
|
||||||
now := time.Now().Unix()
|
|
||||||
p.Lock()
|
|
||||||
sp, ok := p.conns[addr]
|
|
||||||
if !ok {
|
|
||||||
sp = &streamsPool{head: &poolConn{}, busy: &poolConn{}, count: 0, idle: 0}
|
|
||||||
p.conns[addr] = sp
|
|
||||||
}
|
|
||||||
// while we have conns check streams and then return one
|
|
||||||
// otherwise we'll create a new conn
|
|
||||||
conn := sp.head.next
|
|
||||||
for conn != nil {
|
|
||||||
// check conn state
|
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md
|
|
||||||
switch conn.GetState() {
|
|
||||||
case connectivity.Connecting:
|
|
||||||
conn = conn.next
|
|
||||||
continue
|
|
||||||
case connectivity.Shutdown:
|
|
||||||
next := conn.next
|
|
||||||
if conn.streams == 0 {
|
|
||||||
removeConn(conn)
|
|
||||||
sp.idle--
|
|
||||||
}
|
|
||||||
conn = next
|
|
||||||
continue
|
|
||||||
case connectivity.TransientFailure:
|
|
||||||
next := conn.next
|
|
||||||
if conn.streams == 0 {
|
|
||||||
removeConn(conn)
|
|
||||||
conn.ClientConn.Close()
|
|
||||||
sp.idle--
|
|
||||||
}
|
|
||||||
conn = next
|
|
||||||
continue
|
|
||||||
case connectivity.Ready:
|
|
||||||
case connectivity.Idle:
|
|
||||||
}
|
|
||||||
// a old conn
|
|
||||||
if now-conn.created > p.ttl {
|
|
||||||
next := conn.next
|
|
||||||
if conn.streams == 0 {
|
|
||||||
removeConn(conn)
|
|
||||||
conn.ClientConn.Close()
|
|
||||||
sp.idle--
|
|
||||||
}
|
|
||||||
conn = next
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// a busy conn
|
|
||||||
if conn.streams >= p.maxStreams {
|
|
||||||
next := conn.next
|
|
||||||
removeConn(conn)
|
|
||||||
addConnAfter(conn, sp.busy)
|
|
||||||
conn = next
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// a idle conn
|
|
||||||
if conn.streams == 0 {
|
|
||||||
sp.idle--
|
|
||||||
}
|
|
||||||
// a good conn
|
|
||||||
conn.streams++
|
|
||||||
p.Unlock()
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
p.Unlock()
|
|
||||||
|
|
||||||
// create new conn
|
|
||||||
cc, err := grpc.Dial(addr, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn = &poolConn{cc, nil, addr, p, sp, 1, time.Now().Unix(), nil, nil, false}
|
|
||||||
|
|
||||||
// add conn to streams pool
|
|
||||||
p.Lock()
|
|
||||||
if sp.count < p.size {
|
|
||||||
addConnAfter(conn, sp.head)
|
|
||||||
}
|
|
||||||
p.Unlock()
|
|
||||||
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pool) release(addr string, conn *poolConn, err error) {
|
|
||||||
p.Lock()
|
|
||||||
p, sp, created := conn.pool, conn.sp, conn.created
|
|
||||||
// try to add conn
|
|
||||||
if !conn.in && sp.count < p.size {
|
|
||||||
addConnAfter(conn, sp.head)
|
|
||||||
}
|
|
||||||
if !conn.in {
|
|
||||||
p.Unlock()
|
|
||||||
conn.ClientConn.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// a busy conn
|
|
||||||
if conn.streams >= p.maxStreams {
|
|
||||||
removeConn(conn)
|
|
||||||
addConnAfter(conn, sp.head)
|
|
||||||
}
|
|
||||||
conn.streams--
|
|
||||||
// if streams == 0, we can do something
|
|
||||||
if conn.streams == 0 {
|
|
||||||
// 1. it has errored
|
|
||||||
// 2. too many idle conn or
|
|
||||||
// 3. conn is too old
|
|
||||||
now := time.Now().Unix()
|
|
||||||
if err != nil || sp.idle >= p.maxIdle || now-created > p.ttl {
|
|
||||||
removeConn(conn)
|
|
||||||
p.Unlock()
|
|
||||||
conn.ClientConn.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sp.idle++
|
|
||||||
}
|
|
||||||
p.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (conn *poolConn) Close() {
|
|
||||||
conn.pool.release(conn.addr, conn, conn.err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeConn(conn *poolConn) {
|
|
||||||
if conn.pre != nil {
|
|
||||||
conn.pre.next = conn.next
|
|
||||||
}
|
|
||||||
if conn.next != nil {
|
|
||||||
conn.next.pre = conn.pre
|
|
||||||
}
|
|
||||||
conn.pre = nil
|
|
||||||
conn.next = nil
|
|
||||||
conn.in = false
|
|
||||||
conn.sp.count--
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func addConnAfter(conn *poolConn, after *poolConn) {
|
|
||||||
conn.next = after.next
|
|
||||||
conn.pre = after
|
|
||||||
if after.next != nil {
|
|
||||||
after.next.pre = conn
|
|
||||||
}
|
|
||||||
after.next = conn
|
|
||||||
conn.in = true
|
|
||||||
conn.sp.count++
|
|
||||||
return
|
|
||||||
}
|
|
@ -3,6 +3,8 @@ package codec
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/encoding"
|
||||||
|
_ "google.golang.org/grpc/encoding/proto"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -50,3 +52,7 @@ func (Codec) Unmarshal(data []byte, v interface{}) error {
|
|||||||
func (Codec) Name() string {
|
func (Codec) Name() string {
|
||||||
return Name
|
return Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
encoding.RegisterCodec(Codec{})
|
||||||
|
}
|
||||||
|
@ -4,15 +4,14 @@ package file
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/config"
|
"go.linka.cloud/grpc-toolkit/config"
|
||||||
"go.linka.cloud/grpc/logger"
|
"go.linka.cloud/grpc-toolkit/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewConfig(path string) (config.Config, error) {
|
func NewConfig(path string) (config.Config, error) {
|
||||||
@ -27,7 +26,7 @@ type file struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *file) Read() ([]byte, error) {
|
func (c *file) Read() ([]byte, error) {
|
||||||
return ioutil.ReadFile(c.path)
|
return os.ReadFile(c.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch listen for config changes and send updated content to the updates channel
|
// Watch listen for config changes and send updated content to the updates channel
|
||||||
|
@ -4,7 +4,6 @@ package file
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
@ -15,12 +14,12 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/config"
|
"go.linka.cloud/grpc-toolkit/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newConfigFile(t *testing.T) (config.Config, string, func()) {
|
func newConfigFile(t *testing.T) (config.Config, string, func()) {
|
||||||
path := filepath.Join(os.TempDir(), "config.yaml")
|
path := filepath.Join(os.TempDir(), "config.yaml")
|
||||||
if err := ioutil.WriteFile(path, []byte("ok"), os.ModePerm); err != nil {
|
if err := os.WriteFile(path, []byte("ok"), os.ModePerm); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
cleanUp := func() {
|
cleanUp := func() {
|
||||||
@ -32,14 +31,14 @@ func newConfigFile(t *testing.T) (config.Config, string, func()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newSymlinkedConfigFile(t *testing.T) (config.Config, string, string, func()) {
|
func newSymlinkedConfigFile(t *testing.T) (config.Config, string, string, func()) {
|
||||||
watchDir, err := ioutil.TempDir("", "")
|
watchDir, err := os.MkdirTemp("", "")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
dataDir1 := path.Join(watchDir, "data1")
|
dataDir1 := path.Join(watchDir, "data1")
|
||||||
err = os.Mkdir(dataDir1, 0o777)
|
err = os.Mkdir(dataDir1, 0o777)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
realConfigFile := path.Join(dataDir1, "config.yaml")
|
realConfigFile := path.Join(dataDir1, "config.yaml")
|
||||||
t.Logf("Real config file location: %s\n", realConfigFile)
|
t.Logf("Real config file location: %s\n", realConfigFile)
|
||||||
err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0o640)
|
err = os.WriteFile(realConfigFile, []byte("foo: bar\n"), 0o640)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
cleanup := func() {
|
cleanup := func() {
|
||||||
os.RemoveAll(watchDir)
|
os.RemoveAll(watchDir)
|
||||||
@ -64,7 +63,7 @@ func TestWatch(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// when overwriting the file and waiting for the custom change notification handler to be triggered
|
// when overwriting the file and waiting for the custom change notification handler to be triggered
|
||||||
err := ioutil.WriteFile(cpath, []byte("foo: baz\n"), 0o640)
|
err := os.WriteFile(cpath, []byte("foo: baz\n"), 0o640)
|
||||||
b := <-updates
|
b := <-updates
|
||||||
// then the config value should have changed
|
// then the config value should have changed
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@ -87,7 +86,7 @@ func TestWatch(t *testing.T) {
|
|||||||
err := os.MkdirAll(dataDir2, 0o777)
|
err := os.MkdirAll(dataDir2, 0o777)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
configFile2 := path.Join(dataDir2, "config.yaml")
|
configFile2 := path.Join(dataDir2, "config.yaml")
|
||||||
err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0o640)
|
err = os.WriteFile(configFile2, []byte("foo: baz\n"), 0o640)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// change the symlink using the `ln -sfn` command
|
// change the symlink using the `ln -sfn` command
|
||||||
err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
|
err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
|
||||||
|
20
errors/details.go
Normal file
20
errors/details.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BadRequestDetails returns an error details for an invalid argument.
|
||||||
|
// fd is a list of field / description pairs.
|
||||||
|
func BadRequestDetails(fd ...string) *errdetails.BadRequest {
|
||||||
|
var fieldViolations []*errdetails.BadRequest_FieldViolation
|
||||||
|
for i := 0; i < len(fd); i += 2 {
|
||||||
|
fieldViolations = append(fieldViolations, &errdetails.BadRequest_FieldViolation{
|
||||||
|
Field: fd[i],
|
||||||
|
Description: fd[i+1],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return &errdetails.BadRequest{
|
||||||
|
FieldViolations: fieldViolations,
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,10 @@
|
|||||||
package errors
|
package errors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
status2 "google.golang.org/genproto/googleapis/rpc/status"
|
status2 "google.golang.org/genproto/googleapis/rpc/status"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
@ -8,6 +12,10 @@ import (
|
|||||||
"google.golang.org/protobuf/types/known/anypb"
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Canceled(err error) error {
|
||||||
|
return status.Error(codes.Canceled, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
func InvalidArgument(err error) error {
|
func InvalidArgument(err error) error {
|
||||||
return status.Error(codes.InvalidArgument, err.Error())
|
return status.Error(codes.InvalidArgument, err.Error())
|
||||||
}
|
}
|
||||||
@ -63,6 +71,12 @@ func makeDetails(m ...proto.Message) []*anypb.Any {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Canceledf(msg string, args ...interface{}) error {
|
||||||
|
return status.Errorf(codes.Canceled, msg, args...)
|
||||||
|
}
|
||||||
|
func Canceledd(err error, details ...proto.Message) error {
|
||||||
|
return statusErr(codes.Canceled, err, details...)
|
||||||
|
}
|
||||||
func InvalidArgumentf(msg string, args ...interface{}) error {
|
func InvalidArgumentf(msg string, args ...interface{}) error {
|
||||||
return status.Errorf(codes.InvalidArgument, msg, args...)
|
return status.Errorf(codes.InvalidArgument, msg, args...)
|
||||||
}
|
}
|
||||||
@ -152,7 +166,7 @@ func IsCanceled(err error) bool {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return status.Convert(err).Code() == codes.Canceled
|
return status.Convert(err).Code() == codes.Canceled || IsContextCanceled(err)
|
||||||
}
|
}
|
||||||
func IsUnknown(err error) bool {
|
func IsUnknown(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -170,7 +184,7 @@ func IsDeadlineExceeded(err error) bool {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return status.Convert(err).Code() == codes.DeadlineExceeded
|
return status.Convert(err).Code() == codes.DeadlineExceeded || IsContextDeadlineExceeded(err)
|
||||||
}
|
}
|
||||||
func IsNotFound(err error) bool {
|
func IsNotFound(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -244,3 +258,30 @@ func IsUnauthenticated(err error) bool {
|
|||||||
}
|
}
|
||||||
return status.Convert(err).Code() == codes.Unauthenticated
|
return status.Convert(err).Code() == codes.Unauthenticated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsContextCanceled(err error) bool {
|
||||||
|
err = Unwrap(err)
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.Contains(err.Error(), context.Canceled.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsContextDeadlineExceeded(err error) bool {
|
||||||
|
err = Unwrap(err)
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.Contains(err.Error(), context.DeadlineExceeded.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unwrap(err error) error {
|
||||||
|
s, ok := status.FromError(err)
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
return errors.New(s.Message())
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -4,99 +4,110 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"github.com/spf13/cobra"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/uptrace/opentelemetry-go-extra/otellogrus"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/health/grpc_health_v1"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
greflectsvc "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
"google.golang.org/protobuf/types/descriptorpb"
|
|
||||||
|
|
||||||
"go.linka.cloud/grpc/client"
|
"go.linka.cloud/grpc-tookit/example/pb"
|
||||||
"go.linka.cloud/grpc/interceptors/defaulter"
|
"go.linka.cloud/grpc-toolkit/client"
|
||||||
metrics2 "go.linka.cloud/grpc/interceptors/metrics"
|
"go.linka.cloud/grpc-toolkit/interceptors/auth"
|
||||||
validation2 "go.linka.cloud/grpc/interceptors/validation"
|
"go.linka.cloud/grpc-toolkit/interceptors/tracing"
|
||||||
"go.linka.cloud/grpc/logger"
|
"go.linka.cloud/grpc-toolkit/logger"
|
||||||
"go.linka.cloud/grpc/service"
|
"go.linka.cloud/grpc-toolkit/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GreeterHandler struct {
|
func run(ctx context.Context, opts ...service.Option) {
|
||||||
UnimplementedGreeterServer
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
}
|
defer cancel()
|
||||||
|
|
||||||
func hello(name string) string {
|
|
||||||
return fmt.Sprintf("Hello %s !", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GreeterHandler) SayHello(ctx context.Context, req *HelloRequest) (*HelloReply, error) {
|
|
||||||
return &HelloReply{Message: hello(req.Name)}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GreeterHandler) SayHelloStream(req *HelloStreamRequest, s Greeter_SayHelloStreamServer) error {
|
|
||||||
for i := int64(0); i < req.Count; i++ {
|
|
||||||
if err := s.Send(&HelloReply{Message: fmt.Sprintf("Hello %s (%d)!", req.Name, i+1)}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func httpLogger(next http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
|
||||||
start := time.Now()
|
|
||||||
log := logger.From(request.Context()).WithFields(
|
|
||||||
"method", request.Method,
|
|
||||||
"host", request.Host,
|
|
||||||
"path", request.URL.Path,
|
|
||||||
"remoteAddress", request.RemoteAddr,
|
|
||||||
)
|
|
||||||
next.ServeHTTP(writer, request)
|
|
||||||
log.WithField("duration", time.Since(start)).Info()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
f, opts := service.NewFlagSet()
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "example",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
run(opts)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
cmd.Flags().AddFlagSet(f)
|
|
||||||
cmd.Execute()
|
|
||||||
}
|
|
||||||
|
|
||||||
func run(opts ...service.Option) {
|
|
||||||
name := "greeter"
|
name := "greeter"
|
||||||
version := "v0.0.1"
|
version := "v0.0.1"
|
||||||
secure := true
|
secure := true
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
log := logger.New().WithFields("service", name).WithReportCaller(true)
|
||||||
log := logger.New().WithFields("service", name)
|
log.Logger().AddHook(otellogrus.NewHook(otellogrus.WithLevels(
|
||||||
|
logger.PanicLevel,
|
||||||
|
logger.FatalLevel,
|
||||||
|
logger.ErrorLevel,
|
||||||
|
logger.WarnLevel,
|
||||||
|
logger.InfoLevel,
|
||||||
|
)))
|
||||||
ctx = logger.Set(ctx, log)
|
ctx = logger.Set(ctx, log)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
ready := make(chan struct{})
|
ready := make(chan struct{})
|
||||||
var svc service.Service
|
|
||||||
var err error
|
exporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithInsecure(), otlptracegrpc.WithEndpoint("192.168.10.212:4317"))
|
||||||
metrics := metrics2.NewInterceptors()
|
if err != nil {
|
||||||
validation := validation2.NewInterceptors(true)
|
logrus.Fatal(err)
|
||||||
defaulter := defaulter.NewInterceptors()
|
}
|
||||||
|
defer exporter.Shutdown(ctx)
|
||||||
|
|
||||||
|
r, err := resource.New(
|
||||||
|
ctx,
|
||||||
|
resource.WithFromEnv(),
|
||||||
|
resource.WithTelemetrySDK(),
|
||||||
|
resource.WithHost(),
|
||||||
|
resource.WithSchemaURL(semconv.SchemaURL),
|
||||||
|
resource.WithAttributes(
|
||||||
|
semconv.ServiceName("example"),
|
||||||
|
semconv.ServiceVersion("v1.0.0"),
|
||||||
|
semconv.DeploymentEnvironment("tests"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
tp := sdktrace.NewTracerProvider(
|
||||||
|
sdktrace.WithResource(r),
|
||||||
|
// sdktrace.WithBatcher(exporter),
|
||||||
|
sdktrace.WithSyncer(exporter),
|
||||||
|
sdktrace.WithSampler(sdktrace.AlwaysSample()),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err := tp.Shutdown(ctx); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// exporter, err := stdout.New(stdout.WithPrettyPrint())
|
||||||
|
// if err != nil {
|
||||||
|
// log.WithError(err).Fatal("failed to create otel exporter")
|
||||||
|
// }
|
||||||
|
// tp := sdktrace.NewTracerProvider(
|
||||||
|
// sdktrace.WithSampler(sdktrace.AlwaysSample()),
|
||||||
|
// // enable in production
|
||||||
|
// // sdktrace.WithBatcher(exporter),
|
||||||
|
// // enable in development
|
||||||
|
// sdktrace.WithSyncer(exporter),
|
||||||
|
// )
|
||||||
|
|
||||||
|
otel.SetTracerProvider(tp)
|
||||||
|
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
|
||||||
|
defer func() { _ = exporter.Shutdown(context.Background()) }()
|
||||||
|
|
||||||
address := "0.0.0.0:9991"
|
address := "0.0.0.0:9991"
|
||||||
opts = append(opts, service.WithContext(ctx),
|
|
||||||
|
var svc service.Service
|
||||||
|
opts = append(opts,
|
||||||
|
service.WithContext(ctx),
|
||||||
service.WithName(name),
|
service.WithName(name),
|
||||||
service.WithVersion(version),
|
service.WithVersion(version),
|
||||||
service.WithAddress(address),
|
service.WithAddress(address),
|
||||||
// service.WithRegistry(mdns.NewRegistry()),
|
|
||||||
service.WithReflection(true),
|
|
||||||
service.WithSecure(secure),
|
service.WithSecure(secure),
|
||||||
service.WithAfterStart(func() error {
|
service.WithAfterStart(func() error {
|
||||||
log.Info("Server listening on", svc.Options().Address())
|
log.Info("Server listening on", svc.Options().Address())
|
||||||
@ -108,61 +119,83 @@ func run(opts ...service.Option) {
|
|||||||
close(done)
|
close(done)
|
||||||
return nil
|
return nil
|
||||||
}),
|
}),
|
||||||
service.WithGateway(RegisterGreeterHandler),
|
|
||||||
service.WithGatewayPrefix("/rest"),
|
|
||||||
service.WithGRPCWeb(true),
|
|
||||||
service.WithGRPCWebPrefix("/grpc"),
|
|
||||||
service.WithMiddlewares(httpLogger),
|
|
||||||
service.WithInterceptors(metrics, defaulter, validation),
|
|
||||||
)
|
)
|
||||||
svc, err = service.New(opts...)
|
svc, err = newService(ctx, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
RegisterGreeterServer(svc, &GreeterHandler{})
|
|
||||||
metrics.Register(svc)
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := svc.Start(); err != nil {
|
if err := svc.Start(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
http.Handle("/metrics", promhttp.Handler())
|
http.Handle("/metrics", promhttp.HandlerFor(
|
||||||
|
prometheus.DefaultGatherer,
|
||||||
|
promhttp.HandlerOpts{
|
||||||
|
// Opt into OpenMetrics e.g. to support exemplars.
|
||||||
|
EnableOpenMetrics: true,
|
||||||
|
},
|
||||||
|
))
|
||||||
if err := http.ListenAndServe(":9992", nil); err != nil {
|
if err := http.ListenAndServe(":9992", nil); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
<-ready
|
<-ready
|
||||||
s, err := client.New(
|
copts := []client.Option{
|
||||||
// client.WithName(name),
|
// client.WithName(name),
|
||||||
// client.WithVersion(version),
|
// client.WithVersion(version),
|
||||||
client.WithAddress("localhost:9991"),
|
client.WithAddress("localhost:9991"),
|
||||||
// client.WithRegistry(mdns.NewRegistry()),
|
// client.WithRegistry(mdns.NewRegistry()),
|
||||||
client.WithSecure(secure),
|
client.WithSecure(secure),
|
||||||
client.WithUnaryInterceptors(func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
client.WithInterceptors(tracing.NewClientInterceptors()),
|
||||||
logger.From(ctx).WithFields("party", "client", "method", method).Info(req)
|
// client.WithUnaryInterceptors(func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||||
return invoker(ctx, method, req, reply, cc, opts...)
|
// logger.From(ctx).WithFields("party", "client", "method", method).Info(req)
|
||||||
}),
|
// return invoker(ctx, method, req, reply, cc, opts...)
|
||||||
)
|
// }),
|
||||||
|
}
|
||||||
|
s, err := client.New(copts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
g := NewGreeterClient(s)
|
g := pb.NewGreeterClient(s)
|
||||||
defer cancel()
|
h := grpc_health_v1.NewHealthClient(s)
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
_, err := h.Check(ctx, &grpc_health_v1.HealthCheckRequest{})
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
} else {
|
||||||
|
log.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Infof("waiting for unban")
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
s, err = client.New(append(copts, client.WithInterceptors(auth.NewBasicAuthClientIntereptors("admin", "admin")))...)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
g = pb.NewGreeterClient(s)
|
||||||
|
h = grpc_health_v1.NewHealthClient(s)
|
||||||
|
hres, err := h.Check(ctx, &grpc_health_v1.HealthCheckRequest{Service: "helloworld.Greeter"})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Infof("status: %v", hres.Status)
|
||||||
|
|
||||||
md := metadata.MD{}
|
md := metadata.MD{}
|
||||||
res, err := g.SayHello(ctx, &HelloRequest{Name: "test"}, grpc.Header(&md))
|
res, err := g.SayHello(ctx, &pb.HelloRequest{Name: "test"}, grpc.Header(&md))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
logMetadata(ctx, md)
|
logMetadata(ctx, md)
|
||||||
log.Infof("received message: %s", res.Message)
|
log.Infof("received message: %s", res.Message)
|
||||||
md = metadata.MD{}
|
md = metadata.MD{}
|
||||||
res, err = g.SayHello(ctx, &HelloRequest{}, grpc.Header(&md))
|
res, err = g.SayHello(ctx, &pb.HelloRequest{}, grpc.Header(&md))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Fatal("expected validation error")
|
log.Fatal("expected validation error")
|
||||||
}
|
}
|
||||||
logMetadata(ctx, md)
|
logMetadata(ctx, md)
|
||||||
stream, err := g.SayHelloStream(ctx, &HelloStreamRequest{Name: "test"}, grpc.Header(&md))
|
stream, err := g.SayHelloStream(ctx, &pb.HelloStreamRequest{Name: "test"}, grpc.Header(&md))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -180,25 +213,43 @@ func run(opts ...service.Option) {
|
|||||||
log.Infof("received stream message: %s", m.Message)
|
log.Infof("received stream message: %s", m.Message)
|
||||||
}
|
}
|
||||||
scheme := "http://"
|
scheme := "http://"
|
||||||
|
var (
|
||||||
|
tlsConfig *tls.Config
|
||||||
|
dial func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error)
|
||||||
|
)
|
||||||
if secure {
|
if secure {
|
||||||
scheme = "https://"
|
scheme = "https://"
|
||||||
|
tlsConfig = &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dial = func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||||
|
var d net.Dialer
|
||||||
|
return d.DialContext(ctx, network, addr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
httpc := &http.Client{
|
httpc := &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http2.Transport{
|
||||||
TLSClientConfig: &tls.Config{
|
AllowHTTP: true,
|
||||||
InsecureSkipVerify: true,
|
TLSClientConfig: tlsConfig,
|
||||||
},
|
DialTLSContext: dial,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
req := `{"name":"test"}`
|
req := `{"name":"test"}`
|
||||||
|
|
||||||
do := func(url, contentType string) {
|
do := func(url, contentType string) {
|
||||||
resp, err := httpc.Post(url, contentType, strings.NewReader(req))
|
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(req))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
req.Header.Set("content-type", contentType)
|
||||||
|
req.Header.Set("authorization", auth.BasicAuth("admin", "admin"))
|
||||||
|
resp, err := httpc.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
b, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -220,80 +271,3 @@ func logMetadata(ctx context.Context, md metadata.MD) {
|
|||||||
log.Infof("%s: %v", k, v)
|
log.Infof("%s: %v", k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readSvcs(ctx context.Context, c client.Client) (err error) {
|
|
||||||
log := logger.From(ctx)
|
|
||||||
rc := greflectsvc.NewServerReflectionClient(c)
|
|
||||||
rstream, err := rc.ServerReflectionInfo(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err2 := rstream.CloseSend(); err2 != nil && err == nil {
|
|
||||||
err = err2
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if err = rstream.Send(&greflectsvc.ServerReflectionRequest{MessageRequest: &greflectsvc.ServerReflectionRequest_ListServices{}}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var rres *greflectsvc.ServerReflectionResponse
|
|
||||||
rres, err = rstream.Recv()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rlist, ok := rres.MessageResponse.(*greflectsvc.ServerReflectionResponse_ListServicesResponse)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unexpected reflection response type: %T", rres.MessageResponse)
|
|
||||||
}
|
|
||||||
for _, v := range rlist.ListServicesResponse.Service {
|
|
||||||
if v.Name == "grpc.reflection.v1alpha.ServerReflection" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
parts := strings.Split(v.Name, ".")
|
|
||||||
if len(parts) < 2 {
|
|
||||||
return fmt.Errorf("malformed service name: %s", v.Name)
|
|
||||||
}
|
|
||||||
pkg := strings.Join(parts[:len(parts)-1], ".")
|
|
||||||
svc := parts[len(parts)-1]
|
|
||||||
if err = rstream.Send(&greflectsvc.ServerReflectionRequest{MessageRequest: &greflectsvc.ServerReflectionRequest_FileContainingSymbol{
|
|
||||||
FileContainingSymbol: v.Name,
|
|
||||||
}}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rres, err = rstream.Recv()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
rfile, ok := rres.MessageResponse.(*greflectsvc.ServerReflectionResponse_FileDescriptorResponse)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unexpected reflection response type: %T", rres.MessageResponse)
|
|
||||||
}
|
|
||||||
fdps := make(map[string]*descriptorpb.DescriptorProto)
|
|
||||||
var sdp *descriptorpb.ServiceDescriptorProto
|
|
||||||
for _, v := range rfile.FileDescriptorResponse.FileDescriptorProto {
|
|
||||||
fdp := &descriptorpb.FileDescriptorProto{}
|
|
||||||
if err = proto.Unmarshal(v, fdp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, s := range fdp.GetService() {
|
|
||||||
if fdp.GetPackage() == pkg && s.GetName() == svc {
|
|
||||||
if sdp != nil {
|
|
||||||
log.Warnf("service already found: %s.%s", fdp.GetPackage(), s.GetName())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sdp = s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, m := range fdp.GetMessageType() {
|
|
||||||
fdps[fdp.GetPackage()+"."+m.GetName()] = m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if sdp == nil {
|
|
||||||
return fmt.Errorf("%s: service not found", v.Name)
|
|
||||||
}
|
|
||||||
for _, m := range sdp.GetMethod() {
|
|
||||||
log.Infof("%s: %s", v.Name, m.GetName())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
var HelloRequestFields = struct {
|
|
||||||
Name string
|
|
||||||
}{
|
|
||||||
Name: "name",
|
|
||||||
}
|
|
||||||
|
|
||||||
var HelloReplyFields = struct {
|
|
||||||
Message string
|
|
||||||
}{
|
|
||||||
Message: "message",
|
|
||||||
}
|
|
||||||
|
|
||||||
var HelloStreamRequestFields = struct {
|
|
||||||
Name string
|
|
||||||
Count string
|
|
||||||
}{
|
|
||||||
Name: "name",
|
|
||||||
Count: "count",
|
|
||||||
}
|
|
@ -1,305 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.27.1
|
|
||||||
// protoc v3.17.3
|
|
||||||
// source: example/example.proto
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
|
|
||||||
_ "github.com/envoyproxy/protoc-gen-validate/validate"
|
|
||||||
_ "go.linka.cloud/protoc-gen-defaults/defaults"
|
|
||||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
type HelloRequest struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelloRequest) Reset() {
|
|
||||||
*x = HelloRequest{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_example_example_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelloRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*HelloRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *HelloRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_example_example_proto_msgTypes[0]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*HelloRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_example_example_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelloRequest) GetName() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Name
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type HelloReply struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelloReply) Reset() {
|
|
||||||
*x = HelloReply{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_example_example_proto_msgTypes[1]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelloReply) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*HelloReply) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *HelloReply) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_example_example_proto_msgTypes[1]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead.
|
|
||||||
func (*HelloReply) Descriptor() ([]byte, []int) {
|
|
||||||
return file_example_example_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelloReply) GetMessage() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Message
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type HelloStreamRequest struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
|
||||||
Count int64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelloStreamRequest) Reset() {
|
|
||||||
*x = HelloStreamRequest{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_example_example_proto_msgTypes[2]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelloStreamRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*HelloStreamRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *HelloStreamRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_example_example_proto_msgTypes[2]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use HelloStreamRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*HelloStreamRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_example_example_proto_rawDescGZIP(), []int{2}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelloStreamRequest) GetName() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Name
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelloStreamRequest) GetCount() int64 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Count
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_example_example_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_example_example_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x15, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
|
|
||||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f,
|
|
||||||
0x72, 0x6c, 0x64, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f,
|
|
||||||
0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
|
||||||
0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69,
|
|
||||||
0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x64, 0x65, 0x66, 0x61,
|
|
||||||
0x75, 0x6c, 0x74, 0x73, 0x2f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x70, 0x72,
|
|
||||||
0x6f, 0x74, 0x6f, 0x22, 0x2d, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75,
|
|
||||||
0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
|
||||||
0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x02, 0x18, 0x28, 0x52, 0x04, 0x6e, 0x61,
|
|
||||||
0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
|
||||||
0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
|
||||||
0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x59, 0x0a, 0x12, 0x48, 0x65,
|
|
||||||
0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
|
||||||
0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09,
|
|
||||||
0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x02, 0x18, 0x28, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
|
|
||||||
0x24, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, 0x0e,
|
|
||||||
0xfa, 0x42, 0x06, 0x22, 0x04, 0x18, 0x0a, 0x28, 0x01, 0x9a, 0x49, 0x02, 0x20, 0x0a, 0x52, 0x05,
|
|
||||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xb7, 0x01, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65,
|
|
||||||
0x72, 0x12, 0x5e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e,
|
|
||||||
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
|
|
||||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77,
|
|
||||||
0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22,
|
|
||||||
0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31,
|
|
||||||
0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x3a, 0x01,
|
|
||||||
0x2a, 0x12, 0x4c, 0x0a, 0x0e, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72,
|
|
||||||
0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64,
|
|
||||||
0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75,
|
|
||||||
0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64,
|
|
||||||
0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x42,
|
|
||||||
0x22, 0x5a, 0x20, 0x67, 0x6f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, 0x75,
|
|
||||||
0x64, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x3b, 0x6d,
|
|
||||||
0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_example_example_proto_rawDescOnce sync.Once
|
|
||||||
file_example_example_proto_rawDescData = file_example_example_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_example_example_proto_rawDescGZIP() []byte {
|
|
||||||
file_example_example_proto_rawDescOnce.Do(func() {
|
|
||||||
file_example_example_proto_rawDescData = protoimpl.X.CompressGZIP(file_example_example_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_example_example_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_example_example_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
|
||||||
var file_example_example_proto_goTypes = []interface{}{
|
|
||||||
(*HelloRequest)(nil), // 0: helloworld.HelloRequest
|
|
||||||
(*HelloReply)(nil), // 1: helloworld.HelloReply
|
|
||||||
(*HelloStreamRequest)(nil), // 2: helloworld.HelloStreamRequest
|
|
||||||
}
|
|
||||||
var file_example_example_proto_depIdxs = []int32{
|
|
||||||
0, // 0: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest
|
|
||||||
2, // 1: helloworld.Greeter.SayHelloStream:input_type -> helloworld.HelloStreamRequest
|
|
||||||
1, // 2: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply
|
|
||||||
1, // 3: helloworld.Greeter.SayHelloStream:output_type -> helloworld.HelloReply
|
|
||||||
2, // [2:4] is the sub-list for method output_type
|
|
||||||
0, // [0:2] is the sub-list for method input_type
|
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
|
||||||
0, // [0:0] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_example_example_proto_init() }
|
|
||||||
func file_example_example_proto_init() {
|
|
||||||
if File_example_example_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !protoimpl.UnsafeEnabled {
|
|
||||||
file_example_example_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*HelloRequest); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_example_example_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*HelloReply); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_example_example_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*HelloStreamRequest); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_example_example_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 3,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 1,
|
|
||||||
},
|
|
||||||
GoTypes: file_example_example_proto_goTypes,
|
|
||||||
DependencyIndexes: file_example_example_proto_depIdxs,
|
|
||||||
MessageInfos: file_example_example_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_example_example_proto = out.File
|
|
||||||
file_example_example_proto_rawDesc = nil
|
|
||||||
file_example_example_proto_goTypes = nil
|
|
||||||
file_example_example_proto_depIdxs = nil
|
|
||||||
}
|
|
82
example/go.mod
Normal file
82
example/go.mod
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
module go.linka.cloud/grpc-tookit/example
|
||||||
|
|
||||||
|
go 1.23.2
|
||||||
|
|
||||||
|
replace go.linka.cloud/grpc-toolkit => ../
|
||||||
|
|
||||||
|
replace (
|
||||||
|
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.9.1
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus => github.com/linka-cloud/go-grpc-prometheus v1.2.0-lk
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 => github.com/linka-cloud/grpc-gateway/v2 v2.20.0-lk
|
||||||
|
nhooyr.io/websocket => github.com/coder/websocket v1.8.6
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v1.1.0
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0
|
||||||
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587
|
||||||
|
github.com/prometheus/client_golang v1.20.5
|
||||||
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
github.com/spf13/cobra v1.8.1
|
||||||
|
github.com/uptrace/opentelemetry-go-extra/otellogrus v0.3.2
|
||||||
|
go.linka.cloud/grpc-toolkit v0.4.3
|
||||||
|
go.linka.cloud/protoc-gen-defaults v0.4.0
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0
|
||||||
|
go.opentelemetry.io/otel v1.31.0
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0
|
||||||
|
go.opentelemetry.io/otel/sdk v1.31.0
|
||||||
|
go.opentelemetry.io/otel/trace v1.31.0
|
||||||
|
golang.org/x/net v0.30.0
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53
|
||||||
|
google.golang.org/grpc v1.67.1
|
||||||
|
google.golang.org/protobuf v1.35.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go/compute/metadata v0.5.0 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/bombsimon/logrusr/v4 v4.1.0 // indirect
|
||||||
|
github.com/bufbuild/protocompile v0.14.1 // indirect
|
||||||
|
github.com/caitlinelfring/go-env-default v1.1.0 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/desertbit/timer v1.0.1 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||||
|
github.com/fullstorydev/grpchan v1.1.1 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/jaredfolkins/badactor v1.2.0 // indirect
|
||||||
|
github.com/jhump/protoreflect v1.17.0 // indirect
|
||||||
|
github.com/justinas/alice v1.2.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/pires/go-proxyproto v0.7.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
|
github.com/prometheus/common v0.60.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
|
github.com/rs/cors v1.11.1 // indirect
|
||||||
|
github.com/soheilhy/cmux v0.1.5 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect
|
||||||
|
github.com/traefik/grpc-web v0.16.0 // indirect
|
||||||
|
github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/log v0.6.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.31.0 // indirect
|
||||||
|
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||||
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
|
golang.org/x/sys v0.26.0 // indirect
|
||||||
|
golang.org/x/text v0.19.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
|
||||||
|
nhooyr.io/websocket v1.8.17 // indirect
|
||||||
|
)
|
725
example/go.sum
Normal file
725
example/go.sum
Normal file
@ -0,0 +1,725 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||||
|
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||||
|
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||||
|
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||||
|
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||||
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
|
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
|
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||||
|
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
|
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||||
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/bombsimon/logrusr/v4 v4.1.0 h1:uZNPbwusB0eUXlO8hIUwStE6Lr5bLN6IgYgG+75kuh4=
|
||||||
|
github.com/bombsimon/logrusr/v4 v4.1.0/go.mod h1:pjfHC5e59CvjTBIU3V3sGhFWFAnsnhOR03TRc6im0l8=
|
||||||
|
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
|
||||||
|
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
|
||||||
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
|
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||||
|
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
|
github.com/caitlinelfring/go-env-default v1.1.0 h1:bhDfXmUolvcIGfQCX8qevQX8wxC54NGz0aimoUnhvDM=
|
||||||
|
github.com/caitlinelfring/go-env-default v1.1.0/go.mod h1:tESXPr8zFPP/cRy3cwxrHBmjJIf2A1x/o4C9CET2rEk=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
github.com/coder/websocket v1.8.6 h1:OmNKdwUvLj7VvHnl5o8skaVghSPLjWdHGCnFbkWqs9w=
|
||||||
|
github.com/coder/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
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/desertbit/timer v1.0.1 h1:yRpYNn5Vaaj6QXecdLMPMJsW81JLiI1eokUft5nBmeo=
|
||||||
|
github.com/desertbit/timer v1.0.1/go.mod h1:htRrYeY5V/t4iu1xCJ5XsQvp4xve8QulXXctAzxqcwE=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||||
|
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||||
|
github.com/fullstorydev/grpchan v1.1.1 h1:heQqIJlAv5Cnks9a70GRL2EJke6QQoUB25VGR6TZQas=
|
||||||
|
github.com/fullstorydev/grpchan v1.1.1/go.mod h1:f4HpiV8V6htfY/K44GWV1ESQzHBTq7DinhzqQ95lpgc=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
|
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.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||||
|
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||||
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||||
|
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||||
|
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||||
|
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||||
|
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/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
|
||||||
|
github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
|
||||||
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/jaredfolkins/badactor v1.2.0 h1:QTJBsVG9qhdIFmFx5eNet2Q9hX8T+qZ1rC9NJwyN+Hc=
|
||||||
|
github.com/jaredfolkins/badactor v1.2.0/go.mod h1:ZynkTrC/ICU1o8mmFy3JySRCErXVlx7trZiWEH6DDg8=
|
||||||
|
github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=
|
||||||
|
github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=
|
||||||
|
github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ=
|
||||||
|
github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E=
|
||||||
|
github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94=
|
||||||
|
github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8=
|
||||||
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
|
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
|
||||||
|
github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
|
||||||
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
|
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
|
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
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/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
|
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||||
|
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||||
|
github.com/linka-cloud/grpc-gateway/v2 v2.20.0-lk h1:AGMeKR9uLBBLUiTl9D1aYmSdJuqG4FgOrOc6e3nxhuo=
|
||||||
|
github.com/linka-cloud/grpc-gateway/v2 v2.20.0-lk/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||||
|
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
|
||||||
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||||
|
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
||||||
|
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||||
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587 h1:xzZOeCMQLA/W198ZkdVdt4EKFKJtS26B773zNU377ZY=
|
||||||
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||||
|
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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
|
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||||
|
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||||
|
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||||
|
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
|
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||||
|
github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||||
|
github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA=
|
||||||
|
github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
||||||
|
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
|
||||||
|
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
||||||
|
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||||
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
|
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
|
||||||
|
github.com/traefik/grpc-web v0.16.0 h1:eeUWZaFg6ZU0I9dWOYE2D5qkNzRBmXzzuRlxdltascY=
|
||||||
|
github.com/traefik/grpc-web v0.16.0/go.mod h1:2ttniSv7pTgBWIU2HZLokxRfFX3SA60c/DTmQQgVml4=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
|
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
github.com/uptrace/opentelemetry-go-extra/otellogrus v0.3.2 h1:H8wwQwTe5sL6x30z71lUgNiwBdeCHQjrphCfLwqIHGo=
|
||||||
|
github.com/uptrace/opentelemetry-go-extra/otellogrus v0.3.2/go.mod h1:/kR4beFhlz2g+V5ik8jW+3PMiMQAPt29y6K64NNY53c=
|
||||||
|
github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2 h1:3/aHKUq7qaFMWxyQV0W2ryNgg8x8rVeKVA20KJUkfS0=
|
||||||
|
github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2/go.mod h1:Zit4b8AQXaXvA68+nzmbyDzqiyFRISyw1JiD5JqUBjw=
|
||||||
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.linka.cloud/protoc-gen-defaults v0.4.0 h1:ekcfTTY74AhKBGMF9usz+xkUFxLaPVAu6xmQvwmjbfc=
|
||||||
|
go.linka.cloud/protoc-gen-defaults v0.4.0/go.mod h1:IJcTbM/oraQvdE/mz0vxhoBmJHE+rb4vF2IXJztcadY=
|
||||||
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
||||||
|
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||||
|
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o=
|
||||||
|
go.opentelemetry.io/otel/log v0.6.0 h1:nH66tr+dmEgW5y+F9LanGJUBYPrRgP4g2EkmPE3LeK8=
|
||||||
|
go.opentelemetry.io/otel/log v0.6.0/go.mod h1:KdySypjQHhP069JX0z/t26VHwa8vSwzgaKmXtIB3fJM=
|
||||||
|
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||||
|
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
|
||||||
|
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||||
|
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||||
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
|
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||||
|
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
|
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||||
|
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||||
|
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
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/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=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||||
|
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||||
|
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||||
|
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||||
|
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
|
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
|
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||||
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||||
|
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||||
|
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||||
|
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
|
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
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=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
24
example/main.go
Normal file
24
example/main.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
f, opts := service.NewFlagSet()
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "example",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
run(cmd.Context(), opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.Flags().AddFlagSet(f)
|
||||||
|
cmd.ExecuteContext(ctx)
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by protoc-gen-defaults. DO NOT EDIT.
|
// Code generated by protoc-gen-defaults. DO NOT EDIT.
|
||||||
|
|
||||||
package main
|
package pb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"google.golang.org/protobuf/types/known/durationpb"
|
"google.golang.org/protobuf/types/known/durationpb"
|
31
example/pb/example.pb.fields.go
Normal file
31
example/pb/example.pb.fields.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Code generated by protoc-gen-defaults. DO NOT EDIT.
|
||||||
|
|
||||||
|
package pb
|
||||||
|
|
||||||
|
var GreeterMethods = struct {
|
||||||
|
SayHello string
|
||||||
|
SayHelloStream string
|
||||||
|
}{
|
||||||
|
SayHello: "/helloworld.Greeter/SayHello",
|
||||||
|
SayHelloStream: "/helloworld.Greeter/SayHelloStream",
|
||||||
|
}
|
||||||
|
|
||||||
|
var HelloRequestFields = struct {
|
||||||
|
Name string
|
||||||
|
}{
|
||||||
|
Name: "name",
|
||||||
|
}
|
||||||
|
|
||||||
|
var HelloReplyFields = struct {
|
||||||
|
Message string
|
||||||
|
}{
|
||||||
|
Message: "message",
|
||||||
|
}
|
||||||
|
|
||||||
|
var HelloStreamRequestFields = struct {
|
||||||
|
Name string
|
||||||
|
Count string
|
||||||
|
}{
|
||||||
|
Name: "name",
|
||||||
|
Count: "count",
|
||||||
|
}
|
261
example/pb/example.pb.go
Normal file
261
example/pb/example.pb.go
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.35.1
|
||||||
|
// protoc v5.28.2
|
||||||
|
// source: example/pb/example.proto
|
||||||
|
|
||||||
|
package pb
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/envoyproxy/protoc-gen-validate/validate"
|
||||||
|
_ "go.linka.cloud/protoc-gen-defaults/defaults"
|
||||||
|
_ "google.golang.org/genproto/googleapis/api/annotations"
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type HelloRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelloRequest) Reset() {
|
||||||
|
*x = HelloRequest{}
|
||||||
|
mi := &file_example_pb_example_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelloRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HelloRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HelloRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_example_pb_example_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*HelloRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_example_pb_example_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelloRequest) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelloReply struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelloReply) Reset() {
|
||||||
|
*x = HelloReply{}
|
||||||
|
mi := &file_example_pb_example_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelloReply) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HelloReply) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HelloReply) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_example_pb_example_proto_msgTypes[1]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead.
|
||||||
|
func (*HelloReply) Descriptor() ([]byte, []int) {
|
||||||
|
return file_example_pb_example_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelloReply) GetMessage() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Message
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelloStreamRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
Count int64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelloStreamRequest) Reset() {
|
||||||
|
*x = HelloStreamRequest{}
|
||||||
|
mi := &file_example_pb_example_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelloStreamRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HelloStreamRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HelloStreamRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_example_pb_example_proto_msgTypes[2]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HelloStreamRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*HelloStreamRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_example_pb_example_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelloStreamRequest) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelloStreamRequest) GetCount() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Count
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_example_pb_example_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_example_pb_example_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x18, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x70, 0x62, 0x2f, 0x65, 0x78, 0x61,
|
||||||
|
0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c,
|
||||||
|
0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61,
|
||||||
|
0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76,
|
||||||
|
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x64,
|
||||||
|
0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x2f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73,
|
||||||
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2d, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52,
|
||||||
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
|
||||||
|
0x20, 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x02, 0x18, 0x28, 0x52,
|
||||||
|
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65,
|
||||||
|
0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01,
|
||||||
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x59, 0x0a,
|
||||||
|
0x12, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75,
|
||||||
|
0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
|
0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x02, 0x18, 0x28, 0x52, 0x04, 0x6e, 0x61,
|
||||||
|
0x6d, 0x65, 0x12, 0x24, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
|
0x03, 0x42, 0x0e, 0xfa, 0x42, 0x06, 0x22, 0x04, 0x18, 0x0a, 0x28, 0x01, 0x9a, 0x49, 0x02, 0x20,
|
||||||
|
0x0a, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xb7, 0x01, 0x0a, 0x07, 0x47, 0x72, 0x65,
|
||||||
|
0x65, 0x74, 0x65, 0x72, 0x12, 0x5e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
|
||||||
|
0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65,
|
||||||
|
0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c,
|
||||||
|
0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70,
|
||||||
|
0x6c, 0x79, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f,
|
||||||
|
0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x2f, 0x68,
|
||||||
|
0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x4c, 0x0a, 0x0e, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
|
||||||
|
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f,
|
||||||
|
0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52,
|
||||||
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f,
|
||||||
|
0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00,
|
||||||
|
0x30, 0x01, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x6f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x2e, 0x63,
|
||||||
|
0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2d, 0x74, 0x6f, 0x6f, 0x6c, 0x6b, 0x69,
|
||||||
|
0x74, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_example_pb_example_proto_rawDescOnce sync.Once
|
||||||
|
file_example_pb_example_proto_rawDescData = file_example_pb_example_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_example_pb_example_proto_rawDescGZIP() []byte {
|
||||||
|
file_example_pb_example_proto_rawDescOnce.Do(func() {
|
||||||
|
file_example_pb_example_proto_rawDescData = protoimpl.X.CompressGZIP(file_example_pb_example_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_example_pb_example_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_example_pb_example_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||||
|
var file_example_pb_example_proto_goTypes = []any{
|
||||||
|
(*HelloRequest)(nil), // 0: helloworld.HelloRequest
|
||||||
|
(*HelloReply)(nil), // 1: helloworld.HelloReply
|
||||||
|
(*HelloStreamRequest)(nil), // 2: helloworld.HelloStreamRequest
|
||||||
|
}
|
||||||
|
var file_example_pb_example_proto_depIdxs = []int32{
|
||||||
|
0, // 0: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest
|
||||||
|
2, // 1: helloworld.Greeter.SayHelloStream:input_type -> helloworld.HelloStreamRequest
|
||||||
|
1, // 2: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply
|
||||||
|
1, // 3: helloworld.Greeter.SayHelloStream:output_type -> helloworld.HelloReply
|
||||||
|
2, // [2:4] is the sub-list for method output_type
|
||||||
|
0, // [0:2] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_example_pb_example_proto_init() }
|
||||||
|
func file_example_pb_example_proto_init() {
|
||||||
|
if File_example_pb_example_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_example_pb_example_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 3,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 1,
|
||||||
|
},
|
||||||
|
GoTypes: file_example_pb_example_proto_goTypes,
|
||||||
|
DependencyIndexes: file_example_pb_example_proto_depIdxs,
|
||||||
|
MessageInfos: file_example_pb_example_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_example_pb_example_proto = out.File
|
||||||
|
file_example_pb_example_proto_rawDesc = nil
|
||||||
|
file_example_pb_example_proto_goTypes = nil
|
||||||
|
file_example_pb_example_proto_depIdxs = nil
|
||||||
|
}
|
@ -1,12 +1,12 @@
|
|||||||
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
|
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
|
||||||
// source: example/example.proto
|
// source: example/pb/example.proto
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Package main is a reverse proxy.
|
Package pb is a reverse proxy.
|
||||||
|
|
||||||
It translates gRPC into RESTful JSON APIs.
|
It translates gRPC into RESTful JSON APIs.
|
||||||
*/
|
*/
|
||||||
package main
|
package pb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -35,11 +35,7 @@ func request_Greeter_SayHello_0(ctx context.Context, marshaler runtime.Marshaler
|
|||||||
var protoReq HelloRequest
|
var protoReq HelloRequest
|
||||||
var metadata runtime.ServerMetadata
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||||
if berr != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
|
||||||
}
|
|
||||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,11 +48,7 @@ func local_request_Greeter_SayHello_0(ctx context.Context, marshaler runtime.Mar
|
|||||||
var protoReq HelloRequest
|
var protoReq HelloRequest
|
||||||
var metadata runtime.ServerMetadata
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
|
||||||
if berr != nil {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
|
||||||
}
|
|
||||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
|
||||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,20 +69,22 @@ func RegisterGreeterHandlerServer(ctx context.Context, mux *runtime.ServeMux, se
|
|||||||
var stream runtime.ServerTransportStream
|
var stream runtime.ServerTransportStream
|
||||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/helloworld.Greeter/SayHello", runtime.WithHTTPPathPattern("/api/v1/greeter/hello"))
|
var err error
|
||||||
|
var annotatedContext context.Context
|
||||||
|
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/helloworld.Greeter/SayHello", runtime.WithHTTPPathPattern("/api/v1/greeter/hello"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp, md, err := local_request_Greeter_SayHello_0(rctx, inboundMarshaler, server, req, pathParams)
|
resp, md, err := local_request_Greeter_SayHello_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
forward_Greeter_SayHello_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
forward_Greeter_SayHello_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -100,21 +94,21 @@ func RegisterGreeterHandlerServer(ctx context.Context, mux *runtime.ServeMux, se
|
|||||||
// RegisterGreeterHandlerFromEndpoint is same as RegisterGreeterHandler but
|
// RegisterGreeterHandlerFromEndpoint is same as RegisterGreeterHandler but
|
||||||
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
||||||
func RegisterGreeterHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
func RegisterGreeterHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
||||||
conn, err := grpc.Dial(endpoint, opts...)
|
conn, err := grpc.NewClient(endpoint, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if cerr := conn.Close(); cerr != nil {
|
if cerr := conn.Close(); cerr != nil {
|
||||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
if cerr := conn.Close(); cerr != nil {
|
if cerr := conn.Close(); cerr != nil {
|
||||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}()
|
}()
|
||||||
@ -139,19 +133,21 @@ func RegisterGreeterHandlerClient(ctx context.Context, mux *runtime.ServeMux, cl
|
|||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/helloworld.Greeter/SayHello", runtime.WithHTTPPathPattern("/api/v1/greeter/hello"))
|
var err error
|
||||||
|
var annotatedContext context.Context
|
||||||
|
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/helloworld.Greeter/SayHello", runtime.WithHTTPPathPattern("/api/v1/greeter/hello"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp, md, err := request_Greeter_SayHello_0(rctx, inboundMarshaler, client, req, pathParams)
|
resp, md, err := request_Greeter_SayHello_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
forward_Greeter_SayHello_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
forward_Greeter_SayHello_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-validate. DO NOT EDIT.
|
// Code generated by protoc-gen-validate. DO NOT EDIT.
|
||||||
// source: example/example.proto
|
// source: example/pb/example.proto
|
||||||
|
|
||||||
package main
|
package pb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -71,6 +71,7 @@ func (m *HelloRequest) validate(all bool) error {
|
|||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
return HelloRequestMultiError(errors)
|
return HelloRequestMultiError(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,6 +172,7 @@ func (m *HelloReply) validate(all bool) error {
|
|||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
return HelloReplyMultiError(errors)
|
return HelloReplyMultiError(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,6 +293,7 @@ func (m *HelloStreamRequest) validate(all bool) error {
|
|||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
return HelloStreamRequestMultiError(errors)
|
return HelloStreamRequestMultiError(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ syntax = "proto3";
|
|||||||
|
|
||||||
package helloworld;
|
package helloworld;
|
||||||
|
|
||||||
option go_package = "go.linka.cloud/grpc/example;main";
|
option go_package = "go.linka.cloud/grpc-toolkit/example;pb";
|
||||||
|
|
||||||
import "google/api/annotations.proto";
|
import "google/api/annotations.proto";
|
||||||
import "validate/validate.proto";
|
import "validate/validate.proto";
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"swagger": "2.0",
|
"swagger": "2.0",
|
||||||
"info": {
|
"info": {
|
||||||
"title": "example/example.proto",
|
"title": "example/pb/example.proto",
|
||||||
"version": "version not set"
|
"version": "version not set"
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
@ -88,6 +88,7 @@
|
|||||||
"details": {
|
"details": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
|
"type": "object",
|
||||||
"$ref": "#/definitions/protobufAny"
|
"$ref": "#/definitions/protobufAny"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,10 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// - protoc-gen-go-grpc v1.5.1
|
||||||
|
// - protoc v5.28.2
|
||||||
|
// source: example/pb/example.proto
|
||||||
|
|
||||||
package main
|
package pb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
context "context"
|
context "context"
|
||||||
|
|
||||||
grpc "google.golang.org/grpc"
|
grpc "google.golang.org/grpc"
|
||||||
codes "google.golang.org/grpc/codes"
|
codes "google.golang.org/grpc/codes"
|
||||||
status "google.golang.org/grpc/status"
|
status "google.golang.org/grpc/status"
|
||||||
@ -12,15 +15,20 @@ import (
|
|||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
// is compatible with the grpc package it is being compiled against.
|
// is compatible with the grpc package it is being compiled against.
|
||||||
// Requires gRPC-Go v1.32.0 or later.
|
// Requires gRPC-Go v1.64.0 or later.
|
||||||
const _ = grpc.SupportPackageIsVersion7
|
const _ = grpc.SupportPackageIsVersion9
|
||||||
|
|
||||||
|
const (
|
||||||
|
Greeter_SayHello_FullMethodName = "/helloworld.Greeter/SayHello"
|
||||||
|
Greeter_SayHelloStream_FullMethodName = "/helloworld.Greeter/SayHelloStream"
|
||||||
|
)
|
||||||
|
|
||||||
// GreeterClient is the client API for Greeter service.
|
// GreeterClient is the client API for Greeter service.
|
||||||
//
|
//
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
type GreeterClient interface {
|
type GreeterClient interface {
|
||||||
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
|
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
|
||||||
SayHelloStream(ctx context.Context, in *HelloStreamRequest, opts ...grpc.CallOption) (Greeter_SayHelloStreamClient, error)
|
SayHelloStream(ctx context.Context, in *HelloStreamRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HelloReply], error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type greeterClient struct {
|
type greeterClient struct {
|
||||||
@ -32,20 +40,22 @@ func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
|
func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
out := new(HelloReply)
|
out := new(HelloReply)
|
||||||
err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...)
|
err := c.cc.Invoke(ctx, Greeter_SayHello_FullMethodName, in, out, cOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *greeterClient) SayHelloStream(ctx context.Context, in *HelloStreamRequest, opts ...grpc.CallOption) (Greeter_SayHelloStreamClient, error) {
|
func (c *greeterClient) SayHelloStream(ctx context.Context, in *HelloStreamRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HelloReply], error) {
|
||||||
stream, err := c.cc.NewStream(ctx, &Greeter_ServiceDesc.Streams[0], "/helloworld.Greeter/SayHelloStream", opts...)
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &Greeter_ServiceDesc.Streams[0], Greeter_SayHelloStream_FullMethodName, cOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
x := &greeterSayHelloStreamClient{stream}
|
x := &grpc.GenericClientStream[HelloStreamRequest, HelloReply]{ClientStream: stream}
|
||||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -55,43 +65,33 @@ func (c *greeterClient) SayHelloStream(ctx context.Context, in *HelloStreamReque
|
|||||||
return x, nil
|
return x, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Greeter_SayHelloStreamClient interface {
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
Recv() (*HelloReply, error)
|
type Greeter_SayHelloStreamClient = grpc.ServerStreamingClient[HelloReply]
|
||||||
grpc.ClientStream
|
|
||||||
}
|
|
||||||
|
|
||||||
type greeterSayHelloStreamClient struct {
|
|
||||||
grpc.ClientStream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *greeterSayHelloStreamClient) Recv() (*HelloReply, error) {
|
|
||||||
m := new(HelloReply)
|
|
||||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GreeterServer is the server API for Greeter service.
|
// GreeterServer is the server API for Greeter service.
|
||||||
// All implementations must embed UnimplementedGreeterServer
|
// All implementations must embed UnimplementedGreeterServer
|
||||||
// for forward compatibility
|
// for forward compatibility.
|
||||||
type GreeterServer interface {
|
type GreeterServer interface {
|
||||||
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
|
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
|
||||||
SayHelloStream(*HelloStreamRequest, Greeter_SayHelloStreamServer) error
|
SayHelloStream(*HelloStreamRequest, grpc.ServerStreamingServer[HelloReply]) error
|
||||||
mustEmbedUnimplementedGreeterServer()
|
mustEmbedUnimplementedGreeterServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnimplementedGreeterServer must be embedded to have forward compatible implementations.
|
// UnimplementedGreeterServer must be embedded to have
|
||||||
type UnimplementedGreeterServer struct {
|
// forward compatible implementations.
|
||||||
}
|
//
|
||||||
|
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||||
|
// pointer dereference when methods are called.
|
||||||
|
type UnimplementedGreeterServer struct{}
|
||||||
|
|
||||||
func (UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {
|
func (UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedGreeterServer) SayHelloStream(*HelloStreamRequest, Greeter_SayHelloStreamServer) error {
|
func (UnimplementedGreeterServer) SayHelloStream(*HelloStreamRequest, grpc.ServerStreamingServer[HelloReply]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method SayHelloStream not implemented")
|
return status.Errorf(codes.Unimplemented, "method SayHelloStream not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {}
|
func (UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {}
|
||||||
|
func (UnimplementedGreeterServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
// UnsafeGreeterServer may be embedded to opt out of forward compatibility for this service.
|
// UnsafeGreeterServer may be embedded to opt out of forward compatibility for this service.
|
||||||
// Use of this interface is not recommended, as added methods to GreeterServer will
|
// Use of this interface is not recommended, as added methods to GreeterServer will
|
||||||
@ -101,6 +101,13 @@ type UnsafeGreeterServer interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) {
|
func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) {
|
||||||
|
// If the following call pancis, it indicates UnimplementedGreeterServer was
|
||||||
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
|
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||||
|
t.testEmbeddedByValue()
|
||||||
|
}
|
||||||
s.RegisterService(&Greeter_ServiceDesc, srv)
|
s.RegisterService(&Greeter_ServiceDesc, srv)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +121,7 @@ func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(in
|
|||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/helloworld.Greeter/SayHello",
|
FullMethod: Greeter_SayHello_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
|
return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
|
||||||
@ -127,21 +134,11 @@ func _Greeter_SayHelloStream_Handler(srv interface{}, stream grpc.ServerStream)
|
|||||||
if err := stream.RecvMsg(m); err != nil {
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return srv.(GreeterServer).SayHelloStream(m, &greeterSayHelloStreamServer{stream})
|
return srv.(GreeterServer).SayHelloStream(m, &grpc.GenericServerStream[HelloStreamRequest, HelloReply]{ServerStream: stream})
|
||||||
}
|
}
|
||||||
|
|
||||||
type Greeter_SayHelloStreamServer interface {
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
Send(*HelloReply) error
|
type Greeter_SayHelloStreamServer = grpc.ServerStreamingServer[HelloReply]
|
||||||
grpc.ServerStream
|
|
||||||
}
|
|
||||||
|
|
||||||
type greeterSayHelloStreamServer struct {
|
|
||||||
grpc.ServerStream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *greeterSayHelloStreamServer) Send(m *HelloReply) error {
|
|
||||||
return x.ServerStream.SendMsg(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Greeter_ServiceDesc is the grpc.ServiceDesc for Greeter service.
|
// Greeter_ServiceDesc is the grpc.ServiceDesc for Greeter service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
@ -162,5 +159,5 @@ var Greeter_ServiceDesc = grpc.ServiceDesc{
|
|||||||
ServerStreams: true,
|
ServerStreams: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Metadata: "example/example.proto",
|
Metadata: "example/pb/example.proto",
|
||||||
}
|
}
|
@ -1,15 +1,14 @@
|
|||||||
// Code generated by protoc-gen-go-vtproto. DO NOT EDIT.
|
// Code generated by protoc-gen-go-vtproto. DO NOT EDIT.
|
||||||
// protoc-gen-go-vtproto version: v0.2.0
|
// protoc-gen-go-vtproto version: v0.6.1-0.20240917153116-6f2963f01587
|
||||||
// source: example/example.proto
|
// source: example/pb/example.proto
|
||||||
|
|
||||||
package main
|
package pb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
fmt "fmt"
|
fmt "fmt"
|
||||||
io "io"
|
protohelpers "github.com/planetscale/vtprotobuf/protohelpers"
|
||||||
bits "math/bits"
|
|
||||||
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
io "io"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -52,7 +51,7 @@ func (m *HelloRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
|||||||
if len(m.Name) > 0 {
|
if len(m.Name) > 0 {
|
||||||
i -= len(m.Name)
|
i -= len(m.Name)
|
||||||
copy(dAtA[i:], m.Name)
|
copy(dAtA[i:], m.Name)
|
||||||
i = encodeVarint(dAtA, i, uint64(len(m.Name)))
|
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name)))
|
||||||
i--
|
i--
|
||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
}
|
}
|
||||||
@ -92,7 +91,7 @@ func (m *HelloReply) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
|||||||
if len(m.Message) > 0 {
|
if len(m.Message) > 0 {
|
||||||
i -= len(m.Message)
|
i -= len(m.Message)
|
||||||
copy(dAtA[i:], m.Message)
|
copy(dAtA[i:], m.Message)
|
||||||
i = encodeVarint(dAtA, i, uint64(len(m.Message)))
|
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Message)))
|
||||||
i--
|
i--
|
||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
}
|
}
|
||||||
@ -130,31 +129,20 @@ func (m *HelloStreamRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
|||||||
copy(dAtA[i:], m.unknownFields)
|
copy(dAtA[i:], m.unknownFields)
|
||||||
}
|
}
|
||||||
if m.Count != 0 {
|
if m.Count != 0 {
|
||||||
i = encodeVarint(dAtA, i, uint64(m.Count))
|
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Count))
|
||||||
i--
|
i--
|
||||||
dAtA[i] = 0x10
|
dAtA[i] = 0x10
|
||||||
}
|
}
|
||||||
if len(m.Name) > 0 {
|
if len(m.Name) > 0 {
|
||||||
i -= len(m.Name)
|
i -= len(m.Name)
|
||||||
copy(dAtA[i:], m.Name)
|
copy(dAtA[i:], m.Name)
|
||||||
i = encodeVarint(dAtA, i, uint64(len(m.Name)))
|
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name)))
|
||||||
i--
|
i--
|
||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
}
|
}
|
||||||
return len(dAtA) - i, nil
|
return len(dAtA) - i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeVarint(dAtA []byte, offset int, v uint64) int {
|
|
||||||
offset -= sov(v)
|
|
||||||
base := offset
|
|
||||||
for v >= 1<<7 {
|
|
||||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
|
||||||
v >>= 7
|
|
||||||
offset++
|
|
||||||
}
|
|
||||||
dAtA[offset] = uint8(v)
|
|
||||||
return base
|
|
||||||
}
|
|
||||||
func (m *HelloRequest) SizeVT() (n int) {
|
func (m *HelloRequest) SizeVT() (n int) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return 0
|
return 0
|
||||||
@ -163,11 +151,9 @@ func (m *HelloRequest) SizeVT() (n int) {
|
|||||||
_ = l
|
_ = l
|
||||||
l = len(m.Name)
|
l = len(m.Name)
|
||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sov(uint64(l))
|
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||||
}
|
|
||||||
if m.unknownFields != nil {
|
|
||||||
n += len(m.unknownFields)
|
|
||||||
}
|
}
|
||||||
|
n += len(m.unknownFields)
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,11 +165,9 @@ func (m *HelloReply) SizeVT() (n int) {
|
|||||||
_ = l
|
_ = l
|
||||||
l = len(m.Message)
|
l = len(m.Message)
|
||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sov(uint64(l))
|
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||||
}
|
|
||||||
if m.unknownFields != nil {
|
|
||||||
n += len(m.unknownFields)
|
|
||||||
}
|
}
|
||||||
|
n += len(m.unknownFields)
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,23 +179,15 @@ func (m *HelloStreamRequest) SizeVT() (n int) {
|
|||||||
_ = l
|
_ = l
|
||||||
l = len(m.Name)
|
l = len(m.Name)
|
||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sov(uint64(l))
|
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||||
}
|
}
|
||||||
if m.Count != 0 {
|
if m.Count != 0 {
|
||||||
n += 1 + sov(uint64(m.Count))
|
n += 1 + protohelpers.SizeOfVarint(uint64(m.Count))
|
||||||
}
|
|
||||||
if m.unknownFields != nil {
|
|
||||||
n += len(m.unknownFields)
|
|
||||||
}
|
}
|
||||||
|
n += len(m.unknownFields)
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func sov(x uint64) (n int) {
|
|
||||||
return (bits.Len64(x|1) + 6) / 7
|
|
||||||
}
|
|
||||||
func soz(x uint64) (n int) {
|
|
||||||
return sov(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
|
||||||
}
|
|
||||||
func (m *HelloRequest) UnmarshalVT(dAtA []byte) error {
|
func (m *HelloRequest) UnmarshalVT(dAtA []byte) error {
|
||||||
l := len(dAtA)
|
l := len(dAtA)
|
||||||
iNdEx := 0
|
iNdEx := 0
|
||||||
@ -220,7 +196,7 @@ func (m *HelloRequest) UnmarshalVT(dAtA []byte) error {
|
|||||||
var wire uint64
|
var wire uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
if shift >= 64 {
|
if shift >= 64 {
|
||||||
return ErrIntOverflow
|
return protohelpers.ErrIntOverflow
|
||||||
}
|
}
|
||||||
if iNdEx >= l {
|
if iNdEx >= l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -248,7 +224,7 @@ func (m *HelloRequest) UnmarshalVT(dAtA []byte) error {
|
|||||||
var stringLen uint64
|
var stringLen uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
if shift >= 64 {
|
if shift >= 64 {
|
||||||
return ErrIntOverflow
|
return protohelpers.ErrIntOverflow
|
||||||
}
|
}
|
||||||
if iNdEx >= l {
|
if iNdEx >= l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -262,11 +238,11 @@ func (m *HelloRequest) UnmarshalVT(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
intStringLen := int(stringLen)
|
intStringLen := int(stringLen)
|
||||||
if intStringLen < 0 {
|
if intStringLen < 0 {
|
||||||
return ErrInvalidLength
|
return protohelpers.ErrInvalidLength
|
||||||
}
|
}
|
||||||
postIndex := iNdEx + intStringLen
|
postIndex := iNdEx + intStringLen
|
||||||
if postIndex < 0 {
|
if postIndex < 0 {
|
||||||
return ErrInvalidLength
|
return protohelpers.ErrInvalidLength
|
||||||
}
|
}
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -275,12 +251,12 @@ func (m *HelloRequest) UnmarshalVT(dAtA []byte) error {
|
|||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skip(dAtA[iNdEx:])
|
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||||
return ErrInvalidLength
|
return protohelpers.ErrInvalidLength
|
||||||
}
|
}
|
||||||
if (iNdEx + skippy) > l {
|
if (iNdEx + skippy) > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -303,7 +279,7 @@ func (m *HelloReply) UnmarshalVT(dAtA []byte) error {
|
|||||||
var wire uint64
|
var wire uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
if shift >= 64 {
|
if shift >= 64 {
|
||||||
return ErrIntOverflow
|
return protohelpers.ErrIntOverflow
|
||||||
}
|
}
|
||||||
if iNdEx >= l {
|
if iNdEx >= l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -331,7 +307,7 @@ func (m *HelloReply) UnmarshalVT(dAtA []byte) error {
|
|||||||
var stringLen uint64
|
var stringLen uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
if shift >= 64 {
|
if shift >= 64 {
|
||||||
return ErrIntOverflow
|
return protohelpers.ErrIntOverflow
|
||||||
}
|
}
|
||||||
if iNdEx >= l {
|
if iNdEx >= l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -345,11 +321,11 @@ func (m *HelloReply) UnmarshalVT(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
intStringLen := int(stringLen)
|
intStringLen := int(stringLen)
|
||||||
if intStringLen < 0 {
|
if intStringLen < 0 {
|
||||||
return ErrInvalidLength
|
return protohelpers.ErrInvalidLength
|
||||||
}
|
}
|
||||||
postIndex := iNdEx + intStringLen
|
postIndex := iNdEx + intStringLen
|
||||||
if postIndex < 0 {
|
if postIndex < 0 {
|
||||||
return ErrInvalidLength
|
return protohelpers.ErrInvalidLength
|
||||||
}
|
}
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -358,12 +334,12 @@ func (m *HelloReply) UnmarshalVT(dAtA []byte) error {
|
|||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skip(dAtA[iNdEx:])
|
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||||
return ErrInvalidLength
|
return protohelpers.ErrInvalidLength
|
||||||
}
|
}
|
||||||
if (iNdEx + skippy) > l {
|
if (iNdEx + skippy) > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -386,7 +362,7 @@ func (m *HelloStreamRequest) UnmarshalVT(dAtA []byte) error {
|
|||||||
var wire uint64
|
var wire uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
if shift >= 64 {
|
if shift >= 64 {
|
||||||
return ErrIntOverflow
|
return protohelpers.ErrIntOverflow
|
||||||
}
|
}
|
||||||
if iNdEx >= l {
|
if iNdEx >= l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -414,7 +390,7 @@ func (m *HelloStreamRequest) UnmarshalVT(dAtA []byte) error {
|
|||||||
var stringLen uint64
|
var stringLen uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
if shift >= 64 {
|
if shift >= 64 {
|
||||||
return ErrIntOverflow
|
return protohelpers.ErrIntOverflow
|
||||||
}
|
}
|
||||||
if iNdEx >= l {
|
if iNdEx >= l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -428,11 +404,11 @@ func (m *HelloStreamRequest) UnmarshalVT(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
intStringLen := int(stringLen)
|
intStringLen := int(stringLen)
|
||||||
if intStringLen < 0 {
|
if intStringLen < 0 {
|
||||||
return ErrInvalidLength
|
return protohelpers.ErrInvalidLength
|
||||||
}
|
}
|
||||||
postIndex := iNdEx + intStringLen
|
postIndex := iNdEx + intStringLen
|
||||||
if postIndex < 0 {
|
if postIndex < 0 {
|
||||||
return ErrInvalidLength
|
return protohelpers.ErrInvalidLength
|
||||||
}
|
}
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -446,7 +422,7 @@ func (m *HelloStreamRequest) UnmarshalVT(dAtA []byte) error {
|
|||||||
m.Count = 0
|
m.Count = 0
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
if shift >= 64 {
|
if shift >= 64 {
|
||||||
return ErrIntOverflow
|
return protohelpers.ErrIntOverflow
|
||||||
}
|
}
|
||||||
if iNdEx >= l {
|
if iNdEx >= l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -460,12 +436,12 @@ func (m *HelloStreamRequest) UnmarshalVT(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skip(dAtA[iNdEx:])
|
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||||
return ErrInvalidLength
|
return protohelpers.ErrInvalidLength
|
||||||
}
|
}
|
||||||
if (iNdEx + skippy) > l {
|
if (iNdEx + skippy) > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
@ -480,87 +456,3 @@ func (m *HelloStreamRequest) UnmarshalVT(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func skip(dAtA []byte) (n int, err error) {
|
|
||||||
l := len(dAtA)
|
|
||||||
iNdEx := 0
|
|
||||||
depth := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflow
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
switch wireType {
|
|
||||||
case 0:
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflow
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
iNdEx++
|
|
||||||
if dAtA[iNdEx-1] < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
iNdEx += 8
|
|
||||||
case 2:
|
|
||||||
var length int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflow
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
length |= (int(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if length < 0 {
|
|
||||||
return 0, ErrInvalidLength
|
|
||||||
}
|
|
||||||
iNdEx += length
|
|
||||||
case 3:
|
|
||||||
depth++
|
|
||||||
case 4:
|
|
||||||
if depth == 0 {
|
|
||||||
return 0, ErrUnexpectedEndOfGroup
|
|
||||||
}
|
|
||||||
depth--
|
|
||||||
case 5:
|
|
||||||
iNdEx += 4
|
|
||||||
default:
|
|
||||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
|
||||||
}
|
|
||||||
if iNdEx < 0 {
|
|
||||||
return 0, ErrInvalidLength
|
|
||||||
}
|
|
||||||
if depth == 0 {
|
|
||||||
return iNdEx, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidLength = fmt.Errorf("proto: negative length found during unmarshaling")
|
|
||||||
ErrIntOverflow = fmt.Errorf("proto: integer overflow")
|
|
||||||
ErrUnexpectedEndOfGroup = fmt.Errorf("proto: unexpected end of group")
|
|
||||||
)
|
|
91
example/reflect.go
Normal file
91
example/reflect.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
greflectsvc "google.golang.org/grpc/reflection/grpc_reflection_v1"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/types/descriptorpb"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/client"
|
||||||
|
"go.linka.cloud/grpc-toolkit/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func readSvcs(ctx context.Context, c client.Client) (err error) {
|
||||||
|
log := logger.C(ctx)
|
||||||
|
rc := greflectsvc.NewServerReflectionClient(c)
|
||||||
|
rstream, err := rc.ServerReflectionInfo(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err2 := rstream.CloseSend(); err2 != nil && err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err = rstream.Send(&greflectsvc.ServerReflectionRequest{MessageRequest: &greflectsvc.ServerReflectionRequest_ListServices{}}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var rres *greflectsvc.ServerReflectionResponse
|
||||||
|
rres, err = rstream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rlist, ok := rres.MessageResponse.(*greflectsvc.ServerReflectionResponse_ListServicesResponse)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected reflection response type: %T", rres.MessageResponse)
|
||||||
|
}
|
||||||
|
for _, v := range rlist.ListServicesResponse.Service {
|
||||||
|
if v.Name == "grpc.reflection.v1alpha.ServerReflection" || v.Name == "grpc.reflection.v1.ServerReflection" || v.Name == "grpc.health.v1.Health" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Split(v.Name, ".")
|
||||||
|
if len(parts) < 2 {
|
||||||
|
return fmt.Errorf("malformed service name: %s", v.Name)
|
||||||
|
}
|
||||||
|
pkg := strings.Join(parts[:len(parts)-1], ".")
|
||||||
|
svc := parts[len(parts)-1]
|
||||||
|
if err = rstream.Send(&greflectsvc.ServerReflectionRequest{MessageRequest: &greflectsvc.ServerReflectionRequest_FileContainingSymbol{
|
||||||
|
FileContainingSymbol: v.Name,
|
||||||
|
}}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rres, err = rstream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
rfile, ok := rres.MessageResponse.(*greflectsvc.ServerReflectionResponse_FileDescriptorResponse)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected reflection response type: %T", rres.MessageResponse)
|
||||||
|
}
|
||||||
|
fdps := make(map[string]*descriptorpb.DescriptorProto)
|
||||||
|
var sdp *descriptorpb.ServiceDescriptorProto
|
||||||
|
for _, v := range rfile.FileDescriptorResponse.FileDescriptorProto {
|
||||||
|
fdp := &descriptorpb.FileDescriptorProto{}
|
||||||
|
if err = proto.Unmarshal(v, fdp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, s := range fdp.GetService() {
|
||||||
|
if fdp.GetPackage() == pkg && s.GetName() == svc {
|
||||||
|
if sdp != nil {
|
||||||
|
log.Warnf("service already found: %s.%s", fdp.GetPackage(), s.GetName())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sdp = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, m := range fdp.GetMessageType() {
|
||||||
|
fdps[fdp.GetPackage()+"."+m.GetName()] = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sdp == nil {
|
||||||
|
return fmt.Errorf("%s: service not found", v.Name)
|
||||||
|
}
|
||||||
|
for _, m := range sdp.GetMethod() {
|
||||||
|
log.Infof("%s: %s", v.Name, m.GetName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
60
example/server.go
Normal file
60
example/server.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-tookit/example/pb"
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors/iface"
|
||||||
|
"go.linka.cloud/grpc-toolkit/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ iface.UnaryInterceptor = (*GreeterHandler)(nil)
|
||||||
|
_ iface.StreamInterceptor = (*GreeterHandler)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type GreeterHandler struct {
|
||||||
|
pb.UnimplementedGreeterServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func hello(name string) string {
|
||||||
|
return fmt.Sprintf("Hello %s !", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GreeterHandler) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
|
||||||
|
span := trace.SpanFromContext(ctx)
|
||||||
|
span.SetAttributes(attribute.String("name", req.Name))
|
||||||
|
logger.C(ctx).Infof("replying to %s", req.Name)
|
||||||
|
return &pb.HelloReply{Message: hello(req.Name)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GreeterHandler) SayHelloStream(req *pb.HelloStreamRequest, s pb.Greeter_SayHelloStreamServer) error {
|
||||||
|
log := logger.C(s.Context())
|
||||||
|
for i := int64(0); i < req.Count; i++ {
|
||||||
|
log.Infof("sending message %d", i+1)
|
||||||
|
if err := s.Send(&pb.HelloReply{Message: fmt.Sprintf("Hello %s (%d)!", req.Name, i+1)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GreeterHandler) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||||
|
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
||||||
|
logger.C(ctx).Infof("called service interface unary interceptor")
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GreeterHandler) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||||
|
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||||
|
logger.C(ss.Context()).Infof("called service interface stream interceptor")
|
||||||
|
return handler(srv, ss)
|
||||||
|
}
|
||||||
|
}
|
95
example/service.go
Normal file
95
example/service.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
logging2 "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
|
||||||
|
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-tookit/example/pb"
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors/auth"
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors/ban"
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors/defaulter"
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors/iface"
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors/logging"
|
||||||
|
metrics2 "go.linka.cloud/grpc-toolkit/interceptors/metrics"
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors/tracing"
|
||||||
|
validation2 "go.linka.cloud/grpc-toolkit/interceptors/validation"
|
||||||
|
"go.linka.cloud/grpc-toolkit/logger"
|
||||||
|
"go.linka.cloud/grpc-toolkit/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newService(ctx context.Context, opts ...service.Option) (service.Service, error) {
|
||||||
|
log := logger.C(ctx)
|
||||||
|
metrics := metrics2.NewInterceptors(metrics2.WithExemplarFromContext(metrics2.DefaultExemplarFromCtx))
|
||||||
|
|
||||||
|
address := "0.0.0.0:9991"
|
||||||
|
|
||||||
|
var svc service.Service
|
||||||
|
opts = append(opts,
|
||||||
|
service.WithContext(ctx),
|
||||||
|
service.WithAddress(address),
|
||||||
|
// service.WithRegistry(mdns.NewRegistry()),
|
||||||
|
service.WithReflection(true),
|
||||||
|
service.WithoutCmux(),
|
||||||
|
service.WithGateway(pb.RegisterGreeterHandler),
|
||||||
|
service.WithGatewayPrefix("/rest"),
|
||||||
|
service.WithGRPCWeb(true),
|
||||||
|
service.WithGRPCWebPrefix("/grpc"),
|
||||||
|
service.WithMiddlewares(otelhttp.NewMiddleware("hello"), httpLogger),
|
||||||
|
service.WithInterceptors(
|
||||||
|
tracing.NewInterceptors(),
|
||||||
|
metrics,
|
||||||
|
logging.New(ctx, logging2.WithFieldsFromContext(func(ctx context.Context) logging2.Fields {
|
||||||
|
if span := trace.SpanContextFromContext(ctx); span.IsSampled() {
|
||||||
|
return logging2.Fields{"traceID", span.TraceID().String()}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
service.WithServerInterceptors(
|
||||||
|
ban.NewInterceptors(ban.WithDefaultJailDuration(time.Second), ban.WithDefaultCallback(func(action ban.Action, actor string, rule *ban.Rule) error {
|
||||||
|
log.WithFields("action", action, "actor", actor, "rule", rule.Name).Info("ban callback")
|
||||||
|
return nil
|
||||||
|
})),
|
||||||
|
auth.NewServerInterceptors(auth.WithBasicValidators(func(ctx context.Context, user, password string) (context.Context, error) {
|
||||||
|
if !auth.Equals(user, "admin") || !auth.Equals(password, "admin") {
|
||||||
|
return ctx, fmt.Errorf("invalid user or password")
|
||||||
|
}
|
||||||
|
log.Infof("request authenticated")
|
||||||
|
return ctx, nil
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
service.WithInterceptors(
|
||||||
|
defaulter.NewInterceptors(),
|
||||||
|
validation2.NewInterceptors(true),
|
||||||
|
),
|
||||||
|
// enable server interface interceptor
|
||||||
|
service.WithServerInterceptors(iface.New()),
|
||||||
|
)
|
||||||
|
svc, err := service.New(opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pb.RegisterGreeterServer(svc, &GreeterHandler{})
|
||||||
|
metrics.Register(svc)
|
||||||
|
return svc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func httpLogger(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
start := time.Now()
|
||||||
|
log := logger.From(request.Context()).WithFields(
|
||||||
|
"method", request.Method,
|
||||||
|
"host", request.Host,
|
||||||
|
"path", request.URL.Path,
|
||||||
|
"remoteAddress", request.RemoteAddr,
|
||||||
|
)
|
||||||
|
next.ServeHTTP(writer, request)
|
||||||
|
log.WithField("duration", time.Since(start)).Info()
|
||||||
|
})
|
||||||
|
}
|
108
go.mod
108
go.mod
@ -1,50 +1,94 @@
|
|||||||
module go.linka.cloud/grpc
|
module go.linka.cloud/grpc-toolkit
|
||||||
|
|
||||||
go 1.13
|
go 1.22.0
|
||||||
|
|
||||||
|
toolchain go1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alta/protopatch v0.3.4
|
github.com/alta/protopatch v0.5.3
|
||||||
github.com/bombsimon/logrusr/v2 v2.0.1
|
github.com/bombsimon/logrusr/v4 v4.0.0
|
||||||
github.com/caitlinelfring/go-env-default v1.1.0
|
github.com/caitlinelfring/go-env-default v1.1.0
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.6.2
|
github.com/envoyproxy/protoc-gen-validate v1.1.0
|
||||||
github.com/fsnotify/fsnotify v1.5.1
|
github.com/fatih/color v1.13.0
|
||||||
|
github.com/fsnotify/fsnotify v1.5.4
|
||||||
github.com/fullstorydev/grpchan v1.1.1
|
github.com/fullstorydev/grpchan v1.1.1
|
||||||
github.com/go-logr/logr v1.2.3
|
github.com/go-logr/logr v1.4.2
|
||||||
github.com/golang/protobuf v1.5.2
|
github.com/golang/protobuf v1.5.4
|
||||||
github.com/google/uuid v1.1.2
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/websocket v1.4.2 // indirect
|
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0
|
||||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645
|
github.com/jaredfolkins/badactor v1.2.0
|
||||||
github.com/improbable-eng/grpc-web v0.14.1
|
github.com/johnbellone/grpc-middleware-sentry v0.3.0
|
||||||
github.com/jinzhu/gorm v1.9.12
|
|
||||||
github.com/johnbellone/grpc-middleware-sentry v0.2.0
|
|
||||||
github.com/justinas/alice v1.2.0
|
github.com/justinas/alice v1.2.0
|
||||||
github.com/lyft/protoc-gen-star v0.6.0 // indirect
|
|
||||||
github.com/miekg/dns v1.1.41
|
github.com/miekg/dns v1.1.41
|
||||||
github.com/opentracing/opentracing-go v1.1.0
|
github.com/pires/go-proxyproto v0.7.0
|
||||||
github.com/planetscale/vtprotobuf v0.2.0
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587
|
||||||
github.com/prometheus/client_golang v1.11.0
|
github.com/prometheus/client_golang v1.20.4
|
||||||
github.com/rs/cors v1.7.0
|
github.com/rs/cors v1.7.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/soheilhy/cmux v0.1.5
|
github.com/soheilhy/cmux v0.1.5
|
||||||
github.com/spf13/cobra v1.3.0
|
github.com/spf13/cobra v1.3.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5
|
||||||
go.linka.cloud/protoc-gen-defaults v0.1.0
|
github.com/traefik/grpc-web v0.16.0
|
||||||
go.linka.cloud/protoc-gen-go-fields v0.1.1
|
go.linka.cloud/protoc-gen-defaults v0.4.0
|
||||||
go.linka.cloud/protofilters v0.2.2
|
go.linka.cloud/protoc-gen-go-fields v0.4.0
|
||||||
|
go.linka.cloud/protofilters v0.8.1
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0
|
||||||
|
go.opentelemetry.io/otel/trace v1.31.0
|
||||||
go.uber.org/multierr v1.7.0
|
go.uber.org/multierr v1.7.0
|
||||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d
|
golang.org/x/net v0.30.0
|
||||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa
|
golang.org/x/sync v0.8.0
|
||||||
google.golang.org/grpc v1.45.0
|
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9
|
||||||
google.golang.org/protobuf v1.27.1
|
google.golang.org/grpc v1.67.1
|
||||||
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
|
||||||
|
google.golang.org/protobuf v1.35.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||||
|
github.com/fatih/structtag v1.2.0 // indirect
|
||||||
|
github.com/getsentry/sentry-go v0.24.1 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/gobwas/ws v1.1.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
|
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/jhump/protoreflect v1.11.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/lyft/protoc-gen-star v0.6.2 // indirect
|
||||||
|
github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
|
github.com/prometheus/common v0.60.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.31.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.31.0 // indirect
|
||||||
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
|
golang.org/x/mod v0.21.0 // indirect
|
||||||
|
golang.org/x/sys v0.26.0 // indirect
|
||||||
|
golang.org/x/text v0.19.0 // indirect
|
||||||
|
golang.org/x/tools v0.26.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
nhooyr.io/websocket v1.8.7 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace (
|
replace (
|
||||||
|
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus => github.com/linka-cloud/go-grpc-prometheus v1.2.0-lk
|
github.com/grpc-ecosystem/go-grpc-prometheus => github.com/linka-cloud/go-grpc-prometheus v1.2.0-lk
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 => github.com/linka-cloud/grpc-gateway/v2 v2.5.1-0.20210917084803-33b6d54c9e11
|
github.com/grpc-ecosystem/grpc-gateway/v2 => github.com/linka-cloud/grpc-gateway/v2 v2.20.0-lk
|
||||||
|
nhooyr.io/websocket => github.com/coder/websocket v1.8.6
|
||||||
)
|
)
|
||||||
|
@ -5,11 +5,11 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/errors"
|
"go.linka.cloud/grpc-toolkit/errors"
|
||||||
"go.linka.cloud/grpc/interceptors"
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
"go.linka.cloud/grpc/interceptors/metadata"
|
"go.linka.cloud/grpc-toolkit/interceptors/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BasicAuth(user, password string) string {
|
func BasicAuth(user, password string) string {
|
||||||
|
@ -3,19 +3,19 @@ package auth
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"strings"
|
|
||||||
|
|
||||||
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/interceptors"
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ChainedAuthFuncs(fn ...grpc_auth.AuthFunc) grpc_auth.AuthFunc {
|
func ChainedAuthFuncs(fn ...grpc_auth.AuthFunc) grpc_auth.AuthFunc {
|
||||||
return func(ctx context.Context) (context.Context, error) {
|
return func(ctx context.Context) (context.Context, error) {
|
||||||
code := codes.Unauthenticated
|
spb := status.New(codes.Unauthenticated, codes.Unauthenticated.String()).Proto()
|
||||||
for _, v := range fn {
|
for _, v := range fn {
|
||||||
ctx2, err := v(ctx)
|
ctx2, err := v(ctx)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -25,11 +25,14 @@ func ChainedAuthFuncs(fn ...grpc_auth.AuthFunc) grpc_auth.AuthFunc {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return ctx2, err
|
return ctx2, err
|
||||||
}
|
}
|
||||||
if s.Code() == codes.PermissionDenied {
|
if spb.Code != s.Proto().Code {
|
||||||
code = codes.PermissionDenied
|
spb.Code = s.Proto().Code
|
||||||
}
|
}
|
||||||
|
d, _ := anypb.New(s.Proto())
|
||||||
|
spb.Details = append(spb.Details, d)
|
||||||
|
spb.Message += ", " + s.Proto().Message
|
||||||
}
|
}
|
||||||
return ctx, status.Error(code, code.String())
|
return ctx, status.FromProto(spb).Err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,15 +74,8 @@ func (i *interceptor) isNotProtected(endpoint string) bool {
|
|||||||
if len(i.o.ignoredMethods) == 0 && len(i.o.methods) == 0 {
|
if len(i.o.ignoredMethods) == 0 && len(i.o.methods) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// endpoint is like /helloworld.Greeter/SayHello
|
|
||||||
parts := strings.Split(strings.TrimPrefix(endpoint, "/"), "/")
|
|
||||||
// invalid endpoint format
|
|
||||||
if len(parts) != 2 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
method := parts[1]
|
|
||||||
for _, v := range i.o.ignoredMethods {
|
for _, v := range i.o.ignoredMethods {
|
||||||
if v == method {
|
if v == endpoint {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,7 +83,7 @@ func (i *interceptor) isNotProtected(endpoint string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, v := range i.o.methods {
|
for _, v := range i.o.methods {
|
||||||
if v == method {
|
if v == endpoint {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,32 +4,32 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||||
assert2 "github.com/stretchr/testify/assert"
|
assert2 "github.com/stretchr/testify/assert"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/errors"
|
"go.linka.cloud/grpc-toolkit/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNotProtectededOnly(t *testing.T) {
|
func TestNotProtectedOnly(t *testing.T) {
|
||||||
assert := assert2.New(t)
|
assert := assert2.New(t)
|
||||||
i := &interceptor{o: options{ignoredMethods: []string{"ignored"}}}
|
i := &interceptor{o: options{ignoredMethods: []string{"/test.Service/ignored"}}}
|
||||||
assert.False(i.isNotProtected("/test.Service/protected"))
|
assert.False(i.isNotProtected("/test.Service/protected"))
|
||||||
assert.True(i.isNotProtected("/test.Service/ignored"))
|
assert.True(i.isNotProtected("/test.Service/ignored"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProtectedOnly(t *testing.T) {
|
func TestProtectedOnly(t *testing.T) {
|
||||||
assert := assert2.New(t)
|
assert := assert2.New(t)
|
||||||
i := &interceptor{o: options{methods: []string{"protected"}}}
|
i := &interceptor{o: options{methods: []string{"/test.Service/protected"}}}
|
||||||
assert.False(i.isNotProtected("/test.Service/protected"))
|
assert.False(i.isNotProtected("/test.Service/protected"))
|
||||||
assert.True(i.isNotProtected("/test.Service/ignored"))
|
assert.True(i.isNotProtected("/test.Service/ignored"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProtectedAndIgnored(t *testing.T) {
|
func TestProtectedAndIgnored(t *testing.T) {
|
||||||
assert := assert2.New(t)
|
assert := assert2.New(t)
|
||||||
i := &interceptor{o: options{methods: []string{"protected"}, ignoredMethods: []string{"ignored"}}}
|
i := &interceptor{o: options{methods: []string{"/test.Service/protected"}, ignoredMethods: []string{"/test.Service/ignored"}}}
|
||||||
assert.True(i.isNotProtected("/test.Service/ignored"))
|
assert.True(i.isNotProtected("/test.Service/ignored"))
|
||||||
assert.False(i.isNotProtected("/test.Service/protected"))
|
assert.False(i.isNotProtected("/test.Service/protected"))
|
||||||
assert.True(i.isNotProtected("/test.Service/other"))
|
assert.True(i.isNotProtected("/test.Service/other"))
|
||||||
@ -37,7 +37,7 @@ func TestProtectedAndIgnored(t *testing.T) {
|
|||||||
|
|
||||||
func TestProtectedByDefault(t *testing.T) {
|
func TestProtectedByDefault(t *testing.T) {
|
||||||
i := &interceptor{}
|
i := &interceptor{}
|
||||||
assert2.False(t, i.isNotProtected("nooop"))
|
assert2.False(t, i.isNotProtected("/test.Service/noop"))
|
||||||
assert2.False(t, i.isNotProtected("/test.Service/method/cannotExists"))
|
assert2.False(t, i.isNotProtected("/test.Service/method/cannotExists"))
|
||||||
assert2.False(t, i.isNotProtected("/test.Service/validMethod"))
|
assert2.False(t, i.isNotProtected("/test.Service/validMethod"))
|
||||||
}
|
}
|
||||||
@ -99,14 +99,14 @@ func TestChainedAuthFuncs(t *testing.T) {
|
|||||||
name: "empty bearer",
|
name: "empty bearer",
|
||||||
auth: "bearer ",
|
auth: "bearer ",
|
||||||
err: true,
|
err: true,
|
||||||
code: codes.PermissionDenied,
|
code: codes.Unauthenticated,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "internal error",
|
name: "internal error",
|
||||||
auth: "bearer internal",
|
auth: "bearer internal",
|
||||||
internalError: true,
|
internalError: true,
|
||||||
err: true,
|
err: true,
|
||||||
code: codes.PermissionDenied,
|
code: codes.Internal,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "multiple auth: first basic valid",
|
name: "multiple auth: first basic valid",
|
||||||
@ -120,13 +120,13 @@ func TestChainedAuthFuncs(t *testing.T) {
|
|||||||
name: "invalid auth: bearer",
|
name: "invalid auth: bearer",
|
||||||
auth: "bearer noop",
|
auth: "bearer noop",
|
||||||
err: true,
|
err: true,
|
||||||
code: codes.PermissionDenied,
|
code: codes.Unauthenticated,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid auth: basic",
|
name: "invalid auth: basic",
|
||||||
auth: BasicAuth("other", "other"),
|
auth: BasicAuth("other", "other"),
|
||||||
err: true,
|
err: true,
|
||||||
code: codes.PermissionDenied,
|
code: codes.Unauthenticated,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Option func(o *options)
|
type Option func(o *options)
|
||||||
|
|
||||||
|
// WithMethods change the behaviour to not protect by default, it takes a list of fully qualified method names to protect, e.g. /helloworld.Greeter/SayHello
|
||||||
func WithMethods(methods ...string) Option {
|
func WithMethods(methods ...string) Option {
|
||||||
return func(o *options) {
|
return func(o *options) {
|
||||||
o.methods = append(o.methods, methods...)
|
o.methods = append(o.methods, methods...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithIgnoredMethods bypass auth for the given methods, it takes a list of fully qualified method name, e.g. /helloworld.Greeter/SayHello
|
||||||
func WithIgnoredMethods(methods ...string) Option {
|
func WithIgnoredMethods(methods ...string) Option {
|
||||||
return func(o *options) {
|
return func(o *options) {
|
||||||
o.ignoredMethods = append(o.ignoredMethods, methods...)
|
o.ignoredMethods = append(o.ignoredMethods, methods...)
|
||||||
|
@ -3,10 +3,10 @@ package auth
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/interceptors"
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
"go.linka.cloud/grpc/interceptors/metadata"
|
"go.linka.cloud/grpc-toolkit/interceptors/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TokenValidator func(ctx context.Context, token string) (context.Context, error)
|
type TokenValidator func(ctx context.Context, token string) (context.Context, error)
|
||||||
@ -22,5 +22,5 @@ func makeTokenAuthFunc(v TokenValidator) grpc_auth.AuthFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewBearerClientInterceptors(token string) interceptors.ClientInterceptors {
|
func NewBearerClientInterceptors(token string) interceptors.ClientInterceptors {
|
||||||
return metadata.NewInterceptors("authorization", "Bearer "+token)
|
return metadata.NewInterceptors("authorization", "bearer "+token)
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,11 @@ package auth
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/peer"
|
"google.golang.org/grpc/peer"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/errors"
|
"go.linka.cloud/grpc-toolkit/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type X509Validator func(ctx context.Context, sans []string) (context.Context, error)
|
type X509Validator func(ctx context.Context, sans []string) (context.Context, error)
|
||||||
|
126
interceptors/ban/ban.go
Normal file
126
interceptors/ban/ban.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package ban
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/jaredfolkins/badactor"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
|
"go.linka.cloud/grpc-toolkit/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ban struct {
|
||||||
|
s *badactor.Studio
|
||||||
|
rules map[codes.Code]Rule
|
||||||
|
actor func(ctx context.Context) (string, bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInterceptors(opts ...Option) interceptors.ServerInterceptors {
|
||||||
|
o := defaultOptions
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&o)
|
||||||
|
}
|
||||||
|
s := badactor.NewStudio(o.cap)
|
||||||
|
rules := make(map[codes.Code]Rule)
|
||||||
|
for _, r := range o.rules {
|
||||||
|
rules[r.Code] = r
|
||||||
|
callback := r.Callback
|
||||||
|
if callback == nil {
|
||||||
|
callback = o.defaultCallback
|
||||||
|
}
|
||||||
|
expire := r.JailDuration
|
||||||
|
if expire == 0 {
|
||||||
|
expire = o.defaultJailDuration
|
||||||
|
}
|
||||||
|
s.AddRule(&badactor.Rule{
|
||||||
|
Name: r.Name,
|
||||||
|
Message: r.Message,
|
||||||
|
StrikeLimit: r.StrikeLimit,
|
||||||
|
ExpireBase: expire,
|
||||||
|
Sentence: expire,
|
||||||
|
Action: &action{fn: callback},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// we ignore the error because CreateDirectors never returns an error
|
||||||
|
_ = s.CreateDirectors(o.cap)
|
||||||
|
s.StartReaper(o.reaperInterval)
|
||||||
|
return &ban{s: s, rules: rules, actor: o.actorFunc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ban) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||||
|
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
||||||
|
actor, ok, err := b.check(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx = set(ctx, b, actor)
|
||||||
|
if !ok {
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
for _, v := range b.rules {
|
||||||
|
if b.s.IsJailedFor(actor, v.Name) {
|
||||||
|
return nil, status.Error(v.Code, v.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res, err := handler(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, b.handleErr(ctx, actor, err)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ban) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||||
|
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||||
|
actor, ok, err := b.check(ss.Context())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ss = interceptors.NewContextServerStream(set(ss.Context(), b, actor), ss)
|
||||||
|
if !ok {
|
||||||
|
return handler(srv, ss)
|
||||||
|
}
|
||||||
|
if err := handler(srv, ss); err != nil {
|
||||||
|
return b.handleErr(ss.Context(), actor, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ban) check(ctx context.Context) (actor string, ok bool, err error) {
|
||||||
|
actor, ok, err = b.actor(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return "", false, nil
|
||||||
|
}
|
||||||
|
for _, v := range b.rules {
|
||||||
|
if b.s.IsJailedFor(actor, v.Name) {
|
||||||
|
return actor, false, status.Error(v.Code, v.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return actor, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ban) handleErr(ctx context.Context, actor string, err error) error {
|
||||||
|
v, ok := ctx.Value(key{}).(*value)
|
||||||
|
if !ok || v.done {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s, ok := status.FromError(err)
|
||||||
|
if !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, ok := b.rules[s.Code()]
|
||||||
|
if !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := b.s.Infraction(actor, r.Name); err != nil {
|
||||||
|
logger.C(ctx).Warnf("%s: failed to add infraction: %v", r.Name, err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
34
interceptors/ban/context.go
Normal file
34
interceptors/ban/context.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package ban
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type key struct{}
|
||||||
|
|
||||||
|
type value struct {
|
||||||
|
ban *ban
|
||||||
|
actor string
|
||||||
|
done bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func set(ctx context.Context, b *ban, actor string) context.Context {
|
||||||
|
return context.WithValue(ctx, key{}, &value{ban: b, actor: actor})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Infraction(ctx context.Context, rule string) error {
|
||||||
|
v, ok := ctx.Value(key{}).(*value)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
v.done = true
|
||||||
|
return v.ban.s.Infraction(v.actor, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Actor(ctx context.Context) string {
|
||||||
|
v, ok := ctx.Value(key{}).(*value)
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return v.actor
|
||||||
|
}
|
99
interceptors/ban/options.go
Normal file
99
interceptors/ban/options.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package ban
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Unauthorized = "Unauthorized"
|
||||||
|
Unauthenticated = "Unauthenticated"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultOptions = options{
|
||||||
|
cap: 1024,
|
||||||
|
reaperInterval: 10 * time.Minute,
|
||||||
|
rules: defaultRules,
|
||||||
|
actorFunc: DefaultActorFunc,
|
||||||
|
defaultCallback: nil,
|
||||||
|
defaultJailDuration: 10 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultRules = []Rule{
|
||||||
|
{
|
||||||
|
Name: Unauthorized,
|
||||||
|
Message: "Too many unauthorized requests",
|
||||||
|
Code: codes.PermissionDenied,
|
||||||
|
StrikeLimit: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: Unauthenticated,
|
||||||
|
Message: "Too many unauthenticated requests",
|
||||||
|
Code: codes.Unauthenticated,
|
||||||
|
StrikeLimit: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func DefaultActorFunc(ctx context.Context) (string, bool, error) {
|
||||||
|
p, ok := peer.FromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return "", false, nil
|
||||||
|
}
|
||||||
|
if host, _, err := net.SplitHostPort(p.Addr.String()); err == nil {
|
||||||
|
return host, true, nil
|
||||||
|
}
|
||||||
|
return p.Addr.String(), true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*options)
|
||||||
|
|
||||||
|
func WithCapacity(cap int32) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.cap = cap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithRules(rules ...Rule) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.rules = rules
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithReaperInterval(interval time.Duration) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.reaperInterval = interval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithActorFunc(f func(context.Context) (name string, found bool, err error)) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.actorFunc = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithDefaultCallback(f ActionCallback) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.defaultCallback = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithDefaultJailDuration(expire time.Duration) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.defaultJailDuration = expire
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
cap int32
|
||||||
|
rules []Rule
|
||||||
|
reaperInterval time.Duration
|
||||||
|
actorFunc func(ctx context.Context) (name string, found bool, err error)
|
||||||
|
defaultCallback ActionCallback
|
||||||
|
defaultJailDuration time.Duration
|
||||||
|
}
|
66
interceptors/ban/rule.go
Normal file
66
interceptors/ban/rule.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package ban
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jaredfolkins/badactor"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ActionCallback func(action Action, actor string, rule *Rule) error
|
||||||
|
|
||||||
|
type Action int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Jailed Action = iota
|
||||||
|
Released
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a Action) String() string {
|
||||||
|
switch a {
|
||||||
|
case Jailed:
|
||||||
|
return "Jailed"
|
||||||
|
case Released:
|
||||||
|
return "Released"
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rule struct {
|
||||||
|
Name string
|
||||||
|
Message string
|
||||||
|
Code codes.Code
|
||||||
|
StrikeLimit int
|
||||||
|
JailDuration time.Duration
|
||||||
|
// Callback is an optional function to call when an Actor isJailed or released because of timeServed
|
||||||
|
Callback ActionCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
type action struct {
|
||||||
|
fn ActionCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a2 *action) WhenJailed(a *badactor.Actor, r *badactor.Rule) error {
|
||||||
|
if a2.fn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return a2.fn(Jailed, a.Name(), &Rule{
|
||||||
|
Name: r.Name,
|
||||||
|
Message: r.Message,
|
||||||
|
StrikeLimit: r.StrikeLimit,
|
||||||
|
JailDuration: r.ExpireBase,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a2 *action) WhenTimeServed(a *badactor.Actor, r *badactor.Rule) error {
|
||||||
|
if a2.fn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return a2.fn(Released, a.Name(), &Rule{
|
||||||
|
Name: r.Name,
|
||||||
|
Message: r.Message,
|
||||||
|
StrikeLimit: r.StrikeLimit,
|
||||||
|
JailDuration: r.ExpireBase,
|
||||||
|
})
|
||||||
|
}
|
126
interceptors/chain/chain.go
Normal file
126
interceptors/chain/chain.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package chain
|
||||||
|
|
||||||
|
import (
|
||||||
|
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Option func(*chain)
|
||||||
|
|
||||||
|
func WithInterceptors(i ...interceptors.Interceptors) Option {
|
||||||
|
return func(c *chain) {
|
||||||
|
for _, i := range i {
|
||||||
|
if i := i.UnaryServerInterceptor(); i != nil {
|
||||||
|
c.usi = append(c.usi, i)
|
||||||
|
}
|
||||||
|
if i := i.StreamServerInterceptor(); i != nil {
|
||||||
|
c.ssi = append(c.ssi, i)
|
||||||
|
}
|
||||||
|
if i := i.UnaryClientInterceptor(); i != nil {
|
||||||
|
c.uci = append(c.uci, i)
|
||||||
|
}
|
||||||
|
if i := i.StreamClientInterceptor(); i != nil {
|
||||||
|
c.sci = append(c.sci, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithServerInterceptors(si ...interceptors.ServerInterceptors) Option {
|
||||||
|
return func(c *chain) {
|
||||||
|
for _, i := range si {
|
||||||
|
if i := i.UnaryServerInterceptor(); i != nil {
|
||||||
|
c.usi = append(c.usi, i)
|
||||||
|
}
|
||||||
|
if i := i.StreamServerInterceptor(); i != nil {
|
||||||
|
c.ssi = append(c.ssi, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithClientInterceptors(ci ...interceptors.ClientInterceptors) Option {
|
||||||
|
return func(c *chain) {
|
||||||
|
for _, i := range ci {
|
||||||
|
if i := i.UnaryClientInterceptor(); i != nil {
|
||||||
|
c.uci = append(c.uci, i)
|
||||||
|
}
|
||||||
|
if i := i.StreamClientInterceptor(); i != nil {
|
||||||
|
c.sci = append(c.sci, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithUnaryServerInterceptors(usi ...grpc.UnaryServerInterceptor) Option {
|
||||||
|
return func(c *chain) {
|
||||||
|
for _, i := range usi {
|
||||||
|
if i != nil {
|
||||||
|
c.usi = append(c.usi, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithStreamServerInterceptors(ssi ...grpc.StreamServerInterceptor) Option {
|
||||||
|
return func(c *chain) {
|
||||||
|
for _, i := range ssi {
|
||||||
|
if i != nil {
|
||||||
|
c.ssi = append(c.ssi, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithUnaryClientInterceptors(uci ...grpc.UnaryClientInterceptor) Option {
|
||||||
|
return func(c *chain) {
|
||||||
|
for _, i := range uci {
|
||||||
|
if i != nil {
|
||||||
|
c.uci = append(c.uci, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithStreamClientInterceptors(sci ...grpc.StreamClientInterceptor) Option {
|
||||||
|
return func(c *chain) {
|
||||||
|
for _, i := range sci {
|
||||||
|
if i != nil {
|
||||||
|
c.sci = append(c.sci, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(opts ...Option) interceptors.Interceptors {
|
||||||
|
c := &chain{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(c)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
type chain struct {
|
||||||
|
usi []grpc.UnaryServerInterceptor
|
||||||
|
ssi []grpc.StreamServerInterceptor
|
||||||
|
uci []grpc.UnaryClientInterceptor
|
||||||
|
sci []grpc.StreamClientInterceptor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chain) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||||
|
return grpc_middleware.ChainUnaryServer(c.usi...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chain) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||||
|
return grpc_middleware.ChainStreamServer(c.ssi...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chain) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
||||||
|
return grpc_middleware.ChainUnaryClient(c.uci...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chain) StreamClientInterceptor() grpc.StreamClientInterceptor {
|
||||||
|
return grpc_middleware.ChainStreamClient(c.sci...)
|
||||||
|
}
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/interceptors"
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type interceptor struct{}
|
type interceptor struct{}
|
||||||
|
41
interceptors/iface/interceptors.go
Normal file
41
interceptors/iface/interceptors.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UnaryInterceptor interface {
|
||||||
|
UnaryServerInterceptor() grpc.UnaryServerInterceptor
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamInterceptor interface {
|
||||||
|
StreamServerInterceptor() grpc.StreamServerInterceptor
|
||||||
|
}
|
||||||
|
|
||||||
|
type iface struct{}
|
||||||
|
|
||||||
|
func New() interceptors.ServerInterceptors {
|
||||||
|
return &iface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s iface) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||||
|
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
||||||
|
if i, ok := info.Server.(UnaryInterceptor); ok {
|
||||||
|
return i.UnaryServerInterceptor()(ctx, req, info, handler)
|
||||||
|
}
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s iface) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||||
|
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||||
|
if i, ok := srv.(StreamInterceptor); ok {
|
||||||
|
return i.StreamServerInterceptor()(srv, ss, info, handler)
|
||||||
|
}
|
||||||
|
return handler(srv, ss)
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package interceptors
|
package interceptors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,3 +20,16 @@ type Interceptors interface {
|
|||||||
ServerInterceptors
|
ServerInterceptors
|
||||||
ClientInterceptors
|
ClientInterceptors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewContextServerStream(ctx context.Context, ss grpc.ServerStream) grpc.ServerStream {
|
||||||
|
return &ContextWrapper{ServerStream: ss, ctx: ctx}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContextWrapper struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *ContextWrapper) Context() context.Context {
|
||||||
|
return w.ctx
|
||||||
|
}
|
||||||
|
77
interceptors/logging/interceptor.go
Normal file
77
interceptors/logging/interceptor.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||||
|
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
|
"go.linka.cloud/grpc-toolkit/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(ctx context.Context, opts ...logging.Option) interceptors.Interceptors {
|
||||||
|
log := logger.C(ctx)
|
||||||
|
return &interceptor{
|
||||||
|
log: logging.LoggerFunc(func(ctx context.Context, level logging.Level, msg string, fields ...any) {
|
||||||
|
switch level {
|
||||||
|
case logging.LevelDebug:
|
||||||
|
log.WithReportCaller(true, 2).WithFields(fields...).Debug(msg)
|
||||||
|
case logging.LevelInfo:
|
||||||
|
log.WithReportCaller(true, 2).WithFields(fields...).Info(msg)
|
||||||
|
case logging.LevelWarn:
|
||||||
|
log.WithReportCaller(true, 2).WithFields(fields...).Warn(msg)
|
||||||
|
case logging.LevelError:
|
||||||
|
log.WithReportCaller(true, 2).WithFields(fields...).Error(msg)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
opts: opts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type interceptor struct {
|
||||||
|
log logging.Logger
|
||||||
|
opts []logging.Option
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *interceptor) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||||
|
return grpc_middleware.ChainUnaryServer(
|
||||||
|
logging.UnaryServerInterceptor(i.log, i.opts...),
|
||||||
|
func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
|
||||||
|
log := logger.C(ctx)
|
||||||
|
return handler(logger.Set(ctx, log.WithFields(logging.ExtractFields(ctx)...)), req)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *interceptor) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||||
|
return grpc_middleware.ChainStreamServer(
|
||||||
|
logging.StreamServerInterceptor(i.log, i.opts...),
|
||||||
|
func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||||
|
ctx := ss.Context()
|
||||||
|
log := logger.C(ctx)
|
||||||
|
return handler(srv, interceptors.NewContextServerStream(logger.Set(ctx, log.WithFields(logging.ExtractFields(ctx)...)), ss))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *interceptor) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
||||||
|
return grpc_middleware.ChainUnaryClient(
|
||||||
|
logging.UnaryClientInterceptor(i.log, i.opts...),
|
||||||
|
func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||||
|
log := logger.C(ctx)
|
||||||
|
return invoker(logger.Set(ctx, log.WithFields(logging.ExtractFields(ctx)...)), method, req, reply, cc, opts...)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *interceptor) StreamClientInterceptor() grpc.StreamClientInterceptor {
|
||||||
|
return grpc_middleware.ChainStreamClient(
|
||||||
|
logging.StreamClientInterceptor(i.log, i.opts...),
|
||||||
|
func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||||
|
log := logger.C(ctx)
|
||||||
|
return streamer(logger.Set(ctx, log.WithFields(logging.ExtractFields(ctx)...)), desc, cc, method, opts...)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
40
interceptors/metadata/forward.go
Normal file
40
interceptors/metadata/forward.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewForwardInterceptors() interceptors.ServerInterceptors {
|
||||||
|
return &forward{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type forward struct{}
|
||||||
|
|
||||||
|
func (f *forward) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||||
|
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
||||||
|
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||||
|
ctx = metadata.NewOutgoingContext(ctx, md.Copy())
|
||||||
|
}
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *forward) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||||
|
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||||
|
ctx := ss.Context()
|
||||||
|
md1, ok := metadata.FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return handler(srv, ss)
|
||||||
|
}
|
||||||
|
o := md1.Copy()
|
||||||
|
if md2, ok := metadata.FromOutgoingContext(ctx); ok {
|
||||||
|
o = metadata.Join(o, md2.Copy())
|
||||||
|
}
|
||||||
|
return handler(srv, interceptors.NewContextServerStream(metadata.NewOutgoingContext(ctx, o), ss))
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/interceptors"
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewInterceptors(pairs ...string) interceptors.Interceptors {
|
func NewInterceptors(pairs ...string) interceptors.Interceptors {
|
||||||
@ -37,14 +37,14 @@ func (i mdInterceptors) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
|||||||
|
|
||||||
func (i mdInterceptors) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
func (i mdInterceptors) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
||||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||||
ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs(i.pairs...))
|
ctx = metadata.AppendToOutgoingContext(ctx, i.pairs...)
|
||||||
return invoker(ctx, method, req, reply, cc, opts...)
|
return invoker(ctx, method, req, reply, cc, opts...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i mdInterceptors) StreamClientInterceptor() grpc.StreamClientInterceptor {
|
func (i mdInterceptors) StreamClientInterceptor() grpc.StreamClientInterceptor {
|
||||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||||
ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs(i.pairs...))
|
ctx = metadata.AppendToOutgoingContext(ctx, i.pairs...)
|
||||||
return streamer(ctx, desc, cc, method, opts...)
|
return streamer(ctx, desc, cc, method, opts...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,24 @@
|
|||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
|
"context"
|
||||||
|
|
||||||
|
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/interceptors"
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
"go.linka.cloud/grpc/service"
|
"go.linka.cloud/grpc-toolkit/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func DefaultExemplarFromCtx(ctx context.Context) prometheus.Labels {
|
||||||
|
if span := trace.SpanContextFromContext(ctx); span.IsSampled() {
|
||||||
|
return prometheus.Labels{"traceID": span.TraceID().String()}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Registerer interface {
|
type Registerer interface {
|
||||||
Register(svc service.Service)
|
Register(svc service.Service)
|
||||||
}
|
}
|
||||||
@ -22,7 +32,6 @@ type ServerInterceptors interface {
|
|||||||
Registerer
|
Registerer
|
||||||
interceptors.ServerInterceptors
|
interceptors.ServerInterceptors
|
||||||
prometheus.Collector
|
prometheus.Collector
|
||||||
EnableHandlingTimeHistogram(opts ...grpc_prometheus.HistogramOption)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientInterceptors interface {
|
type ClientInterceptors interface {
|
||||||
@ -32,12 +41,7 @@ type ClientInterceptors interface {
|
|||||||
type metrics struct {
|
type metrics struct {
|
||||||
s *grpc_prometheus.ServerMetrics
|
s *grpc_prometheus.ServerMetrics
|
||||||
c *grpc_prometheus.ClientMetrics
|
c *grpc_prometheus.ClientMetrics
|
||||||
}
|
o *options
|
||||||
|
|
||||||
func (m *metrics) EnableHandlingTimeHistogram(opts ...grpc_prometheus.HistogramOption) {
|
|
||||||
if m.s != nil {
|
|
||||||
m.s.EnableHandlingTimeHistogram(opts...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *metrics) Describe(descs chan<- *prometheus.Desc) {
|
func (m *metrics) Describe(descs chan<- *prometheus.Desc) {
|
||||||
@ -58,36 +62,45 @@ func (m *metrics) Register(svc service.Service) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInterceptors(opts ...grpc_prometheus.CounterOption) Interceptors {
|
func NewInterceptors(opts ...Option) Interceptors {
|
||||||
s := grpc_prometheus.NewServerMetrics(opts...)
|
o := (&options{}).apply(opts...)
|
||||||
c := grpc_prometheus.NewClientMetrics(opts...)
|
s := grpc_prometheus.NewServerMetrics(
|
||||||
return &metrics{s: s, c: c}
|
grpc_prometheus.WithServerCounterOptions(o.copts...),
|
||||||
|
grpc_prometheus.WithServerHandlingTimeHistogram(o.hopts...),
|
||||||
|
)
|
||||||
|
c := grpc_prometheus.NewClientMetrics(
|
||||||
|
grpc_prometheus.WithClientCounterOptions(o.copts...),
|
||||||
|
grpc_prometheus.WithClientHandlingTimeHistogram(o.hopts...),
|
||||||
|
)
|
||||||
|
m := &metrics{s: s, c: c, o: o}
|
||||||
|
o.reg.MustRegister(m)
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServerInterceptors(opts ...grpc_prometheus.CounterOption) ServerInterceptors {
|
func NewServerInterceptors(opts ...Option) ServerInterceptors {
|
||||||
s := grpc_prometheus.NewServerMetrics(opts...)
|
o := (&options{}).apply(opts...)
|
||||||
return &metrics{s: s}
|
s := grpc_prometheus.NewServerMetrics(
|
||||||
|
grpc_prometheus.WithServerCounterOptions(o.copts...),
|
||||||
|
grpc_prometheus.WithServerHandlingTimeHistogram(o.hopts...),
|
||||||
|
)
|
||||||
|
m := &metrics{s: s, o: o}
|
||||||
|
o.reg.MustRegister(m)
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientInterceptors(opts ...grpc_prometheus.CounterOption) ClientInterceptors {
|
func NewClientInterceptors(opts ...Option) ClientInterceptors {
|
||||||
c := grpc_prometheus.NewClientMetrics(opts...)
|
o := (&options{}).apply(opts...)
|
||||||
return &metrics{c: c}
|
c := grpc_prometheus.NewClientMetrics(
|
||||||
}
|
grpc_prometheus.WithClientCounterOptions(o.copts...),
|
||||||
|
grpc_prometheus.WithClientHandlingTimeHistogram(o.hopts...),
|
||||||
func DefaultInterceptors() Interceptors {
|
)
|
||||||
return &metrics{s: grpc_prometheus.DefaultServerMetrics, c: grpc_prometheus.DefaultClientMetrics}
|
m := &metrics{c: c, o: o}
|
||||||
}
|
o.reg.MustRegister(m)
|
||||||
|
return m
|
||||||
func DefaultServerInterceptors() ServerInterceptors {
|
|
||||||
return &metrics{s: grpc_prometheus.DefaultServerMetrics}
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultClientInterceptors() ClientInterceptors {
|
|
||||||
return &metrics{c: grpc_prometheus.DefaultClientMetrics}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *metrics) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
func (m *metrics) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||||
return m.s.UnaryServerInterceptor()
|
return m.s.UnaryServerInterceptor(grpc_prometheus.WithExemplarFromContext(m.o.fn))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *metrics) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
func (m *metrics) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||||
|
59
interceptors/metrics/options.go
Normal file
59
interceptors/metrics/options.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExemplarFromCtxFunc func(ctx context.Context) prometheus.Labels
|
||||||
|
|
||||||
|
type Option func(*options)
|
||||||
|
|
||||||
|
func WithCounterOptons(opts ...grpc_prometheus.CounterOption) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.copts = append(o.copts, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithHandlingTimeHistogram(opts ...grpc_prometheus.HistogramOption) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.hopts = append(o.hopts, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithHistogramOpts(opts ...grpc_prometheus.HistogramOption) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.hopts = append(o.hopts, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithExemplarFromContext(fn ExemplarFromCtxFunc) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.fn = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithRegisterer(reg prometheus.Registerer) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.reg = reg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
copts []grpc_prometheus.CounterOption
|
||||||
|
hopts []grpc_prometheus.HistogramOption
|
||||||
|
fn func(ctx context.Context) prometheus.Labels
|
||||||
|
reg prometheus.Registerer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *options) apply(opts ...Option) *options {
|
||||||
|
for _, v := range opts {
|
||||||
|
v(o)
|
||||||
|
}
|
||||||
|
if o.reg == nil {
|
||||||
|
o.reg = prometheus.DefaultRegisterer
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
39
interceptors/noop/interceptors.go
Normal file
39
interceptors/noop/interceptors.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package noop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New() interceptors.Interceptors {
|
||||||
|
return &noopInterceptor{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type noopInterceptor struct{}
|
||||||
|
|
||||||
|
func (m *noopInterceptor) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||||
|
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *noopInterceptor) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||||
|
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||||
|
return handler(srv, ss)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *noopInterceptor) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
||||||
|
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||||
|
return invoker(ctx, method, req, reply, cc, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *noopInterceptor) StreamClientInterceptor() grpc.StreamClientInterceptor {
|
||||||
|
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||||
|
return streamer(ctx, desc, cc, method, opts...)
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ import (
|
|||||||
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
|
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/interceptors"
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewInterceptors(opts ...grpc_recovery.Option) interceptors.ServerInterceptors {
|
func NewInterceptors(opts ...grpc_recovery.Option) interceptors.ServerInterceptors {
|
||||||
@ -22,11 +22,3 @@ func (i *recovery) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
|||||||
func (i *recovery) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
func (i *recovery) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||||
return grpc_recovery.StreamServerInterceptor(i.opts...)
|
return grpc_recovery.StreamServerInterceptor(i.opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *recovery) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *recovery) StreamClientInterceptor() grpc.StreamClientInterceptor {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
grpc_sentry "github.com/johnbellone/grpc-middleware-sentry"
|
grpc_sentry "github.com/johnbellone/grpc-middleware-sentry"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/interceptors"
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type interceptor struct {
|
type interceptor struct {
|
||||||
|
@ -1,41 +1,40 @@
|
|||||||
package tracing
|
package tracing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
|
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||||
"github.com/opentracing/opentracing-go"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/interceptors"
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tracing struct {
|
type tracing struct {
|
||||||
opts []otgrpc.Option
|
opts []otelgrpc.Option
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInterceptors(opts ...otgrpc.Option) interceptors.Interceptors {
|
func NewInterceptors(opts ...otelgrpc.Option) interceptors.Interceptors {
|
||||||
return tracing{opts: opts}
|
return tracing{opts: opts}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientInterceptors(opts ...otgrpc.Option) interceptors.ClientInterceptors {
|
func NewClientInterceptors(opts ...otelgrpc.Option) interceptors.ClientInterceptors {
|
||||||
return tracing{opts: opts}
|
return tracing{opts: opts}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t tracing) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
func (t tracing) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
||||||
return otgrpc.OpenTracingClientInterceptor(opentracing.GlobalTracer(), t.opts...)
|
return otelgrpc.UnaryClientInterceptor(t.opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t tracing) StreamClientInterceptor() grpc.StreamClientInterceptor {
|
func (t tracing) StreamClientInterceptor() grpc.StreamClientInterceptor {
|
||||||
return otgrpc.OpenTracingStreamClientInterceptor(opentracing.GlobalTracer(), t.opts...)
|
return otelgrpc.StreamClientInterceptor(t.opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServerInterceptors(opts ...otgrpc.Option) interceptors.ServerInterceptors {
|
func NewServerInterceptors(opts ...otelgrpc.Option) interceptors.ServerInterceptors {
|
||||||
return tracing{opts: opts}
|
return tracing{opts: opts}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t tracing) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
func (t tracing) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||||
return otgrpc.OpenTracingServerInterceptor(opentracing.GlobalTracer(), t.opts...)
|
return otelgrpc.UnaryServerInterceptor(t.opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t tracing) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
func (t tracing) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||||
return otgrpc.OpenTracingStreamServerInterceptor(opentracing.GlobalTracer(), t.opts...)
|
return otelgrpc.StreamServerInterceptor(t.opts...)
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,9 @@ import (
|
|||||||
|
|
||||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
|
|
||||||
"go.linka.cloud/grpc/errors"
|
"go.linka.cloud/grpc-toolkit/errors"
|
||||||
"go.linka.cloud/grpc/interceptors"
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type validatorAll interface {
|
type validatorAll interface {
|
||||||
@ -39,10 +38,24 @@ type validatorError interface {
|
|||||||
ErrorName() string
|
ErrorName() string
|
||||||
}
|
}
|
||||||
|
|
||||||
func validatorErrorToGrpc(e validatorError) *errdetails.BadRequest_FieldViolation {
|
func validatorErrorToGrpc(e validatorError, prefix string) []*errdetails.BadRequest_FieldViolation {
|
||||||
return &errdetails.BadRequest_FieldViolation{
|
// check nested errors for validation error, e.g. "embedded message failed validation"
|
||||||
Field: e.Field(),
|
switch v := e.Cause().(type) {
|
||||||
Description: e.Reason(),
|
case validatorError:
|
||||||
|
return validatorErrorToGrpc(v, e.Field()+".")
|
||||||
|
case validatorMultiError:
|
||||||
|
var details []*errdetails.BadRequest_FieldViolation
|
||||||
|
for _, vv := range v.AllErrors() {
|
||||||
|
if ee, ok := vv.(validatorError); ok {
|
||||||
|
details = append(details, validatorErrorToGrpc(ee, e.Field()+".")...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return details
|
||||||
|
default:
|
||||||
|
return []*errdetails.BadRequest_FieldViolation{{
|
||||||
|
Field: prefix + e.Field(),
|
||||||
|
Description: e.Reason(),
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,15 +65,15 @@ func errToStatus(err error) error {
|
|||||||
}
|
}
|
||||||
switch v := err.(type) {
|
switch v := err.(type) {
|
||||||
case validatorError:
|
case validatorError:
|
||||||
return errors.InvalidArgumentd(err, validatorErrorToGrpc(v))
|
return errors.InvalidArgumentd(err, &errdetails.BadRequest{FieldViolations: validatorErrorToGrpc(v, "")})
|
||||||
case validatorMultiError:
|
case validatorMultiError:
|
||||||
var details []proto.Message
|
details := &errdetails.BadRequest{}
|
||||||
for _, v := range v.AllErrors() {
|
for _, v := range v.AllErrors() {
|
||||||
if d, ok := v.(validatorError); ok {
|
if d, ok := v.(validatorError); ok {
|
||||||
details = append(details, validatorErrorToGrpc(d))
|
details.FieldViolations = append(details.FieldViolations, validatorErrorToGrpc(d, "")...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errors.InvalidArgumentd(err, details...)
|
return errors.InvalidArgumentd(err, details)
|
||||||
default:
|
default:
|
||||||
return errors.InvalidArgument(err)
|
return errors.InvalidArgument(err)
|
||||||
}
|
}
|
||||||
|
50
internal/injectlogger/injectlogger.go
Normal file
50
internal/injectlogger/injectlogger.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package injectlogger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
|
"go.linka.cloud/grpc-toolkit/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(ctx context.Context) interceptors.Interceptors {
|
||||||
|
return &interceptor{
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type interceptor struct {
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *interceptor) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||||
|
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
|
||||||
|
log := logger.C(i.ctx)
|
||||||
|
return handler(logger.Set(ctx, log.WithFields(logging.ExtractFields(ctx)...)), req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *interceptor) StreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||||
|
return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||||
|
ctx := ss.Context()
|
||||||
|
log := logger.C(i.ctx)
|
||||||
|
return handler(srv, interceptors.NewContextServerStream(logger.Set(ctx, log.WithFields(logging.ExtractFields(ctx)...)), ss))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *interceptor) UnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
||||||
|
return func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||||
|
log := logger.C(i.ctx)
|
||||||
|
return invoker(logger.Set(ctx, log.WithFields(logging.ExtractFields(ctx)...)), method, req, reply, cc, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *interceptor) StreamClientInterceptor() grpc.StreamClientInterceptor {
|
||||||
|
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||||
|
log := logger.C(i.ctx)
|
||||||
|
return streamer(logger.Set(ctx, log.WithFields(logging.ExtractFields(ctx)...)), desc, cc, method, opts...)
|
||||||
|
}
|
||||||
|
}
|
@ -33,7 +33,7 @@ func Set(ctx context.Context, logger Logger) context.Context {
|
|||||||
func From(ctx context.Context) Logger {
|
func From(ctx context.Context) Logger {
|
||||||
log, ok := ctx.Value(log{}).(Logger)
|
log, ok := ctx.Value(log{}).(Logger)
|
||||||
if ok {
|
if ok {
|
||||||
return log
|
return log.WithContext(ctx)
|
||||||
}
|
}
|
||||||
if defaultLogger != nil {
|
if defaultLogger != nil {
|
||||||
return defaultLogger
|
return defaultLogger
|
||||||
@ -42,5 +42,5 @@ func From(ctx context.Context) Logger {
|
|||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
defaultLogger = logr
|
defaultLogger = logr
|
||||||
return defaultLogger
|
return defaultLogger.WithContext(ctx)
|
||||||
}
|
}
|
||||||
|
222
logger/logger.go
222
logger/logger.go
@ -1,10 +1,14 @@
|
|||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/bombsimon/logrusr/v2"
|
"github.com/bombsimon/logrusr/v4"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -13,6 +17,27 @@ var (
|
|||||||
standardLogger Logger = &logger{fl: logrus.StandardLogger()}
|
standardLogger Logger = &logger{fl: logrus.StandardLogger()}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PanicLevel level, highest level of severity. Logs and then calls panic with the
|
||||||
|
// message passed to Debug, Info, ...
|
||||||
|
PanicLevel Level = iota
|
||||||
|
// FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the
|
||||||
|
// logging level is set to Panic.
|
||||||
|
FatalLevel
|
||||||
|
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
|
||||||
|
// Commonly used for hooks to send errors to an error tracking service.
|
||||||
|
ErrorLevel
|
||||||
|
// WarnLevel level. Non-critical entries that deserve eyes.
|
||||||
|
WarnLevel
|
||||||
|
// InfoLevel level. General operational entries about what's going on inside the
|
||||||
|
// application.
|
||||||
|
InfoLevel
|
||||||
|
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
|
||||||
|
DebugLevel
|
||||||
|
// TraceLevel level. Designates finer-grained informational events than the Debug.
|
||||||
|
TraceLevel
|
||||||
|
)
|
||||||
|
|
||||||
func StandardLogger() Logger {
|
func StandardLogger() Logger {
|
||||||
return standardLogger
|
return standardLogger
|
||||||
}
|
}
|
||||||
@ -21,14 +46,27 @@ func New() Logger {
|
|||||||
return &logger{fl: logrus.New()}
|
return &logger{fl: logrus.New()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FromLogrus(fl logrus.Ext1FieldLogger) Logger {
|
||||||
|
return &logger{fl: fl}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Level = logrus.Level
|
||||||
|
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
|
WithContext(ctx context.Context) Logger
|
||||||
|
|
||||||
|
WithReportCaller(b bool, depth ...uint) Logger
|
||||||
|
|
||||||
WithField(key string, value interface{}) Logger
|
WithField(key string, value interface{}) Logger
|
||||||
WithFields(kv ...interface{}) Logger
|
WithFields(kv ...interface{}) Logger
|
||||||
WithError(err error) Logger
|
WithError(err error) Logger
|
||||||
|
|
||||||
SetLevel(level logrus.Level) Logger
|
SetLevel(level Level) Logger
|
||||||
WriterLevel(level logrus.Level) *io.PipeWriter
|
WriterLevel(level Level) *io.PipeWriter
|
||||||
|
|
||||||
|
SetOutput(w io.Writer) Logger
|
||||||
|
|
||||||
|
Tracef(format string, args ...interface{})
|
||||||
Debugf(format string, args ...interface{})
|
Debugf(format string, args ...interface{})
|
||||||
Infof(format string, args ...interface{})
|
Infof(format string, args ...interface{})
|
||||||
Printf(format string, args ...interface{})
|
Printf(format string, args ...interface{})
|
||||||
@ -38,6 +76,7 @@ type Logger interface {
|
|||||||
Fatalf(format string, args ...interface{})
|
Fatalf(format string, args ...interface{})
|
||||||
Panicf(format string, args ...interface{})
|
Panicf(format string, args ...interface{})
|
||||||
|
|
||||||
|
Trace(args ...interface{})
|
||||||
Debug(args ...interface{})
|
Debug(args ...interface{})
|
||||||
Info(args ...interface{})
|
Info(args ...interface{})
|
||||||
Print(args ...interface{})
|
Print(args ...interface{})
|
||||||
@ -47,6 +86,7 @@ type Logger interface {
|
|||||||
Fatal(args ...interface{})
|
Fatal(args ...interface{})
|
||||||
Panic(args ...interface{})
|
Panic(args ...interface{})
|
||||||
|
|
||||||
|
Traceln(args ...interface{})
|
||||||
Debugln(args ...interface{})
|
Debugln(args ...interface{})
|
||||||
Infoln(args ...interface{})
|
Infoln(args ...interface{})
|
||||||
Println(args ...interface{})
|
Println(args ...interface{})
|
||||||
@ -57,146 +97,240 @@ type Logger interface {
|
|||||||
Panicln(args ...interface{})
|
Panicln(args ...interface{})
|
||||||
|
|
||||||
Logr() logr.Logger
|
Logr() logr.Logger
|
||||||
|
FieldLogger() logrus.FieldLogger
|
||||||
|
Logger() *logrus.Logger
|
||||||
|
|
||||||
|
Clone() Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type logger struct {
|
type logger struct {
|
||||||
fl logrus.FieldLogger
|
fl logrus.Ext1FieldLogger
|
||||||
|
reportCaller *int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) Tracef(format string, args ...interface{}) {
|
||||||
|
l.withCaller().Tracef(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Debugf(format string, args ...interface{}) {
|
func (l *logger) Debugf(format string, args ...interface{}) {
|
||||||
l.fl.Debugf(format, args...)
|
l.withCaller().Debugf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Infof(format string, args ...interface{}) {
|
func (l *logger) Infof(format string, args ...interface{}) {
|
||||||
l.fl.Infof(format, args...)
|
l.withCaller().Infof(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Printf(format string, args ...interface{}) {
|
func (l *logger) Printf(format string, args ...interface{}) {
|
||||||
l.fl.Printf(format, args...)
|
l.withCaller().Printf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Warnf(format string, args ...interface{}) {
|
func (l *logger) Warnf(format string, args ...interface{}) {
|
||||||
l.fl.Warnf(format, args...)
|
l.withCaller().Warnf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Warningf(format string, args ...interface{}) {
|
func (l *logger) Warningf(format string, args ...interface{}) {
|
||||||
l.fl.Warningf(format, args...)
|
l.withCaller().Warningf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Errorf(format string, args ...interface{}) {
|
func (l *logger) Errorf(format string, args ...interface{}) {
|
||||||
l.fl.Errorf(format, args...)
|
l.withCaller().Errorf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Fatalf(format string, args ...interface{}) {
|
func (l *logger) Fatalf(format string, args ...interface{}) {
|
||||||
l.fl.Fatalf(format, args...)
|
l.withCaller().Fatalf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Panicf(format string, args ...interface{}) {
|
func (l *logger) Panicf(format string, args ...interface{}) {
|
||||||
l.fl.Panicf(format, args...)
|
l.withCaller().Panicf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) Trace(args ...interface{}) {
|
||||||
|
l.withCaller().Trace(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Debug(args ...interface{}) {
|
func (l *logger) Debug(args ...interface{}) {
|
||||||
l.fl.Debug(args...)
|
l.withCaller().Debug(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Info(args ...interface{}) {
|
func (l *logger) Info(args ...interface{}) {
|
||||||
l.fl.Info(args...)
|
l.withCaller().Info(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Print(args ...interface{}) {
|
func (l *logger) Print(args ...interface{}) {
|
||||||
l.fl.Print(args...)
|
l.withCaller().Print(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Warn(args ...interface{}) {
|
func (l *logger) Warn(args ...interface{}) {
|
||||||
l.fl.Warn(args...)
|
l.withCaller().Warn(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Warning(args ...interface{}) {
|
func (l *logger) Warning(args ...interface{}) {
|
||||||
l.fl.Warning(args...)
|
l.withCaller().Warning(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Error(args ...interface{}) {
|
func (l *logger) Error(args ...interface{}) {
|
||||||
l.fl.Error(args...)
|
l.withCaller().Error(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Fatal(args ...interface{}) {
|
func (l *logger) Fatal(args ...interface{}) {
|
||||||
l.fl.Fatal(args...)
|
l.withCaller().Fatal(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Panic(args ...interface{}) {
|
func (l *logger) Panic(args ...interface{}) {
|
||||||
l.fl.Panic(args...)
|
l.withCaller().Panic(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) Traceln(args ...interface{}) {
|
||||||
|
l.withCaller().Traceln(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Debugln(args ...interface{}) {
|
func (l *logger) Debugln(args ...interface{}) {
|
||||||
l.fl.Debugln(args...)
|
l.withCaller().Debugln(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Infoln(args ...interface{}) {
|
func (l *logger) Infoln(args ...interface{}) {
|
||||||
l.fl.Infoln(args...)
|
l.withCaller().Infoln(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Println(args ...interface{}) {
|
func (l *logger) Println(args ...interface{}) {
|
||||||
l.fl.Println(args...)
|
l.withCaller().Println(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Warnln(args ...interface{}) {
|
func (l *logger) Warnln(args ...interface{}) {
|
||||||
l.fl.Warnln(args...)
|
l.withCaller().Warnln(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Warningln(args ...interface{}) {
|
func (l *logger) Warningln(args ...interface{}) {
|
||||||
l.fl.Warningln(args...)
|
l.withCaller().Warningln(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Errorln(args ...interface{}) {
|
func (l *logger) Errorln(args ...interface{}) {
|
||||||
l.fl.Errorln(args...)
|
l.withCaller().Errorln(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Fatalln(args ...interface{}) {
|
func (l *logger) Fatalln(args ...interface{}) {
|
||||||
l.fl.Fatalln(args...)
|
l.withCaller().Fatalln(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Panicln(args ...interface{}) {
|
func (l *logger) Panicln(args ...interface{}) {
|
||||||
l.fl.Panicln(args...)
|
l.withCaller().Panicln(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) WriterLevel(level logrus.Level) *io.PipeWriter {
|
func (l *logger) WriterLevel(level Level) *io.PipeWriter {
|
||||||
switch t := l.fl.(type) {
|
return l.Logger().WriterLevel(level)
|
||||||
case *logrus.Logger:
|
|
||||||
return t.WriterLevel(level)
|
|
||||||
case *logrus.Entry:
|
|
||||||
return t.WriterLevel(level)
|
|
||||||
}
|
|
||||||
panic(fmt.Sprintf("unexpected logger type %T", l.fl))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) SetLevel(level logrus.Level) Logger {
|
func (l *logger) SetLevel(level Level) Logger {
|
||||||
|
l.Logger().SetLevel(level)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) WithContext(ctx context.Context) Logger {
|
||||||
switch t := l.fl.(type) {
|
switch t := l.fl.(type) {
|
||||||
case *logrus.Logger:
|
case *logrus.Logger:
|
||||||
t.SetLevel(level)
|
return &logger{fl: t.WithContext(ctx), reportCaller: l.reportCaller}
|
||||||
return l
|
|
||||||
case *logrus.Entry:
|
case *logrus.Entry:
|
||||||
t.Logger.SetLevel(level)
|
return &logger{fl: t.WithContext(ctx), reportCaller: l.reportCaller}
|
||||||
return l
|
|
||||||
}
|
}
|
||||||
panic(fmt.Sprintf("unexpected logger type %T", l.fl))
|
panic(fmt.Sprintf("unexpected logger type %T", l.fl))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) WithField(key string, value interface{}) Logger {
|
func (l *logger) WithField(key string, value interface{}) Logger {
|
||||||
return &logger{fl: l.fl.WithField(key, value)}
|
return &logger{fl: l.fl.WithField(key, value), reportCaller: l.reportCaller}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) WithFields(kv ...interface{}) Logger {
|
func (l *logger) WithFields(kv ...interface{}) Logger {
|
||||||
log := &logger{fl: l.fl}
|
log := &logger{fl: l.fl}
|
||||||
for i := 0; i < len(kv); i += 2 {
|
for i := 0; i < len(kv); i += 2 {
|
||||||
log = &logger{fl: log.fl.WithField(fmt.Sprintf("%v", kv[i]), kv[i+1])}
|
log = &logger{fl: log.fl.WithField(fmt.Sprintf("%v", kv[i]), kv[i+1]), reportCaller: l.reportCaller}
|
||||||
}
|
}
|
||||||
return log
|
return log
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) WithError(err error) Logger {
|
func (l *logger) WithError(err error) Logger {
|
||||||
return &logger{fl: l.fl.WithError(err)}
|
return &logger{fl: l.fl.WithError(err), reportCaller: l.reportCaller}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) WithReportCaller(b bool, depth ...uint) Logger {
|
||||||
|
if !b {
|
||||||
|
return &logger{fl: l.fl}
|
||||||
|
}
|
||||||
|
var d int
|
||||||
|
if len(depth) > 0 {
|
||||||
|
d = int(depth[0])
|
||||||
|
} else {
|
||||||
|
d = 0
|
||||||
|
}
|
||||||
|
return &logger{fl: l.fl, reportCaller: &d}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logger) Logr() logr.Logger {
|
func (l *logger) Logr() logr.Logger {
|
||||||
return logrusr.New(l.fl)
|
return logrusr.New(l.fl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *logger) FieldLogger() logrus.FieldLogger {
|
||||||
|
return l.fl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) Logger() *logrus.Logger {
|
||||||
|
switch t := l.fl.(type) {
|
||||||
|
case *logrus.Logger:
|
||||||
|
return t
|
||||||
|
case *logrus.Entry:
|
||||||
|
return t.Logger
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("unexpected logger type %T", l.fl))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) SetOutput(w io.Writer) Logger {
|
||||||
|
l.Logger().SetOutput(w)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) Clone() Logger {
|
||||||
|
n := logrus.New()
|
||||||
|
switch t := l.fl.(type) {
|
||||||
|
case *logrus.Logger:
|
||||||
|
n.Level = t.Level
|
||||||
|
n.Out = t.Out
|
||||||
|
n.Formatter = t.Formatter
|
||||||
|
n.Hooks = t.Hooks
|
||||||
|
return &logger{fl: n, reportCaller: l.reportCaller}
|
||||||
|
case *logrus.Entry:
|
||||||
|
t = t.Dup()
|
||||||
|
n.Level = t.Logger.Level
|
||||||
|
n.Out = t.Logger.Out
|
||||||
|
n.Formatter = t.Logger.Formatter
|
||||||
|
n.Hooks = t.Logger.Hooks
|
||||||
|
t.Logger = n
|
||||||
|
return &logger{fl: t, reportCaller: l.reportCaller}
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("unexpected logger type %T", l.fl))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) withCaller() logrus.Ext1FieldLogger {
|
||||||
|
if l.reportCaller == nil {
|
||||||
|
return l.fl
|
||||||
|
}
|
||||||
|
pcs := make([]uintptr, 1)
|
||||||
|
runtime.Callers(3+*l.reportCaller, pcs)
|
||||||
|
f, _ := runtime.CallersFrames(pcs).Next()
|
||||||
|
pkg := getPackageName(f.Function)
|
||||||
|
return l.fl.WithField("caller", fmt.Sprintf("%s/%s:%d", pkg, filepath.Base(f.File), f.Line)).WithField("func", f.Func.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPackageName reduces a fully qualified function name to the package name
|
||||||
|
// There really ought to be a better way...
|
||||||
|
func getPackageName(f string) string {
|
||||||
|
for {
|
||||||
|
lastPeriod := strings.LastIndex(f, ".")
|
||||||
|
lastSlash := strings.LastIndex(f, "/")
|
||||||
|
if lastPeriod > lastSlash {
|
||||||
|
f = f[:lastPeriod]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
83
proxy/README.md
Normal file
83
proxy/README.md
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# gRPC Proxy
|
||||||
|
|
||||||
|
[](https://travis-ci.org/mwitkow/grpc-proxy)
|
||||||
|
[](https://goreportcard.com/report/github.com/mwitkow/grpc-proxy)
|
||||||
|
[](https://pkg.go.dev/github.com/mwitkow/grpc-proxy)
|
||||||
|
[](LICENSE)
|
||||||
|
|
||||||
|
[gRPC Go](https://github.com/grpc/grpc-go) Proxy server
|
||||||
|
|
||||||
|
## Project Goal
|
||||||
|
|
||||||
|
Build a transparent reverse proxy for gRPC targets that will make it easy to expose gRPC services
|
||||||
|
over the internet. This includes:
|
||||||
|
* no needed knowledge of the semantics of requests exchanged in the call (independent rollouts)
|
||||||
|
* easy, declarative definition of backends and their mappings to frontends
|
||||||
|
* simple round-robin load balancing of inbound requests from a single connection to multiple backends
|
||||||
|
|
||||||
|
The project now exists as a **proof of concept**, with the key piece being the `proxy` package that
|
||||||
|
is a generic gRPC reverse proxy handler.
|
||||||
|
|
||||||
|
## Proxy Handler
|
||||||
|
|
||||||
|
The package [`proxy`](proxy/) contains a generic gRPC reverse proxy handler that allows a gRPC server to
|
||||||
|
not know about registered handlers or their data types. Please consult the docs, here's an exaple usage.
|
||||||
|
|
||||||
|
You can call `proxy.NewProxy` to create a `*grpc.Server` that proxies requests.
|
||||||
|
```go
|
||||||
|
proxy := proxy.NewProxy(clientConn)
|
||||||
|
```
|
||||||
|
|
||||||
|
More advanced users will want to define a `StreamDirector` that can make more complex decisions on what
|
||||||
|
to do with the request.
|
||||||
|
```go
|
||||||
|
director = func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {
|
||||||
|
md, _ := metadata.FromIncomingContext(ctx)
|
||||||
|
outCtx = metadata.NewOutgoingContext(ctx, md.Copy())
|
||||||
|
return outCtx, cc, nil
|
||||||
|
|
||||||
|
// Make sure we never forward internal services.
|
||||||
|
if strings.HasPrefix(fullMethodName, "/com.example.internal.") {
|
||||||
|
return outCtx, nil, status.Errorf(codes.Unimplemented, "Unknown method")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
// Decide on which backend to dial
|
||||||
|
if val, exists := md[":authority"]; exists && val[0] == "staging.api.example.com" {
|
||||||
|
// Make sure we use DialContext so the dialing can be cancelled/time out together with the context.
|
||||||
|
return outCtx, grpc.DialContext(ctx, "api-service.staging.svc.local", grpc.WithCodec(proxy.Codec())), nil
|
||||||
|
} else if val, exists := md[":authority"]; exists && val[0] == "api.example.com" {
|
||||||
|
return outCtx, grpc.DialContext(ctx, "api-service.prod.svc.local", grpc.WithCodec(proxy.Codec())), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outCtx, nil, status.Errorf(codes.Unimplemented, "Unknown method")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you need to register it with a `grpc.Server`. The server may have other handlers that will be served
|
||||||
|
locally.
|
||||||
|
|
||||||
|
```go
|
||||||
|
server := grpc.NewServer(
|
||||||
|
grpc.CustomCodec(proxy.Codec()),
|
||||||
|
grpc.UnknownServiceHandler(proxy.TransparentHandler(director)))
|
||||||
|
pb_test.RegisterTestServiceServer(server, &testImpl{})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
To make debugging a bit simpler, there are some helpers.
|
||||||
|
|
||||||
|
`testservice` contains a method `TestTestServiceServerImpl` which performs a complete test against
|
||||||
|
the reference implementation of the `TestServiceServer`.
|
||||||
|
|
||||||
|
In `proxy_test.go`, the test framework spins up a `TestServiceServer` that it tests the proxy
|
||||||
|
against. To make debugging a bit simpler (eg. if the developer needs to step into
|
||||||
|
`google.golang.org/grpc` methods), this `TestServiceServer` can be provided by a server by
|
||||||
|
passing `-test-backend=addr` to `go test`. A simple, local-only implementation of
|
||||||
|
`TestServiceServer` exists in [`testservice/server`](./testservice/server).
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
`grpc-proxy` is released under the Apache 2.0 license. See [LICENSE.txt](LICENSE.txt).
|
||||||
|
|
69
proxy/codec.go
Normal file
69
proxy/codec.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Codec returns a proxying grpc.Codec with the default protobuf codec as parent.
|
||||||
|
//
|
||||||
|
// See CodecWithParent.
|
||||||
|
//
|
||||||
|
// Deprecated: No longer necessary.
|
||||||
|
func Codec() grpc.Codec {
|
||||||
|
return CodecWithParent(&protoCodec{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodecWithParent returns a proxying grpc.Codec with a user provided codec as parent.
|
||||||
|
//
|
||||||
|
// Deprecated: No longer necessary.
|
||||||
|
func CodecWithParent(fallback grpc.Codec) grpc.Codec {
|
||||||
|
return &rawCodec{fallback}
|
||||||
|
}
|
||||||
|
|
||||||
|
type rawCodec struct {
|
||||||
|
parentCodec grpc.Codec
|
||||||
|
}
|
||||||
|
|
||||||
|
type frame struct {
|
||||||
|
payload []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rawCodec) Marshal(v interface{}) ([]byte, error) {
|
||||||
|
out, ok := v.(*frame)
|
||||||
|
if !ok {
|
||||||
|
return c.parentCodec.Marshal(v)
|
||||||
|
}
|
||||||
|
return out.payload, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rawCodec) Unmarshal(data []byte, v interface{}) error {
|
||||||
|
dst, ok := v.(*frame)
|
||||||
|
if !ok {
|
||||||
|
return c.parentCodec.Unmarshal(data, v)
|
||||||
|
}
|
||||||
|
dst.payload = data
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rawCodec) String() string {
|
||||||
|
return fmt.Sprintf("proxy>%s", c.parentCodec.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// protoCodec is a Codec implementation with protobuf. It is the default rawCodec for gRPC.
|
||||||
|
type protoCodec struct{}
|
||||||
|
|
||||||
|
func (protoCodec) Marshal(v interface{}) ([]byte, error) {
|
||||||
|
return proto.Marshal(v.(proto.Message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (protoCodec) Unmarshal(data []byte, v interface{}) error {
|
||||||
|
return proto.Unmarshal(data, v.(proto.Message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (protoCodec) String() string {
|
||||||
|
return "proto"
|
||||||
|
}
|
23
proxy/codec_test.go
Normal file
23
proxy/codec_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCodec_ReadYourWrites(t *testing.T) {
|
||||||
|
framePtr := &frame{}
|
||||||
|
data := []byte{0xDE, 0xAD, 0xBE, 0xEF}
|
||||||
|
codec := rawCodec{}
|
||||||
|
require.NoError(t, codec.Unmarshal(data, framePtr), "unmarshalling must go ok")
|
||||||
|
out, err := codec.Marshal(framePtr)
|
||||||
|
require.NoError(t, err, "no marshal error")
|
||||||
|
require.Equal(t, data, out, "output and data must be the same")
|
||||||
|
|
||||||
|
// reuse
|
||||||
|
require.NoError(t, codec.Unmarshal([]byte{0x55}, framePtr), "unmarshalling must go ok")
|
||||||
|
out, err = codec.Marshal(framePtr)
|
||||||
|
require.NoError(t, err, "no marshal error")
|
||||||
|
require.Equal(t, []byte{0x55}, out, "output and data must be the same")
|
||||||
|
}
|
25
proxy/director.go
Normal file
25
proxy/director.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2017 Michal Witkowski. All Rights Reserved.
|
||||||
|
// See LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StreamDirector returns a gRPC ClientConn to be used to forward the call to.
|
||||||
|
//
|
||||||
|
// The presence of the `Context` allows for rich filtering, e.g. based on Metadata (headers).
|
||||||
|
// If no handling is meant to be done, a `codes.NotImplemented` gRPC error should be returned.
|
||||||
|
//
|
||||||
|
// The context returned from this function should be the context for the *outgoing* (to backend) call. In case you want
|
||||||
|
// to forward any Metadata between the inbound request and outbound requests, you should do it manually. However, you
|
||||||
|
// *must* propagate the cancel function (`context.WithCancel`) of the inbound context to the one returned.
|
||||||
|
//
|
||||||
|
// It is worth noting that the StreamDirector will be fired *after* all server-side stream interceptors
|
||||||
|
// are invoked. So decisions around authorization, monitoring etc. are better to be handled there.
|
||||||
|
//
|
||||||
|
// See the rather rich example.
|
||||||
|
type StreamDirector func(ctx context.Context, fullMethodName string) (context.Context, grpc.ClientConnInterface, error)
|
15
proxy/doc.go
Normal file
15
proxy/doc.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2017 Michal Witkowski. All Rights Reserved.
|
||||||
|
// See LICENSE for licensing terms.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package proxy provides a reverse proxy handler for gRPC.
|
||||||
|
|
||||||
|
The implementation allows a grpc.Server to pass a received ServerStream to a ClientStream without understanding
|
||||||
|
the semantics of the messages exchanged. It basically provides a transparent reverse-proxy.
|
||||||
|
|
||||||
|
This package is intentionally generic, exposing a StreamDirector function that allows users of this package
|
||||||
|
to implement whatever logic of backend-picking, dialing and service verification to perform.
|
||||||
|
|
||||||
|
See examples on documented functions.
|
||||||
|
*/
|
||||||
|
package proxy
|
71
proxy/examples_test.go
Normal file
71
proxy/examples_test.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// Copyright 2017 Michal Witkowski. All Rights Reserved.
|
||||||
|
// See LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package proxy_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/proxy"
|
||||||
|
"go.linka.cloud/grpc-toolkit/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
director proxy.StreamDirector
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleNew() {
|
||||||
|
dst, err := grpc.Dial("example.com")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("dialing example.org: %v", err)
|
||||||
|
}
|
||||||
|
proxy, _ := proxy.New(dst)
|
||||||
|
_ = proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleRegisterService() {
|
||||||
|
// A gRPC service with the proxying codec enabled.
|
||||||
|
svc, _ := service.New()
|
||||||
|
// Register a TestService with 4 of its methods explicitly.
|
||||||
|
proxy.RegisterService(svc, director,
|
||||||
|
"mwitkow.testproto.TestService",
|
||||||
|
"PingEmpty", "Ping", "PingError", "PingList")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleTransparentHandler() {
|
||||||
|
grpc.NewServer(
|
||||||
|
grpc.UnknownServiceHandler(proxy.TransparentHandler(director)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provides a simple example of a director that shields internal services and dials a staging or production backend.
|
||||||
|
// This is a *very naive* implementation that creates a new connection on every request. Consider using pooling.
|
||||||
|
func ExampleStreamDirector() {
|
||||||
|
director = func(ctx context.Context, fullMethodName string) (context.Context, grpc.ClientConnInterface, error) {
|
||||||
|
// Make sure we never forward internal services.
|
||||||
|
if strings.HasPrefix(fullMethodName, "/com.example.internal.") {
|
||||||
|
return nil, nil, status.Errorf(codes.Unimplemented, "Unknown method")
|
||||||
|
}
|
||||||
|
md, ok := metadata.FromIncomingContext(ctx)
|
||||||
|
// Copy the inbound metadata explicitly.
|
||||||
|
outCtx := metadata.NewOutgoingContext(ctx, md.Copy())
|
||||||
|
if ok {
|
||||||
|
// Decide on which backend to dial
|
||||||
|
if val, exists := md[":authority"]; exists && val[0] == "staging.api.example.com" {
|
||||||
|
// Make sure we use DialContext so the dialing can be cancelled/time out together with the context.
|
||||||
|
conn, err := grpc.DialContext(ctx, "api-service.staging.svc.local")
|
||||||
|
return outCtx, conn, err
|
||||||
|
} else if val, exists := md[":authority"]; exists && val[0] == "api.example.com" {
|
||||||
|
conn, err := grpc.DialContext(ctx, "api-service.prod.svc.local")
|
||||||
|
return outCtx, conn, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil, status.Errorf(codes.Unimplemented, "Unknown method")
|
||||||
|
}
|
||||||
|
}
|
189
proxy/handler.go
Normal file
189
proxy/handler.go
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
// Copyright 2017 Michal Witkowski. All Rights Reserved.
|
||||||
|
// See LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
clientStreamDescForProxying = &grpc.StreamDesc{
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterService sets up a proxy handler for a particular gRPC service and method.
|
||||||
|
// The behaviour is the same as if you were registering a handler method, e.g. from a generated pb.go file.
|
||||||
|
func RegisterService(server grpc.ServiceRegistrar, director StreamDirector, serviceName string, methodNames ...string) {
|
||||||
|
streamer := &handler{director}
|
||||||
|
fakeDesc := &grpc.ServiceDesc{
|
||||||
|
ServiceName: serviceName,
|
||||||
|
HandlerType: (*interface{})(nil),
|
||||||
|
}
|
||||||
|
for _, m := range methodNames {
|
||||||
|
streamDesc := grpc.StreamDesc{
|
||||||
|
StreamName: m,
|
||||||
|
Handler: streamer.handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
}
|
||||||
|
fakeDesc.Streams = append(fakeDesc.Streams, streamDesc)
|
||||||
|
}
|
||||||
|
server.RegisterService(fakeDesc, streamer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterServiceDescs(server grpc.ServiceRegistrar, director StreamDirector, descs ...grpc.ServiceDesc) {
|
||||||
|
streamer := &handler{director}
|
||||||
|
for _, desc := range descs {
|
||||||
|
fakeDesc := &grpc.ServiceDesc{
|
||||||
|
ServiceName: desc.ServiceName,
|
||||||
|
HandlerType: (*interface{})(nil),
|
||||||
|
}
|
||||||
|
for _, desc := range desc.Methods {
|
||||||
|
streamDesc := grpc.StreamDesc{
|
||||||
|
StreamName: desc.MethodName,
|
||||||
|
Handler: streamer.handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
}
|
||||||
|
fakeDesc.Streams = append(fakeDesc.Streams, streamDesc)
|
||||||
|
}
|
||||||
|
for _, desc := range desc.Streams {
|
||||||
|
streamDesc := grpc.StreamDesc{
|
||||||
|
StreamName: desc.StreamName,
|
||||||
|
Handler: streamer.handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
}
|
||||||
|
fakeDesc.Streams = append(fakeDesc.Streams, streamDesc)
|
||||||
|
}
|
||||||
|
server.RegisterService(fakeDesc, streamer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransparentHandler returns a handler that attempts to proxy all requests that are not registered in the server.
|
||||||
|
// The indented use here is as a transparent proxy, where the server doesn't know about the services implemented by the
|
||||||
|
// backends. It should be used as a `grpc.UnknownServiceHandler`.
|
||||||
|
func TransparentHandler(director StreamDirector) grpc.StreamHandler {
|
||||||
|
streamer := &handler{director: director}
|
||||||
|
return streamer.handler
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
director StreamDirector
|
||||||
|
}
|
||||||
|
|
||||||
|
// handler is where the real magic of proxying happens.
|
||||||
|
// It is invoked like any gRPC server stream and uses the emptypb.Empty type server
|
||||||
|
// to proxy calls between the input and output streams.
|
||||||
|
func (s *handler) handler(srv interface{}, serverStream grpc.ServerStream) error {
|
||||||
|
// little bit of gRPC internals never hurt anyone
|
||||||
|
fullMethodName, ok := grpc.MethodFromServerStream(serverStream)
|
||||||
|
if !ok {
|
||||||
|
return status.Errorf(codes.Internal, "lowLevelServerStream not exists in context")
|
||||||
|
}
|
||||||
|
// We require that the director's returned context inherits from the serverStream.Context().
|
||||||
|
outgoingCtx, backendConn, err := s.director(serverStream.Context(), fullMethodName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCtx, clientCancel := context.WithCancel(outgoingCtx)
|
||||||
|
defer clientCancel()
|
||||||
|
// TODO(mwitkow): Add a `forwarded` header to metadata, https://en.wikipedia.org/wiki/X-Forwarded-For.
|
||||||
|
clientStream, err := backendConn.NewStream(clientCtx, clientStreamDescForProxying, fullMethodName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Explicitly *do not close* s2cErrChan and c2sErrChan, otherwise the select below will not terminate.
|
||||||
|
// Channels do not have to be closed, it is just a control flow mechanism, see
|
||||||
|
// https://groups.google.com/forum/#!msg/golang-nuts/pZwdYRGxCIk/qpbHxRRPJdUJ
|
||||||
|
s2cErrChan := s.forwardServerToClient(serverStream, clientStream)
|
||||||
|
c2sErrChan := s.forwardClientToServer(clientStream, serverStream)
|
||||||
|
// We don't know which side is going to stop sending first, so we need a select between the two.
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
select {
|
||||||
|
case s2cErr := <-s2cErrChan:
|
||||||
|
if s2cErr == io.EOF {
|
||||||
|
// this is the happy case where the sender has encountered io.EOF, and won't be sending anymore./
|
||||||
|
// the clientStream>serverStream may continue pumping though.
|
||||||
|
clientStream.CloseSend()
|
||||||
|
} else {
|
||||||
|
// however, we may have gotten a receive error (stream disconnected, a read error etc) in which case we need
|
||||||
|
// to cancel the clientStream to the backend, let all of its goroutines be freed up by the CancelFunc and
|
||||||
|
// exit with an error to the stack
|
||||||
|
clientCancel()
|
||||||
|
return status.Errorf(codes.Internal, "failed proxying s2c: %v", s2cErr)
|
||||||
|
}
|
||||||
|
case c2sErr := <-c2sErrChan:
|
||||||
|
// This happens when the clientStream has nothing else to offer (io.EOF), returned a gRPC error. In those two
|
||||||
|
// cases we may have received Trailers as part of the call. In case of other errors (stream closed) the trailers
|
||||||
|
// will be nil.
|
||||||
|
serverStream.SetTrailer(clientStream.Trailer())
|
||||||
|
// c2sErr will contain RPC error from client code. If not io.EOF return the RPC error as server stream error.
|
||||||
|
if c2sErr != io.EOF {
|
||||||
|
return c2sErr
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status.Errorf(codes.Internal, "gRPC proxying should never reach this stage.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *handler) forwardClientToServer(src grpc.ClientStream, dst grpc.ServerStream) chan error {
|
||||||
|
ret := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
f := &emptypb.Empty{}
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
if err := src.RecvMsg(f); err != nil {
|
||||||
|
ret <- err // this can be io.EOF which is happy case
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
// This is a bit of a hack, but client to server headers are only readable after first client msg is
|
||||||
|
// received but must be written to server stream before the first msg is flushed.
|
||||||
|
// This is the only place to do it nicely.
|
||||||
|
md, err := src.Header()
|
||||||
|
if err != nil {
|
||||||
|
ret <- err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err := dst.SendHeader(md); err != nil {
|
||||||
|
ret <- err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := dst.SendMsg(f); err != nil {
|
||||||
|
ret <- err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *handler) forwardServerToClient(src grpc.ServerStream, dst grpc.ClientStream) chan error {
|
||||||
|
ret := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
f := &emptypb.Empty{}
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
if err := src.RecvMsg(f); err != nil {
|
||||||
|
ret <- err // this can be io.EOF which is happy case
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err := dst.SendMsg(f); err != nil {
|
||||||
|
ret <- err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return ret
|
||||||
|
}
|
260
proxy/handler_test.go
Normal file
260
proxy/handler_test.go
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
// Copyright 2017 Michal Witkowski. All Rights Reserved.
|
||||||
|
// See LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package proxy_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/proxy"
|
||||||
|
pb "go.linka.cloud/grpc-toolkit/proxy/testservice"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
pingDefaultValue = "I like kittens."
|
||||||
|
clientMdKey = "test-client-header"
|
||||||
|
serverHeaderMdKey = "test-client-header"
|
||||||
|
serverTrailerMdKey = "test-client-trailer"
|
||||||
|
|
||||||
|
rejectingMdKey = "test-reject-rpc-if-in-context"
|
||||||
|
|
||||||
|
countListResponses = 20
|
||||||
|
)
|
||||||
|
|
||||||
|
// asserting service is implemented on the server side and serves as a handler for stuff
|
||||||
|
type assertingService struct {
|
||||||
|
t *testing.T
|
||||||
|
pb.UnsafeTestServiceServer
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ pb.TestServiceServer = (*assertingService)(nil)
|
||||||
|
|
||||||
|
func (s *assertingService) PingEmpty(ctx context.Context, _ *emptypb.Empty) (*pb.PingResponse, error) {
|
||||||
|
// Check that this call has client's metadata.
|
||||||
|
md, ok := metadata.FromIncomingContext(ctx)
|
||||||
|
assert.True(s.t, ok, "PingEmpty call must have metadata in context")
|
||||||
|
_, ok = md[clientMdKey]
|
||||||
|
assert.True(s.t, ok, "PingEmpty call must have clients's custom headers in metadata")
|
||||||
|
return &pb.PingResponse{Value: pingDefaultValue, Counter: 42}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *assertingService) Ping(ctx context.Context, ping *pb.PingRequest) (*pb.PingResponse, error) {
|
||||||
|
// Send user trailers and headers.
|
||||||
|
grpc.SendHeader(ctx, metadata.Pairs(serverHeaderMdKey, "I like turtles."))
|
||||||
|
grpc.SetTrailer(ctx, metadata.Pairs(serverTrailerMdKey, "I like ending turtles."))
|
||||||
|
return &pb.PingResponse{Value: ping.Value, Counter: 42}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *assertingService) PingError(ctx context.Context, ping *pb.PingRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.FailedPrecondition, "Userspace error.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *assertingService) PingList(ping *pb.PingRequest, stream pb.TestService_PingListServer) error {
|
||||||
|
// Send user trailers and headers.
|
||||||
|
stream.SendHeader(metadata.Pairs(serverHeaderMdKey, "I like turtles."))
|
||||||
|
for i := 0; i < countListResponses; i++ {
|
||||||
|
stream.Send(&pb.PingResponse{Value: ping.Value, Counter: int32(i)})
|
||||||
|
}
|
||||||
|
stream.SetTrailer(metadata.Pairs(serverTrailerMdKey, "I like ending turtles."))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *assertingService) PingStream(stream pb.TestService_PingStreamServer) error {
|
||||||
|
stream.SendHeader(metadata.Pairs(serverHeaderMdKey, "I like turtles."))
|
||||||
|
counter := int32(0)
|
||||||
|
for {
|
||||||
|
ping, err := stream.Recv()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
require.NoError(s.t, err, "can't fail reading stream")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pong := &pb.PingResponse{Value: ping.Value, Counter: counter}
|
||||||
|
if err := stream.Send(pong); err != nil {
|
||||||
|
require.NoError(s.t, err, "can't fail sending back a pong")
|
||||||
|
}
|
||||||
|
counter += 1
|
||||||
|
}
|
||||||
|
stream.SetTrailer(metadata.Pairs(serverTrailerMdKey, "I like ending turtles."))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProxyHappySuite tests the "happy" path of handling: that everything works in absence of connection issues.
|
||||||
|
type ProxyHappySuite struct {
|
||||||
|
suite.Suite
|
||||||
|
|
||||||
|
serverListener net.Listener
|
||||||
|
server *grpc.Server
|
||||||
|
proxyListener net.Listener
|
||||||
|
proxy *grpc.Server
|
||||||
|
serverClientConn *grpc.ClientConn
|
||||||
|
|
||||||
|
client *grpc.ClientConn
|
||||||
|
testClient pb.TestServiceClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProxyHappySuite) TestPingEmptyCarriesClientMetadata() {
|
||||||
|
ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(clientMdKey, "true"))
|
||||||
|
out, err := s.testClient.PingEmpty(ctx, &emptypb.Empty{})
|
||||||
|
require.NoError(s.T(), err, "PingEmpty should succeed without errors")
|
||||||
|
want := &pb.PingResponse{Value: pingDefaultValue, Counter: 42}
|
||||||
|
require.True(s.T(), proto.Equal(want, out))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProxyHappySuite) TestPingEmpty_StressTest() {
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
s.TestPingEmptyCarriesClientMetadata()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProxyHappySuite) TestPingCarriesServerHeadersAndTrailers() {
|
||||||
|
headerMd := make(metadata.MD)
|
||||||
|
trailerMd := make(metadata.MD)
|
||||||
|
// This is an awkward calling convention... but meh.
|
||||||
|
out, err := s.testClient.Ping(context.Background(), &pb.PingRequest{Value: "foo"}, grpc.Header(&headerMd), grpc.Trailer(&trailerMd))
|
||||||
|
want := &pb.PingResponse{Value: "foo", Counter: 42}
|
||||||
|
require.NoError(s.T(), err, "Ping should succeed without errors")
|
||||||
|
require.True(s.T(), proto.Equal(want, out))
|
||||||
|
assert.Contains(s.T(), headerMd, serverHeaderMdKey, "server response headers must contain server data")
|
||||||
|
assert.Len(s.T(), trailerMd, 1, "server response trailers must contain server data")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProxyHappySuite) TestPingErrorPropagatesAppError() {
|
||||||
|
_, err := s.testClient.PingError(context.Background(), &pb.PingRequest{Value: "foo"})
|
||||||
|
require.Error(s.T(), err, "PingError should never succeed")
|
||||||
|
assert.Equal(s.T(), codes.FailedPrecondition, status.Code(err))
|
||||||
|
assert.Equal(s.T(), "Userspace error.", status.Convert(err).Message())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProxyHappySuite) TestDirectorErrorIsPropagated() {
|
||||||
|
// See SetupSuite where the StreamDirector has a special case.
|
||||||
|
ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(rejectingMdKey, "true"))
|
||||||
|
_, err := s.testClient.Ping(ctx, &pb.PingRequest{Value: "foo"})
|
||||||
|
require.Error(s.T(), err, "Director should reject this RPC")
|
||||||
|
assert.Equal(s.T(), codes.PermissionDenied, status.Code(err))
|
||||||
|
assert.Equal(s.T(), "testing rejection", status.Convert(err).Message())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProxyHappySuite) TestPingStream_FullDuplexWorks() {
|
||||||
|
stream, err := s.testClient.PingStream(context.Background())
|
||||||
|
require.NoError(s.T(), err, "PingStream request should be successful.")
|
||||||
|
|
||||||
|
for i := 0; i < countListResponses; i++ {
|
||||||
|
ping := &pb.PingRequest{Value: fmt.Sprintf("foo:%d", i)}
|
||||||
|
require.NoError(s.T(), stream.Send(ping), "sending to PingStream must not fail")
|
||||||
|
resp, err := stream.Recv()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
// Check that the header arrives before all entries.
|
||||||
|
headerMd, err := stream.Header()
|
||||||
|
require.NoError(s.T(), err, "PingStream headers should not error.")
|
||||||
|
assert.Contains(s.T(), headerMd, serverHeaderMdKey, "PingStream response headers user contain metadata")
|
||||||
|
}
|
||||||
|
assert.EqualValues(s.T(), i, resp.Counter, "ping roundtrip must succeed with the correct id")
|
||||||
|
}
|
||||||
|
require.NoError(s.T(), stream.CloseSend(), "no error on close send")
|
||||||
|
_, err = stream.Recv()
|
||||||
|
require.Equal(s.T(), io.EOF, err, "stream should close with io.EOF, meaining OK")
|
||||||
|
// Check that the trailer headers are here.
|
||||||
|
trailerMd := stream.Trailer()
|
||||||
|
assert.Len(s.T(), trailerMd, 1, "PingList trailer headers user contain metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProxyHappySuite) TestPingStream_StressTest() {
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
s.TestPingStream_FullDuplexWorks()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProxyHappySuite) SetupSuite() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
s.proxyListener, err = net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
require.NoError(s.T(), err, "must be able to allocate a port for proxyListener")
|
||||||
|
s.serverListener, err = net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
require.NoError(s.T(), err, "must be able to allocate a port for serverListener")
|
||||||
|
|
||||||
|
s.server = grpc.NewServer()
|
||||||
|
pb.RegisterTestServiceServer(s.server, &assertingService{t: s.T()})
|
||||||
|
|
||||||
|
// Setup of the proxy's Director.
|
||||||
|
//lint:ignore SA1019 regression test
|
||||||
|
s.serverClientConn, err = grpc.Dial(s.serverListener.Addr().String(), grpc.WithInsecure(), grpc.WithCodec(proxy.Codec()))
|
||||||
|
require.NoError(s.T(), err, "must not error on deferred client Dial")
|
||||||
|
director := func(ctx context.Context, fullName string) (context.Context, grpc.ClientConnInterface, error) {
|
||||||
|
md, ok := metadata.FromIncomingContext(ctx)
|
||||||
|
if ok {
|
||||||
|
if _, exists := md[rejectingMdKey]; exists {
|
||||||
|
return ctx, nil, status.Errorf(codes.PermissionDenied, "testing rejection")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Explicitly copy the metadata, otherwise the tests will fail.
|
||||||
|
outCtx := metadata.NewOutgoingContext(ctx, md.Copy())
|
||||||
|
return outCtx, s.serverClientConn, nil
|
||||||
|
}
|
||||||
|
s.proxy = grpc.NewServer(
|
||||||
|
//lint:ignore SA1019 regression test
|
||||||
|
grpc.CustomCodec(proxy.Codec()),
|
||||||
|
grpc.UnknownServiceHandler(proxy.TransparentHandler(director)),
|
||||||
|
)
|
||||||
|
|
||||||
|
proxy.RegisterServiceDescs(s.proxy, director, pb.TestService_ServiceDesc)
|
||||||
|
|
||||||
|
// Start the serving loops.
|
||||||
|
s.T().Logf("starting grpc.Server at: %v", s.serverListener.Addr().String())
|
||||||
|
go func() {
|
||||||
|
s.server.Serve(s.serverListener)
|
||||||
|
}()
|
||||||
|
s.T().Logf("starting grpc.Proxy at: %v", s.proxyListener.Addr().String())
|
||||||
|
go func() {
|
||||||
|
s.proxy.Serve(s.proxyListener)
|
||||||
|
}()
|
||||||
|
|
||||||
|
dCtx, ccl := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
defer ccl()
|
||||||
|
clientConn, err := grpc.DialContext(dCtx, strings.Replace(s.proxyListener.Addr().String(), "127.0.0.1", "localhost", 1), grpc.WithInsecure())
|
||||||
|
require.NoError(s.T(), err, "must not error on deferred client Dial")
|
||||||
|
s.testClient = pb.NewTestServiceClient(clientConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProxyHappySuite) TearDownSuite() {
|
||||||
|
if s.client != nil {
|
||||||
|
s.client.Close()
|
||||||
|
}
|
||||||
|
if s.serverClientConn != nil {
|
||||||
|
s.serverClientConn.Close()
|
||||||
|
}
|
||||||
|
// Close all transports so the logs don't get spammy.
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
if s.proxy != nil {
|
||||||
|
s.proxy.Stop()
|
||||||
|
s.proxyListener.Close()
|
||||||
|
}
|
||||||
|
if s.serverListener != nil {
|
||||||
|
s.server.Stop()
|
||||||
|
s.serverListener.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProxyHappySuite(t *testing.T) {
|
||||||
|
suite.Run(t, &ProxyHappySuite{})
|
||||||
|
}
|
39
proxy/proxy.go
Normal file
39
proxy/proxy.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2021 Michal Witkowski. All Rights Reserved.
|
||||||
|
// See LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New sets up a simple proxy that forwards all requests to dst.
|
||||||
|
func New(dst grpc.ClientConnInterface, opts ...service.Option) (service.Service, error) {
|
||||||
|
opts = append(opts, WithDefault(dst))
|
||||||
|
// Set up the proxy server and then serve from it like in step one.
|
||||||
|
return service.New(opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDefault returns a grpc.UnknownServiceHandler with a DefaultDirector.
|
||||||
|
func WithDefault(cc grpc.ClientConnInterface) service.Option {
|
||||||
|
return service.WithGRPCServerOpts(grpc.UnknownServiceHandler(TransparentHandler(DefaultDirector(cc))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultDirector returns a very simple forwarding StreamDirector that forwards all
|
||||||
|
// calls.
|
||||||
|
func DefaultDirector(cc grpc.ClientConnInterface) StreamDirector {
|
||||||
|
return func(ctx context.Context, fullMethodName string) (context.Context, grpc.ClientConnInterface, error) {
|
||||||
|
md, _ := metadata.FromIncomingContext(ctx)
|
||||||
|
ctx = metadata.NewOutgoingContext(ctx, md.Copy())
|
||||||
|
return ctx, cc, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func With(director StreamDirector) service.Option {
|
||||||
|
return service.WithGRPCServerOpts(grpc.UnknownServiceHandler(TransparentHandler(director)))
|
||||||
|
}
|
217
proxy/proxy_test.go
Normal file
217
proxy/proxy_test.go
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
package proxy_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/test/bufconn"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/proxy"
|
||||||
|
"go.linka.cloud/grpc-toolkit/proxy/testservice"
|
||||||
|
"go.linka.cloud/grpc-toolkit/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testBackend = flag.String("test-backend", "", "Service providing TestServiceServer")
|
||||||
|
|
||||||
|
// TestIntegrationV1 is a regression test of the proxy.
|
||||||
|
func TestLegacyBehaviour(t *testing.T) {
|
||||||
|
// These bufconns are test listeners used to make connections between our
|
||||||
|
// services. This test actually starts two fully functional grpc services.
|
||||||
|
proxyBc := bufconn.Listen(10)
|
||||||
|
|
||||||
|
// Setup is a little thorough, but here's the gist of it:
|
||||||
|
// 1. Create the test backend using testservice.DefaultTestServiceServer
|
||||||
|
// 2. Create the proxy backend using this package
|
||||||
|
// 3. Make calls to 1 via 2.
|
||||||
|
|
||||||
|
// 1.
|
||||||
|
//lint:ignore SA1019 regression test
|
||||||
|
testCC, err := backendDialer(t, grpc.WithCodec(proxy.Codec()))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
go func() {
|
||||||
|
// Second, we need to implement the SteamDirector.
|
||||||
|
directorFn := func(ctx context.Context, fullMethodName string) (context.Context, grpc.ClientConnInterface, error) {
|
||||||
|
md, _ := metadata.FromIncomingContext(ctx)
|
||||||
|
outCtx := metadata.NewOutgoingContext(ctx, md.Copy())
|
||||||
|
return outCtx, testCC, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the proxy server and then serve from it like in step one.
|
||||||
|
proxySrv, err := service.New(proxy.With(directorFn))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// run the proxy backend
|
||||||
|
go func() {
|
||||||
|
t.Log("Running proxySrv")
|
||||||
|
if err := proxySrv.Serve(proxyBc); err != nil {
|
||||||
|
if err == grpc.ErrServerStopped {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("running proxy server: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
t.Cleanup(func() {
|
||||||
|
t.Log("Gracefully stopping proxySrv")
|
||||||
|
proxySrv.Stop()
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 3.
|
||||||
|
// Connect to the proxy. We should not need to do anything special here -
|
||||||
|
// users do not need to know they're talking to a proxy.
|
||||||
|
proxyCC, err := grpc.Dial(
|
||||||
|
"bufnet",
|
||||||
|
grpc.WithInsecure(),
|
||||||
|
grpc.WithBlock(),
|
||||||
|
grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) {
|
||||||
|
return proxyBc.Dial()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("dialing proxy: %v", err)
|
||||||
|
}
|
||||||
|
proxyClient := testservice.NewTestServiceClient(proxyCC)
|
||||||
|
|
||||||
|
// 4. Run the tests!
|
||||||
|
testservice.TestTestServiceServerImpl(t, proxyClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewProxy(t *testing.T) {
|
||||||
|
proxyBc := bufconn.Listen(10)
|
||||||
|
|
||||||
|
// Setup is a little thorough, but here's the gist of it:
|
||||||
|
// 1. Create the test backend using testservice.DefaultTestServiceServer
|
||||||
|
// 2. Create the proxy backend using this package
|
||||||
|
// 3. Make calls to 1 via 2.
|
||||||
|
|
||||||
|
// 1.
|
||||||
|
// First, we need to create a client connection to this backend.
|
||||||
|
testCC, err := backendDialer(t)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
go func() {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// First, we need to create a client connection to this backend.
|
||||||
|
proxySrv, err := proxy.New(testCC)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the proxy backend
|
||||||
|
go func() {
|
||||||
|
t.Log("Running proxySrv")
|
||||||
|
if err := proxySrv.Serve(proxyBc); err != nil {
|
||||||
|
if err == grpc.ErrServerStopped {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("running proxy server: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
t.Cleanup(func() {
|
||||||
|
t.Log("Gracefully stopping proxySrv")
|
||||||
|
proxySrv.Stop()
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 3.
|
||||||
|
// Connect to the proxy. We should not need to do anything special here -
|
||||||
|
// users do not need to know they're talking to a proxy.
|
||||||
|
t.Logf("dialing %s", proxyBc.Addr())
|
||||||
|
proxyCC, err := grpc.Dial(
|
||||||
|
proxyBc.Addr().String(),
|
||||||
|
grpc.WithInsecure(),
|
||||||
|
grpc.WithBlock(),
|
||||||
|
grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) {
|
||||||
|
return proxyBc.Dial()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("dialing proxy: %v", err)
|
||||||
|
}
|
||||||
|
proxyClient := testservice.NewTestServiceClient(proxyCC)
|
||||||
|
|
||||||
|
// 4. Run the tests!
|
||||||
|
testservice.TestTestServiceServerImpl(t, proxyClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
// backendDialer dials the testservice.TestServiceServer either by connecting
|
||||||
|
// to the user-supplied server, or by creating a mock server using bufconn.
|
||||||
|
func backendDialer(t *testing.T, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
if *testBackend != "" {
|
||||||
|
return backendSvcDialer(t, *testBackend, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
backendBc := bufconn.Listen(10)
|
||||||
|
// set up the backend using a "real" server over a bufconn
|
||||||
|
testSrv, err := service.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
testservice.RegisterTestServiceServer(testSrv, testservice.DefaultTestServiceServer)
|
||||||
|
|
||||||
|
// run the test backend
|
||||||
|
go func() {
|
||||||
|
t.Log("Running testSrv")
|
||||||
|
if err := testSrv.Serve(backendBc); err != nil {
|
||||||
|
if err == grpc.ErrServerStopped {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("running test server: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
t.Cleanup(func() {
|
||||||
|
t.Log("Gracefully stopping testSrv")
|
||||||
|
testSrv.Stop()
|
||||||
|
})
|
||||||
|
|
||||||
|
opts = append(opts,
|
||||||
|
grpc.WithInsecure(),
|
||||||
|
grpc.WithBlock(),
|
||||||
|
grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) {
|
||||||
|
return backendBc.Dial()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
backendCC, err := grpc.Dial(
|
||||||
|
"bufnet",
|
||||||
|
opts...,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("dialing backend: %v", err)
|
||||||
|
}
|
||||||
|
return backendCC, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func backendSvcDialer(t *testing.T, addr string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||||
|
opts = append(opts,
|
||||||
|
grpc.WithInsecure(),
|
||||||
|
grpc.WithBlock(),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Logf("connecting to %s", addr)
|
||||||
|
cc, err := grpc.Dial(
|
||||||
|
addr,
|
||||||
|
opts...,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("dialing backend: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cc, nil
|
||||||
|
}
|
9
proxy/testservice/Makefile
Normal file
9
proxy/testservice/Makefile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
all: test_go
|
||||||
|
|
||||||
|
test_go: test.proto
|
||||||
|
protoc --go_out=. --go_opt=paths=source_relative \
|
||||||
|
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
|
||||||
|
test.proto
|
||||||
|
|
||||||
|
|
168
proxy/testservice/ping.go
Normal file
168
proxy/testservice/ping.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package testservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DefaultTestServiceServer = defaultPingServer{}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PingHeader = "ping-header"
|
||||||
|
PingHeaderCts = "Arbitrary header text"
|
||||||
|
PingTrailer = "ping-trailer"
|
||||||
|
PingTrailerCts = "Arbitrary trailer text"
|
||||||
|
PingEchoHeader = "ping-echo-header"
|
||||||
|
PingEchoTrailer = "ping-echo-trailer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultPingServer is the canonical implementation of a TestServiceServer.
|
||||||
|
type defaultPingServer struct {
|
||||||
|
UnsafeTestServiceServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s defaultPingServer) PingEmpty(ctx context.Context, empty *emptypb.Empty) (*PingResponse, error) {
|
||||||
|
if err := s.sendHeader(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := s.setTrailer(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &PingResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s defaultPingServer) Ping(ctx context.Context, request *PingRequest) (*PingResponse, error) {
|
||||||
|
if err := s.sendHeader(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := s.setTrailer(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &PingResponse{Value: request.Value}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s defaultPingServer) PingError(ctx context.Context, request *PingRequest) (*emptypb.Empty, error) {
|
||||||
|
if err := s.sendHeader(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := s.setTrailer(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, status.Error(codes.Unknown, "Something is wrong and this is a message that describes it")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s defaultPingServer) PingList(request *PingRequest, server TestService_PingListServer) error {
|
||||||
|
if err := s.sendHeader(server.Context()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.setStreamTrailer(server)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
if err := server.Send(&PingResponse{
|
||||||
|
Value: request.Value,
|
||||||
|
Counter: int32(i),
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s defaultPingServer) PingStream(server TestService_PingStreamServer) error {
|
||||||
|
g, ctx := errgroup.WithContext(context.Background())
|
||||||
|
|
||||||
|
if err := s.sendHeader(server.Context()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pings := make(chan *PingRequest)
|
||||||
|
g.Go(func() error {
|
||||||
|
defer close(pings)
|
||||||
|
for {
|
||||||
|
m, err := server.Recv()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case pings <- m:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
g.Go(func() error {
|
||||||
|
var i int32
|
||||||
|
for m := range pings {
|
||||||
|
if err := server.Send(&PingResponse{
|
||||||
|
Value: m.Value,
|
||||||
|
Counter: i,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return g.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *defaultPingServer) sendHeader(ctx context.Context) error {
|
||||||
|
md, ok := metadata.FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
md = metadata.New(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tvs := md.Get(PingEchoHeader); len(tvs) > 0 {
|
||||||
|
md.Append(PingEchoHeader, tvs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
md.Append(PingHeader, PingHeaderCts)
|
||||||
|
|
||||||
|
if err := grpc.SendHeader(ctx, md); err != nil {
|
||||||
|
return fmt.Errorf("setting header: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *defaultPingServer) setTrailer(ctx context.Context) error {
|
||||||
|
md := s.buildTrailer(ctx)
|
||||||
|
|
||||||
|
if err := grpc.SetTrailer(ctx, md); err != nil {
|
||||||
|
return fmt.Errorf("setting trailer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *defaultPingServer) buildTrailer(ctx context.Context) metadata.MD {
|
||||||
|
md, ok := metadata.FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
md = metadata.New(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tvs := md.Get(PingEchoTrailer); len(tvs) > 0 {
|
||||||
|
md.Append(PingEchoTrailer, tvs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
md.Append(PingTrailer, PingTrailerCts)
|
||||||
|
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s defaultPingServer) setStreamTrailer(server grpc.ServerStream) {
|
||||||
|
server.SetTrailer(s.buildTrailer(server.Context()))
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TestServiceServer = (*defaultPingServer)(nil)
|
54
proxy/testservice/server/main.go
Normal file
54
proxy/testservice/server/main.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/proxy/testservice"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
port = flag.Uint("port", 8080, "Port to listen to")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
srv := grpc.NewServer()
|
||||||
|
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
testservice.RegisterTestServiceServer(srv, testservice.DefaultTestServiceServer)
|
||||||
|
|
||||||
|
errs := make(chan error)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Printf("listening on %s", lis.Addr().String())
|
||||||
|
errs <- srv.Serve(lis)
|
||||||
|
}()
|
||||||
|
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
sig := <-sigs
|
||||||
|
log.Printf("shutdown due to %s", sig)
|
||||||
|
srv.GracefulStop()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := <-errs; err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.Parse()
|
||||||
|
}
|
21
proxy/testservice/test.pb.defaults.go
Normal file
21
proxy/testservice/test.pb.defaults.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Code generated by protoc-gen-defaults. DO NOT EDIT.
|
||||||
|
|
||||||
|
package testservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ *timestamppb.Timestamp
|
||||||
|
_ *durationpb.Duration
|
||||||
|
_ *wrapperspb.BoolValue
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x *PingRequest) Default() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PingResponse) Default() {
|
||||||
|
}
|
31
proxy/testservice/test.pb.fields.go
Normal file
31
proxy/testservice/test.pb.fields.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Code generated by protoc-gen-defaults. DO NOT EDIT.
|
||||||
|
|
||||||
|
package testservice
|
||||||
|
|
||||||
|
var TestServiceMethods = struct {
|
||||||
|
PingEmpty string
|
||||||
|
Ping string
|
||||||
|
PingError string
|
||||||
|
PingList string
|
||||||
|
PingStream string
|
||||||
|
}{
|
||||||
|
PingEmpty: "/mwitkow.testproto.TestService/PingEmpty",
|
||||||
|
Ping: "/mwitkow.testproto.TestService/Ping",
|
||||||
|
PingError: "/mwitkow.testproto.TestService/PingError",
|
||||||
|
PingList: "/mwitkow.testproto.TestService/PingList",
|
||||||
|
PingStream: "/mwitkow.testproto.TestService/PingStream",
|
||||||
|
}
|
||||||
|
|
||||||
|
var PingRequestFields = struct {
|
||||||
|
Value string
|
||||||
|
}{
|
||||||
|
Value: "value",
|
||||||
|
}
|
||||||
|
|
||||||
|
var PingResponseFields = struct {
|
||||||
|
Value string
|
||||||
|
Counter string
|
||||||
|
}{
|
||||||
|
Value: "value",
|
||||||
|
Counter: "counter",
|
||||||
|
}
|
227
proxy/testservice/test.pb.go
Normal file
227
proxy/testservice/test.pb.go
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.35.1
|
||||||
|
// protoc v5.28.2
|
||||||
|
// source: proxy/testservice/test.proto
|
||||||
|
|
||||||
|
package testservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type PingRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PingRequest) Reset() {
|
||||||
|
*x = PingRequest{}
|
||||||
|
mi := &file_proxy_testservice_test_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PingRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PingRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *PingRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_proxy_testservice_test_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use PingRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*PingRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_proxy_testservice_test_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PingRequest) GetValue() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Value
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type PingResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
|
||||||
|
Counter int32 `protobuf:"varint,2,opt,name=counter,proto3" json:"counter,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PingResponse) Reset() {
|
||||||
|
*x = PingResponse{}
|
||||||
|
mi := &file_proxy_testservice_test_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PingResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PingResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *PingResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_proxy_testservice_test_proto_msgTypes[1]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use PingResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*PingResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_proxy_testservice_test_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PingResponse) GetValue() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Value
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PingResponse) GetCounter() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Counter
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_proxy_testservice_test_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_proxy_testservice_test_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x1c, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x73, 0x65, 0x72, 0x76,
|
||||||
|
0x69, 0x63, 0x65, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11,
|
||||||
|
0x6d, 0x77, 0x69, 0x74, 0x6b, 0x6f, 0x77, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
|
0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x23,
|
||||||
|
0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a,
|
||||||
|
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
|
||||||
|
0x6c, 0x75, 0x65, 0x22, 0x3e, 0x0a, 0x0c, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
|
0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||||
|
0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75,
|
||||||
|
0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e,
|
||||||
|
0x74, 0x65, 0x72, 0x32, 0x8d, 0x03, 0x0a, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76,
|
||||||
|
0x69, 0x63, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x50, 0x69, 0x6e, 0x67, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
|
0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
|
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1f, 0x2e, 0x6d, 0x77, 0x69, 0x74, 0x6b,
|
||||||
|
0x6f, 0x77, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x6e,
|
||||||
|
0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x04, 0x50,
|
||||||
|
0x69, 0x6e, 0x67, 0x12, 0x1e, 0x2e, 0x6d, 0x77, 0x69, 0x74, 0x6b, 0x6f, 0x77, 0x2e, 0x74, 0x65,
|
||||||
|
0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75,
|
||||||
|
0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x77, 0x69, 0x74, 0x6b, 0x6f, 0x77, 0x2e, 0x74, 0x65,
|
||||||
|
0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70,
|
||||||
|
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x09, 0x50, 0x69, 0x6e, 0x67, 0x45, 0x72,
|
||||||
|
0x72, 0x6f, 0x72, 0x12, 0x1e, 0x2e, 0x6d, 0x77, 0x69, 0x74, 0x6b, 0x6f, 0x77, 0x2e, 0x74, 0x65,
|
||||||
|
0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75,
|
||||||
|
0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x4f, 0x0a,
|
||||||
|
0x08, 0x50, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1e, 0x2e, 0x6d, 0x77, 0x69, 0x74,
|
||||||
|
0x6b, 0x6f, 0x77, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69,
|
||||||
|
0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x77, 0x69, 0x74,
|
||||||
|
0x6b, 0x6f, 0x77, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69,
|
||||||
|
0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x53,
|
||||||
|
0x0a, 0x0a, 0x50, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x6d,
|
||||||
|
0x77, 0x69, 0x74, 0x6b, 0x6f, 0x77, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
|
0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d,
|
||||||
|
0x77, 0x69, 0x74, 0x6b, 0x6f, 0x77, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
|
0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28,
|
||||||
|
0x01, 0x30, 0x01, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x6f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x2e,
|
||||||
|
0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2d, 0x74, 0x6f, 0x6f, 0x6c, 0x6b,
|
||||||
|
0x69, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x73, 0x65, 0x72,
|
||||||
|
0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_proxy_testservice_test_proto_rawDescOnce sync.Once
|
||||||
|
file_proxy_testservice_test_proto_rawDescData = file_proxy_testservice_test_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_proxy_testservice_test_proto_rawDescGZIP() []byte {
|
||||||
|
file_proxy_testservice_test_proto_rawDescOnce.Do(func() {
|
||||||
|
file_proxy_testservice_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_testservice_test_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_proxy_testservice_test_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_proxy_testservice_test_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
|
var file_proxy_testservice_test_proto_goTypes = []any{
|
||||||
|
(*PingRequest)(nil), // 0: mwitkow.testproto.PingRequest
|
||||||
|
(*PingResponse)(nil), // 1: mwitkow.testproto.PingResponse
|
||||||
|
(*emptypb.Empty)(nil), // 2: google.protobuf.Empty
|
||||||
|
}
|
||||||
|
var file_proxy_testservice_test_proto_depIdxs = []int32{
|
||||||
|
2, // 0: mwitkow.testproto.TestService.PingEmpty:input_type -> google.protobuf.Empty
|
||||||
|
0, // 1: mwitkow.testproto.TestService.Ping:input_type -> mwitkow.testproto.PingRequest
|
||||||
|
0, // 2: mwitkow.testproto.TestService.PingError:input_type -> mwitkow.testproto.PingRequest
|
||||||
|
0, // 3: mwitkow.testproto.TestService.PingList:input_type -> mwitkow.testproto.PingRequest
|
||||||
|
0, // 4: mwitkow.testproto.TestService.PingStream:input_type -> mwitkow.testproto.PingRequest
|
||||||
|
1, // 5: mwitkow.testproto.TestService.PingEmpty:output_type -> mwitkow.testproto.PingResponse
|
||||||
|
1, // 6: mwitkow.testproto.TestService.Ping:output_type -> mwitkow.testproto.PingResponse
|
||||||
|
2, // 7: mwitkow.testproto.TestService.PingError:output_type -> google.protobuf.Empty
|
||||||
|
1, // 8: mwitkow.testproto.TestService.PingList:output_type -> mwitkow.testproto.PingResponse
|
||||||
|
1, // 9: mwitkow.testproto.TestService.PingStream:output_type -> mwitkow.testproto.PingResponse
|
||||||
|
5, // [5:10] is the sub-list for method output_type
|
||||||
|
0, // [0:5] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_proxy_testservice_test_proto_init() }
|
||||||
|
func file_proxy_testservice_test_proto_init() {
|
||||||
|
if File_proxy_testservice_test_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_proxy_testservice_test_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 2,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 1,
|
||||||
|
},
|
||||||
|
GoTypes: file_proxy_testservice_test_proto_goTypes,
|
||||||
|
DependencyIndexes: file_proxy_testservice_test_proto_depIdxs,
|
||||||
|
MessageInfos: file_proxy_testservice_test_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_proxy_testservice_test_proto = out.File
|
||||||
|
file_proxy_testservice_test_proto_rawDesc = nil
|
||||||
|
file_proxy_testservice_test_proto_goTypes = nil
|
||||||
|
file_proxy_testservice_test_proto_depIdxs = nil
|
||||||
|
}
|
240
proxy/testservice/test.pb.validate.go
Normal file
240
proxy/testservice/test.pb.validate.go
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
// Code generated by protoc-gen-validate. DO NOT EDIT.
|
||||||
|
// source: proxy/testservice/test.proto
|
||||||
|
|
||||||
|
package testservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/mail"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ensure the imports are used
|
||||||
|
var (
|
||||||
|
_ = bytes.MinRead
|
||||||
|
_ = errors.New("")
|
||||||
|
_ = fmt.Print
|
||||||
|
_ = utf8.UTFMax
|
||||||
|
_ = (*regexp.Regexp)(nil)
|
||||||
|
_ = (*strings.Reader)(nil)
|
||||||
|
_ = net.IPv4len
|
||||||
|
_ = time.Duration(0)
|
||||||
|
_ = (*url.URL)(nil)
|
||||||
|
_ = (*mail.Address)(nil)
|
||||||
|
_ = anypb.Any{}
|
||||||
|
_ = sort.Sort
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validate checks the field values on PingRequest with the rules defined in
|
||||||
|
// the proto definition for this message. If any rules are violated, the first
|
||||||
|
// error encountered is returned, or nil if there are no violations.
|
||||||
|
func (m *PingRequest) Validate() error {
|
||||||
|
return m.validate(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateAll checks the field values on PingRequest with the rules defined in
|
||||||
|
// the proto definition for this message. If any rules are violated, the
|
||||||
|
// result is a list of violation errors wrapped in PingRequestMultiError, or
|
||||||
|
// nil if none found.
|
||||||
|
func (m *PingRequest) ValidateAll() error {
|
||||||
|
return m.validate(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PingRequest) validate(all bool) error {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errors []error
|
||||||
|
|
||||||
|
// no validation rules for Value
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return PingRequestMultiError(errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PingRequestMultiError is an error wrapping multiple validation errors
|
||||||
|
// returned by PingRequest.ValidateAll() if the designated constraints aren't met.
|
||||||
|
type PingRequestMultiError []error
|
||||||
|
|
||||||
|
// Error returns a concatenation of all the error messages it wraps.
|
||||||
|
func (m PingRequestMultiError) Error() string {
|
||||||
|
var msgs []string
|
||||||
|
for _, err := range m {
|
||||||
|
msgs = append(msgs, err.Error())
|
||||||
|
}
|
||||||
|
return strings.Join(msgs, "; ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllErrors returns a list of validation violation errors.
|
||||||
|
func (m PingRequestMultiError) AllErrors() []error { return m }
|
||||||
|
|
||||||
|
// PingRequestValidationError is the validation error returned by
|
||||||
|
// PingRequest.Validate if the designated constraints aren't met.
|
||||||
|
type PingRequestValidationError struct {
|
||||||
|
field string
|
||||||
|
reason string
|
||||||
|
cause error
|
||||||
|
key bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field function returns field value.
|
||||||
|
func (e PingRequestValidationError) Field() string { return e.field }
|
||||||
|
|
||||||
|
// Reason function returns reason value.
|
||||||
|
func (e PingRequestValidationError) Reason() string { return e.reason }
|
||||||
|
|
||||||
|
// Cause function returns cause value.
|
||||||
|
func (e PingRequestValidationError) Cause() error { return e.cause }
|
||||||
|
|
||||||
|
// Key function returns key value.
|
||||||
|
func (e PingRequestValidationError) Key() bool { return e.key }
|
||||||
|
|
||||||
|
// ErrorName returns error name.
|
||||||
|
func (e PingRequestValidationError) ErrorName() string { return "PingRequestValidationError" }
|
||||||
|
|
||||||
|
// Error satisfies the builtin error interface
|
||||||
|
func (e PingRequestValidationError) Error() string {
|
||||||
|
cause := ""
|
||||||
|
if e.cause != nil {
|
||||||
|
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := ""
|
||||||
|
if e.key {
|
||||||
|
key = "key for "
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"invalid %sPingRequest.%s: %s%s",
|
||||||
|
key,
|
||||||
|
e.field,
|
||||||
|
e.reason,
|
||||||
|
cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = PingRequestValidationError{}
|
||||||
|
|
||||||
|
var _ interface {
|
||||||
|
Field() string
|
||||||
|
Reason() string
|
||||||
|
Key() bool
|
||||||
|
Cause() error
|
||||||
|
ErrorName() string
|
||||||
|
} = PingRequestValidationError{}
|
||||||
|
|
||||||
|
// Validate checks the field values on PingResponse with the rules defined in
|
||||||
|
// the proto definition for this message. If any rules are violated, the first
|
||||||
|
// error encountered is returned, or nil if there are no violations.
|
||||||
|
func (m *PingResponse) Validate() error {
|
||||||
|
return m.validate(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateAll checks the field values on PingResponse with the rules defined
|
||||||
|
// in the proto definition for this message. If any rules are violated, the
|
||||||
|
// result is a list of violation errors wrapped in PingResponseMultiError, or
|
||||||
|
// nil if none found.
|
||||||
|
func (m *PingResponse) ValidateAll() error {
|
||||||
|
return m.validate(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PingResponse) validate(all bool) error {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errors []error
|
||||||
|
|
||||||
|
// no validation rules for Value
|
||||||
|
|
||||||
|
// no validation rules for Counter
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return PingResponseMultiError(errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PingResponseMultiError is an error wrapping multiple validation errors
|
||||||
|
// returned by PingResponse.ValidateAll() if the designated constraints aren't met.
|
||||||
|
type PingResponseMultiError []error
|
||||||
|
|
||||||
|
// Error returns a concatenation of all the error messages it wraps.
|
||||||
|
func (m PingResponseMultiError) Error() string {
|
||||||
|
var msgs []string
|
||||||
|
for _, err := range m {
|
||||||
|
msgs = append(msgs, err.Error())
|
||||||
|
}
|
||||||
|
return strings.Join(msgs, "; ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllErrors returns a list of validation violation errors.
|
||||||
|
func (m PingResponseMultiError) AllErrors() []error { return m }
|
||||||
|
|
||||||
|
// PingResponseValidationError is the validation error returned by
|
||||||
|
// PingResponse.Validate if the designated constraints aren't met.
|
||||||
|
type PingResponseValidationError struct {
|
||||||
|
field string
|
||||||
|
reason string
|
||||||
|
cause error
|
||||||
|
key bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field function returns field value.
|
||||||
|
func (e PingResponseValidationError) Field() string { return e.field }
|
||||||
|
|
||||||
|
// Reason function returns reason value.
|
||||||
|
func (e PingResponseValidationError) Reason() string { return e.reason }
|
||||||
|
|
||||||
|
// Cause function returns cause value.
|
||||||
|
func (e PingResponseValidationError) Cause() error { return e.cause }
|
||||||
|
|
||||||
|
// Key function returns key value.
|
||||||
|
func (e PingResponseValidationError) Key() bool { return e.key }
|
||||||
|
|
||||||
|
// ErrorName returns error name.
|
||||||
|
func (e PingResponseValidationError) ErrorName() string { return "PingResponseValidationError" }
|
||||||
|
|
||||||
|
// Error satisfies the builtin error interface
|
||||||
|
func (e PingResponseValidationError) Error() string {
|
||||||
|
cause := ""
|
||||||
|
if e.cause != nil {
|
||||||
|
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := ""
|
||||||
|
if e.key {
|
||||||
|
key = "key for "
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"invalid %sPingResponse.%s: %s%s",
|
||||||
|
key,
|
||||||
|
e.field,
|
||||||
|
e.reason,
|
||||||
|
cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = PingResponseValidationError{}
|
||||||
|
|
||||||
|
var _ interface {
|
||||||
|
Field() string
|
||||||
|
Reason() string
|
||||||
|
Key() bool
|
||||||
|
Cause() error
|
||||||
|
ErrorName() string
|
||||||
|
} = PingResponseValidationError{}
|
30
proxy/testservice/test.proto
Normal file
30
proxy/testservice/test.proto
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package mwitkow.testproto;
|
||||||
|
|
||||||
|
option go_package="go.linka.cloud/grpc-toolkit/proxy/testservice";
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
message PingRequest {
|
||||||
|
string value = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PingResponse {
|
||||||
|
string value = 1;
|
||||||
|
int32 counter = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
service TestService {
|
||||||
|
rpc PingEmpty(google.protobuf.Empty) returns (PingResponse) {}
|
||||||
|
|
||||||
|
rpc Ping(PingRequest) returns (PingResponse) {}
|
||||||
|
|
||||||
|
rpc PingError(PingRequest) returns (google.protobuf.Empty) {}
|
||||||
|
|
||||||
|
rpc PingList(PingRequest) returns (stream PingResponse) {}
|
||||||
|
|
||||||
|
rpc PingStream(stream PingRequest) returns (stream PingResponse) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
61
proxy/testservice/test.swagger.json
Normal file
61
proxy/testservice/test.swagger.json
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"swagger": "2.0",
|
||||||
|
"info": {
|
||||||
|
"title": "proxy/testservice/test.proto",
|
||||||
|
"version": "version not set"
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"name": "TestService"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"paths": {},
|
||||||
|
"definitions": {
|
||||||
|
"protobufAny": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"@type": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": {}
|
||||||
|
},
|
||||||
|
"rpcStatus": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"details": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/protobufAny"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"testprotoPingResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"value": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"counter": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
272
proxy/testservice/test_grpc.pb.go
Normal file
272
proxy/testservice/test_grpc.pb.go
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// - protoc-gen-go-grpc v1.5.1
|
||||||
|
// - protoc v5.28.2
|
||||||
|
// source: proxy/testservice/test.proto
|
||||||
|
|
||||||
|
package testservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.64.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion9
|
||||||
|
|
||||||
|
const (
|
||||||
|
TestService_PingEmpty_FullMethodName = "/mwitkow.testproto.TestService/PingEmpty"
|
||||||
|
TestService_Ping_FullMethodName = "/mwitkow.testproto.TestService/Ping"
|
||||||
|
TestService_PingError_FullMethodName = "/mwitkow.testproto.TestService/PingError"
|
||||||
|
TestService_PingList_FullMethodName = "/mwitkow.testproto.TestService/PingList"
|
||||||
|
TestService_PingStream_FullMethodName = "/mwitkow.testproto.TestService/PingStream"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestServiceClient is the client API for TestService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type TestServiceClient interface {
|
||||||
|
PingEmpty(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*PingResponse, error)
|
||||||
|
Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error)
|
||||||
|
PingError(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
PingList(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[PingResponse], error)
|
||||||
|
PingStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[PingRequest, PingResponse], error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type testServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTestServiceClient(cc grpc.ClientConnInterface) TestServiceClient {
|
||||||
|
return &testServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testServiceClient) PingEmpty(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*PingResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(PingResponse)
|
||||||
|
err := c.cc.Invoke(ctx, TestService_PingEmpty_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testServiceClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(PingResponse)
|
||||||
|
err := c.cc.Invoke(ctx, TestService_Ping_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testServiceClient) PingError(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, TestService_PingError_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testServiceClient) PingList(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[PingResponse], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &TestService_ServiceDesc.Streams[0], TestService_PingList_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[PingRequest, PingResponse]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type TestService_PingListClient = grpc.ServerStreamingClient[PingResponse]
|
||||||
|
|
||||||
|
func (c *testServiceClient) PingStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[PingRequest, PingResponse], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &TestService_ServiceDesc.Streams[1], TestService_PingStream_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[PingRequest, PingResponse]{ClientStream: stream}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type TestService_PingStreamClient = grpc.BidiStreamingClient[PingRequest, PingResponse]
|
||||||
|
|
||||||
|
// TestServiceServer is the server API for TestService service.
|
||||||
|
// All implementations must embed UnimplementedTestServiceServer
|
||||||
|
// for forward compatibility.
|
||||||
|
type TestServiceServer interface {
|
||||||
|
PingEmpty(context.Context, *emptypb.Empty) (*PingResponse, error)
|
||||||
|
Ping(context.Context, *PingRequest) (*PingResponse, error)
|
||||||
|
PingError(context.Context, *PingRequest) (*emptypb.Empty, error)
|
||||||
|
PingList(*PingRequest, grpc.ServerStreamingServer[PingResponse]) error
|
||||||
|
PingStream(grpc.BidiStreamingServer[PingRequest, PingResponse]) error
|
||||||
|
mustEmbedUnimplementedTestServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedTestServiceServer must be embedded to have
|
||||||
|
// forward compatible implementations.
|
||||||
|
//
|
||||||
|
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||||
|
// pointer dereference when methods are called.
|
||||||
|
type UnimplementedTestServiceServer struct{}
|
||||||
|
|
||||||
|
func (UnimplementedTestServiceServer) PingEmpty(context.Context, *emptypb.Empty) (*PingResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method PingEmpty not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedTestServiceServer) Ping(context.Context, *PingRequest) (*PingResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedTestServiceServer) PingError(context.Context, *PingRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method PingError not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedTestServiceServer) PingList(*PingRequest, grpc.ServerStreamingServer[PingResponse]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method PingList not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedTestServiceServer) PingStream(grpc.BidiStreamingServer[PingRequest, PingResponse]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method PingStream not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedTestServiceServer) mustEmbedUnimplementedTestServiceServer() {}
|
||||||
|
func (UnimplementedTestServiceServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
|
// UnsafeTestServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to TestServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeTestServiceServer interface {
|
||||||
|
mustEmbedUnimplementedTestServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterTestServiceServer(s grpc.ServiceRegistrar, srv TestServiceServer) {
|
||||||
|
// If the following call pancis, it indicates UnimplementedTestServiceServer was
|
||||||
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
|
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||||
|
t.testEmbeddedByValue()
|
||||||
|
}
|
||||||
|
s.RegisterService(&TestService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _TestService_PingEmpty_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(TestServiceServer).PingEmpty(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: TestService_PingEmpty_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(TestServiceServer).PingEmpty(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _TestService_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(PingRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(TestServiceServer).Ping(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: TestService_Ping_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(TestServiceServer).Ping(ctx, req.(*PingRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _TestService_PingError_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(PingRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(TestServiceServer).PingError(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: TestService_PingError_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(TestServiceServer).PingError(ctx, req.(*PingRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _TestService_PingList_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(PingRequest)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(TestServiceServer).PingList(m, &grpc.GenericServerStream[PingRequest, PingResponse]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type TestService_PingListServer = grpc.ServerStreamingServer[PingResponse]
|
||||||
|
|
||||||
|
func _TestService_PingStream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
return srv.(TestServiceServer).PingStream(&grpc.GenericServerStream[PingRequest, PingResponse]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type TestService_PingStreamServer = grpc.BidiStreamingServer[PingRequest, PingResponse]
|
||||||
|
|
||||||
|
// TestService_ServiceDesc is the grpc.ServiceDesc for TestService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var TestService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "mwitkow.testproto.TestService",
|
||||||
|
HandlerType: (*TestServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "PingEmpty",
|
||||||
|
Handler: _TestService_PingEmpty_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "Ping",
|
||||||
|
Handler: _TestService_Ping_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "PingError",
|
||||||
|
Handler: _TestService_PingError_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "PingList",
|
||||||
|
Handler: _TestService_PingList_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "PingStream",
|
||||||
|
Handler: _TestService_PingStream_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: "proxy/testservice/test.proto",
|
||||||
|
}
|
322
proxy/testservice/test_vtproto.pb.go
Normal file
322
proxy/testservice/test_vtproto.pb.go
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
// Code generated by protoc-gen-go-vtproto. DO NOT EDIT.
|
||||||
|
// protoc-gen-go-vtproto version: v0.6.1-0.20240917153116-6f2963f01587
|
||||||
|
// source: proxy/testservice/test.proto
|
||||||
|
|
||||||
|
package testservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
fmt "fmt"
|
||||||
|
io "io"
|
||||||
|
|
||||||
|
protohelpers "github.com/planetscale/vtprotobuf/protohelpers"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *PingRequest) MarshalVT() (dAtA []byte, err error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
size := m.SizeVT()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PingRequest) MarshalToVT(dAtA []byte) (int, error) {
|
||||||
|
size := m.SizeVT()
|
||||||
|
return m.MarshalToSizedBufferVT(dAtA[:size])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PingRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||||
|
if m == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
i := len(dAtA)
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.unknownFields != nil {
|
||||||
|
i -= len(m.unknownFields)
|
||||||
|
copy(dAtA[i:], m.unknownFields)
|
||||||
|
}
|
||||||
|
if len(m.Value) > 0 {
|
||||||
|
i -= len(m.Value)
|
||||||
|
copy(dAtA[i:], m.Value)
|
||||||
|
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Value)))
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
}
|
||||||
|
return len(dAtA) - i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PingResponse) MarshalVT() (dAtA []byte, err error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
size := m.SizeVT()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PingResponse) MarshalToVT(dAtA []byte) (int, error) {
|
||||||
|
size := m.SizeVT()
|
||||||
|
return m.MarshalToSizedBufferVT(dAtA[:size])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PingResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||||
|
if m == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
i := len(dAtA)
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.unknownFields != nil {
|
||||||
|
i -= len(m.unknownFields)
|
||||||
|
copy(dAtA[i:], m.unknownFields)
|
||||||
|
}
|
||||||
|
if m.Counter != 0 {
|
||||||
|
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Counter))
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0x10
|
||||||
|
}
|
||||||
|
if len(m.Value) > 0 {
|
||||||
|
i -= len(m.Value)
|
||||||
|
copy(dAtA[i:], m.Value)
|
||||||
|
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Value)))
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
}
|
||||||
|
return len(dAtA) - i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PingRequest) SizeVT() (n int) {
|
||||||
|
if m == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.Value)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||||
|
}
|
||||||
|
n += len(m.unknownFields)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PingResponse) SizeVT() (n int) {
|
||||||
|
if m == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.Value)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||||
|
}
|
||||||
|
if m.Counter != 0 {
|
||||||
|
n += 1 + protohelpers.SizeOfVarint(uint64(m.Counter))
|
||||||
|
}
|
||||||
|
n += len(m.unknownFields)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PingRequest) UnmarshalVT(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return protohelpers.ErrIntOverflow
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: PingRequest: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: PingRequest: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return protohelpers.ErrIntOverflow
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return protohelpers.ErrInvalidLength
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return protohelpers.ErrInvalidLength
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Value = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||||
|
return protohelpers.ErrInvalidLength
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *PingResponse) UnmarshalVT(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return protohelpers.ErrIntOverflow
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: PingResponse: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: PingResponse: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return protohelpers.ErrIntOverflow
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return protohelpers.ErrInvalidLength
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return protohelpers.ErrInvalidLength
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Value = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Counter", wireType)
|
||||||
|
}
|
||||||
|
m.Counter = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return protohelpers.ErrIntOverflow
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.Counter |= int32(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||||
|
return protohelpers.ErrInvalidLength
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
158
proxy/testservice/testping.go
Normal file
158
proxy/testservice/testping.go
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
package testservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
returnHeader = "test-client-header"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestTestServiceServerImpl can be called to test the underlying TestServiceServer.
|
||||||
|
func TestTestServiceServerImpl(t *testing.T, client TestServiceClient) {
|
||||||
|
t.Run("Unary ping", func(t *testing.T) {
|
||||||
|
want := "hello, world"
|
||||||
|
hdr := metadata.MD{}
|
||||||
|
res, err := client.Ping(context.TODO(), &PingRequest{Value: want}, grpc.Header(&hdr))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("want no err; got %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
checkHeaders(t, hdr)
|
||||||
|
t.Logf("got %v (%d)", res.Value, res.Counter)
|
||||||
|
if got := res.Value; got != want {
|
||||||
|
t.Errorf("res.Value = %q; want %q", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Error ping", func(t *testing.T) {
|
||||||
|
_, err := client.PingError(context.TODO(), &PingRequest{})
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("want err; got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Server streaming ping", func(t *testing.T) {
|
||||||
|
want := "hello, world"
|
||||||
|
stream, err := client.PingList(context.TODO(), &PingRequest{Value: want})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("want no err; got %v", err)
|
||||||
|
if err := stream.CloseSend(); err != nil {
|
||||||
|
t.Fatalf("closing send channel: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hdr, err := stream.Header()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("reading headers: %v", err)
|
||||||
|
}
|
||||||
|
checkHeaders(t, hdr)
|
||||||
|
|
||||||
|
for {
|
||||||
|
res, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
checkTrailers(t, stream.Trailer())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Errorf("want no err; got %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("got %v (%d)", res.Value, res.Counter)
|
||||||
|
if got := res.Value; got != want {
|
||||||
|
t.Errorf("res.Value = %q; want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Bidirectional pinging", func(t *testing.T) {
|
||||||
|
want := "hello, world"
|
||||||
|
stream, err := client.PingStream(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("want no err; got %v", err)
|
||||||
|
if err := stream.CloseSend(); err != nil {
|
||||||
|
t.Fatalf("closing send channel: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
hdr, err := stream.Header()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("reading headers: %v", err)
|
||||||
|
}
|
||||||
|
checkHeaders(t, hdr)
|
||||||
|
close(d)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for i := 0; i < 25; i++ {
|
||||||
|
if err := stream.Send(&PingRequest{Value: want}); err != nil {
|
||||||
|
t.Errorf("want no err; got %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("receiving full duplex stream: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("got %v (%d)", res.Value, res.Counter)
|
||||||
|
if got := res.Value; got != want {
|
||||||
|
t.Errorf("res.Value = %q; want %q", got, want)
|
||||||
|
}
|
||||||
|
if got, want := res.Counter, int32(i); got != want {
|
||||||
|
t.Errorf("res.Counter = %d; want %d", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := stream.CloseSend(); err != nil {
|
||||||
|
t.Errorf("closing full duplex stream: %v", err)
|
||||||
|
}
|
||||||
|
<-d
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Unary ping with headers", func(t *testing.T) {
|
||||||
|
want := "hello, world"
|
||||||
|
req := &PingRequest{Value: want}
|
||||||
|
|
||||||
|
ctx := metadata.AppendToOutgoingContext(context.Background(), returnHeader, "I like turtles.")
|
||||||
|
inHeader := make(metadata.MD)
|
||||||
|
|
||||||
|
res, err := client.Ping(ctx, req, grpc.Header(&inHeader))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("want no err; got %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("got %v (%d)", res.Value, res.Counter)
|
||||||
|
if !reflect.DeepEqual(inHeader.Get(returnHeader), []string{"I like turtles."}) {
|
||||||
|
t.Errorf("did not receive correct return headers")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkTrailers(t *testing.T, md metadata.MD) {
|
||||||
|
vs := md.Get(PingTrailer)
|
||||||
|
if want, got := 1, len(vs); want != got {
|
||||||
|
t.Errorf("trailer %q not present", PingTrailer)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if want, got := []string{PingTrailerCts}, vs; !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("trailer mismatch; want %q, got %q", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkHeaders(t *testing.T, md metadata.MD) {
|
||||||
|
vs := md.Get(PingHeader)
|
||||||
|
if want, got := 1, len(vs); want != got {
|
||||||
|
t.Errorf("header %q not present", PingHeader)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if want, got := []string{PingHeaderCts}, vs; !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("header mismatch; want %q, got %q", want, got)
|
||||||
|
}
|
||||||
|
}
|
15
react/ui.go
15
react/ui.go
@ -15,7 +15,6 @@
|
|||||||
package react
|
package react
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
@ -24,14 +23,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewHandler(dir embed.FS, subpath string) (http.Handler, error) {
|
const (
|
||||||
if e := os.Getenv("REACT_ENDPOINT"); e != "" {
|
EndpointEnv = "REACT_ENDPOINT"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewHandler(dir fs.FS, subpath string) (http.Handler, error) {
|
||||||
|
if e := os.Getenv(EndpointEnv); e != "" {
|
||||||
return newProxy(e)
|
return newProxy(e)
|
||||||
}
|
}
|
||||||
return newStatic(dir, subpath)
|
return newStatic(dir, subpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStatic(dir embed.FS, subpath string) (http.Handler, error) {
|
func newStatic(dir fs.FS, subpath string) (http.Handler, error) {
|
||||||
s, err := fs.Sub(dir, subpath)
|
s, err := fs.Sub(dir, subpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -53,3 +56,7 @@ func newProxy(endpoint string) (http.Handler, error) {
|
|||||||
p := httputil.NewSingleHostReverseProxy(u)
|
p := httputil.NewSingleHostReverseProxy(u)
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DevEnv() bool {
|
||||||
|
return os.Getenv(EndpointEnv) != ""
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -17,8 +17,8 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"google.golang.org/grpc/resolver"
|
"google.golang.org/grpc/resolver"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/registry"
|
"go.linka.cloud/grpc-toolkit/registry"
|
||||||
resolver2 "go.linka.cloud/grpc/resolver"
|
resolver2 "go.linka.cloud/grpc-toolkit/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -122,7 +122,7 @@ func decode(record []string) (*mdnsTxt, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rbuf, err := ioutil.ReadAll(zr)
|
rbuf, err := io.ReadAll(zr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/registry"
|
"go.linka.cloud/grpc-toolkit/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRegistry(t *testing.T) {
|
func TestRegistry(t *testing.T) {
|
||||||
|
@ -194,7 +194,7 @@ func (s *Server) recv(c *net.UDPConn) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := s.parsePacket(buf[:n], from); err != nil {
|
if err := s.parsePacket(buf[:n], from); err != nil {
|
||||||
logrus.Errorf("[ERR] mdns: Failed to handle query: %v", err)
|
logrus.Errorf("mdns: Failed to handle query: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ func (s *Server) recv(c *net.UDPConn) {
|
|||||||
func (s *Server) parsePacket(packet []byte, from net.Addr) error {
|
func (s *Server) parsePacket(packet []byte, from net.Addr) error {
|
||||||
var msg dns.Msg
|
var msg dns.Msg
|
||||||
if err := msg.Unpack(packet); err != nil {
|
if err := msg.Unpack(packet); err != nil {
|
||||||
logrus.Errorf("[ERR] mdns: Failed to unpack packet: %v", err)
|
logrus.Errorf("mdns: Failed to unpack packet: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// TODO: This is a bit of a hack
|
// TODO: This is a bit of a hack
|
||||||
|
@ -5,8 +5,8 @@ import (
|
|||||||
|
|
||||||
"google.golang.org/grpc/resolver"
|
"google.golang.org/grpc/resolver"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/registry"
|
"go.linka.cloud/grpc-toolkit/registry"
|
||||||
resolver2 "go.linka.cloud/grpc/resolver"
|
resolver2 "go.linka.cloud/grpc-toolkit/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New() registry.Registry {
|
func New() registry.Registry {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"google.golang.org/grpc/resolver"
|
"google.golang.org/grpc/resolver"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/registry"
|
"go.linka.cloud/grpc-toolkit/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(reg registry.Registry) resolver.Builder {
|
func New(reg registry.Registry) resolver.Builder {
|
||||||
@ -35,11 +35,11 @@ type resolvr struct {
|
|||||||
|
|
||||||
func (r *resolvr) run() {
|
func (r *resolvr) run() {
|
||||||
if r.reg.String() == "noop" {
|
if r.reg.String() == "noop" {
|
||||||
r.cc.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: r.target.Endpoint}}})
|
r.cc.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: r.target.Endpoint()}}})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var name, version string
|
var name, version string
|
||||||
parts := strings.Split(r.target.Endpoint, ":")
|
parts := strings.Split(r.target.Endpoint(), ":")
|
||||||
name = parts[0]
|
name = parts[0]
|
||||||
if len(parts) > 1 {
|
if len(parts) > 1 {
|
||||||
version = parts[1]
|
version = parts[1]
|
||||||
@ -57,7 +57,7 @@ func (r *resolvr) run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
r.cc.UpdateState(resolver.State{Addresses: r.addrs})
|
r.cc.UpdateState(resolver.State{Addresses: r.addrs})
|
||||||
w, err := r.reg.Watch(registry.WatchService(r.target.Endpoint))
|
w, err := r.reg.Watch(registry.WatchService(r.target.Endpoint()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ func (r *resolvr) run() {
|
|||||||
|
|
||||||
func (r *resolvr) ResolveNow(options resolver.ResolveNowOptions) {
|
func (r *resolvr) ResolveNow(options resolver.ResolveNowOptions) {
|
||||||
if r.reg.String() == "noop" {
|
if r.reg.String() == "noop" {
|
||||||
r.cc.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: r.target.Endpoint}}})
|
r.cc.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: r.target.Endpoint()}}})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
env "github.com/caitlinelfring/go-env-default"
|
"github.com/caitlinelfring/go-env-default"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,6 +17,10 @@ const (
|
|||||||
caCert = "ca-cert"
|
caCert = "ca-cert"
|
||||||
serverCert = "server-cert"
|
serverCert = "server-cert"
|
||||||
serverKey = "server-key"
|
serverKey = "server-key"
|
||||||
|
|
||||||
|
clientCACert = "client-ca-cert"
|
||||||
|
clientCert = "client-cert"
|
||||||
|
clientKey = "client-key"
|
||||||
)
|
)
|
||||||
|
|
||||||
var u = strings.ToUpper
|
var u = strings.ToUpper
|
||||||
@ -37,13 +41,19 @@ func NewFlagSet() (*pflag.FlagSet, Option) {
|
|||||||
flags.StringVar(&optCACert, caCert, "", "Path to Root CA certificate"+flagEnv(caCert))
|
flags.StringVar(&optCACert, caCert, "", "Path to Root CA certificate"+flagEnv(caCert))
|
||||||
flags.StringVar(&optCert, serverCert, "", "Path to Server certificate"+flagEnv(serverCert))
|
flags.StringVar(&optCert, serverCert, "", "Path to Server certificate"+flagEnv(serverCert))
|
||||||
flags.StringVar(&optKey, serverKey, "", "Path to Server key"+flagEnv(serverKey))
|
flags.StringVar(&optKey, serverKey, "", "Path to Server key"+flagEnv(serverKey))
|
||||||
|
flags.StringVar(&optCACert, clientCACert, "", "Path to Root CA certificate"+flagEnv(clientCACert))
|
||||||
|
flags.StringVar(&optCert, clientCert, "", "Path to Client certificate"+flagEnv(clientCert))
|
||||||
|
flags.StringVar(&optKey, clientKey, "", "Path to Client key"+flagEnv(clientKey))
|
||||||
return flags, func(o *options) {
|
return flags, func(o *options) {
|
||||||
o.address = optAddress
|
o.address = optAddress
|
||||||
o.secure = !optInsecure
|
o.secure = !optInsecure
|
||||||
o.reflection = optReflection
|
o.reflection = optReflection
|
||||||
// o.caCert = optCACert
|
o.caCert = optCACert
|
||||||
// o.cert = optCert
|
o.cert = optCert
|
||||||
// o.key = optKey
|
o.key = optKey
|
||||||
|
o.clientCACert = optCACert
|
||||||
|
o.clientCert = optCert
|
||||||
|
o.clientKey = optKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,13 +18,13 @@ func (s *service) gateway(opts ...runtime.ServeMuxOption) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
mux := runtime.NewServeMux(append(defaultGatewayOptions, opts...)...)
|
mux := runtime.NewServeMux(append(defaultGatewayOptions, opts...)...)
|
||||||
if err := s.opts.gateway(s.opts.ctx, mux, s.inproc); err != nil {
|
if err := s.opts.gateway(s.opts.ctx, mux, s.wrapCC()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if s.opts.gatewayPrefix != "" {
|
if s.opts.gatewayPrefix != "" {
|
||||||
s.opts.mux.Handle(s.opts.gatewayPrefix+"/", http.StripPrefix(s.opts.gatewayPrefix, wsproxy.WebsocketProxy(mux)))
|
s.lazyMux().Handle(s.opts.gatewayPrefix+"/", http.StripPrefix(s.opts.gatewayPrefix, wsproxy.WebsocketProxy(mux)))
|
||||||
} else {
|
} else {
|
||||||
s.opts.mux.Handle("/", wsproxy.WebsocketProxy(mux))
|
s.lazyMux().Handle("/", wsproxy.WebsocketProxy(mux))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
72
service/interceptors.go
Normal file
72
service/interceptors.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/fullstorydev/grpchan/inprocgrpc"
|
||||||
|
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
insecure2 "google.golang.org/grpc/credentials/insecure"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
|
"go.linka.cloud/grpc-toolkit/interceptors/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
func md(opts *options) interceptors.Interceptors {
|
||||||
|
var pairs []string
|
||||||
|
if opts.name != "" {
|
||||||
|
pairs = append(pairs, "grpc-service-name", opts.name)
|
||||||
|
}
|
||||||
|
if opts.version != "" {
|
||||||
|
pairs = append(pairs, "grpc-service-version", opts.version)
|
||||||
|
}
|
||||||
|
if len(pairs) != 0 {
|
||||||
|
return metadata.NewInterceptors(pairs...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) wrapCC() grpc.ClientConnInterface {
|
||||||
|
c, err := grpc.NewClient("internal", grpc.WithTransportCredentials(insecure2.NewCredentials()))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to create fake grpc client: %v", err))
|
||||||
|
}
|
||||||
|
w := &client{ch: s.inproc, c: c}
|
||||||
|
if len(s.opts.unaryClientInterceptors) != 0 {
|
||||||
|
w.ui = grpc_middleware.ChainUnaryClient(s.opts.unaryClientInterceptors...)
|
||||||
|
}
|
||||||
|
if len(s.opts.streamClientInterceptors) != 0 {
|
||||||
|
w.si = grpc_middleware.ChainStreamClient(s.opts.streamClientInterceptors...)
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
ui grpc.UnaryClientInterceptor
|
||||||
|
si grpc.StreamClientInterceptor
|
||||||
|
ch *inprocgrpc.Channel
|
||||||
|
c *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) Invoke(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error {
|
||||||
|
if c.ui != nil {
|
||||||
|
return c.ui(ctx, method, args, reply, c.c, c.invoke, opts...)
|
||||||
|
}
|
||||||
|
return c.ch.Invoke(ctx, method, args, reply, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) NewStream(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||||
|
if c.si != nil {
|
||||||
|
return c.si(ctx, desc, c.c, method, c.stream, opts...)
|
||||||
|
}
|
||||||
|
return c.ch.NewStream(ctx, desc, method, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) invoke(ctx context.Context, method string, req, reply any, _ *grpc.ClientConn, opts ...grpc.CallOption) error {
|
||||||
|
return c.ch.Invoke(ctx, method, req, reply, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) stream(ctx context.Context, desc *grpc.StreamDesc, _ *grpc.ClientConn, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||||
|
return c.ch.NewStream(ctx, desc, method, opts...)
|
||||||
|
}
|
@ -5,52 +5,24 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/fs"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||||
"github.com/improbable-eng/grpc-web/go/grpcweb"
|
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
"go.uber.org/multierr"
|
"github.com/traefik/grpc-web/go/grpcweb"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/certs"
|
"go.linka.cloud/grpc-toolkit/certs"
|
||||||
"go.linka.cloud/grpc/interceptors"
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
||||||
"go.linka.cloud/grpc/registry"
|
"go.linka.cloud/grpc-toolkit/registry"
|
||||||
"go.linka.cloud/grpc/transport"
|
"go.linka.cloud/grpc-toolkit/transport"
|
||||||
"go.linka.cloud/grpc/utils/addr"
|
"go.linka.cloud/grpc-toolkit/utils/addr"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
var _ Options = (*options)(nil)
|
||||||
GLOBAL OPTIONS:
|
|
||||||
--client value Client for go-micro; rpc [$MICRO_CLIENT]
|
|
||||||
--client_request_timeout value Sets the client request timeout. e.g 500ms, 5s, 1m. Default: 5s [$MICRO_CLIENT_REQUEST_TIMEOUT]
|
|
||||||
--client_retries value Sets the client retries. Default: 1 (default: 1) [$MICRO_CLIENT_RETRIES]
|
|
||||||
--client_pool_size value Sets the client connection pool size. Default: 1 (default: 0) [$MICRO_CLIENT_POOL_SIZE]
|
|
||||||
--client_pool_ttl value Sets the client connection pool ttl. e.g 500ms, 5s, 1m. Default: 1m [$MICRO_CLIENT_POOL_TTL]
|
|
||||||
--help, -h show help
|
|
||||||
|
|
||||||
--secure SECURE
|
|
||||||
--ca_cert CA_CERT
|
|
||||||
--server_cert SERVER_CERT
|
|
||||||
--server_key SERVER_KEY
|
|
||||||
|
|
||||||
--register_ttl REGISTER_TTL
|
|
||||||
--register_interval REGISTER_INTERVAL
|
|
||||||
|
|
||||||
--server_address SERVER_ADDRESS
|
|
||||||
--server_name SERVER_NAME
|
|
||||||
|
|
||||||
--broker BROKER
|
|
||||||
--broker_address BROKER_ADDRESS
|
|
||||||
|
|
||||||
--registry REGISTRY
|
|
||||||
--registry_address REGISTRY_ADDRESS
|
|
||||||
|
|
||||||
--db_path DB_PATH
|
|
||||||
*/
|
|
||||||
|
|
||||||
type RegisterGatewayFunc func(ctx context.Context, mux *runtime.ServeMux, cc grpc.ClientConnInterface) error
|
type RegisterGatewayFunc func(ctx context.Context, mux *runtime.ServeMux, cc grpc.ClientConnInterface) error
|
||||||
|
|
||||||
@ -66,13 +38,14 @@ type Options interface {
|
|||||||
CACert() string
|
CACert() string
|
||||||
Cert() string
|
Cert() string
|
||||||
Key() string
|
Key() string
|
||||||
|
ClientCACert() string
|
||||||
|
ClientCert() string
|
||||||
|
ClientKey() string
|
||||||
TLSConfig() *tls.Config
|
TLSConfig() *tls.Config
|
||||||
Secure() bool
|
Secure() bool
|
||||||
|
|
||||||
Registry() registry.Registry
|
Registry() registry.Registry
|
||||||
|
|
||||||
DB() *gorm.DB
|
|
||||||
|
|
||||||
BeforeStart() []func() error
|
BeforeStart() []func() error
|
||||||
AfterStart() []func() error
|
AfterStart() []func() error
|
||||||
BeforeStop() []func() error
|
BeforeStop() []func() error
|
||||||
@ -97,6 +70,9 @@ type Options interface {
|
|||||||
|
|
||||||
// TODO(adphi): metrics + tracing
|
// TODO(adphi): metrics + tracing
|
||||||
|
|
||||||
|
WithoutCmux() bool
|
||||||
|
ProxyProtocol() bool
|
||||||
|
|
||||||
Default()
|
Default()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +94,6 @@ func (o *options) Default() {
|
|||||||
if o.transport == nil {
|
if o.transport == nil {
|
||||||
o.transport = &grpc.Server{}
|
o.transport = &grpc.Server{}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Option func(*options)
|
type Option func(*options)
|
||||||
@ -157,6 +132,15 @@ func WithAddress(addr string) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithListener specifies a listener for the service.
|
||||||
|
// It can be used to specify a custom listener.
|
||||||
|
// This will override the WithAddress and WithTLSConfig options
|
||||||
|
func WithListener(lis net.Listener) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.lis = lis
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WithReflection(r bool) Option {
|
func WithReflection(r bool) Option {
|
||||||
return func(o *options) {
|
return func(o *options) {
|
||||||
o.reflection = r
|
o.reflection = r
|
||||||
@ -199,11 +183,21 @@ func WithKey(path string) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithDB(dialect string, args ...interface{}) Option {
|
func WithClientCACert(path string) Option {
|
||||||
db, err := gorm.Open(dialect, args...)
|
|
||||||
return func(o *options) {
|
return func(o *options) {
|
||||||
o.db = db
|
o.clientCACert = path
|
||||||
o.error = multierr.Append(o.error, err)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithClientCert(path string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.clientCert = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithClientKey(path string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.clientKey = path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,22 +346,49 @@ func WithGatewayOpts(opts ...runtime.ServeMuxOption) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithReactUI add static single page app serving to the http server
|
||||||
|
// subpath is the path in the read-only file embed.FS to use as root to serve
|
||||||
|
// static content
|
||||||
|
func WithReactUI(fs fs.FS, subpath string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.reactUI = fs
|
||||||
|
o.reactUISubPath = subpath
|
||||||
|
o.hasReactUI = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithoutCmux disables the use of cmux for http support to instead use grpc.Server.ServeHTTP method when http support is enabled
|
||||||
|
func WithoutCmux() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.withoutCmux = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithProxyProtocol(addrs ...string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.proxyProtocol = true
|
||||||
|
o.proxyProtocolAddrs = addrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
name string
|
name string
|
||||||
version string
|
version string
|
||||||
address string
|
address string
|
||||||
|
lis net.Listener
|
||||||
|
|
||||||
reflection bool
|
reflection bool
|
||||||
health bool
|
health bool
|
||||||
|
|
||||||
secure bool
|
secure bool
|
||||||
caCert string
|
caCert string
|
||||||
cert string
|
cert string
|
||||||
key string
|
key string
|
||||||
tlsConfig *tls.Config
|
clientCACert string
|
||||||
|
clientCert string
|
||||||
db *gorm.DB
|
clientKey string
|
||||||
|
tlsConfig *tls.Config
|
||||||
|
|
||||||
transport transport.Transport
|
transport transport.Transport
|
||||||
registry registry.Registry
|
registry registry.Registry
|
||||||
@ -394,8 +415,15 @@ type options struct {
|
|||||||
gatewayOpts []runtime.ServeMuxOption
|
gatewayOpts []runtime.ServeMuxOption
|
||||||
cors cors.Options
|
cors cors.Options
|
||||||
|
|
||||||
error error
|
reactUI fs.FS
|
||||||
gatewayPrefix string
|
reactUISubPath string
|
||||||
|
hasReactUI bool
|
||||||
|
|
||||||
|
error error
|
||||||
|
gatewayPrefix string
|
||||||
|
withoutCmux bool
|
||||||
|
proxyProtocol bool
|
||||||
|
proxyProtocolAddrs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *options) Name() string {
|
func (o *options) Name() string {
|
||||||
@ -438,6 +466,18 @@ func (o *options) Key() string {
|
|||||||
return o.key
|
return o.key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *options) ClientCACert() string {
|
||||||
|
return o.clientCACert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *options) ClientCert() string {
|
||||||
|
return o.clientCert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *options) ClientKey() string {
|
||||||
|
return o.clientKey
|
||||||
|
}
|
||||||
|
|
||||||
func (o *options) TLSConfig() *tls.Config {
|
func (o *options) TLSConfig() *tls.Config {
|
||||||
return o.tlsConfig
|
return o.tlsConfig
|
||||||
}
|
}
|
||||||
@ -446,10 +486,6 @@ func (o *options) Secure() bool {
|
|||||||
return o.secure
|
return o.secure
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *options) DB() *gorm.DB {
|
|
||||||
return o.db
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *options) BeforeStart() []func() error {
|
func (o *options) BeforeStart() []func() error {
|
||||||
return o.beforeStart
|
return o.beforeStart
|
||||||
}
|
}
|
||||||
@ -518,10 +554,19 @@ func (o *options) GatewayOpts() []runtime.ServeMuxOption {
|
|||||||
return o.gatewayOpts
|
return o.gatewayOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *options) WithoutCmux() bool {
|
||||||
|
return o.withoutCmux
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *options) ProxyProtocol() bool {
|
||||||
|
return o.proxyProtocol
|
||||||
|
}
|
||||||
|
|
||||||
func (o *options) parseTLSConfig() error {
|
func (o *options) parseTLSConfig() error {
|
||||||
if o.tlsConfig != nil {
|
if o.tlsConfig != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
nextProtos := []string{"h2", "h2c", "http/1.1", "acme-tls/1"}
|
||||||
if !o.hasTLSConfig() {
|
if !o.hasTLSConfig() {
|
||||||
if !o.secure {
|
if !o.secure {
|
||||||
return nil
|
return nil
|
||||||
@ -549,10 +594,11 @@ func (o *options) parseTLSConfig() error {
|
|||||||
o.tlsConfig = &tls.Config{
|
o.tlsConfig = &tls.Config{
|
||||||
Certificates: []tls.Certificate{cert},
|
Certificates: []tls.Certificate{cert},
|
||||||
ClientAuth: tls.NoClientCert,
|
ClientAuth: tls.NoClientCert,
|
||||||
|
NextProtos: nextProtos,
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
caCert, err := ioutil.ReadFile(o.caCert)
|
caCert, err := os.ReadFile(o.caCert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -568,10 +614,34 @@ func (o *options) parseTLSConfig() error {
|
|||||||
o.tlsConfig = &tls.Config{
|
o.tlsConfig = &tls.Config{
|
||||||
Certificates: []tls.Certificate{cert},
|
Certificates: []tls.Certificate{cert},
|
||||||
RootCAs: caCertPool,
|
RootCAs: caCertPool,
|
||||||
|
NextProtos: nextProtos,
|
||||||
}
|
}
|
||||||
|
if !o.hasClientTLSConfig() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
clientCACert, err := os.ReadFile(o.clientCACert)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
clientCACertPool := x509.NewCertPool()
|
||||||
|
ok = clientCACertPool.AppendCertsFromPEM(clientCACert)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("failed to load Client CA Cert from %s", o.clientCACert)
|
||||||
|
}
|
||||||
|
clientCert, err := tls.LoadX509KeyPair(o.clientCert, o.clientKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
o.tlsConfig.ClientCAs = clientCACertPool
|
||||||
|
o.tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||||
|
o.tlsConfig.Certificates = append(o.tlsConfig.Certificates, clientCert)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *options) hasTLSConfig() bool {
|
func (o *options) hasTLSConfig() bool {
|
||||||
return o.caCert != "" && o.cert != "" && o.key != "" && o.tlsConfig == nil
|
return o.caCert != "" && o.cert != "" && o.tlsConfig == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *options) hasClientTLSConfig() bool {
|
||||||
|
return o.clientCACert != "" && o.clientCert != "" && o.clientKey != ""
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/registry"
|
"go.linka.cloud/grpc-toolkit/registry"
|
||||||
"go.linka.cloud/grpc/utils/addr"
|
"go.linka.cloud/grpc-toolkit/utils/addr"
|
||||||
"go.linka.cloud/grpc/utils/backoff"
|
"go.linka.cloud/grpc-toolkit/utils/backoff"
|
||||||
net2 "go.linka.cloud/grpc/utils/net"
|
net2 "go.linka.cloud/grpc-toolkit/utils/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *service) register() error {
|
func (s *service) register() error {
|
||||||
|
@ -17,28 +17,31 @@ import (
|
|||||||
"github.com/fullstorydev/grpchan/inprocgrpc"
|
"github.com/fullstorydev/grpchan/inprocgrpc"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"github.com/justinas/alice"
|
"github.com/justinas/alice"
|
||||||
|
"github.com/pires/go-proxyproto"
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/soheilhy/cmux"
|
"github.com/soheilhy/cmux"
|
||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/net/http2/h2c"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/health"
|
"google.golang.org/grpc/health"
|
||||||
"google.golang.org/grpc/health/grpc_health_v1"
|
"google.golang.org/grpc/health/grpc_health_v1"
|
||||||
greflect "google.golang.org/grpc/reflection"
|
greflect "google.golang.org/grpc/reflection"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/interceptors/metadata"
|
"go.linka.cloud/grpc-toolkit/internal/injectlogger"
|
||||||
"go.linka.cloud/grpc/registry"
|
"go.linka.cloud/grpc-toolkit/logger"
|
||||||
"go.linka.cloud/grpc/registry/noop"
|
"go.linka.cloud/grpc-toolkit/registry"
|
||||||
|
"go.linka.cloud/grpc-toolkit/registry/noop"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
greflect.GRPCServer
|
greflect.GRPCServer
|
||||||
|
|
||||||
Options() Options
|
Options() Options
|
||||||
DB() *gorm.DB
|
|
||||||
Start() error
|
Start() error
|
||||||
|
Serve(lis net.Listener) error
|
||||||
Stop() error
|
Stop() error
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
@ -59,8 +62,11 @@ type service struct {
|
|||||||
inproc *inprocgrpc.Channel
|
inproc *inprocgrpc.Channel
|
||||||
services map[string]*serviceInfo
|
services map[string]*serviceInfo
|
||||||
|
|
||||||
|
healthServer *health.Server
|
||||||
|
|
||||||
id string
|
id string
|
||||||
regSvc *registry.Service
|
regSvc *registry.Service
|
||||||
|
o sync.Once
|
||||||
closed chan struct{}
|
closed chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,32 +82,41 @@ func newService(opts ...Option) (*service, error) {
|
|||||||
for _, f := range opts {
|
for _, f := range opts {
|
||||||
f(s.opts)
|
f(s.opts)
|
||||||
}
|
}
|
||||||
if s.opts.name != "" {
|
s.opts.ctx, s.cancel = context.WithCancel(s.opts.ctx)
|
||||||
i := metadata.NewInterceptors("grpc-service-name", s.opts.name)
|
|
||||||
s.opts.unaryServerInterceptors = append([]grpc.UnaryServerInterceptor{i.UnaryServerInterceptor()}, s.opts.unaryServerInterceptors...)
|
md := md(s.opts)
|
||||||
s.opts.unaryClientInterceptors = append([]grpc.UnaryClientInterceptor{i.UnaryClientInterceptor()}, s.opts.unaryClientInterceptors...)
|
if md != nil {
|
||||||
s.opts.streamServerInterceptors = append([]grpc.StreamServerInterceptor{i.StreamServerInterceptor()}, s.opts.streamServerInterceptors...)
|
s.opts.unaryServerInterceptors = append([]grpc.UnaryServerInterceptor{
|
||||||
s.opts.streamClientInterceptors = append([]grpc.StreamClientInterceptor{i.StreamClientInterceptor()}, s.opts.streamClientInterceptors...)
|
md.UnaryServerInterceptor(),
|
||||||
}
|
injectlogger.New(s.opts.ctx).UnaryServerInterceptor()},
|
||||||
if s.opts.version != "" {
|
s.opts.unaryServerInterceptors...,
|
||||||
i := metadata.NewInterceptors("grpc-service-version", s.opts.version)
|
)
|
||||||
s.opts.unaryServerInterceptors = append([]grpc.UnaryServerInterceptor{i.UnaryServerInterceptor()}, s.opts.unaryServerInterceptors...)
|
s.opts.unaryClientInterceptors = append([]grpc.UnaryClientInterceptor{
|
||||||
s.opts.unaryClientInterceptors = append([]grpc.UnaryClientInterceptor{i.UnaryClientInterceptor()}, s.opts.unaryClientInterceptors...)
|
md.UnaryClientInterceptor(),
|
||||||
s.opts.streamServerInterceptors = append([]grpc.StreamServerInterceptor{i.StreamServerInterceptor()}, s.opts.streamServerInterceptors...)
|
injectlogger.New(s.opts.ctx).UnaryClientInterceptor()},
|
||||||
s.opts.streamClientInterceptors = append([]grpc.StreamClientInterceptor{i.StreamClientInterceptor()}, s.opts.streamClientInterceptors...)
|
s.opts.unaryClientInterceptors...,
|
||||||
}
|
)
|
||||||
if s.opts.mux == nil {
|
s.opts.streamServerInterceptors = append([]grpc.StreamServerInterceptor{
|
||||||
s.opts.mux = http.NewServeMux()
|
md.StreamServerInterceptor(),
|
||||||
|
injectlogger.New(s.opts.ctx).StreamServerInterceptor()},
|
||||||
|
s.opts.streamServerInterceptors...,
|
||||||
|
)
|
||||||
|
s.opts.streamClientInterceptors = append([]grpc.StreamClientInterceptor{
|
||||||
|
md.StreamClientInterceptor(),
|
||||||
|
injectlogger.New(s.opts.ctx).StreamClientInterceptor()},
|
||||||
|
s.opts.streamClientInterceptors...,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.opts.error != nil {
|
if s.opts.error != nil {
|
||||||
return nil, s.opts.error
|
return nil, s.opts.error
|
||||||
}
|
}
|
||||||
s.opts.ctx, s.cancel = context.WithCancel(s.opts.ctx)
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-s.opts.ctx.Done():
|
case <-s.opts.ctx.Done():
|
||||||
s.Stop()
|
s.Stop()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -128,11 +143,15 @@ func newService(opts ...Option) (*service, error) {
|
|||||||
greflect.Register(s.server)
|
greflect.Register(s.server)
|
||||||
}
|
}
|
||||||
if s.opts.health {
|
if s.opts.health {
|
||||||
s.registerService(&grpc_health_v1.Health_ServiceDesc, health.NewServer())
|
s.healthServer = health.NewServer()
|
||||||
|
s.registerService(&grpc_health_v1.Health_ServiceDesc, s.healthServer)
|
||||||
}
|
}
|
||||||
if err := s.gateway(s.opts.gatewayOpts...); err != nil {
|
if err := s.gateway(s.opts.gatewayOpts...); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := s.reactApp(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
// we do not configure grpc web here as the grpc handlers are not yet registered
|
// we do not configure grpc web here as the grpc handlers are not yet registered
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
@ -141,49 +160,75 @@ func (s *service) Options() Options {
|
|||||||
return s.opts
|
return s.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) DB() *gorm.DB {
|
func (s *service) start() (*errgroup.Group, error) {
|
||||||
return s.opts.db
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) run() error {
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
s.closed = make(chan struct{})
|
s.closed = make(chan struct{})
|
||||||
|
|
||||||
// configure grpc web now that we are ready to go
|
// configure grpc web now that we are ready to go
|
||||||
if err := s.grpcWeb(s.opts.grpcWebOpts...); err != nil {
|
if err := s.grpcWeb(s.opts.grpcWebOpts...); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lis, err := net.Listen("tcp", s.opts.address)
|
network := "tcp"
|
||||||
if err != nil {
|
if strings.HasPrefix(s.opts.address, "unix://") {
|
||||||
return err
|
network = "unix"
|
||||||
}
|
s.opts.address = strings.TrimPrefix(s.opts.address, "unix://")
|
||||||
if s.opts.tlsConfig != nil {
|
|
||||||
lis = tls.NewListener(lis, s.opts.tlsConfig)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.opts.address = lis.Addr().String()
|
if s.opts.lis == nil {
|
||||||
|
lis, err := net.Listen(network, s.opts.address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if s.opts.tlsConfig != nil {
|
||||||
|
lis = tls.NewListener(lis, s.opts.tlsConfig)
|
||||||
|
}
|
||||||
|
s.opts.lis = lis
|
||||||
|
s.opts.address = lis.Addr().String()
|
||||||
|
} else {
|
||||||
|
s.opts.address = s.opts.lis.Addr().String()
|
||||||
|
}
|
||||||
|
|
||||||
mux := cmux.New(lis)
|
if s.opts.proxyProtocol {
|
||||||
mux.SetReadTimeout(5 * time.Second)
|
p := func(upstream net.Addr) (proxyproto.Policy, error) {
|
||||||
|
u, _, err := net.SplitHostPort(upstream.String())
|
||||||
gLis := mux.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"))
|
if err != nil {
|
||||||
hList := mux.Match(cmux.Any())
|
return proxyproto.REJECT, err
|
||||||
|
}
|
||||||
|
ip := net.ParseIP(u)
|
||||||
|
if ip == nil {
|
||||||
|
return proxyproto.REJECT, fmt.Errorf("proxyproto: invalid IP address")
|
||||||
|
}
|
||||||
|
if ip.IsPrivate() || ip.IsLoopback() {
|
||||||
|
return proxyproto.USE, nil
|
||||||
|
}
|
||||||
|
return proxyproto.REJECT, nil
|
||||||
|
}
|
||||||
|
if len(s.opts.proxyProtocolAddrs) > 0 {
|
||||||
|
var err error
|
||||||
|
p, err = proxyproto.StrictWhiteListPolicy(s.opts.proxyProtocolAddrs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.opts.lis = &proxyproto.Listener{
|
||||||
|
Listener: s.opts.lis,
|
||||||
|
Policy: p,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for i := range s.opts.beforeStart {
|
for i := range s.opts.beforeStart {
|
||||||
if err := s.opts.beforeStart[i](); err != nil {
|
if err := s.opts.beforeStart[i](); err != nil {
|
||||||
s.mu.Unlock()
|
return nil, err
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.register(); err != nil {
|
if err := s.register(); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
s.running = true
|
s.running = true
|
||||||
|
|
||||||
errs := make(chan error, 3)
|
|
||||||
|
|
||||||
if reflect.DeepEqual(s.opts.cors, cors.Options{}) {
|
if reflect.DeepEqual(s.opts.cors, cors.Options{}) {
|
||||||
s.opts.cors = cors.Options{
|
s.opts.cors = cors.Options{
|
||||||
AllowedHeaders: []string{"*"},
|
AllowedHeaders: []string{"*"},
|
||||||
@ -200,59 +245,135 @@ func (s *service) run() error {
|
|||||||
AllowCredentials: true,
|
AllowCredentials: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hServer := &http.Server{
|
|
||||||
Handler: alice.New(s.opts.middlewares...).Then(cors.New(s.opts.cors).Handler(s.opts.mux)),
|
fn := s.runWithCmux
|
||||||
|
if s.opts.withoutCmux || s.opts.mux == nil {
|
||||||
|
fn = s.runWithoutCmux
|
||||||
}
|
}
|
||||||
if s.opts.Gateway() || s.opts.grpcWeb {
|
|
||||||
go func() {
|
g, ctx := errgroup.WithContext(s.opts.ctx)
|
||||||
errs <- hServer.Serve(hList)
|
if err := fn(ctx, g); err != nil {
|
||||||
hServer.Shutdown(s.opts.ctx)
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.healthServer != nil {
|
||||||
|
for k := range s.services {
|
||||||
|
s.healthServer.SetServingStatus(k, grpc_health_v1.HealthCheckResponse_SERVING)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
for k := range s.services {
|
||||||
|
s.healthServer.SetServingStatus(k, grpc_health_v1.HealthCheckResponse_NOT_SERVING)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
go func() {
|
|
||||||
errs <- s.server.Serve(gLis)
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
if err := mux.Serve(); err != nil {
|
|
||||||
// TODO(adphi): find more elegant solution
|
|
||||||
if ignoreMuxError(err) {
|
|
||||||
errs <- nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
errs <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
errs <- nil
|
|
||||||
}()
|
|
||||||
for i := range s.opts.afterStart {
|
for i := range s.opts.afterStart {
|
||||||
if err := s.opts.afterStart[i](); err != nil {
|
if err := s.opts.afterStart[i](); err != nil {
|
||||||
s.mu.Unlock()
|
return nil, err
|
||||||
s.Stop()
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.mu.Unlock()
|
return g, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) run() error {
|
||||||
|
g, err := s.start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
sigs := s.notify()
|
sigs := s.notify()
|
||||||
|
|
||||||
|
errs := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
errs <- g.Wait()
|
||||||
|
}()
|
||||||
select {
|
select {
|
||||||
case sig := <-sigs:
|
case sig := <-sigs:
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
logrus.Warnf("received %v", sig)
|
logger.C(s.opts.ctx).Warnf("received %v", sig)
|
||||||
return s.Close()
|
return s.Close()
|
||||||
case err := <-errs:
|
case err := <-errs:
|
||||||
if err != nil && !ignoreMuxError(err) {
|
if !isMuxError(err) {
|
||||||
logrus.Error(err)
|
logger.C(s.opts.ctx).Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) runWithoutCmux(ctx context.Context, g *errgroup.Group) error {
|
||||||
|
if s.opts.mux != nil {
|
||||||
|
handler := alice.New(s.opts.middlewares...).Then(cors.New(s.opts.cors).Handler(s.opts.mux))
|
||||||
|
hServer := &http.Server{
|
||||||
|
Handler: h2c.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.ProtoMajor == 2 && r.Header.Get("Content-Type") == "application/grpc" {
|
||||||
|
s.server.ServeHTTP(w, r)
|
||||||
|
} else {
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}), &http2.Server{}),
|
||||||
|
}
|
||||||
|
if err := http2.ConfigureServer(hServer, &http2.Server{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
g.Go(func() error {
|
||||||
|
defer hServer.Shutdown(ctx)
|
||||||
|
return hServer.Serve(s.opts.lis)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
g.Go(func() error {
|
||||||
|
return s.server.Serve(s.opts.lis)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) runWithCmux(ctx context.Context, g *errgroup.Group) error {
|
||||||
|
mux := cmux.New(s.opts.lis)
|
||||||
|
mux.SetReadTimeout(5 * time.Second)
|
||||||
|
|
||||||
|
gLis := mux.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"))
|
||||||
|
hList := mux.Match(cmux.Any())
|
||||||
|
|
||||||
|
if s.opts.mux != nil {
|
||||||
|
hServer := &http.Server{
|
||||||
|
Handler: alice.New(s.opts.middlewares...).Then(cors.New(s.opts.cors).Handler(s.opts.mux)),
|
||||||
|
}
|
||||||
|
g.Go(func() error {
|
||||||
|
defer hServer.Shutdown(ctx)
|
||||||
|
return hServer.Serve(hList)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Go(func() error {
|
||||||
|
return s.server.Serve(gLis)
|
||||||
|
})
|
||||||
|
|
||||||
|
g.Go(func() error {
|
||||||
|
return ignoreMuxError(mux.Serve())
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) Serve(lis net.Listener) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
s.opts.lis = lis
|
||||||
|
s.mu.Unlock()
|
||||||
|
return s.run()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *service) Start() error {
|
func (s *service) Start() error {
|
||||||
return s.run()
|
return s.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Stop() error {
|
func (s *service) Stop() error {
|
||||||
|
var err error
|
||||||
|
s.o.Do(func() {
|
||||||
|
err = s.stop()
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (s *service) stop() error {
|
||||||
|
log := logger.C(s.opts.ctx)
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
if !s.running {
|
if !s.running {
|
||||||
@ -264,9 +385,11 @@ func (s *service) Stop() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := s.opts.registry.Deregister(s.regSvc); err != nil {
|
if err := s.opts.registry.Deregister(s.regSvc); err != nil {
|
||||||
logrus.Errorf("failed to deregister service: %v", err)
|
log.Errorf("failed to deregister service: %v", err)
|
||||||
}
|
}
|
||||||
defer close(s.closed)
|
defer close(s.closed)
|
||||||
|
t := time.NewTimer(5 * time.Second)
|
||||||
|
defer t.Stop()
|
||||||
sigs := s.notify()
|
sigs := s.notify()
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
@ -276,14 +399,17 @@ func (s *service) Stop() error {
|
|||||||
// catch: Drain() is not implemented
|
// catch: Drain() is not implemented
|
||||||
recover()
|
recover()
|
||||||
}()
|
}()
|
||||||
logrus.Warn("shutting down gracefully")
|
log.Warn("shutting down gracefully")
|
||||||
s.server.GracefulStop()
|
s.server.GracefulStop()
|
||||||
}()
|
}()
|
||||||
select {
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
log.Warnf("timeout waiting for server to stop")
|
||||||
|
s.server.Stop()
|
||||||
case sig := <-sigs:
|
case sig := <-sigs:
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
logrus.Warnf("received %v", sig)
|
log.Warnf("received %v", sig)
|
||||||
logrus.Warn("forcing shutdown")
|
log.Warn("forcing shutdown")
|
||||||
s.server.Stop()
|
s.server.Stop()
|
||||||
case <-done:
|
case <-done:
|
||||||
}
|
}
|
||||||
@ -294,7 +420,7 @@ func (s *service) Stop() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logrus.Info("server stopped")
|
log.Info("server stopped")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,7 +445,7 @@ func (s *service) registerService(sd *grpc.ServiceDesc, ss interface{}) {
|
|||||||
s.inproc.RegisterService(sd, ss)
|
s.inproc.RegisterService(sd, ss)
|
||||||
|
|
||||||
if _, ok := s.services[sd.ServiceName]; ok {
|
if _, ok := s.services[sd.ServiceName]; ok {
|
||||||
logrus.Fatalf("grpc: Service.RegisterService found duplicate service registration for %q", sd.ServiceName)
|
logger.C(s.opts.ctx).Fatalf("grpc: Service.RegisterService found duplicate service registration for %q", sd.ServiceName)
|
||||||
}
|
}
|
||||||
info := &serviceInfo{
|
info := &serviceInfo{
|
||||||
serviceImpl: ss,
|
serviceImpl: ss,
|
||||||
@ -367,23 +493,37 @@ func (s *service) GetServiceInfo() map[string]grpc.ServiceInfo {
|
|||||||
|
|
||||||
func (s *service) Close() error {
|
func (s *service) Close() error {
|
||||||
err := multierr.Combine(s.Stop())
|
err := multierr.Combine(s.Stop())
|
||||||
if s.opts.db != nil {
|
|
||||||
err = multierr.Append(s.opts.db.Close(), err)
|
|
||||||
}
|
|
||||||
<-s.closed
|
<-s.closed
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) notify() <-chan os.Signal {
|
func (s *service) notify() <-chan os.Signal {
|
||||||
sigs := make(chan os.Signal, 2)
|
sigs := make(chan os.Signal, 2)
|
||||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGQUIT)
|
signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
|
||||||
return sigs
|
return sigs
|
||||||
}
|
}
|
||||||
|
|
||||||
func ignoreMuxError(err error) bool {
|
func (s *service) lazyMux() ServeMux {
|
||||||
|
if s.opts.mux == nil {
|
||||||
|
s.opts.mux = http.NewServeMux()
|
||||||
|
}
|
||||||
|
return s.opts.mux
|
||||||
|
}
|
||||||
|
|
||||||
|
func ignoreMuxError(err error) error {
|
||||||
|
if !isMuxError(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMuxError(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if strings.Contains(err.Error(), "use of closed network connection") ||
|
||||||
|
strings.Contains(err.Error(), "mux: server closed") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return strings.Contains(err.Error(), "use of closed network connection") ||
|
return false
|
||||||
strings.Contains(err.Error(), "mux: server closed")
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/improbable-eng/grpc-web/go/grpcweb"
|
"github.com/traefik/grpc-web/go/grpcweb"
|
||||||
|
|
||||||
|
"go.linka.cloud/grpc-toolkit/react"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultWebOptions = []grpcweb.Option{
|
var defaultWebOptions = []grpcweb.Option{
|
||||||
@ -26,10 +28,22 @@ func (s *service) grpcWeb(opts ...grpcweb.Option) error {
|
|||||||
h := grpcweb.WrapServer(s.server, append(defaultWebOptions, opts...)...)
|
h := grpcweb.WrapServer(s.server, append(defaultWebOptions, opts...)...)
|
||||||
for _, v := range grpcweb.ListGRPCResources(s.server) {
|
for _, v := range grpcweb.ListGRPCResources(s.server) {
|
||||||
if s.opts.grpcWebPrefix != "" {
|
if s.opts.grpcWebPrefix != "" {
|
||||||
s.opts.mux.Handle(s.opts.grpcWebPrefix+v, http.StripPrefix(s.opts.grpcWebPrefix, h))
|
s.lazyMux().Handle(s.opts.grpcWebPrefix+v, http.StripPrefix(s.opts.grpcWebPrefix, h))
|
||||||
} else {
|
} else {
|
||||||
s.opts.mux.Handle(v, h)
|
s.lazyMux().Handle(v, h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) reactApp() error {
|
||||||
|
if !s.opts.hasReactUI {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
h, err := react.NewHandler(s.opts.reactUI, s.opts.reactUISubPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.lazyMux().Handle("/", h)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -3,7 +3,7 @@ package grpc
|
|||||||
import (
|
import (
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"go.linka.cloud/grpc/transport"
|
"go.linka.cloud/grpc-toolkit/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user