mirror of
https://github.com/linka-cloud/grpc.git
synced 2024-12-22 17:00:45 +00:00
648 lines
13 KiB
Go
648 lines
13 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"io/fs"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
|
"github.com/rs/cors"
|
|
"github.com/traefik/grpc-web/go/grpcweb"
|
|
"google.golang.org/grpc"
|
|
|
|
"go.linka.cloud/grpc-toolkit/certs"
|
|
"go.linka.cloud/grpc-toolkit/interceptors"
|
|
"go.linka.cloud/grpc-toolkit/registry"
|
|
"go.linka.cloud/grpc-toolkit/transport"
|
|
"go.linka.cloud/grpc-toolkit/utils/addr"
|
|
)
|
|
|
|
var _ Options = (*options)(nil)
|
|
|
|
type RegisterGatewayFunc func(ctx context.Context, mux *runtime.ServeMux, cc grpc.ClientConnInterface) error
|
|
|
|
type Options interface {
|
|
Context() context.Context
|
|
Name() string
|
|
Version() string
|
|
Address() string
|
|
|
|
Reflection() bool
|
|
Health() bool
|
|
|
|
CACert() string
|
|
Cert() string
|
|
Key() string
|
|
ClientCACert() string
|
|
ClientCert() string
|
|
ClientKey() string
|
|
TLSConfig() *tls.Config
|
|
Secure() bool
|
|
|
|
Registry() registry.Registry
|
|
|
|
BeforeStart() []func() error
|
|
AfterStart() []func() error
|
|
BeforeStop() []func() error
|
|
AfterStop() []func() error
|
|
|
|
ServerOpts() []grpc.ServerOption
|
|
ServerInterceptors() []grpc.UnaryServerInterceptor
|
|
StreamServerInterceptors() []grpc.StreamServerInterceptor
|
|
|
|
ClientInterceptors() []grpc.UnaryClientInterceptor
|
|
StreamClientInterceptors() []grpc.StreamClientInterceptor
|
|
|
|
Cors() cors.Options
|
|
Mux() ServeMux
|
|
GRPCWeb() bool
|
|
GRPCWebPrefix() string
|
|
GRPCWebOpts() []grpcweb.Option
|
|
|
|
Gateway() bool
|
|
GatewayPrefix() string
|
|
GatewayOpts() []runtime.ServeMuxOption
|
|
|
|
// TODO(adphi): metrics + tracing
|
|
|
|
WithoutCmux() bool
|
|
ProxyProtocol() bool
|
|
|
|
Default()
|
|
}
|
|
|
|
func NewOptions() *options {
|
|
return &options{
|
|
ctx: context.Background(),
|
|
address: ":0",
|
|
health: true,
|
|
}
|
|
}
|
|
|
|
func (o *options) Default() {
|
|
if o.ctx == nil {
|
|
o.ctx = context.Background()
|
|
}
|
|
if o.address == "" {
|
|
o.address = "0.0.0.0:0"
|
|
}
|
|
if o.transport == nil {
|
|
o.transport = &grpc.Server{}
|
|
}
|
|
}
|
|
|
|
type Option func(*options)
|
|
|
|
func WithName(name string) Option {
|
|
return func(o *options) {
|
|
o.name = name
|
|
}
|
|
}
|
|
|
|
func WithVersion(version string) Option {
|
|
return func(o *options) {
|
|
o.version = version
|
|
}
|
|
}
|
|
|
|
func WithRegistry(registry registry.Registry) Option {
|
|
return func(o *options) {
|
|
o.registry = registry
|
|
}
|
|
}
|
|
|
|
// WithContext specifies a context for the service.
|
|
// Can be used to signal shutdown of the service.
|
|
// Can be used for extra option values.
|
|
func WithContext(ctx context.Context) Option {
|
|
return func(o *options) {
|
|
o.ctx = ctx
|
|
}
|
|
}
|
|
|
|
// WithAddress sets the address of the server
|
|
func WithAddress(addr string) Option {
|
|
return func(o *options) {
|
|
o.address = addr
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
return func(o *options) {
|
|
o.reflection = r
|
|
}
|
|
}
|
|
|
|
func WithHealth(h bool) Option {
|
|
return func(o *options) {
|
|
o.health = h
|
|
}
|
|
}
|
|
|
|
func WithSecure(s bool) Option {
|
|
return func(o *options) {
|
|
o.secure = s
|
|
}
|
|
}
|
|
|
|
func WithGRPCServerOpts(opts ...grpc.ServerOption) Option {
|
|
return func(o *options) {
|
|
o.serverOpts = append(o.serverOpts, opts...)
|
|
}
|
|
}
|
|
|
|
func WithCACert(path string) Option {
|
|
return func(o *options) {
|
|
o.caCert = path
|
|
}
|
|
}
|
|
|
|
func WithCert(path string) Option {
|
|
return func(o *options) {
|
|
o.cert = path
|
|
}
|
|
}
|
|
|
|
func WithKey(path string) Option {
|
|
return func(o *options) {
|
|
o.key = path
|
|
}
|
|
}
|
|
|
|
func WithClientCACert(path string) Option {
|
|
return func(o *options) {
|
|
o.clientCACert = path
|
|
}
|
|
}
|
|
|
|
func WithClientCert(path string) Option {
|
|
return func(o *options) {
|
|
o.clientCert = path
|
|
}
|
|
}
|
|
|
|
func WithClientKey(path string) Option {
|
|
return func(o *options) {
|
|
o.clientKey = path
|
|
}
|
|
}
|
|
|
|
func WithTLSConfig(conf *tls.Config) Option {
|
|
return func(o *options) {
|
|
o.tlsConfig = conf
|
|
}
|
|
}
|
|
|
|
func WithBeforeStart(fn ...func() error) Option {
|
|
return func(o *options) {
|
|
o.beforeStart = append(o.beforeStart, fn...)
|
|
}
|
|
}
|
|
|
|
func WithBeforeStop(fn ...func() error) Option {
|
|
return func(o *options) {
|
|
o.beforeStop = append(o.beforeStop, fn...)
|
|
}
|
|
}
|
|
|
|
func WithAfterStart(fn ...func() error) Option {
|
|
return func(o *options) {
|
|
o.afterStart = append(o.afterStart, fn...)
|
|
}
|
|
}
|
|
|
|
func WithAfterStop(fn ...func() error) Option {
|
|
return func(o *options) {
|
|
o.afterStop = append(o.afterStop, fn...)
|
|
}
|
|
}
|
|
|
|
func WithInterceptors(i ...interceptors.Interceptors) Option {
|
|
return func(o *options) {
|
|
for _, v := range i {
|
|
o.unaryServerInterceptors = append(o.unaryServerInterceptors, v.UnaryServerInterceptor())
|
|
o.streamServerInterceptors = append(o.streamServerInterceptors, v.StreamServerInterceptor())
|
|
o.unaryClientInterceptors = append(o.unaryClientInterceptors, v.UnaryClientInterceptor())
|
|
o.streamClientInterceptors = append(o.streamClientInterceptors, v.StreamClientInterceptor())
|
|
}
|
|
}
|
|
}
|
|
|
|
func WithServerInterceptors(i ...interceptors.ServerInterceptors) Option {
|
|
return func(o *options) {
|
|
for _, v := range i {
|
|
o.unaryServerInterceptors = append(o.unaryServerInterceptors, v.UnaryServerInterceptor())
|
|
o.streamServerInterceptors = append(o.streamServerInterceptors, v.StreamServerInterceptor())
|
|
}
|
|
}
|
|
}
|
|
|
|
func WithClientInterceptors(i ...interceptors.ClientInterceptors) Option {
|
|
return func(o *options) {
|
|
for _, v := range i {
|
|
o.unaryClientInterceptors = append(o.unaryClientInterceptors, v.UnaryClientInterceptor())
|
|
o.streamClientInterceptors = append(o.streamClientInterceptors, v.StreamClientInterceptor())
|
|
}
|
|
}
|
|
}
|
|
|
|
func WithUnaryClientInterceptor(i ...grpc.UnaryClientInterceptor) Option {
|
|
return func(o *options) {
|
|
o.unaryClientInterceptors = append(o.unaryClientInterceptors, i...)
|
|
}
|
|
}
|
|
|
|
// WithUnaryServerInterceptor adds unary Wrapper interceptors to the options passed into the server
|
|
func WithUnaryServerInterceptor(i ...grpc.UnaryServerInterceptor) Option {
|
|
return func(o *options) {
|
|
o.unaryServerInterceptors = append(o.unaryServerInterceptors, i...)
|
|
}
|
|
}
|
|
|
|
func WithStreamServerInterceptor(i ...grpc.StreamServerInterceptor) Option {
|
|
return func(o *options) {
|
|
o.streamServerInterceptors = append(o.streamServerInterceptors, i...)
|
|
}
|
|
}
|
|
|
|
func WithStreamClientInterceptor(i ...grpc.StreamClientInterceptor) Option {
|
|
return func(o *options) {
|
|
o.streamClientInterceptors = append(o.streamClientInterceptors, i...)
|
|
}
|
|
}
|
|
|
|
// WithSubscriberInterceptor adds subscriber interceptors to the options passed into the server
|
|
func WithSubscriberInterceptor(w ...interface{}) Option {
|
|
return func(o *options) {
|
|
|
|
}
|
|
}
|
|
|
|
func WithCors(opts cors.Options) Option {
|
|
return func(o *options) {
|
|
o.cors = opts
|
|
}
|
|
}
|
|
|
|
func WithMux(mux ServeMux) Option {
|
|
return func(o *options) {
|
|
o.mux = mux
|
|
}
|
|
}
|
|
|
|
func WithMiddlewares(m ...Middleware) Option {
|
|
return func(o *options) {
|
|
o.middlewares = m
|
|
}
|
|
}
|
|
|
|
func WithGRPCWeb(b bool) Option {
|
|
return func(o *options) {
|
|
o.grpcWeb = b
|
|
}
|
|
}
|
|
|
|
func WithGRPCWebPrefix(prefix string) Option {
|
|
return func(o *options) {
|
|
o.grpcWebPrefix = strings.TrimSuffix(prefix, "/")
|
|
}
|
|
}
|
|
|
|
func WithGRPCWebOpts(opts ...grpcweb.Option) Option {
|
|
return func(o *options) {
|
|
o.grpcWebOpts = opts
|
|
}
|
|
}
|
|
|
|
func WithGateway(fn RegisterGatewayFunc) Option {
|
|
return func(o *options) {
|
|
o.gateway = fn
|
|
}
|
|
}
|
|
|
|
func WithGatewayPrefix(prefix string) Option {
|
|
return func(o *options) {
|
|
o.gatewayPrefix = strings.TrimSuffix(prefix, "/")
|
|
}
|
|
}
|
|
|
|
func WithGatewayOpts(opts ...runtime.ServeMuxOption) Option {
|
|
return func(o *options) {
|
|
o.gatewayOpts = opts
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
ctx context.Context
|
|
name string
|
|
version string
|
|
address string
|
|
lis net.Listener
|
|
|
|
reflection bool
|
|
health bool
|
|
|
|
secure bool
|
|
caCert string
|
|
cert string
|
|
key string
|
|
clientCACert string
|
|
clientCert string
|
|
clientKey string
|
|
tlsConfig *tls.Config
|
|
|
|
transport transport.Transport
|
|
registry registry.Registry
|
|
|
|
beforeStart []func() error
|
|
afterStart []func() error
|
|
beforeStop []func() error
|
|
afterStop []func() error
|
|
|
|
serverOpts []grpc.ServerOption
|
|
|
|
unaryServerInterceptors []grpc.UnaryServerInterceptor
|
|
streamServerInterceptors []grpc.StreamServerInterceptor
|
|
|
|
unaryClientInterceptors []grpc.UnaryClientInterceptor
|
|
streamClientInterceptors []grpc.StreamClientInterceptor
|
|
|
|
mux ServeMux
|
|
middlewares []Middleware
|
|
grpcWeb bool
|
|
grpcWebOpts []grpcweb.Option
|
|
grpcWebPrefix string
|
|
gateway RegisterGatewayFunc
|
|
gatewayOpts []runtime.ServeMuxOption
|
|
cors cors.Options
|
|
|
|
reactUI fs.FS
|
|
reactUISubPath string
|
|
hasReactUI bool
|
|
|
|
error error
|
|
gatewayPrefix string
|
|
withoutCmux bool
|
|
proxyProtocol bool
|
|
proxyProtocolAddrs []string
|
|
}
|
|
|
|
func (o *options) Name() string {
|
|
return o.name
|
|
}
|
|
|
|
func (o *options) Version() string {
|
|
return o.version
|
|
}
|
|
|
|
func (o *options) Context() context.Context {
|
|
return o.ctx
|
|
}
|
|
|
|
func (o *options) Address() string {
|
|
return o.address
|
|
}
|
|
|
|
func (o *options) Registry() registry.Registry {
|
|
return o.registry
|
|
}
|
|
|
|
func (o *options) Reflection() bool {
|
|
return o.reflection
|
|
}
|
|
|
|
func (o *options) Health() bool {
|
|
return o.health
|
|
}
|
|
|
|
func (o *options) CACert() string {
|
|
return o.caCert
|
|
}
|
|
|
|
func (o *options) Cert() string {
|
|
return o.cert
|
|
}
|
|
|
|
func (o *options) Key() string {
|
|
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 {
|
|
return o.tlsConfig
|
|
}
|
|
|
|
func (o *options) Secure() bool {
|
|
return o.secure
|
|
}
|
|
|
|
func (o *options) BeforeStart() []func() error {
|
|
return o.beforeStart
|
|
}
|
|
|
|
func (o *options) AfterStart() []func() error {
|
|
return o.afterStart
|
|
}
|
|
|
|
func (o *options) BeforeStop() []func() error {
|
|
return o.beforeStop
|
|
}
|
|
|
|
func (o *options) AfterStop() []func() error {
|
|
return o.afterStop
|
|
}
|
|
|
|
func (o *options) ServerOpts() []grpc.ServerOption {
|
|
return o.serverOpts
|
|
}
|
|
|
|
func (o *options) ServerInterceptors() []grpc.UnaryServerInterceptor {
|
|
return o.unaryServerInterceptors
|
|
}
|
|
|
|
func (o *options) StreamServerInterceptors() []grpc.StreamServerInterceptor {
|
|
return o.streamServerInterceptors
|
|
}
|
|
|
|
func (o *options) ClientInterceptors() []grpc.UnaryClientInterceptor {
|
|
return o.unaryClientInterceptors
|
|
}
|
|
|
|
func (o *options) StreamClientInterceptors() []grpc.StreamClientInterceptor {
|
|
return o.streamClientInterceptors
|
|
}
|
|
|
|
func (o *options) Cors() cors.Options {
|
|
return o.cors
|
|
}
|
|
|
|
func (o *options) Mux() ServeMux {
|
|
return o.mux
|
|
}
|
|
|
|
func (o *options) GRPCWeb() bool {
|
|
return o.grpcWeb
|
|
}
|
|
|
|
func (o *options) GRPCWebPrefix() string {
|
|
return o.grpcWebPrefix
|
|
}
|
|
|
|
func (o *options) GRPCWebOpts() []grpcweb.Option {
|
|
return o.grpcWebOpts
|
|
}
|
|
|
|
func (o *options) Gateway() bool {
|
|
return o.gateway != nil
|
|
}
|
|
|
|
func (o *options) GatewayPrefix() string {
|
|
return o.gatewayPrefix
|
|
}
|
|
|
|
func (o *options) GatewayOpts() []runtime.ServeMuxOption {
|
|
return o.gatewayOpts
|
|
}
|
|
|
|
func (o *options) WithoutCmux() bool {
|
|
return o.withoutCmux
|
|
}
|
|
|
|
func (o *options) ProxyProtocol() bool {
|
|
return o.proxyProtocol
|
|
}
|
|
|
|
func (o *options) parseTLSConfig() error {
|
|
if o.tlsConfig != nil {
|
|
return nil
|
|
}
|
|
nextProtos := []string{"h2", "h2c", "http/1.1", "acme-tls/1"}
|
|
if !o.hasTLSConfig() {
|
|
if !o.secure {
|
|
return nil
|
|
}
|
|
var hosts []string
|
|
if host, _, err := net.SplitHostPort(o.address); err == nil {
|
|
if len(host) == 0 {
|
|
hosts = addr.IPs()
|
|
} else {
|
|
hosts = []string{host}
|
|
}
|
|
}
|
|
for i, h := range hosts {
|
|
a, err := addr.Extract(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hosts[i] = a
|
|
}
|
|
// generate a certificate
|
|
cert, err := certs.New(hosts...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
o.tlsConfig = &tls.Config{
|
|
Certificates: []tls.Certificate{cert},
|
|
ClientAuth: tls.NoClientCert,
|
|
NextProtos: nextProtos,
|
|
}
|
|
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,
|
|
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
|
|
}
|
|
|
|
func (o *options) hasTLSConfig() bool {
|
|
return o.caCert != "" && o.cert != "" && o.tlsConfig == nil
|
|
}
|
|
|
|
func (o *options) hasClientTLSConfig() bool {
|
|
return o.clientCACert != "" && o.clientCert != "" && o.clientKey != ""
|
|
}
|