Bump reva deps (#8412)
* bump dependencies Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * bump reva and add config options Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> --------- Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# Develop tools
|
||||
/.vscode/
|
||||
/.idea/
|
||||
/.trunk
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
@@ -34,3 +35,7 @@ _cgo_export.*
|
||||
*~
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# go work files
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
# This file contains all available configuration options
|
||||
# with their default values.
|
||||
|
||||
# options for analysis running
|
||||
run:
|
||||
# go: '1.18'
|
||||
# default concurrency is a available CPU number
|
||||
# concurrency: 4
|
||||
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
deadline: 10m
|
||||
|
||||
# exit code when at least one issue was found, default is 1
|
||||
issues-exit-code: 1
|
||||
|
||||
# include test files or not, default is true
|
||||
tests: true
|
||||
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
skip-files:
|
||||
[]
|
||||
# - .*\\.pb\\.go$
|
||||
|
||||
allow-parallel-runners: true
|
||||
|
||||
# list of build tags, all linters use it. Default is empty list.
|
||||
build-tags: []
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
# Format: colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions
|
||||
#
|
||||
# Multiple can be specified by separating them by comma, output can be provided
|
||||
# for each of them by separating format name and path by colon symbol.
|
||||
# Output path can be either `stdout`, `stderr` or path to the file to write to.
|
||||
# Example: "checkstyle:report.json,colored-line-number"
|
||||
#
|
||||
# Default: colored-line-number
|
||||
format: colored-line-number
|
||||
# Print lines of code with issue.
|
||||
# Default: true
|
||||
print-issued-lines: true
|
||||
# Print linter name in the end of issue text.
|
||||
# Default: true
|
||||
print-linter-name: true
|
||||
# Make issues output unique by line.
|
||||
# Default: true
|
||||
uniq-by-line: true
|
||||
# Add a prefix to the output file references.
|
||||
# Default is no prefix.
|
||||
path-prefix: ""
|
||||
# Sort results by: filepath, line and column.
|
||||
sort-results: true
|
||||
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
wsl:
|
||||
allow-cuddle-with-calls: ["Lock", "RLock", "defer"]
|
||||
funlen:
|
||||
lines: 80
|
||||
statements: 60
|
||||
varnamelen:
|
||||
# The longest distance, in source lines, that is being considered a "small scope".
|
||||
# Variables used in at most this many lines will be ignored.
|
||||
# Default: 5
|
||||
max-distance: 26
|
||||
ignore-names:
|
||||
- err
|
||||
- id
|
||||
- ch
|
||||
- wg
|
||||
- mu
|
||||
ignore-decls:
|
||||
- c echo.Context
|
||||
- t testing.T
|
||||
- f *foo.Bar
|
||||
- e error
|
||||
- i int
|
||||
- const C
|
||||
- T any
|
||||
- m map[string]int
|
||||
errcheck:
|
||||
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-type-assertions: true
|
||||
|
||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-blank: true
|
||||
govet:
|
||||
# report about shadowed variables
|
||||
check-shadowing: false
|
||||
gofmt:
|
||||
# simplify code: gofmt with `-s` option, true by default
|
||||
simplify: true
|
||||
gocyclo:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 15
|
||||
maligned:
|
||||
# print struct with more effective memory layout or not, false by default
|
||||
suggest-new: true
|
||||
dupl:
|
||||
# tokens count to trigger issue, 150 by default
|
||||
threshold: 100
|
||||
goconst:
|
||||
# minimal length of string constant, 3 by default
|
||||
min-len: 3
|
||||
# minimal occurrences count to trigger, 3 by default
|
||||
min-occurrences: 3
|
||||
depguard:
|
||||
list-type: blacklist
|
||||
# Packages listed here will reported as error if imported
|
||||
packages:
|
||||
- github.com/golang/protobuf/proto
|
||||
misspell:
|
||||
# Correct spellings using locale preferences for US or UK.
|
||||
# Default is to use a neutral variety of English.
|
||||
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
|
||||
locale: US
|
||||
lll:
|
||||
# max line length, lines longer will be reported. Default is 120.
|
||||
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
|
||||
line-length: 120
|
||||
# tab width in spaces. Default to 1.
|
||||
tab-width: 1
|
||||
unused:
|
||||
# treat code as a program (not a library) and report unused exported identifiers; default is false.
|
||||
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
unparam:
|
||||
# call graph construction algorithm (cha, rta). In general, use cha for libraries,
|
||||
# and rta for programs with main packages. Default is cha.
|
||||
algo: cha
|
||||
|
||||
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
|
||||
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
nakedret:
|
||||
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
|
||||
max-func-lines: 60
|
||||
nolintlint:
|
||||
allow-unused: false
|
||||
allow-leading-space: false
|
||||
allow-no-explanation: []
|
||||
require-explanation: false
|
||||
require-specific: true
|
||||
prealloc:
|
||||
# XXX: we don't recommend using this linter before doing performance profiling.
|
||||
# For most programs usage of prealloc will be a premature optimization.
|
||||
|
||||
# Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.
|
||||
# True by default.
|
||||
simple: true
|
||||
range-loops: true # Report preallocation suggestions on range loops, true by default
|
||||
for-loops: false # Report preallocation suggestions on for loops, false by default
|
||||
cyclop:
|
||||
# the maximal code complexity to report
|
||||
max-complexity: 20
|
||||
gomoddirectives:
|
||||
replace-local: true
|
||||
retract-allow-no-explanation: false
|
||||
exclude-forbidden: true
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable-all: false
|
||||
fast: false
|
||||
disable:
|
||||
- golint
|
||||
- varcheck
|
||||
- ifshort
|
||||
- structcheck
|
||||
- deadcode
|
||||
# - nosnakecase
|
||||
- interfacer
|
||||
- maligned
|
||||
- scopelint
|
||||
- exhaustivestruct
|
||||
- testpackage
|
||||
- promlinter
|
||||
- nonamedreturns
|
||||
- makezero
|
||||
- gofumpt
|
||||
- nlreturn
|
||||
- thelper
|
||||
|
||||
# Can be considered to be enabled
|
||||
- gochecknoinits
|
||||
- gochecknoglobals # RIP
|
||||
- dogsled
|
||||
- wrapcheck
|
||||
- paralleltest
|
||||
- ireturn
|
||||
- gomnd
|
||||
- goerr113
|
||||
- exhaustruct
|
||||
- containedctx
|
||||
- godox
|
||||
- forcetypeassert
|
||||
- gci
|
||||
- lll
|
||||
|
||||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
# But independently from this option we use default exclude patterns,
|
||||
# it can be disabled by `exclude-use-default: false`. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`
|
||||
# exclude:
|
||||
# - package comment should be of the form "Package services ..." # revive
|
||||
# - ^ST1000 # ST1000: at least one file in a package should have a package comment (stylecheck)
|
||||
|
||||
# exclude-rules:
|
||||
# - path: internal/app/machined/pkg/system/services
|
||||
# linters:
|
||||
# - dupl
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gocyclo
|
||||
- dupl
|
||||
- gosec
|
||||
- funlen
|
||||
- varnamelen
|
||||
- wsl
|
||||
|
||||
# Independently from option `exclude` we use default exclude patterns,
|
||||
# it can be disabled by this option. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`.
|
||||
# Default value for this option is true.
|
||||
exclude-use-default: false
|
||||
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
max-issues-per-linter: 0
|
||||
|
||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||
max-same-issues: 0
|
||||
|
||||
# Show only new issues: if there are unstaged changes or untracked files,
|
||||
# only those changes are analyzed, else only changes in HEAD~ are analyzed.
|
||||
# It's a super-useful option for integration of golangci-lint into existing
|
||||
# large codebase. It's not practical to fix all existing issues at the moment
|
||||
# of integration: much better don't allow issues in new code.
|
||||
# Default is false.
|
||||
new: false
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
labelType: long
|
||||
coverThreshold: 70
|
||||
buildStyle:
|
||||
bold: true
|
||||
foreground: yellow
|
||||
startStyle:
|
||||
foreground: lightBlack
|
||||
passStyle:
|
||||
foreground: green
|
||||
failStyle:
|
||||
bold: true
|
||||
foreground: "#821515"
|
||||
skipStyle:
|
||||
foreground: lightBlack
|
||||
passPackageStyle:
|
||||
foreground: green
|
||||
hide: false
|
||||
failPackageStyle:
|
||||
bold: true
|
||||
foreground: "#821515"
|
||||
coveredStyle:
|
||||
foreground: green
|
||||
uncoveredStyle:
|
||||
bold: true
|
||||
foreground: yellow
|
||||
fileStyle:
|
||||
foreground: cyan
|
||||
lineStyle:
|
||||
foreground: magenta
|
||||
Vendored
+48
-4
@@ -1,8 +1,9 @@
|
||||
# Go Micro [](https://opensource.org/licenses/Apache-2.0) [](https://pkg.go.dev/go-micro.dev/v4?tab=doc) [](https://goreportcard.com/report/github.com/go-micro/go-micro) [](https://discord.gg/qV3HvnEJfB)
|
||||
|
||||
# Go Micro [](https://opensource.org/licenses/Apache-2.0) [](https://pkg.go.dev/go-micro.dev/v4?tab=doc) [](https://goreportcard.com/report/github.com/go-micro/go-micro) [](https://discord.gg/qV3HvnEJfB)
|
||||
|
||||
Go Micro is a framework for distributed systems development.
|
||||
|
||||
Note: V5 is in development. Leave feedback in this [form](https://forms.gle/41gEWAcSgnf88GKt7)
|
||||
|
||||
## Overview
|
||||
|
||||
Go Micro provides the core requirements for distributed systems development including RPC and Event driven communication.
|
||||
@@ -52,14 +53,38 @@ Go Micro abstracts away the details of distributed systems. Here are the main fe
|
||||
|
||||
## Getting Started
|
||||
|
||||
To make use of Go Micro
|
||||
To make use of Go Micro import it
|
||||
|
||||
```golang
|
||||
import "go-micro.dev/v4"
|
||||
import "go-micro.dev/v4
|
||||
```
|
||||
|
||||
Define a handler (protobuf is optionally supported - see [example](https://github.com/go-micro/examples/blob/main/helloworld/main.go))
|
||||
|
||||
```golang
|
||||
type Request struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type Helloworld struct{}
|
||||
|
||||
func (h *Helloworld) Greeting(ctx context.Context, req *Request, rsp *Response) error {
|
||||
rsp.Message = "Hello " + req.Name
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
Create, initialise and run the service
|
||||
|
||||
```golang
|
||||
// create a new service
|
||||
service := micro.NewService(
|
||||
micro.Name("helloworld"),
|
||||
micro.Handle(new(Helloworld)),
|
||||
)
|
||||
|
||||
// initialise flags
|
||||
@@ -69,6 +94,25 @@ service.Init()
|
||||
service.Run()
|
||||
```
|
||||
|
||||
Optionally set fixed address
|
||||
|
||||
```golang
|
||||
service := micro.NewService(
|
||||
// set address
|
||||
micro.Handle(":8080"),
|
||||
)
|
||||
```
|
||||
|
||||
Call it via curl
|
||||
|
||||
```
|
||||
curl -XPOST \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Micro-Endpoint: Helloworld.Greeting' \
|
||||
-d '{"name": "alice"}' \
|
||||
http://localhost:8080
|
||||
```
|
||||
|
||||
See the [examples](https://github.com/go-micro/examples) for detailed information on usage.
|
||||
|
||||
## Toolkit
|
||||
|
||||
Vendored
+20
-14
@@ -8,14 +8,15 @@ import (
|
||||
"strings"
|
||||
|
||||
"go-micro.dev/v4/api/router"
|
||||
"go-micro.dev/v4/client"
|
||||
"go-micro.dev/v4/registry"
|
||||
"go-micro.dev/v4/server"
|
||||
)
|
||||
|
||||
// The gateway interface provides a way to
|
||||
// create composable API gateways
|
||||
// API interface provides a way to
|
||||
// create composable API gateways.
|
||||
type Api interface {
|
||||
// Initialise options
|
||||
// Initialize options
|
||||
Init(...Option) error
|
||||
// Get the options
|
||||
Options() Options
|
||||
@@ -29,16 +30,20 @@ type Api interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// Options are API options.
|
||||
type Options struct {
|
||||
// Address of the server
|
||||
Address string
|
||||
// Router for resolving routes
|
||||
Router router.Router
|
||||
// Client to use for RPC
|
||||
Client client.Client
|
||||
// Address of the server
|
||||
Address string
|
||||
}
|
||||
|
||||
// Option type are API option args.
|
||||
type Option func(*Options) error
|
||||
|
||||
// Endpoint is a mapping between an RPC method and HTTP endpoint
|
||||
// Endpoint is a mapping between an RPC method and HTTP endpoint.
|
||||
type Endpoint struct {
|
||||
// RPC Method e.g. Greeter.Hello
|
||||
Name string
|
||||
@@ -56,7 +61,7 @@ type Endpoint struct {
|
||||
Stream bool
|
||||
}
|
||||
|
||||
// Service represents an API service
|
||||
// Service represents an API service.
|
||||
type Service struct {
|
||||
// Name of service
|
||||
Name string
|
||||
@@ -82,21 +87,22 @@ func slice(s string) []string {
|
||||
return sl
|
||||
}
|
||||
|
||||
// Encode encodes an endpoint to endpoint metadata
|
||||
// Encode encodes an endpoint to endpoint metadata.
|
||||
func Encode(e *Endpoint) map[string]string {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// endpoint map
|
||||
ep := make(map[string]string)
|
||||
em := make(map[string]string)
|
||||
|
||||
// set vals only if they exist
|
||||
set := func(k, v string) {
|
||||
if len(v) == 0 {
|
||||
return
|
||||
}
|
||||
ep[k] = v
|
||||
|
||||
em[k] = v
|
||||
}
|
||||
|
||||
set("endpoint", e.Name)
|
||||
@@ -106,10 +112,10 @@ func Encode(e *Endpoint) map[string]string {
|
||||
set("path", strings.Join(e.Path, ","))
|
||||
set("host", strings.Join(e.Host, ","))
|
||||
|
||||
return ep
|
||||
return em
|
||||
}
|
||||
|
||||
// Decode decodes endpoint metadata into an endpoint
|
||||
// Decode decodes endpoint metadata into an endpoint.
|
||||
func Decode(e map[string]string) *Endpoint {
|
||||
if e == nil {
|
||||
return nil
|
||||
@@ -125,7 +131,7 @@ func Decode(e map[string]string) *Endpoint {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validates an endpoint to guarantee it won't blow up when being served
|
||||
// Validate validates an endpoint to guarantee it won't blow up when being served.
|
||||
func Validate(e *Endpoint) error {
|
||||
if e == nil {
|
||||
return errors.New("endpoint is nil")
|
||||
@@ -172,7 +178,7 @@ func WithEndpoint(e *Endpoint) server.HandlerOption {
|
||||
return server.EndpointMetadata(e.Name, Encode(e))
|
||||
}
|
||||
|
||||
// NewApi returns a new api gateway
|
||||
// NewApi returns a new api gateway.
|
||||
func NewApi(opts ...Option) Api {
|
||||
return newApi(opts...)
|
||||
}
|
||||
|
||||
+3
-4
@@ -45,7 +45,6 @@ func newApi(opts ...Option) Api {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialise options
|
||||
func (a *api) Init(opts ...Option) error {
|
||||
for _, o := range opts {
|
||||
o(&a.options)
|
||||
@@ -53,17 +52,17 @@ func (a *api) Init(opts ...Option) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the options
|
||||
// Get the options.
|
||||
func (a *api) Options() Options {
|
||||
return a.options
|
||||
}
|
||||
|
||||
// Register a http handler
|
||||
// Register a http handler.
|
||||
func (a *api) Register(*Endpoint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Register a route
|
||||
// Register a route.
|
||||
func (a *api) Deregister(*Endpoint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Handler represents a HTTP handler that manages a request
|
||||
// Handler represents a HTTP handler that manages a request.
|
||||
type Handler interface {
|
||||
// standard http handler
|
||||
http.Handler
|
||||
|
||||
+12
-8
@@ -7,20 +7,23 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultMaxRecvSize int64 = 1024 * 1024 * 100 // 10Mb
|
||||
// DefaultMaxRecvSize is 10MiB.
|
||||
DefaultMaxRecvSize int64 = 1024 * 1024 * 100
|
||||
)
|
||||
|
||||
// Options is the list of api Options.
|
||||
type Options struct {
|
||||
MaxRecvSize int64
|
||||
Namespace string
|
||||
Router router.Router
|
||||
Client client.Client
|
||||
Logger logger.Logger
|
||||
Namespace string
|
||||
MaxRecvSize int64
|
||||
}
|
||||
|
||||
// Option is a api Option.
|
||||
type Option func(o *Options)
|
||||
|
||||
// NewOptions fills in the blanks
|
||||
// NewOptions fills in the blanks.
|
||||
func NewOptions(opts ...Option) Options {
|
||||
options := Options{
|
||||
Logger: logger.DefaultLogger,
|
||||
@@ -45,34 +48,35 @@ func NewOptions(opts ...Option) Options {
|
||||
return options
|
||||
}
|
||||
|
||||
// WithNamespace specifies the namespace for the handler
|
||||
// WithNamespace specifies the namespace for the handler.
|
||||
func WithNamespace(s string) Option {
|
||||
return func(o *Options) {
|
||||
o.Namespace = s
|
||||
}
|
||||
}
|
||||
|
||||
// WithRouter specifies a router to be used by the handler
|
||||
// WithRouter specifies a router to be used by the handler.
|
||||
func WithRouter(r router.Router) Option {
|
||||
return func(o *Options) {
|
||||
o.Router = r
|
||||
}
|
||||
}
|
||||
|
||||
// WithClient sets the client for the handler.
|
||||
func WithClient(c client.Client) Option {
|
||||
return func(o *Options) {
|
||||
o.Client = c
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxRecvSize specifies max body size
|
||||
// WithMaxRecvSize specifies max body size.
|
||||
func WithMaxRecvSize(size int64) Option {
|
||||
return func(o *Options) {
|
||||
o.MaxRecvSize = size
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger specifies the logger
|
||||
// WithLogger specifies the logger.
|
||||
func WithLogger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
|
||||
+88
-50
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||
"github.com/oxtoacart/bpool"
|
||||
|
||||
"go-micro.dev/v4/api/handler"
|
||||
"go-micro.dev/v4/api/internal/proto"
|
||||
"go-micro.dev/v4/api/router"
|
||||
@@ -29,19 +28,20 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// Handler is the name of this handler.
|
||||
Handler = "rpc"
|
||||
packageID = "go.micro.api"
|
||||
)
|
||||
|
||||
var (
|
||||
// supported json codecs
|
||||
// supported json codecs.
|
||||
jsonCodecs = []string{
|
||||
"application/grpc+json",
|
||||
"application/json",
|
||||
"application/json-rpc",
|
||||
}
|
||||
|
||||
// support proto codecs
|
||||
// support proto codecs.
|
||||
protoCodecs = []string{
|
||||
"application/grpc",
|
||||
"application/grpc+proto",
|
||||
@@ -66,7 +66,7 @@ func (b *buffer) Write(_ []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// strategy is a hack for selection
|
||||
// strategy is a hack for selection.
|
||||
func strategy(services []*registry.Service) selector.Strategy {
|
||||
return func(_ []*registry.Service) selector.Next {
|
||||
// ignore input to this function, use services above
|
||||
@@ -77,6 +77,7 @@ func strategy(services []*registry.Service) selector.Strategy {
|
||||
func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
logger := h.opts.Logger
|
||||
bsize := handler.DefaultMaxRecvSize
|
||||
|
||||
if h.opts.MaxRecvSize > 0 {
|
||||
bsize = h.opts.MaxRecvSize
|
||||
}
|
||||
@@ -84,6 +85,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, bsize)
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
var service *router.Route
|
||||
|
||||
if h.opts.Router != nil {
|
||||
@@ -94,8 +96,10 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if werr != nil {
|
||||
logger.Log(log.ErrorLevel, werr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
service = s
|
||||
} else {
|
||||
// we have no way of routing the request
|
||||
@@ -106,18 +110,18 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
ct := r.Header.Get("Content-Type")
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
|
||||
// Strip charset from Content-Type (like `application/json; charset=UTF-8`)
|
||||
if idx := strings.IndexRune(ct, ';'); idx >= 0 {
|
||||
ct = ct[:idx]
|
||||
if idx := strings.IndexRune(contentType, ';'); idx >= 0 {
|
||||
contentType = contentType[:idx]
|
||||
}
|
||||
|
||||
// micro client
|
||||
c := h.opts.Client
|
||||
myClient := h.opts.Client
|
||||
|
||||
// create context
|
||||
cx := ctx.FromRequest(r)
|
||||
myContext := ctx.FromRequest(r)
|
||||
// get context from http handler wrappers
|
||||
md, ok := metadata.FromContext(r.Context())
|
||||
if !ok {
|
||||
@@ -133,23 +137,24 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// merge context with overwrite
|
||||
cx = metadata.MergeContext(cx, md, true)
|
||||
myContext = metadata.MergeContext(myContext, md, true)
|
||||
|
||||
// set merged context to request
|
||||
*r = *r.Clone(cx)
|
||||
*r = *r.Clone(myContext)
|
||||
// if stream we currently only support json
|
||||
if isStream(r, service) {
|
||||
// drop older context as it can have timeouts and create new
|
||||
// md, _ := metadata.FromContext(cx)
|
||||
//serveWebsocket(context.TODO(), w, r, service, c)
|
||||
if err := serveWebsocket(cx, w, r, service, c); err != nil {
|
||||
// serveWebsocket(context.TODO(), w, r, service, c)
|
||||
if err := serveWebsocket(myContext, w, r, service, myClient); err != nil {
|
||||
logger.Log(log.ErrorLevel, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// create strategy
|
||||
so := selector.WithStrategy(strategy(service.Versions))
|
||||
mySelector := selector.WithStrategy(strategy(service.Versions))
|
||||
|
||||
// walk the standard call path
|
||||
// get payload
|
||||
@@ -158,6 +163,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if werr := writeError(w, r, err); werr != nil {
|
||||
logger.Log(log.ErrorLevel, werr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -165,7 +171,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
switch {
|
||||
// proto codecs
|
||||
case hasCodec(ct, protoCodecs):
|
||||
case hasCodec(contentType, protoCodecs):
|
||||
request := &proto.Message{}
|
||||
// if the extracted payload isn't empty lets use it
|
||||
if len(br) > 0 {
|
||||
@@ -175,18 +181,19 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// create request/response
|
||||
response := &proto.Message{}
|
||||
|
||||
req := c.NewRequest(
|
||||
req := myClient.NewRequest(
|
||||
service.Service,
|
||||
service.Endpoint.Name,
|
||||
request,
|
||||
client.WithContentType(ct),
|
||||
client.WithContentType(contentType),
|
||||
)
|
||||
|
||||
// make the call
|
||||
if err := c.Call(cx, req, response, client.WithSelectOption(so)); err != nil {
|
||||
if err := myClient.Call(myContext, req, response, client.WithSelectOption(mySelector)); err != nil {
|
||||
if werr := writeError(w, r, err); werr != nil {
|
||||
logger.Log(log.ErrorLevel, werr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -196,13 +203,14 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if werr := writeError(w, r, err); werr != nil {
|
||||
logger.Log(log.ErrorLevel, werr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
default:
|
||||
// if json codec is not present set to json
|
||||
if !hasCodec(ct, jsonCodecs) {
|
||||
ct = "application/json"
|
||||
if !hasCodec(contentType, jsonCodecs) {
|
||||
contentType = "application/json"
|
||||
}
|
||||
|
||||
// default to trying json
|
||||
@@ -215,17 +223,18 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// create request/response
|
||||
var response json.RawMessage
|
||||
|
||||
req := c.NewRequest(
|
||||
req := myClient.NewRequest(
|
||||
service.Service,
|
||||
service.Endpoint.Name,
|
||||
&request,
|
||||
client.WithContentType(ct),
|
||||
client.WithContentType(contentType),
|
||||
)
|
||||
// make the call
|
||||
if err := c.Call(cx, req, &response, client.WithSelectOption(so)); err != nil {
|
||||
if err := myClient.Call(myContext, req, &response, client.WithSelectOption(mySelector)); err != nil {
|
||||
if werr := writeError(w, r, err); werr != nil {
|
||||
logger.Log(log.ErrorLevel, werr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -235,6 +244,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if werr := writeError(w, r, err); werr != nil {
|
||||
logger.Log(log.ErrorLevel, werr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -245,8 +255,8 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (rh *rpcHandler) String() string {
|
||||
return "rpc"
|
||||
func (h *rpcHandler) String() string {
|
||||
return Handler
|
||||
}
|
||||
|
||||
func hasCodec(ct string, codecs []string) bool {
|
||||
@@ -255,49 +265,57 @@ func hasCodec(ct string, codecs []string) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// requestPayload takes a *http.Request.
|
||||
// If the request is a GET the query string parameters are extracted and marshaled to JSON and the raw bytes are returned.
|
||||
// If the request method is a POST the request body is read and returned
|
||||
// If the request method is a POST the request body is read and returned.
|
||||
func requestPayload(r *http.Request) ([]byte, error) {
|
||||
var err error
|
||||
|
||||
// we have to decode json-rpc and proto-rpc because we suck
|
||||
// well actually because there's no proxy codec right now
|
||||
|
||||
ct := r.Header.Get("Content-Type")
|
||||
myCt := r.Header.Get("Content-Type")
|
||||
|
||||
switch {
|
||||
case strings.Contains(ct, "application/json-rpc"):
|
||||
case strings.Contains(myCt, "application/json-rpc"):
|
||||
msg := codec.Message{
|
||||
Type: codec.Request,
|
||||
Header: make(map[string]string),
|
||||
}
|
||||
|
||||
c := jsonrpc.NewCodec(&buffer{r.Body})
|
||||
if err = c.ReadHeader(&msg, codec.Request); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var raw json.RawMessage
|
||||
if err = c.ReadBody(&raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ([]byte)(raw), nil
|
||||
case strings.Contains(ct, "application/proto-rpc"), strings.Contains(ct, "application/octet-stream"):
|
||||
case strings.Contains(myCt, "application/proto-rpc"), strings.Contains(myCt, "application/octet-stream"):
|
||||
msg := codec.Message{
|
||||
Type: codec.Request,
|
||||
Header: make(map[string]string),
|
||||
}
|
||||
|
||||
c := protorpc.NewCodec(&buffer{r.Body})
|
||||
if err = c.ReadHeader(&msg, codec.Request); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var raw proto.Message
|
||||
if err = c.ReadBody(&raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return raw.Marshal()
|
||||
case strings.Contains(ct, "application/www-x-form-urlencoded"):
|
||||
case strings.Contains(myCt, "application/www-x-form-urlencoded"), strings.Contains(myCt, "application/x-www-form-urlencoded"):
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -315,6 +333,7 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
|
||||
// otherwise as per usual
|
||||
ctx := r.Context()
|
||||
|
||||
// dont user meadata.FromContext as it mangles names
|
||||
md, ok := metadata.FromContext(ctx)
|
||||
if !ok {
|
||||
@@ -331,9 +350,11 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
// filter own keys
|
||||
if strings.HasPrefix(k, "x-api-field-") {
|
||||
matches[strings.TrimPrefix(k, "x-api-field-")] = v
|
||||
|
||||
delete(md, k)
|
||||
} else if k == "x-api-body" {
|
||||
bodydst = v
|
||||
|
||||
delete(md, k)
|
||||
}
|
||||
}
|
||||
@@ -344,10 +365,12 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
// get fields from url values
|
||||
if len(r.URL.RawQuery) > 0 {
|
||||
umd := make(map[string]interface{})
|
||||
|
||||
err = qson.Unmarshal(&umd, r.URL.RawQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range umd {
|
||||
matches[k] = v
|
||||
}
|
||||
@@ -362,24 +385,29 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
req[k] = v
|
||||
continue
|
||||
}
|
||||
|
||||
em := make(map[string]interface{})
|
||||
em[ps[len(ps)-1]] = v
|
||||
|
||||
for i := len(ps) - 2; i > 0; i-- {
|
||||
nm := make(map[string]interface{})
|
||||
nm[ps[i]] = em
|
||||
em = nm
|
||||
}
|
||||
|
||||
if vm, ok := req[ps[0]]; ok {
|
||||
// nested map
|
||||
nm := vm.(map[string]interface{})
|
||||
for vk, vv := range em {
|
||||
nm[vk] = vv
|
||||
}
|
||||
|
||||
req[ps[0]] = nm
|
||||
} else {
|
||||
req[ps[0]] = em
|
||||
}
|
||||
}
|
||||
|
||||
pathbuf := []byte("{}")
|
||||
if len(req) > 0 {
|
||||
pathbuf, err = json.Marshal(req)
|
||||
@@ -389,48 +417,55 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
}
|
||||
|
||||
urlbuf := []byte("{}")
|
||||
|
||||
out, err := jsonpatch.MergeMergePatches(urlbuf, pathbuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case "GET":
|
||||
case http.MethodGet:
|
||||
// empty response
|
||||
if strings.Contains(ct, "application/json") && string(out) == "{}" {
|
||||
if strings.Contains(myCt, "application/json") && string(out) == "{}" {
|
||||
return out, nil
|
||||
} else if string(out) == "{}" && !strings.Contains(ct, "application/json") {
|
||||
} else if string(out) == "{}" && !strings.Contains(myCt, "application/json") {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
return out, nil
|
||||
case "PATCH", "POST", "PUT", "DELETE":
|
||||
case http.MethodPatch, http.MethodPost, http.MethodPut, http.MethodDelete:
|
||||
bodybuf := []byte("{}")
|
||||
buf := bufferPool.Get()
|
||||
|
||||
defer bufferPool.Put(buf)
|
||||
if _, err := buf.ReadFrom(r.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b := buf.Bytes(); len(b) > 0 {
|
||||
bodybuf = b
|
||||
}
|
||||
|
||||
if bodydst == "" || bodydst == "*" {
|
||||
if out, err = jsonpatch.MergeMergePatches(out, bodybuf); err == nil {
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
|
||||
var jsonbody map[string]interface{}
|
||||
if json.Valid(bodybuf) {
|
||||
if err = json.Unmarshal(bodybuf, &jsonbody); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
dstmap := make(map[string]interface{})
|
||||
ps := strings.Split(bodydst, ".")
|
||||
if len(ps) == 1 {
|
||||
if jsonbody != nil {
|
||||
dstmap[ps[0]] = jsonbody
|
||||
} else {
|
||||
// old unexpected behaviour
|
||||
// old unexpected behavior
|
||||
dstmap[ps[0]] = bodybuf
|
||||
}
|
||||
} else {
|
||||
@@ -438,7 +473,7 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
if jsonbody != nil {
|
||||
em[ps[len(ps)-1]] = jsonbody
|
||||
} else {
|
||||
// old unexpected behaviour
|
||||
// old unexpected behavior
|
||||
em[ps[len(ps)-1]] = bodybuf
|
||||
}
|
||||
for i := len(ps) - 2; i > 0; i-- {
|
||||
@@ -458,41 +493,41 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
//fallback to previous unknown behaviour
|
||||
return bodybuf, nil
|
||||
|
||||
}
|
||||
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
func writeError(w http.ResponseWriter, r *http.Request, err error) error {
|
||||
func writeError(rsp http.ResponseWriter, req *http.Request, err error) error {
|
||||
ce := errors.Parse(err.Error())
|
||||
|
||||
switch ce.Code {
|
||||
case 0:
|
||||
// assuming it's totally screwed
|
||||
ce.Code = 500
|
||||
ce.Code = http.StatusInternalServerError
|
||||
ce.Id = packageID
|
||||
ce.Status = http.StatusText(500)
|
||||
ce.Status = http.StatusText(http.StatusInternalServerError)
|
||||
ce.Detail = "error during request: " + ce.Detail
|
||||
w.WriteHeader(500)
|
||||
|
||||
rsp.WriteHeader(http.StatusInternalServerError)
|
||||
default:
|
||||
w.WriteHeader(int(ce.Code))
|
||||
rsp.WriteHeader(int(ce.Code))
|
||||
}
|
||||
|
||||
// response content type
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
rsp.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Set trailers
|
||||
if strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
|
||||
w.Header().Set("Trailer", "grpc-status")
|
||||
w.Header().Set("Trailer", "grpc-message")
|
||||
w.Header().Set("grpc-status", "13")
|
||||
w.Header().Set("grpc-message", ce.Detail)
|
||||
if strings.Contains(req.Header.Get("Content-Type"), "application/grpc") {
|
||||
rsp.Header().Set("Trailer", "grpc-status")
|
||||
rsp.Header().Set("Trailer", "grpc-message")
|
||||
rsp.Header().Set("grpc-status", "13")
|
||||
rsp.Header().Set("grpc-message", ce.Detail)
|
||||
}
|
||||
|
||||
_, werr := w.Write([]byte(ce.Error()))
|
||||
_, werr := rsp.Write([]byte(ce.Error()))
|
||||
|
||||
return werr
|
||||
}
|
||||
|
||||
@@ -515,11 +550,14 @@ func writeResponse(w http.ResponseWriter, r *http.Request, rsp []byte) error {
|
||||
|
||||
// write response
|
||||
_, err := w.Write(rsp)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewHandler returns a new RPC handler.
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
options := handler.NewOptions(opts...)
|
||||
|
||||
return &rpcHandler{
|
||||
opts: options,
|
||||
}
|
||||
|
||||
+35
-23
@@ -12,41 +12,41 @@ import (
|
||||
"github.com/gobwas/httphead"
|
||||
"github.com/gobwas/ws"
|
||||
"github.com/gobwas/ws/wsutil"
|
||||
|
||||
"go-micro.dev/v4/api/router"
|
||||
"go-micro.dev/v4/client"
|
||||
raw "go-micro.dev/v4/codec/bytes"
|
||||
"go-micro.dev/v4/selector"
|
||||
)
|
||||
|
||||
// serveWebsocket will stream rpc back over websockets assuming json
|
||||
// serveWebsocket will stream rpc back over websockets assuming json.
|
||||
func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request, service *router.Route, c client.Client) (err error) {
|
||||
var op ws.OpCode
|
||||
var opCode ws.OpCode
|
||||
|
||||
ct := r.Header.Get("Content-Type")
|
||||
myCt := r.Header.Get("Content-Type")
|
||||
// Strip charset from Content-Type (like `application/json; charset=UTF-8`)
|
||||
if idx := strings.IndexRune(ct, ';'); idx >= 0 {
|
||||
ct = ct[:idx]
|
||||
if idx := strings.IndexRune(myCt, ';'); idx >= 0 {
|
||||
myCt = myCt[:idx]
|
||||
}
|
||||
|
||||
// check proto from request
|
||||
switch ct {
|
||||
switch myCt {
|
||||
case "application/json":
|
||||
op = ws.OpText
|
||||
opCode = ws.OpText
|
||||
default:
|
||||
op = ws.OpBinary
|
||||
opCode = ws.OpBinary
|
||||
}
|
||||
|
||||
hdr := make(http.Header)
|
||||
|
||||
if proto, ok := r.Header["Sec-Websocket-Protocol"]; ok {
|
||||
for _, p := range proto {
|
||||
switch p {
|
||||
case "binary":
|
||||
if p == "binary" {
|
||||
hdr["Sec-WebSocket-Protocol"] = []string{"binary"}
|
||||
op = ws.OpBinary
|
||||
opCode = ws.OpBinary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
payload, err := requestPayload(r)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -67,7 +67,7 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
Header: hdr,
|
||||
}
|
||||
|
||||
conn, rw, _, err := upgrader.Upgrade(r, w)
|
||||
conn, uRw, _, err := upgrader.Upgrade(r, w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -79,8 +79,9 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
}()
|
||||
|
||||
var request interface{}
|
||||
|
||||
if !bytes.Equal(payload, []byte(`{}`)) {
|
||||
switch ct {
|
||||
switch myCt {
|
||||
case "application/json", "":
|
||||
m := json.RawMessage(payload)
|
||||
request = &m
|
||||
@@ -90,20 +91,25 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
}
|
||||
|
||||
// we always need to set content type for message
|
||||
if ct == "" {
|
||||
ct = "application/json"
|
||||
if myCt == "" {
|
||||
myCt = "application/json"
|
||||
}
|
||||
|
||||
req := c.NewRequest(
|
||||
service.Service,
|
||||
service.Endpoint.Name,
|
||||
request,
|
||||
client.WithContentType(ct),
|
||||
client.WithContentType(myCt),
|
||||
client.StreamingRequest(),
|
||||
)
|
||||
|
||||
cCtx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
so := selector.WithStrategy(strategy(service.Versions))
|
||||
|
||||
// create a new stream
|
||||
stream, err := c.Stream(ctx, req, client.WithSelectOption(so))
|
||||
stream, err := c.Stream(cCtx, req, client.WithSelectOption(so))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -115,7 +121,7 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
}
|
||||
|
||||
go func() {
|
||||
if wErr := writeLoop(rw, stream); wErr != nil && err == nil {
|
||||
if wErr := writeLoop(uRw, stream); wErr != nil && err == nil {
|
||||
err = wErr
|
||||
}
|
||||
}()
|
||||
@@ -137,21 +143,23 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
if strings.Contains(err.Error(), "context canceled") {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// write the response
|
||||
if err = wsutil.WriteServerMessage(rw, op, buf); err != nil {
|
||||
if err = wsutil.WriteServerMessage(uRw, opCode, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = rw.Flush(); err != nil {
|
||||
|
||||
if err = uRw.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// writeLoop
|
||||
// writeLoop.
|
||||
func writeLoop(rw io.ReadWriter, stream client.Stream) error {
|
||||
// close stream when done
|
||||
defer stream.Close()
|
||||
@@ -171,10 +179,12 @@ func writeLoop(rw io.ReadWriter, stream client.Stream) error {
|
||||
case ws.StatusNormalClosure, ws.StatusNoStatusRcvd:
|
||||
// this happens when user close ws connection, or we don't get any status
|
||||
return nil
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
switch op {
|
||||
default:
|
||||
// not relevant
|
||||
@@ -211,6 +221,7 @@ func isStream(r *http.Request, srv *router.Route) bool {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -222,6 +233,7 @@ func isWebSocket(r *http.Request) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
+13
-1
@@ -2,6 +2,9 @@ package api
|
||||
|
||||
import (
|
||||
"go-micro.dev/v4/api/router"
|
||||
registry2 "go-micro.dev/v4/api/router/registry"
|
||||
"go-micro.dev/v4/client"
|
||||
"go-micro.dev/v4/registry"
|
||||
)
|
||||
|
||||
func NewOptions(opts ...Option) Options {
|
||||
@@ -16,10 +19,19 @@ func NewOptions(opts ...Option) Options {
|
||||
return options
|
||||
}
|
||||
|
||||
// WithRouter sets the router to use e.g static or registry
|
||||
// WithRouter sets the router to use e.g static or registry.
|
||||
func WithRouter(r router.Router) Option {
|
||||
return func(o *Options) error {
|
||||
o.Router = r
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithRegistry sets the api's client and router to use registry.
|
||||
func WithRegistry(r registry.Registry) Option {
|
||||
return func(o *Options) error {
|
||||
o.Client = client.NewClient(client.Registry(r))
|
||||
o.Router = registry2.NewRouter(router.WithRegistry(r))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// NewOptions returns new initialised options
|
||||
// NewOptions wires options together.
|
||||
func NewOptions(opts ...Option) Options {
|
||||
var options Options
|
||||
for _, o := range opts {
|
||||
@@ -18,14 +18,14 @@ func NewOptions(opts ...Option) Options {
|
||||
return options
|
||||
}
|
||||
|
||||
// WithHandler sets the handler being used
|
||||
// WithHandler sets the handler being used.
|
||||
func WithHandler(h string) Option {
|
||||
return func(o *Options) {
|
||||
o.Handler = h
|
||||
}
|
||||
}
|
||||
|
||||
// WithNamespace sets the function which determines the namespace for a request
|
||||
// WithNamespace sets the function which determines the namespace for a request.
|
||||
func WithNamespace(n func(*http.Request) string) Option {
|
||||
return func(o *Options) {
|
||||
o.Namespace = n
|
||||
|
||||
+6
-4
@@ -11,13 +11,13 @@ var (
|
||||
ErrInvalidPath = errors.New("invalid path")
|
||||
)
|
||||
|
||||
// Resolver resolves requests to endpoints
|
||||
// Resolver resolves requests to endpoints.
|
||||
type Resolver interface {
|
||||
Resolve(r *http.Request) (*Endpoint, error)
|
||||
String() string
|
||||
}
|
||||
|
||||
// Endpoint is the endpoint for a http request
|
||||
// Endpoint is the endpoint for a http request.
|
||||
type Endpoint struct {
|
||||
// e.g greeter
|
||||
Name string
|
||||
@@ -29,14 +29,16 @@ type Endpoint struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Options is a struct of available options.
|
||||
type Options struct {
|
||||
Handler string
|
||||
Namespace func(*http.Request) string
|
||||
Handler string
|
||||
}
|
||||
|
||||
// Option is a helper for a single option.
|
||||
type Option func(o *Options)
|
||||
|
||||
// StaticNamespace returns the same namespace for each request
|
||||
// StaticNamespace returns the same namespace for each request.
|
||||
func StaticNamespace(ns string) func(*http.Request) string {
|
||||
return func(*http.Request) string {
|
||||
return ns
|
||||
|
||||
+4
-1
@@ -1,4 +1,4 @@
|
||||
// Package vpath resolves using http path and recognised versioned urls
|
||||
// Package vpath resolves using http path and recognized versioned urls
|
||||
package vpath
|
||||
|
||||
import (
|
||||
@@ -10,10 +10,12 @@ import (
|
||||
"go-micro.dev/v4/api/resolver"
|
||||
)
|
||||
|
||||
// NewResolver returns a new vpath resolver.
|
||||
func NewResolver(opts ...resolver.Option) resolver.Resolver {
|
||||
return &Resolver{opts: resolver.NewOptions(opts...)}
|
||||
}
|
||||
|
||||
// Resolver is a vpath resolver.
|
||||
type Resolver struct {
|
||||
opts resolver.Options
|
||||
}
|
||||
@@ -22,6 +24,7 @@ var (
|
||||
re = regexp.MustCompile("^v[0-9]+$")
|
||||
)
|
||||
|
||||
// Resolve resolves a http.Request to an grpc Endpoint.
|
||||
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
|
||||
if req.URL.Path == "/" {
|
||||
return nil, errors.New("unknown name")
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ func slice(s string) []string {
|
||||
return sl
|
||||
}
|
||||
|
||||
// Encode encodes an endpoint to endpoint metadata
|
||||
// Encode encodes an endpoint to endpoint metadata.
|
||||
func Encode(e *Endpoint) map[string]string {
|
||||
if e == nil {
|
||||
return nil
|
||||
@@ -49,7 +49,7 @@ func Encode(e *Endpoint) map[string]string {
|
||||
return ep
|
||||
}
|
||||
|
||||
// Decode decodes endpoint metadata into an endpoint
|
||||
// Decode decodes endpoint metadata into an endpoint.
|
||||
func Decode(e map[string]string) *Endpoint {
|
||||
if e == nil {
|
||||
return nil
|
||||
@@ -65,7 +65,7 @@ func Decode(e map[string]string) *Endpoint {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validates an endpoint to guarantee it won't blow up when being served
|
||||
// Validate validates an endpoint to guarantee it won't blow up when being served.
|
||||
func Validate(e *Endpoint) error {
|
||||
if e == nil {
|
||||
return errors.New("endpoint is nil")
|
||||
|
||||
+5
-2
@@ -7,15 +7,18 @@ import (
|
||||
"go-micro.dev/v4/registry"
|
||||
)
|
||||
|
||||
// Options is a struct of options available.
|
||||
type Options struct {
|
||||
Handler string
|
||||
Registry registry.Registry
|
||||
Resolver resolver.Resolver
|
||||
Logger logger.Logger
|
||||
Handler string
|
||||
}
|
||||
|
||||
// Option is a helper for a single options.
|
||||
type Option func(o *Options)
|
||||
|
||||
// NewOptions wires options together.
|
||||
func NewOptions(opts ...Option) Options {
|
||||
options := Options{
|
||||
Handler: "meta",
|
||||
@@ -54,7 +57,7 @@ func WithResolver(r resolver.Resolver) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger sets the underline logger
|
||||
// WithLogger sets the underline logger.
|
||||
func WithLogger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
|
||||
+62
-21
@@ -18,25 +18,26 @@ import (
|
||||
"go-micro.dev/v4/registry/cache"
|
||||
)
|
||||
|
||||
// endpoint struct, that holds compiled pcre
|
||||
// endpoint struct, that holds compiled pcre.
|
||||
type endpoint struct {
|
||||
hostregs []*regexp.Regexp
|
||||
pathregs []util.Pattern
|
||||
pcreregs []*regexp.Regexp
|
||||
}
|
||||
|
||||
// router is the default router
|
||||
// router is the default router.
|
||||
type registryRouter struct {
|
||||
exit chan bool
|
||||
opts router.Options
|
||||
|
||||
// registry cache
|
||||
rc cache.Cache
|
||||
|
||||
sync.RWMutex
|
||||
eps map[string]*router.Route
|
||||
exit chan bool
|
||||
eps map[string]*router.Route
|
||||
// compiled regexp for host and path
|
||||
ceps map[string]*endpoint
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (r *registryRouter) isStopped() bool {
|
||||
@@ -48,17 +49,20 @@ func (r *registryRouter) isStopped() bool {
|
||||
}
|
||||
}
|
||||
|
||||
// refresh list of api services
|
||||
// refresh list of api services.
|
||||
func (r *registryRouter) refresh() {
|
||||
var attempts int
|
||||
|
||||
logger := r.Options().Logger
|
||||
|
||||
for {
|
||||
services, err := r.opts.Registry.ListServices()
|
||||
if err != nil {
|
||||
attempts++
|
||||
|
||||
logger.Logf(log.ErrorLevel, "unable to list services: %v", err)
|
||||
time.Sleep(time.Duration(attempts) * time.Second)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -71,6 +75,7 @@ func (r *registryRouter) refresh() {
|
||||
logger.Logf(log.ErrorLevel, "unable to get service: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
r.store(service)
|
||||
}
|
||||
|
||||
@@ -84,7 +89,7 @@ func (r *registryRouter) refresh() {
|
||||
}
|
||||
}
|
||||
|
||||
// process watch event
|
||||
// process watch event.
|
||||
func (r *registryRouter) process(res *registry.Result) {
|
||||
logger := r.Options().Logger
|
||||
// skip these things
|
||||
@@ -103,7 +108,7 @@ func (r *registryRouter) process(res *registry.Result) {
|
||||
r.store(service)
|
||||
}
|
||||
|
||||
// store local endpoint cache
|
||||
// store local endpoint cache.
|
||||
func (r *registryRouter) store(services []*registry.Service) {
|
||||
logger := r.Options().Logger
|
||||
// endpoints
|
||||
@@ -169,11 +174,13 @@ func (r *registryRouter) store(services []*registry.Service) {
|
||||
if h == "" || h == "*" {
|
||||
continue
|
||||
}
|
||||
|
||||
hostreg, err := regexp.CompilePOSIX(h)
|
||||
if err != nil {
|
||||
logger.Logf(log.TraceLevel, "endpoint have invalid host regexp: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
cep.hostregs = append(cep.hostregs, hostreg)
|
||||
}
|
||||
|
||||
@@ -197,11 +204,13 @@ func (r *registryRouter) store(services []*registry.Service) {
|
||||
}
|
||||
|
||||
tpl := rule.Compile()
|
||||
|
||||
pathreg, err := util.NewPattern(tpl.Version, tpl.OpCodes, tpl.Pool, "", util.PatternLogger(logger))
|
||||
if err != nil {
|
||||
logger.Logf(log.TraceLevel, "endpoint have invalid path pattern: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
cep.pathregs = append(cep.pathregs, pathreg)
|
||||
}
|
||||
|
||||
@@ -209,9 +218,10 @@ func (r *registryRouter) store(services []*registry.Service) {
|
||||
}
|
||||
}
|
||||
|
||||
// watch for endpoint changes
|
||||
// watch for endpoint changes.
|
||||
func (r *registryRouter) watch() {
|
||||
var attempts int
|
||||
|
||||
logger := r.Options().Logger
|
||||
|
||||
for {
|
||||
@@ -223,8 +233,10 @@ func (r *registryRouter) watch() {
|
||||
w, err := r.opts.Registry.Watch()
|
||||
if err != nil {
|
||||
attempts++
|
||||
|
||||
logger.Logf(log.ErrorLevel, "error watching endpoints: %v", err)
|
||||
time.Sleep(time.Duration(attempts) * time.Second)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -248,8 +260,10 @@ func (r *registryRouter) watch() {
|
||||
if err != nil {
|
||||
logger.Logf(log.ErrorLevel, "error getting next endoint: %v", err)
|
||||
close(ch)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
r.process(res)
|
||||
}
|
||||
}
|
||||
@@ -267,6 +281,7 @@ func (r *registryRouter) Stop() error {
|
||||
close(r.exit)
|
||||
r.rc.Stop()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -280,6 +295,7 @@ func (r *registryRouter) Deregister(ep *router.Route) error {
|
||||
|
||||
func (r *registryRouter) Endpoint(req *http.Request) (*router.Route, error) {
|
||||
logger := r.Options().Logger
|
||||
|
||||
if r.isStopped() {
|
||||
return nil, errors.New("router closed")
|
||||
}
|
||||
@@ -291,16 +307,19 @@ func (r *registryRouter) Endpoint(req *http.Request) (*router.Route, error) {
|
||||
if len(req.URL.Path) > 0 && req.URL.Path != "/" {
|
||||
idx = 1
|
||||
}
|
||||
|
||||
path := strings.Split(req.URL.Path[idx:], "/")
|
||||
|
||||
// use the first match
|
||||
// TODO: weighted matching
|
||||
for n, e := range r.eps {
|
||||
for n, endpoint := range r.eps {
|
||||
cep, ok := r.ceps[n]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
ep := e.Endpoint
|
||||
|
||||
ep := endpoint.Endpoint
|
||||
|
||||
var mMatch, hMatch, pMatch bool
|
||||
// 1. try method
|
||||
for _, m := range ep.Method {
|
||||
@@ -309,6 +328,7 @@ func (r *registryRouter) Endpoint(req *http.Request) (*router.Route, error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !mMatch {
|
||||
continue
|
||||
}
|
||||
@@ -323,14 +343,13 @@ func (r *registryRouter) Endpoint(req *http.Request) (*router.Route, error) {
|
||||
if h == "" || h == "*" {
|
||||
hMatch = true
|
||||
break
|
||||
} else {
|
||||
if cep.hostregs[idx].MatchString(req.URL.Host) {
|
||||
hMatch = true
|
||||
break
|
||||
}
|
||||
} else if cep.hostregs[idx].MatchString(req.URL.Host) {
|
||||
hMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !hMatch {
|
||||
continue
|
||||
}
|
||||
@@ -344,17 +363,23 @@ func (r *registryRouter) Endpoint(req *http.Request) (*router.Route, error) {
|
||||
logger.Logf(log.DebugLevel, "api gpath not match %s != %v", path, pathreg)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Logf(log.DebugLevel, "api gpath match %s = %v", path, pathreg)
|
||||
|
||||
pMatch = true
|
||||
ctx := req.Context()
|
||||
|
||||
md, ok := metadata.FromContext(ctx)
|
||||
if !ok {
|
||||
md = make(metadata.Metadata)
|
||||
}
|
||||
|
||||
for k, v := range matches {
|
||||
md[fmt.Sprintf("x-api-field-%s", k)] = v
|
||||
}
|
||||
|
||||
*req = *req.Clone(metadata.NewContext(ctx, md))
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -365,8 +390,11 @@ func (r *registryRouter) Endpoint(req *http.Request) (*router.Route, error) {
|
||||
logger.Logf(log.DebugLevel, "api pcre path not match %s != %v", path, pathreg)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Logf(log.DebugLevel, "api pcre path match %s != %v", path, pathreg)
|
||||
|
||||
pMatch = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -377,7 +405,7 @@ func (r *registryRouter) Endpoint(req *http.Request) (*router.Route, error) {
|
||||
|
||||
// TODO: Percentage traffic
|
||||
// we got here, so its a match
|
||||
return e, nil
|
||||
return endpoint, nil
|
||||
}
|
||||
|
||||
// no match
|
||||
@@ -400,13 +428,13 @@ func (r *registryRouter) Route(req *http.Request) (*router.Route, error) {
|
||||
// TODO: don't ignore that shit
|
||||
|
||||
// get the service name
|
||||
rp, err := r.opts.Resolver.Resolve(req)
|
||||
rsp, err := r.opts.Resolver.Resolve(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// service name
|
||||
name := rp.Name
|
||||
name := rsp.Name
|
||||
|
||||
// get service
|
||||
services, err := r.rc.GetService(name)
|
||||
@@ -425,11 +453,22 @@ func (r *registryRouter) Route(req *http.Request) (*router.Route, error) {
|
||||
handler = "rpc"
|
||||
}
|
||||
|
||||
// extract endpoint from Path, case-sensitive
|
||||
// just test it in this case, maybe should put the code somewhere else
|
||||
ep_name := rsp.Method
|
||||
comps := strings.Split(rsp.Path, "/")
|
||||
switch len(comps) {
|
||||
case 3:
|
||||
ep_name = comps[1] + "." + comps[2]
|
||||
case 4:
|
||||
ep_name = comps[2] + "." + comps[3]
|
||||
}
|
||||
|
||||
// construct api service
|
||||
return &router.Route{
|
||||
Service: name,
|
||||
Endpoint: &router.Endpoint{
|
||||
Name: rp.Method,
|
||||
Name: ep_name,
|
||||
Handler: handler,
|
||||
},
|
||||
Versions: services,
|
||||
@@ -462,12 +501,14 @@ func newRouter(opts ...router.Option) *registryRouter {
|
||||
eps: make(map[string]*router.Route),
|
||||
ceps: make(map[string]*endpoint),
|
||||
}
|
||||
|
||||
go r.watch()
|
||||
go r.refresh()
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// NewRouter returns the default router
|
||||
// NewRouter returns the default router.
|
||||
func NewRouter(opts ...router.Option) router.Router {
|
||||
return newRouter(opts...)
|
||||
}
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ import (
|
||||
"go-micro.dev/v4/registry"
|
||||
)
|
||||
|
||||
// Router is used to determine an endpoint for a request
|
||||
// Router is used to determine an endpoint for a request.
|
||||
type Router interface {
|
||||
// Returns options
|
||||
Options() Options
|
||||
@@ -30,7 +30,7 @@ type Route struct {
|
||||
Versions []*registry.Service
|
||||
}
|
||||
|
||||
// Endpoint is a mapping between an RPC method and HTTP endpoint
|
||||
// Endpoint is a mapping between an RPC method and HTTP endpoint.
|
||||
type Endpoint struct {
|
||||
// RPC Method e.g. Greeter.Hello
|
||||
Name string
|
||||
|
||||
+16
-9
@@ -1,6 +1,7 @@
|
||||
package util
|
||||
|
||||
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/compile.go
|
||||
// download from
|
||||
// https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/compile.go
|
||||
|
||||
const (
|
||||
opcodeVersion = 1
|
||||
@@ -8,18 +9,18 @@ const (
|
||||
|
||||
// Template is a compiled representation of path templates.
|
||||
type Template struct {
|
||||
// Version is the version number of the format.
|
||||
Version int
|
||||
// Verb is a VERB part in the template.
|
||||
Verb string
|
||||
// Original template (example: /v1/a_bit_of_everything)
|
||||
Template string
|
||||
// OpCodes is a sequence of operations.
|
||||
OpCodes []int
|
||||
// Pool is a constant pool
|
||||
Pool []string
|
||||
// Verb is a VERB part in the template.
|
||||
Verb string
|
||||
// Fields is a list of field paths bound in this template.
|
||||
Fields []string
|
||||
// Original template (example: /v1/a_bit_of_everything)
|
||||
Template string
|
||||
// Version is the version number of the format.
|
||||
Version int
|
||||
}
|
||||
|
||||
// Compiler compiles utilities representation of path templates into marshallable operations.
|
||||
@@ -29,13 +30,14 @@ type Compiler interface {
|
||||
}
|
||||
|
||||
type op struct {
|
||||
// code is the opcode of the operation
|
||||
code OpCode
|
||||
|
||||
// str is a string operand of the code.
|
||||
// operand is ignored if str is not empty.
|
||||
str string
|
||||
|
||||
// code is the opcode of the operation
|
||||
code OpCode
|
||||
|
||||
// operand is a numeric operand of the code.
|
||||
operand int
|
||||
}
|
||||
@@ -66,6 +68,7 @@ func (v variable) compile() []op {
|
||||
for _, s := range v.segments {
|
||||
ops = append(ops, s.compile()...)
|
||||
}
|
||||
|
||||
ops = append(ops, op{
|
||||
code: OpConcatN,
|
||||
operand: len(v.segments),
|
||||
@@ -88,7 +91,9 @@ func (t template) Compile() Template {
|
||||
pool []string
|
||||
fields []string
|
||||
)
|
||||
|
||||
consts := make(map[string]int)
|
||||
|
||||
for _, op := range rawOps {
|
||||
ops = append(ops, int(op.code))
|
||||
if op.str == "" {
|
||||
@@ -100,10 +105,12 @@ func (t template) Compile() Template {
|
||||
}
|
||||
ops = append(ops, consts[op.str])
|
||||
}
|
||||
|
||||
if op.code == OpCapture {
|
||||
fields = append(fields, op.str)
|
||||
}
|
||||
}
|
||||
|
||||
return Template{
|
||||
Version: opcodeVersion,
|
||||
OpCodes: ops,
|
||||
|
||||
+42
-6
@@ -1,6 +1,7 @@
|
||||
package util
|
||||
|
||||
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/parse.go
|
||||
// download from
|
||||
// https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/parse.go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -19,14 +20,15 @@ func (e InvalidTemplateError) Error() string {
|
||||
return fmt.Sprintf("%s: %s", e.msg, e.tmpl)
|
||||
}
|
||||
|
||||
// Parse parses the string representation of path template
|
||||
// Parse parses the string representation of path template.
|
||||
func Parse(tmpl string) (Compiler, error) {
|
||||
if !strings.HasPrefix(tmpl, "/") {
|
||||
return template{}, InvalidTemplateError{tmpl: tmpl, msg: "no leading /"}
|
||||
}
|
||||
tokens, verb := tokenize(tmpl[1:])
|
||||
|
||||
tokens, verb := tokenize(tmpl[1:])
|
||||
p := parser{tokens: tokens}
|
||||
|
||||
segs, err := p.topLevelSegments()
|
||||
if err != nil {
|
||||
return template{}, InvalidTemplateError{tmpl: tmpl, msg: err.Error()}
|
||||
@@ -52,8 +54,10 @@ func tokenize(path string) (tokens []string, verb string) {
|
||||
var (
|
||||
st = init
|
||||
)
|
||||
|
||||
for path != "" {
|
||||
var idx int
|
||||
|
||||
switch st {
|
||||
case init:
|
||||
idx = strings.IndexAny(path, "/{")
|
||||
@@ -62,10 +66,12 @@ func tokenize(path string) (tokens []string, verb string) {
|
||||
case nested:
|
||||
idx = strings.IndexAny(path, "/}")
|
||||
}
|
||||
|
||||
if idx < 0 {
|
||||
tokens = append(tokens, path)
|
||||
break
|
||||
}
|
||||
|
||||
switch r := path[idx]; r {
|
||||
case '/', '.':
|
||||
case '{':
|
||||
@@ -75,30 +81,35 @@ func tokenize(path string) (tokens []string, verb string) {
|
||||
case '}':
|
||||
st = init
|
||||
}
|
||||
|
||||
if idx == 0 {
|
||||
tokens = append(tokens, path[idx:idx+1])
|
||||
} else {
|
||||
tokens = append(tokens, path[:idx], path[idx:idx+1])
|
||||
}
|
||||
|
||||
path = path[idx+1:]
|
||||
}
|
||||
|
||||
l := len(tokens)
|
||||
t := tokens[l-1]
|
||||
|
||||
if idx := strings.LastIndex(t, ":"); idx == 0 {
|
||||
tokens, verb = tokens[:l-1], t[1:]
|
||||
} else if idx > 0 {
|
||||
tokens[l-1], verb = t[:idx], t[idx+1:]
|
||||
}
|
||||
|
||||
tokens = append(tokens, eof)
|
||||
|
||||
return tokens, verb
|
||||
}
|
||||
|
||||
// parser is a parser of the template syntax defined in github.com/googleapis/googleapis/google/api/http.proto.
|
||||
type parser struct {
|
||||
logger log.Logger
|
||||
tokens []string
|
||||
accepted []string
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
// topLevelSegments is the target of this parser.
|
||||
@@ -111,17 +122,22 @@ func (p *parser) topLevelSegments() ([]segment, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Logf(log.DebugLevel, "accept segments: %q; %q", p.accepted, p.tokens)
|
||||
|
||||
if _, err := p.accept(typeEOF); err != nil {
|
||||
return nil, fmt.Errorf("unexpected token %q after segments %q", p.tokens[0], strings.Join(p.accepted, ""))
|
||||
}
|
||||
|
||||
logger.Logf(log.DebugLevel, "accept eof: %q; %q", p.accepted, p.tokens)
|
||||
|
||||
return segs, nil
|
||||
}
|
||||
|
||||
func (p *parser) segments() ([]segment, error) {
|
||||
logger := log.LoggerOrDefault(p.logger)
|
||||
s, err := p.segment()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -133,11 +149,14 @@ func (p *parser) segments() ([]segment, error) {
|
||||
if _, err := p.accept("/"); err != nil {
|
||||
return segs, nil
|
||||
}
|
||||
|
||||
s, err := p.segment()
|
||||
if err != nil {
|
||||
return segs, err
|
||||
}
|
||||
|
||||
segs = append(segs, s)
|
||||
|
||||
logger.Logf(log.DebugLevel, "accept segment: %q; %q", p.accepted, p.tokens)
|
||||
}
|
||||
}
|
||||
@@ -146,17 +165,20 @@ func (p *parser) segment() (segment, error) {
|
||||
if _, err := p.accept("*"); err == nil {
|
||||
return wildcard{}, nil
|
||||
}
|
||||
|
||||
if _, err := p.accept("**"); err == nil {
|
||||
return deepWildcard{}, nil
|
||||
}
|
||||
|
||||
if l, err := p.literal(); err == nil {
|
||||
return l, nil
|
||||
}
|
||||
|
||||
v, err := p.variable()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("segment neither wildcards, literal or variable: %v", err)
|
||||
return nil, fmt.Errorf("segment neither wildcards, literal or variable: %w", err)
|
||||
}
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
||||
@@ -165,6 +187,7 @@ func (p *parser) literal() (segment, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return literal(lit), nil
|
||||
}
|
||||
|
||||
@@ -182,7 +205,7 @@ func (p *parser) variable() (segment, error) {
|
||||
if _, err := p.accept("="); err == nil {
|
||||
segs, err = p.segments()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid segment in variable %q: %v", path, err)
|
||||
return nil, fmt.Errorf("invalid segment in variable %q: %w", path, err)
|
||||
}
|
||||
} else {
|
||||
segs = []segment{wildcard{}}
|
||||
@@ -191,6 +214,7 @@ func (p *parser) variable() (segment, error) {
|
||||
if _, err := p.accept("}"); err != nil {
|
||||
return nil, fmt.Errorf("unterminated variable segment: %s", path)
|
||||
}
|
||||
|
||||
return variable{
|
||||
path: path,
|
||||
segments: segs,
|
||||
@@ -202,7 +226,9 @@ func (p *parser) fieldPath() (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
components := []string{c}
|
||||
|
||||
for {
|
||||
if _, err = p.accept("."); err != nil {
|
||||
return strings.Join(components, "."), nil
|
||||
@@ -238,6 +264,7 @@ const (
|
||||
// If it doesn't match, the function does not consume any tokens and return an error.
|
||||
func (p *parser) accept(term termType) (string, error) {
|
||||
t := p.tokens[0]
|
||||
|
||||
switch term {
|
||||
case "/", "*", "**", ".", "=", "{", "}":
|
||||
if t != string(term) && t != "/" {
|
||||
@@ -258,8 +285,10 @@ func (p *parser) accept(term termType) (string, error) {
|
||||
default:
|
||||
return "", fmt.Errorf("unknown termType %q", term)
|
||||
}
|
||||
|
||||
p.tokens = p.tokens[1:]
|
||||
p.accepted = append(p.accepted, t)
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
@@ -278,6 +307,7 @@ func expectPChars(t string) error {
|
||||
pct1
|
||||
pct2
|
||||
)
|
||||
|
||||
st := init
|
||||
for _, r := range t {
|
||||
if st != init {
|
||||
@@ -316,9 +346,11 @@ func expectPChars(t string) error {
|
||||
return fmt.Errorf("invalid character in path segment: %q(%U)", r, r)
|
||||
}
|
||||
}
|
||||
|
||||
if st != init {
|
||||
return fmt.Errorf("invalid percent-encoding in %q", t)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -327,12 +359,14 @@ func expectIdent(ident string) error {
|
||||
if ident == "" {
|
||||
return fmt.Errorf("empty identifier")
|
||||
}
|
||||
|
||||
for pos, r := range ident {
|
||||
switch {
|
||||
case '0' <= r && r <= '9':
|
||||
if pos == 0 {
|
||||
return fmt.Errorf("identifier starting with digit: %s", ident)
|
||||
}
|
||||
|
||||
continue
|
||||
case 'A' <= r && r <= 'Z':
|
||||
continue
|
||||
@@ -344,6 +378,7 @@ func expectIdent(ident string) error {
|
||||
return fmt.Errorf("invalid character %q(%U) in identifier: %s", r, r, ident)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -356,5 +391,6 @@ func isHexDigit(r rune) bool {
|
||||
case 'a' <= r && r <= 'f':
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
+6
-6
@@ -7,17 +7,17 @@ type OpCode int
|
||||
|
||||
// These constants are the valid values of OpCode.
|
||||
const (
|
||||
// OpNop does nothing
|
||||
// OpNop does nothing.
|
||||
OpNop = OpCode(iota)
|
||||
// OpPush pushes a component to stack
|
||||
// OpPush pushes a component to stack.
|
||||
OpPush
|
||||
// OpLitPush pushes a component to stack if it matches to the literal
|
||||
// OpLitPush pushes a component to stack if it matches to the literal.
|
||||
OpLitPush
|
||||
// OpPushM concatenates the remaining components and pushes it to stack
|
||||
// OpPushM concatenates the remaining components and pushes it to stack.
|
||||
OpPushM
|
||||
// OpConcatN pops N items from stack, concatenates them and pushes it back to stack
|
||||
// OpConcatN pops N items from stack, concatenates them and pushes it back to stack.
|
||||
OpConcatN
|
||||
// OpCapture pops an item and binds it to the variable
|
||||
// OpCapture pops an item and binds it to the variable.
|
||||
OpCapture
|
||||
// OpEnd is the least positive invalid opcode.
|
||||
OpEnd
|
||||
|
||||
+13
-4
@@ -24,6 +24,8 @@ type rop struct {
|
||||
|
||||
// Pattern is a template pattern of http request paths defined in github.com/googleapis/googleapis/google/api/http.proto.
|
||||
type Pattern struct {
|
||||
// verb is the VERB part of the path pattern. It is empty if the pattern does not have VERB part.
|
||||
verb string
|
||||
// ops is a list of operations
|
||||
ops []rop
|
||||
// pool is a constant pool indexed by the operands or vars.
|
||||
@@ -34,22 +36,20 @@ type Pattern struct {
|
||||
stacksize int
|
||||
// tailLen is the length of the fixed-size segments after a deep wildcard
|
||||
tailLen int
|
||||
// verb is the VERB part of the path pattern. It is empty if the pattern does not have VERB part.
|
||||
verb string
|
||||
// assumeColonVerb indicates whether a path suffix after a final
|
||||
// colon may only be interpreted as a verb.
|
||||
assumeColonVerb bool
|
||||
}
|
||||
|
||||
type patternOptions struct {
|
||||
assumeColonVerb bool
|
||||
logger log.Logger
|
||||
assumeColonVerb bool
|
||||
}
|
||||
|
||||
// PatternOpt is an option for creating Patterns.
|
||||
type PatternOpt func(*patternOptions)
|
||||
|
||||
// Logger sets the logger
|
||||
// PatternLogger sets the logger.
|
||||
func PatternLogger(l log.Logger) PatternOpt {
|
||||
return func(po *patternOptions) {
|
||||
po.logger = l
|
||||
@@ -89,8 +89,10 @@ func NewPattern(version int, ops []int, pool []string, verb string, opts ...Patt
|
||||
pushMSeen bool
|
||||
vars []string
|
||||
)
|
||||
|
||||
for i := 0; i < l; i += 2 {
|
||||
op := rop{code: OpCode(ops[i]), operand: ops[i+1]}
|
||||
|
||||
switch op.code {
|
||||
case OpNop:
|
||||
continue
|
||||
@@ -104,6 +106,7 @@ func NewPattern(version int, ops []int, pool []string, verb string, opts ...Patt
|
||||
logger.Logf(log.DebugLevel, "pushM appears twice")
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
|
||||
pushMSeen = true
|
||||
stack++
|
||||
case OpLitPush:
|
||||
@@ -111,6 +114,7 @@ func NewPattern(version int, ops []int, pool []string, verb string, opts ...Patt
|
||||
logger.Logf(log.DebugLevel, "negative literal index: %d", op.operand)
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
|
||||
if pushMSeen {
|
||||
tailLen++
|
||||
}
|
||||
@@ -120,6 +124,7 @@ func NewPattern(version int, ops []int, pool []string, verb string, opts ...Patt
|
||||
logger.Logf(log.DebugLevel, "negative concat size: %d", op.operand)
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
|
||||
stack -= op.operand
|
||||
if stack < 0 {
|
||||
logger.Logf(log.DebugLevel, "stack underflow")
|
||||
@@ -131,10 +136,12 @@ func NewPattern(version int, ops []int, pool []string, verb string, opts ...Patt
|
||||
logger.Logf(log.DebugLevel, "variable name index out of bound: %d", op.operand)
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
}
|
||||
|
||||
v := pool[op.operand]
|
||||
op.operand = len(vars)
|
||||
vars = append(vars, v)
|
||||
stack--
|
||||
|
||||
if stack < 0 {
|
||||
logger.Logf(log.DebugLevel, "stack underflow")
|
||||
return Pattern{}, ErrInvalidPattern
|
||||
@@ -147,8 +154,10 @@ func NewPattern(version int, ops []int, pool []string, verb string, opts ...Patt
|
||||
if maxstack < stack {
|
||||
maxstack = stack
|
||||
}
|
||||
|
||||
typedOps = append(typedOps, op)
|
||||
}
|
||||
|
||||
return Pattern{
|
||||
ops: typedOps,
|
||||
pool: pool,
|
||||
|
||||
+6
-2
@@ -1,6 +1,7 @@
|
||||
package util
|
||||
|
||||
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/types.go
|
||||
// download from
|
||||
// https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/types.go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -8,9 +9,9 @@ import (
|
||||
)
|
||||
|
||||
type template struct {
|
||||
segments []segment
|
||||
verb string
|
||||
template string
|
||||
segments []segment
|
||||
}
|
||||
|
||||
type segment interface {
|
||||
@@ -46,6 +47,7 @@ func (v variable) String() string {
|
||||
for _, s := range v.segments {
|
||||
segs = append(segs, s.String())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("{%s=%s}", v.path, strings.Join(segs, "/"))
|
||||
}
|
||||
|
||||
@@ -54,9 +56,11 @@ func (t template) String() string {
|
||||
for _, s := range t.segments {
|
||||
segs = append(segs, s.String())
|
||||
}
|
||||
|
||||
str := strings.Join(segs, "/")
|
||||
if t.verb != "" {
|
||||
str = fmt.Sprintf("%s:%s", str, t.verb)
|
||||
}
|
||||
|
||||
return "/" + str
|
||||
}
|
||||
|
||||
+3
-3
@@ -9,11 +9,11 @@ import (
|
||||
|
||||
var (
|
||||
// ErrProviderNotImplemented can be returned when attempting to
|
||||
// instantiate an unimplemented provider
|
||||
// instantiate an unimplemented provider.
|
||||
ErrProviderNotImplemented = errors.New("Provider not implemented")
|
||||
)
|
||||
|
||||
// Provider is a ACME provider interface
|
||||
// Provider is a ACME provider interface.
|
||||
type Provider interface {
|
||||
// Listen returns a new listener
|
||||
Listen(...string) (net.Listener, error)
|
||||
@@ -21,7 +21,7 @@ type Provider interface {
|
||||
TLSConfig(...string) (*tls.Config, error)
|
||||
}
|
||||
|
||||
// The Let's Encrypt ACME endpoints
|
||||
// The Let's Encrypt ACME endpoints.
|
||||
const (
|
||||
LetsEncryptStagingCA = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
LetsEncryptProductionCA = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
|
||||
+14
-15
@@ -2,26 +2,17 @@ package acme
|
||||
|
||||
import (
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
|
||||
"go-micro.dev/v4/logger"
|
||||
)
|
||||
|
||||
// Option (or Options) are passed to New() to configure providers
|
||||
// Option (or Options) are passed to New() to configure providers.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options represents various options you can present to ACME providers
|
||||
// Options represents various options you can present to ACME providers.
|
||||
type Options struct {
|
||||
// AcceptTLS must be set to true to indicate that you have read your
|
||||
// provider's terms of service.
|
||||
AcceptToS bool
|
||||
// CA is the CA to use
|
||||
CA string
|
||||
// ChallengeProvider is a go-acme/lego challenge provider. Set this if you
|
||||
// want to use DNS Challenges. Otherwise, tls-alpn-01 will be used
|
||||
ChallengeProvider challenge.Provider
|
||||
// Issue certificates for domains on demand. Otherwise, certs will be
|
||||
// retrieved / issued on start-up.
|
||||
OnDemand bool
|
||||
// Cache is a storage interface. Most ACME libraries have an cache, but
|
||||
// there's no defined interface, so if you consume this option
|
||||
// sanity check it before using.
|
||||
@@ -29,16 +20,24 @@ type Options struct {
|
||||
|
||||
// Logger is the underling logging framework
|
||||
Logger logger.Logger
|
||||
// CA is the CA to use
|
||||
CA string
|
||||
// AcceptTLS must be set to true to indicate that you have read your
|
||||
// provider's terms of service.
|
||||
AcceptToS bool
|
||||
// Issue certificates for domains on demand. Otherwise, certs will be
|
||||
// retrieved / issued on start-up.
|
||||
OnDemand bool
|
||||
}
|
||||
|
||||
// AcceptToS indicates whether you accept your CA's terms of service
|
||||
// AcceptToS indicates whether you accept your CA's terms of service.
|
||||
func AcceptToS(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.AcceptToS = b
|
||||
}
|
||||
}
|
||||
|
||||
// CA sets the CA of an acme.Options
|
||||
// CA sets the CA of an acme.Options.
|
||||
func CA(CA string) Option {
|
||||
return func(o *Options) {
|
||||
o.CA = CA
|
||||
@@ -63,14 +62,14 @@ func OnDemand(b bool) Option {
|
||||
|
||||
// Cache provides a cache / storage interface to the underlying ACME library
|
||||
// as there is no standard, this needs to be validated by the underlying
|
||||
// implentation.
|
||||
// implementation.
|
||||
func Cache(c interface{}) Option {
|
||||
return func(o *Options) {
|
||||
o.Cache = c
|
||||
}
|
||||
}
|
||||
|
||||
// Logger sets the underline logger
|
||||
// Logger sets the underline logger.
|
||||
func Logger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
|
||||
+4
-4
@@ -6,12 +6,12 @@ import (
|
||||
|
||||
type Config struct {
|
||||
AllowOrigin string
|
||||
AllowCredentials bool
|
||||
AllowMethods string
|
||||
AllowHeaders string
|
||||
AllowCredentials bool
|
||||
}
|
||||
|
||||
// CombinedCORSHandler wraps a server and provides CORS headers
|
||||
// CombinedCORSHandler wraps a server and provides CORS headers.
|
||||
func CombinedCORSHandler(h http.Handler, config *Config) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if config != nil {
|
||||
@@ -25,7 +25,7 @@ func CombinedCORSHandler(h http.Handler, config *Config) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
// SetHeaders sets the CORS headers
|
||||
// SetHeaders sets the CORS headers.
|
||||
func SetHeaders(w http.ResponseWriter, _ *http.Request, config *Config) {
|
||||
set := func(w http.ResponseWriter, k, v string) {
|
||||
if v := w.Header().Get(k); len(v) > 0 {
|
||||
@@ -33,7 +33,7 @@ func SetHeaders(w http.ResponseWriter, _ *http.Request, config *Config) {
|
||||
}
|
||||
w.Header().Set(k, v)
|
||||
}
|
||||
//For forward-compatible code, default values may not be provided in the future
|
||||
// For forward-compatible code, default values may not be provided in the future
|
||||
if config.AllowCredentials {
|
||||
set(w, "Access-Control-Allow-Credentials", "true")
|
||||
} else {
|
||||
|
||||
+5
-5
@@ -9,19 +9,19 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
|
||||
"go-micro.dev/v4/api/server"
|
||||
"go-micro.dev/v4/api/server/cors"
|
||||
log "go-micro.dev/v4/logger"
|
||||
)
|
||||
|
||||
type httpServer struct {
|
||||
mux *http.ServeMux
|
||||
opts server.Options
|
||||
|
||||
mtx sync.RWMutex
|
||||
address string
|
||||
mux *http.ServeMux
|
||||
exit chan chan error
|
||||
address string
|
||||
|
||||
mtx sync.RWMutex
|
||||
}
|
||||
|
||||
func NewServer(address string, opts ...server.Option) server.Server {
|
||||
@@ -94,7 +94,7 @@ func (s *httpServer) Start() error {
|
||||
go func() {
|
||||
if err := http.Serve(l, s.mux); err != nil {
|
||||
// temporary fix
|
||||
//logger.Log(log.FatalLevel, err)
|
||||
// logger.Log(log.FatalLevel, err)
|
||||
logger.Log(log.ErrorLevel, err)
|
||||
}
|
||||
}()
|
||||
|
||||
+10
-11
@@ -4,26 +4,25 @@ import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
|
||||
"go-micro.dev/v4/api/server/cors"
|
||||
"go-micro.dev/v4/logger"
|
||||
|
||||
"go-micro.dev/v4/api/resolver"
|
||||
"go-micro.dev/v4/api/server/acme"
|
||||
"go-micro.dev/v4/api/server/cors"
|
||||
"go-micro.dev/v4/logger"
|
||||
)
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
type Options struct {
|
||||
ACMEProvider acme.Provider
|
||||
Resolver resolver.Resolver
|
||||
Logger logger.Logger
|
||||
CORSConfig *cors.Config
|
||||
TLSConfig *tls.Config
|
||||
ACMEHosts []string
|
||||
Wrappers []Wrapper
|
||||
EnableACME bool
|
||||
EnableCORS bool
|
||||
CORSConfig *cors.Config
|
||||
ACMEProvider acme.Provider
|
||||
EnableTLS bool
|
||||
ACMEHosts []string
|
||||
TLSConfig *tls.Config
|
||||
Resolver resolver.Resolver
|
||||
Wrappers []Wrapper
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type Wrapper func(h http.Handler) http.Handler
|
||||
@@ -94,7 +93,7 @@ func Resolver(r resolver.Resolver) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Logger sets the underline logging framework
|
||||
// Logger sets the underline logging framework.
|
||||
func Logger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Server serves api requests
|
||||
// Server serves api requests.
|
||||
type Server interface {
|
||||
Address() string
|
||||
Init(opts ...Option) error
|
||||
|
||||
Vendored
+27
-27
@@ -8,22 +8,22 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// BearerScheme used for Authorization header
|
||||
// BearerScheme used for Authorization header.
|
||||
BearerScheme = "Bearer "
|
||||
// ScopePublic is the scope applied to a rule to allow access to the public
|
||||
// ScopePublic is the scope applied to a rule to allow access to the public.
|
||||
ScopePublic = ""
|
||||
// ScopeAccount is the scope applied to a rule to limit to users with any valid account
|
||||
// ScopeAccount is the scope applied to a rule to limit to users with any valid account.
|
||||
ScopeAccount = "*"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidToken is when the token provided is not valid
|
||||
// ErrInvalidToken is when the token provided is not valid.
|
||||
ErrInvalidToken = errors.New("invalid token provided")
|
||||
// ErrForbidden is when a user does not have the necessary scope to access a resource
|
||||
// ErrForbidden is when a user does not have the necessary scope to access a resource.
|
||||
ErrForbidden = errors.New("resource forbidden")
|
||||
)
|
||||
|
||||
// Auth provides authentication and authorization
|
||||
// Auth provides authentication and authorization.
|
||||
type Auth interface {
|
||||
// Init the auth
|
||||
Init(opts ...Option)
|
||||
@@ -39,7 +39,7 @@ type Auth interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// Rules manages access to resources
|
||||
// Rules manages access to resources.
|
||||
type Rules interface {
|
||||
// Verify an account has access to a resource using the rules
|
||||
Verify(acc *Account, res *Resource, opts ...VerifyOption) error
|
||||
@@ -51,40 +51,40 @@ type Rules interface {
|
||||
List(...ListOption) ([]*Rule, error)
|
||||
}
|
||||
|
||||
// Account provided by an auth provider
|
||||
// Account provided by an auth provider.
|
||||
type Account struct {
|
||||
// Any other associated metadata
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
// ID of the account e.g. email
|
||||
ID string `json:"id"`
|
||||
// Type of the account, e.g. service
|
||||
Type string `json:"type"`
|
||||
// Issuer of the account
|
||||
Issuer string `json:"issuer"`
|
||||
// Any other associated metadata
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
// Scopes the account has access to
|
||||
Scopes []string `json:"scopes"`
|
||||
// Secret for the account, e.g. the password
|
||||
Secret string `json:"secret"`
|
||||
// Scopes the account has access to
|
||||
Scopes []string `json:"scopes"`
|
||||
}
|
||||
|
||||
// Token can be short or long lived
|
||||
// Token can be short or long lived.
|
||||
type Token struct {
|
||||
// The token to be used for accessing resources
|
||||
AccessToken string `json:"access_token"`
|
||||
// RefreshToken to be used to generate a new token
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
// Time of token creation
|
||||
Created time.Time `json:"created"`
|
||||
// Time of token expiry
|
||||
Expiry time.Time `json:"expiry"`
|
||||
// The token to be used for accessing resources
|
||||
AccessToken string `json:"access_token"`
|
||||
// RefreshToken to be used to generate a new token
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
// Expired returns a boolean indicating if the token needs to be refreshed
|
||||
// Expired returns a boolean indicating if the token needs to be refreshed.
|
||||
func (t *Token) Expired() bool {
|
||||
return t.Expiry.Unix() < time.Now().Unix()
|
||||
}
|
||||
|
||||
// Resource is an entity such as a user or
|
||||
// Resource is an entity such as a user or.
|
||||
type Resource struct {
|
||||
// Name of the resource, e.g. go.micro.service.notes
|
||||
Name string `json:"name"`
|
||||
@@ -94,25 +94,25 @@ type Resource struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
}
|
||||
|
||||
// Access defines the type of access a rule grants
|
||||
// Access defines the type of access a rule grants.
|
||||
type Access int
|
||||
|
||||
const (
|
||||
// AccessGranted to a resource
|
||||
// AccessGranted to a resource.
|
||||
AccessGranted Access = iota
|
||||
// AccessDenied to a resource
|
||||
// AccessDenied to a resource.
|
||||
AccessDenied
|
||||
)
|
||||
|
||||
// Rule is used to verify access to a resource
|
||||
// Rule is used to verify access to a resource.
|
||||
type Rule struct {
|
||||
// Resource the rule applies to
|
||||
Resource *Resource
|
||||
// ID of the rule, e.g. "public"
|
||||
ID string
|
||||
// Scope the rule requires, a blank scope indicates open to the public and * indicates the rule
|
||||
// applies to any valid account
|
||||
Scope string
|
||||
// Resource the rule applies to
|
||||
Resource *Resource
|
||||
// Access determines if the rule grants or denies access to the resource
|
||||
Access Access
|
||||
// Priority the rule should take when verifying a request, the higher the value the sooner the
|
||||
@@ -125,13 +125,13 @@ type accountKey struct{}
|
||||
// AccountFromContext gets the account from the context, which
|
||||
// is set by the auth wrapper at the start of a call. If the account
|
||||
// is not set, a nil account will be returned. The error is only returned
|
||||
// when there was a problem retrieving an account
|
||||
// when there was a problem retrieving an account.
|
||||
func AccountFromContext(ctx context.Context) (*Account, bool) {
|
||||
acc, ok := ctx.Value(accountKey{}).(*Account)
|
||||
return acc, ok
|
||||
}
|
||||
|
||||
// ContextWithAccount sets the account in the context
|
||||
// ContextWithAccount sets the account in the context.
|
||||
func ContextWithAccount(ctx context.Context, account *Account) context.Context {
|
||||
return context.WithValue(ctx, accountKey{}, account)
|
||||
}
|
||||
|
||||
Vendored
+9
-9
@@ -30,24 +30,24 @@ type noop struct {
|
||||
|
||||
type noopRules struct{}
|
||||
|
||||
// String returns the name of the implementation
|
||||
// String returns the name of the implementation.
|
||||
func (n *noop) String() string {
|
||||
return "noop"
|
||||
}
|
||||
|
||||
// Init the auth
|
||||
// Init the auth.
|
||||
func (n *noop) Init(opts ...Option) {
|
||||
for _, o := range opts {
|
||||
o(&n.opts)
|
||||
}
|
||||
}
|
||||
|
||||
// Options set for auth
|
||||
// Options set for auth.
|
||||
func (n *noop) Options() Options {
|
||||
return n.opts
|
||||
}
|
||||
|
||||
// Generate a new account
|
||||
// Generate a new account.
|
||||
func (n *noop) Generate(id string, opts ...GenerateOption) (*Account, error) {
|
||||
options := NewGenerateOptions(opts...)
|
||||
|
||||
@@ -60,18 +60,18 @@ func (n *noop) Generate(id string, opts ...GenerateOption) (*Account, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Grant access to a resource
|
||||
// Grant access to a resource.
|
||||
func (n *noopRules) Grant(rule *Rule) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Revoke access to a resource
|
||||
// Revoke access to a resource.
|
||||
func (n *noopRules) Revoke(rule *Rule) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Rules used to verify requests
|
||||
// Verify an account has access to a resource
|
||||
// Verify an account has access to a resource.
|
||||
func (n *noopRules) Verify(acc *Account, res *Resource, opts ...VerifyOption) error {
|
||||
return nil
|
||||
}
|
||||
@@ -80,12 +80,12 @@ func (n *noopRules) List(opts ...ListOption) ([]*Rule, error) {
|
||||
return []*Rule{}, nil
|
||||
}
|
||||
|
||||
// Inspect a token
|
||||
// Inspect a token.
|
||||
func (n *noop) Inspect(token string) (*Account, error) {
|
||||
return &Account{ID: uuid.New().String(), Issuer: n.Options().Namespace}, nil
|
||||
}
|
||||
|
||||
// Token generation using an account id and secret
|
||||
// Token generation using an account id and secret.
|
||||
func (n *noop) Token(opts ...TokenOption) (*Token, error) {
|
||||
return &Token{}, nil
|
||||
}
|
||||
|
||||
+22
-22
@@ -20,62 +20,62 @@ func NewOptions(opts ...Option) Options {
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
// Logger is the underline logger
|
||||
Logger logger.Logger
|
||||
// Token is the services token used to authenticate itself
|
||||
Token *Token
|
||||
// Namespace the service belongs to
|
||||
Namespace string
|
||||
// ID is the services auth ID
|
||||
ID string
|
||||
// Secret is used to authenticate the service
|
||||
Secret string
|
||||
// Token is the services token used to authenticate itself
|
||||
Token *Token
|
||||
// PublicKey for decoding JWTs
|
||||
PublicKey string
|
||||
// PrivateKey for encoding JWTs
|
||||
PrivateKey string
|
||||
// Addrs sets the addresses of auth
|
||||
Addrs []string
|
||||
// Logger is the underline logger
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
// Addrs is the auth addresses to use
|
||||
// Addrs is the auth addresses to use.
|
||||
func Addrs(addrs ...string) Option {
|
||||
return func(o *Options) {
|
||||
o.Addrs = addrs
|
||||
}
|
||||
}
|
||||
|
||||
// Namespace the service belongs to
|
||||
// Namespace the service belongs to.
|
||||
func Namespace(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Namespace = n
|
||||
}
|
||||
}
|
||||
|
||||
// PublicKey is the JWT public key
|
||||
// PublicKey is the JWT public key.
|
||||
func PublicKey(key string) Option {
|
||||
return func(o *Options) {
|
||||
o.PublicKey = key
|
||||
}
|
||||
}
|
||||
|
||||
// PrivateKey is the JWT private key
|
||||
// PrivateKey is the JWT private key.
|
||||
func PrivateKey(key string) Option {
|
||||
return func(o *Options) {
|
||||
o.PrivateKey = key
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger sets the underline logger
|
||||
// WithLogger sets the underline logger.
|
||||
func WithLogger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
}
|
||||
}
|
||||
|
||||
// Credentials sets the auth credentials
|
||||
// Credentials sets the auth credentials.
|
||||
func Credentials(id, secret string) Option {
|
||||
return func(o *Options) {
|
||||
o.ID = id
|
||||
@@ -83,7 +83,7 @@ func Credentials(id, secret string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// ClientToken sets the auth token to use when making requests
|
||||
// ClientToken sets the auth token to use when making requests.
|
||||
func ClientToken(token *Token) Option {
|
||||
return func(o *Options) {
|
||||
o.Token = token
|
||||
@@ -93,54 +93,54 @@ func ClientToken(token *Token) Option {
|
||||
type GenerateOptions struct {
|
||||
// Metadata associated with the account
|
||||
Metadata map[string]string
|
||||
// Scopes the account has access too
|
||||
Scopes []string
|
||||
// Provider of the account, e.g. oauth
|
||||
Provider string
|
||||
// Type of the account, e.g. user
|
||||
Type string
|
||||
// Secret used to authenticate the account
|
||||
Secret string
|
||||
// Scopes the account has access too
|
||||
Scopes []string
|
||||
}
|
||||
|
||||
type GenerateOption func(o *GenerateOptions)
|
||||
|
||||
// WithSecret for the generated account
|
||||
// WithSecret for the generated account.
|
||||
func WithSecret(s string) GenerateOption {
|
||||
return func(o *GenerateOptions) {
|
||||
o.Secret = s
|
||||
}
|
||||
}
|
||||
|
||||
// WithType for the generated account
|
||||
// WithType for the generated account.
|
||||
func WithType(t string) GenerateOption {
|
||||
return func(o *GenerateOptions) {
|
||||
o.Type = t
|
||||
}
|
||||
}
|
||||
|
||||
// WithMetadata for the generated account
|
||||
// WithMetadata for the generated account.
|
||||
func WithMetadata(md map[string]string) GenerateOption {
|
||||
return func(o *GenerateOptions) {
|
||||
o.Metadata = md
|
||||
}
|
||||
}
|
||||
|
||||
// WithProvider for the generated account
|
||||
// WithProvider for the generated account.
|
||||
func WithProvider(p string) GenerateOption {
|
||||
return func(o *GenerateOptions) {
|
||||
o.Provider = p
|
||||
}
|
||||
}
|
||||
|
||||
// WithScopes for the generated account
|
||||
// WithScopes for the generated account.
|
||||
func WithScopes(s ...string) GenerateOption {
|
||||
return func(o *GenerateOptions) {
|
||||
o.Scopes = s
|
||||
}
|
||||
}
|
||||
|
||||
// NewGenerateOptions from a slice of options
|
||||
// NewGenerateOptions from a slice of options.
|
||||
func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
|
||||
var options GenerateOptions
|
||||
for _, o := range opts {
|
||||
@@ -162,7 +162,7 @@ type TokenOptions struct {
|
||||
|
||||
type TokenOption func(o *TokenOptions)
|
||||
|
||||
// WithExpiry for the token
|
||||
// WithExpiry for the token.
|
||||
func WithExpiry(ex time.Duration) TokenOption {
|
||||
return func(o *TokenOptions) {
|
||||
o.Expiry = ex
|
||||
@@ -182,14 +182,14 @@ func WithToken(rt string) TokenOption {
|
||||
}
|
||||
}
|
||||
|
||||
// NewTokenOptions from a slice of options
|
||||
// NewTokenOptions from a slice of options.
|
||||
func NewTokenOptions(opts ...TokenOption) TokenOptions {
|
||||
var options TokenOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
// set defualt expiry of token
|
||||
// set default expiry of token
|
||||
if options.Expiry == 0 {
|
||||
options.Expiry = time.Minute
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
// Verify an account has access to a resource using the rules provided. If the account does not have
|
||||
// access an error will be returned. If there are no rules provided which match the resource, an error
|
||||
// will be returned
|
||||
// will be returned.
|
||||
func Verify(rules []*Rule, acc *Account, res *Resource) error {
|
||||
// the rule is only to be applied if the type matches the resource or is catch-all (*)
|
||||
validTypes := []string{"*", res.Type}
|
||||
|
||||
+6
-3
@@ -18,12 +18,13 @@ type Broker interface {
|
||||
// message and optional Ack method to acknowledge receipt of the message.
|
||||
type Handler func(Event) error
|
||||
|
||||
// Message is a message send/received from the broker.
|
||||
type Message struct {
|
||||
Header map[string]string
|
||||
Body []byte
|
||||
}
|
||||
|
||||
// Event is given to a subscription handler for processing
|
||||
// Event is given to a subscription handler for processing.
|
||||
type Event interface {
|
||||
Topic() string
|
||||
Message() *Message
|
||||
@@ -31,7 +32,7 @@ type Event interface {
|
||||
Error() error
|
||||
}
|
||||
|
||||
// Subscriber is a convenience return type for the Subscribe method
|
||||
// Subscriber is a convenience return type for the Subscribe method.
|
||||
type Subscriber interface {
|
||||
Options() SubscribeOptions
|
||||
Topic() string
|
||||
@@ -39,7 +40,8 @@ type Subscriber interface {
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultBroker Broker = NewBroker()
|
||||
// DefaultBroker is the default Broker.
|
||||
DefaultBroker = NewBroker()
|
||||
)
|
||||
|
||||
func Init(opts ...Option) error {
|
||||
@@ -62,6 +64,7 @@ func Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscrib
|
||||
return DefaultBroker.Subscribe(topic, handler, opts...)
|
||||
}
|
||||
|
||||
// String returns the name of the Broker.
|
||||
func String() string {
|
||||
return DefaultBroker.String()
|
||||
}
|
||||
|
||||
+23
-21
@@ -1,4 +1,4 @@
|
||||
// Package http provides a http based message broker
|
||||
// Package broker provides a http based message broker
|
||||
package broker
|
||||
|
||||
import (
|
||||
@@ -16,51 +16,53 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"go-micro.dev/v4/codec/json"
|
||||
merr "go-micro.dev/v4/errors"
|
||||
"go-micro.dev/v4/registry"
|
||||
"go-micro.dev/v4/registry/cache"
|
||||
"go-micro.dev/v4/transport/headers"
|
||||
maddr "go-micro.dev/v4/util/addr"
|
||||
mnet "go-micro.dev/v4/util/net"
|
||||
mls "go-micro.dev/v4/util/tls"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
// HTTP Broker is a point to point async broker
|
||||
// HTTP Broker is a point to point async broker.
|
||||
type httpBroker struct {
|
||||
id string
|
||||
address string
|
||||
opts Options
|
||||
opts Options
|
||||
|
||||
r registry.Registry
|
||||
|
||||
mux *http.ServeMux
|
||||
|
||||
c *http.Client
|
||||
r registry.Registry
|
||||
|
||||
sync.RWMutex
|
||||
c *http.Client
|
||||
subscribers map[string][]*httpSubscriber
|
||||
running bool
|
||||
exit chan chan error
|
||||
|
||||
inbox map[string][][]byte
|
||||
id string
|
||||
address string
|
||||
|
||||
sync.RWMutex
|
||||
|
||||
// offline message inbox
|
||||
mtx sync.RWMutex
|
||||
inbox map[string][][]byte
|
||||
mtx sync.RWMutex
|
||||
running bool
|
||||
}
|
||||
|
||||
type httpSubscriber struct {
|
||||
opts SubscribeOptions
|
||||
id string
|
||||
topic string
|
||||
fn Handler
|
||||
svc *registry.Service
|
||||
hb *httpBroker
|
||||
id string
|
||||
topic string
|
||||
}
|
||||
|
||||
type httpEvent struct {
|
||||
err error
|
||||
m *Message
|
||||
t string
|
||||
err error
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -314,8 +316,8 @@ func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
topic := m.Header["Micro-Topic"]
|
||||
//delete(m.Header, ":topic")
|
||||
topic := m.Header[headers.Message]
|
||||
// delete(m.Header, ":topic")
|
||||
|
||||
if len(topic) == 0 {
|
||||
errr := merr.InternalServerError("go.micro.broker", "Topic not found")
|
||||
@@ -518,7 +520,7 @@ func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption)
|
||||
m.Header[k] = v
|
||||
}
|
||||
|
||||
m.Header["Micro-Topic"] = topic
|
||||
m.Header[headers.Message] = topic
|
||||
|
||||
// encode the message
|
||||
b, err := h.opts.Codec.Marshal(m)
|
||||
@@ -703,7 +705,7 @@ func (h *httpBroker) String() string {
|
||||
return "http"
|
||||
}
|
||||
|
||||
// NewBroker returns a new http broker
|
||||
// NewBroker returns a new http broker.
|
||||
func NewBroker(opts ...Option) Broker {
|
||||
return newHttpBroker(opts...)
|
||||
}
|
||||
|
||||
+8
-8
@@ -8,7 +8,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
log "go-micro.dev/v4/logger"
|
||||
maddr "go-micro.dev/v4/util/addr"
|
||||
mnet "go-micro.dev/v4/util/net"
|
||||
@@ -17,25 +16,26 @@ import (
|
||||
type memoryBroker struct {
|
||||
opts *Options
|
||||
|
||||
Subscribers map[string][]*memorySubscriber
|
||||
|
||||
addr string
|
||||
sync.RWMutex
|
||||
connected bool
|
||||
Subscribers map[string][]*memorySubscriber
|
||||
connected bool
|
||||
}
|
||||
|
||||
type memoryEvent struct {
|
||||
opts *Options
|
||||
topic string
|
||||
err error
|
||||
message interface{}
|
||||
opts *Options
|
||||
topic string
|
||||
}
|
||||
|
||||
type memorySubscriber struct {
|
||||
id string
|
||||
topic string
|
||||
opts SubscribeOptions
|
||||
exit chan bool
|
||||
handler Handler
|
||||
opts SubscribeOptions
|
||||
id string
|
||||
topic string
|
||||
}
|
||||
|
||||
func (m *memoryBroker) Options() Options {
|
||||
|
||||
+25
-23
@@ -10,23 +10,24 @@ import (
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Addrs []string
|
||||
Secure bool
|
||||
Codec codec.Marshaler
|
||||
Codec codec.Marshaler
|
||||
|
||||
// Logger is the underlying logger
|
||||
Logger logger.Logger
|
||||
|
||||
// Registry used for clustering
|
||||
Registry registry.Registry
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
|
||||
// Handler executed when error happens in broker mesage
|
||||
// processing
|
||||
ErrorHandler Handler
|
||||
|
||||
TLSConfig *tls.Config
|
||||
// Registry used for clustering
|
||||
Registry registry.Registry
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
Addrs []string
|
||||
Secure bool
|
||||
}
|
||||
|
||||
type PublishOptions struct {
|
||||
@@ -36,24 +37,25 @@ type PublishOptions struct {
|
||||
}
|
||||
|
||||
type SubscribeOptions struct {
|
||||
// AutoAck defaults to true. When a handler returns
|
||||
// with a nil error the message is acked.
|
||||
AutoAck bool
|
||||
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
// Subscribers with the same queue name
|
||||
// will create a shared subscription where each
|
||||
// receives a subset of messages.
|
||||
Queue string
|
||||
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
// AutoAck defaults to true. When a handler returns
|
||||
// with a nil error the message is acked.
|
||||
AutoAck bool
|
||||
}
|
||||
|
||||
type Option func(*Options)
|
||||
|
||||
type PublishOption func(*PublishOptions)
|
||||
|
||||
// PublishContext set context
|
||||
// PublishContext set context.
|
||||
func PublishContext(ctx context.Context) PublishOption {
|
||||
return func(o *PublishOptions) {
|
||||
o.Context = ctx
|
||||
@@ -87,7 +89,7 @@ func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions {
|
||||
return opt
|
||||
}
|
||||
|
||||
// Addrs sets the host addresses to be used by the broker
|
||||
// Addrs sets the host addresses to be used by the broker.
|
||||
func Addrs(addrs ...string) Option {
|
||||
return func(o *Options) {
|
||||
o.Addrs = addrs
|
||||
@@ -95,7 +97,7 @@ func Addrs(addrs ...string) Option {
|
||||
}
|
||||
|
||||
// Codec sets the codec used for encoding/decoding used where
|
||||
// a broker does not support headers
|
||||
// a broker does not support headers.
|
||||
func Codec(c codec.Marshaler) Option {
|
||||
return func(o *Options) {
|
||||
o.Codec = c
|
||||
@@ -111,14 +113,14 @@ func DisableAutoAck() SubscribeOption {
|
||||
}
|
||||
|
||||
// ErrorHandler will catch all broker errors that cant be handled
|
||||
// in normal way, for example Codec errors
|
||||
// in normal way, for example Codec errors.
|
||||
func ErrorHandler(h Handler) Option {
|
||||
return func(o *Options) {
|
||||
o.ErrorHandler = h
|
||||
}
|
||||
}
|
||||
|
||||
// Queue sets the name of the queue to share messages on
|
||||
// Queue sets the name of the queue to share messages on.
|
||||
func Queue(name string) SubscribeOption {
|
||||
return func(o *SubscribeOptions) {
|
||||
o.Queue = name
|
||||
@@ -131,28 +133,28 @@ func Registry(r registry.Registry) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Secure communication with the broker
|
||||
// Secure communication with the broker.
|
||||
func Secure(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.Secure = b
|
||||
}
|
||||
}
|
||||
|
||||
// Specify TLS Config
|
||||
// Specify TLS Config.
|
||||
func TLSConfig(t *tls.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.TLSConfig = t
|
||||
}
|
||||
}
|
||||
|
||||
// Logger sets the underline logger
|
||||
// Logger sets the underline logger.
|
||||
func Logger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
}
|
||||
}
|
||||
|
||||
// SubscribeContext set context
|
||||
// SubscribeContext set context.
|
||||
func SubscribeContext(ctx context.Context) SubscribeOption {
|
||||
return func(o *SubscribeOptions) {
|
||||
o.Context = ctx
|
||||
|
||||
+1
-1
@@ -8,9 +8,9 @@ import (
|
||||
|
||||
type memCache struct {
|
||||
opts Options
|
||||
sync.RWMutex
|
||||
|
||||
items map[string]Item
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (c *memCache) Get(ctx context.Context, key string) (interface{}, time.Time, error) {
|
||||
|
||||
+7
-7
@@ -9,14 +9,14 @@ import (
|
||||
|
||||
// Options represents the options for the cache.
|
||||
type Options struct {
|
||||
Expiration time.Duration
|
||||
Items map[string]Item
|
||||
// Address represents the address or other connection information of the cache service.
|
||||
Address string
|
||||
// Context should contain all implementation specific options, using context.WithValue.
|
||||
Context context.Context
|
||||
// Logger is the be used logger
|
||||
Logger logger.Logger
|
||||
Items map[string]Item
|
||||
// Address represents the address or other connection information of the cache service.
|
||||
Address string
|
||||
Expiration time.Duration
|
||||
}
|
||||
|
||||
// Option manipulates the Options passed.
|
||||
@@ -36,21 +36,21 @@ func Items(i map[string]Item) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithAddress sets the cache service address or connection information
|
||||
// WithAddress sets the cache service address or connection information.
|
||||
func WithAddress(addr string) Option {
|
||||
return func(o *Options) {
|
||||
o.Address = addr
|
||||
}
|
||||
}
|
||||
|
||||
// WithContext sets the cache context, for any extra configuration
|
||||
// WithContext sets the cache context, for any extra configuration.
|
||||
func WithContext(c context.Context) Option {
|
||||
return func(o *Options) {
|
||||
o.Context = c
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger sets underline logger
|
||||
// WithLogger sets underline logger.
|
||||
func WithLogger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
|
||||
+11
-7
@@ -8,36 +8,39 @@ import (
|
||||
"time"
|
||||
|
||||
cache "github.com/patrickmn/go-cache"
|
||||
|
||||
"go-micro.dev/v4/metadata"
|
||||
"go-micro.dev/v4/transport/headers"
|
||||
)
|
||||
|
||||
// NewCache returns an initialised cache.
|
||||
// NewCache returns an initialized cache.
|
||||
func NewCache() *Cache {
|
||||
return &Cache{
|
||||
cache: cache.New(cache.NoExpiration, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
// Cache for responses
|
||||
// Cache for responses.
|
||||
type Cache struct {
|
||||
cache *cache.Cache
|
||||
}
|
||||
|
||||
// Get a response from the cache
|
||||
// Get a response from the cache.
|
||||
func (c *Cache) Get(ctx context.Context, req *Request) (interface{}, bool) {
|
||||
return c.cache.Get(key(ctx, req))
|
||||
}
|
||||
|
||||
// Set a response in the cache
|
||||
// Set a response in the cache.
|
||||
func (c *Cache) Set(ctx context.Context, req *Request, rsp interface{}, expiry time.Duration) {
|
||||
c.cache.Set(key(ctx, req), rsp, expiry)
|
||||
}
|
||||
|
||||
// List the key value pairs in the cache
|
||||
// List the key value pairs in the cache.
|
||||
func (c *Cache) List() map[string]string {
|
||||
items := c.cache.Items()
|
||||
|
||||
rsp := make(map[string]string, len(items))
|
||||
|
||||
for k, v := range items {
|
||||
bytes, _ := json.Marshal(v.Object)
|
||||
rsp[k] = string(bytes)
|
||||
@@ -46,9 +49,9 @@ func (c *Cache) List() map[string]string {
|
||||
return rsp
|
||||
}
|
||||
|
||||
// key returns a hash for the context and request
|
||||
// key returns a hash for the context and request.
|
||||
func key(ctx context.Context, req *Request) string {
|
||||
ns, _ := metadata.Get(ctx, "Micro-Namespace")
|
||||
ns, _ := metadata.Get(ctx, headers.Namespace)
|
||||
|
||||
bytes, _ := json.Marshal(map[string]interface{}{
|
||||
"namespace": ns,
|
||||
@@ -62,5 +65,6 @@ func key(ctx context.Context, req *Request) string {
|
||||
|
||||
h := fnv.New64()
|
||||
h.Write(bytes)
|
||||
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
+21
-35
@@ -3,11 +3,17 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go-micro.dev/v4/codec"
|
||||
)
|
||||
|
||||
var (
|
||||
// NewClient returns a new client.
|
||||
NewClient func(...Option) Client = newRPCClient
|
||||
// DefaultClient is a default client to use out of the box.
|
||||
DefaultClient Client = newRPCClient()
|
||||
)
|
||||
|
||||
// Client is the interface used to make requests to services.
|
||||
// It supports Request/Response via Transport and Publishing via the Broker.
|
||||
// It also supports bidirectional streaming of requests.
|
||||
@@ -22,19 +28,19 @@ type Client interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// Router manages request routing
|
||||
// Router manages request routing.
|
||||
type Router interface {
|
||||
SendRequest(context.Context, Request) (Response, error)
|
||||
}
|
||||
|
||||
// Message is the interface for publishing asynchronously
|
||||
// Message is the interface for publishing asynchronously.
|
||||
type Message interface {
|
||||
Topic() string
|
||||
Payload() interface{}
|
||||
ContentType() string
|
||||
}
|
||||
|
||||
// Request is the interface for a synchronous request used by Call or Stream
|
||||
// Request is the interface for a synchronous request used by Call or Stream.
|
||||
type Request interface {
|
||||
// The service to call
|
||||
Service() string
|
||||
@@ -52,7 +58,7 @@ type Request interface {
|
||||
Stream() bool
|
||||
}
|
||||
|
||||
// Response is the response received from a service
|
||||
// Response is the response received from a service.
|
||||
type Response interface {
|
||||
// Read the response
|
||||
Codec() codec.Reader
|
||||
@@ -62,7 +68,7 @@ type Response interface {
|
||||
Read() ([]byte, error)
|
||||
}
|
||||
|
||||
// Stream is the inteface for a bidirectional synchronous stream
|
||||
// Stream is the inteface for a bidirectional synchronous stream.
|
||||
type Stream interface {
|
||||
Closer
|
||||
// Context for the stream
|
||||
@@ -81,48 +87,28 @@ type Stream interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
// Closer handle client close
|
||||
// Closer handle client close.
|
||||
type Closer interface {
|
||||
// CloseSend closes the send direction of the stream.
|
||||
CloseSend() error
|
||||
}
|
||||
|
||||
// Option used by the Client
|
||||
// Option used by the Client.
|
||||
type Option func(*Options)
|
||||
|
||||
// CallOption used by Call or Stream
|
||||
// CallOption used by Call or Stream.
|
||||
type CallOption func(*CallOptions)
|
||||
|
||||
// PublishOption used by Publish
|
||||
// PublishOption used by Publish.
|
||||
type PublishOption func(*PublishOptions)
|
||||
|
||||
// MessageOption used by NewMessage
|
||||
// MessageOption used by NewMessage.
|
||||
type MessageOption func(*MessageOptions)
|
||||
|
||||
// RequestOption used by NewRequest
|
||||
// RequestOption used by NewRequest.
|
||||
type RequestOption func(*RequestOptions)
|
||||
|
||||
var (
|
||||
// DefaultClient is a default client to use out of the box
|
||||
DefaultClient Client = newRpcClient()
|
||||
// DefaultBackoff is the default backoff function for retries
|
||||
DefaultBackoff = exponentialBackoff
|
||||
// DefaultRetry is the default check-for-retry function for retries
|
||||
DefaultRetry = RetryOnError
|
||||
// DefaultRetries is the default number of times a request is tried
|
||||
DefaultRetries = 1
|
||||
// DefaultRequestTimeout is the default request timeout
|
||||
DefaultRequestTimeout = time.Second * 5
|
||||
// DefaultPoolSize sets the connection pool size
|
||||
DefaultPoolSize = 100
|
||||
// DefaultPoolTTL sets the connection pool ttl
|
||||
DefaultPoolTTL = time.Minute
|
||||
|
||||
// NewClient returns a new client
|
||||
NewClient func(...Option) Client = newRpcClient
|
||||
)
|
||||
|
||||
// Makes a synchronous call to a service using the default client
|
||||
// Makes a synchronous call to a service using the default client.
|
||||
func Call(ctx context.Context, request Request, response interface{}, opts ...CallOption) error {
|
||||
return DefaultClient.Call(ctx, request, response, opts...)
|
||||
}
|
||||
@@ -133,13 +119,13 @@ func Publish(ctx context.Context, msg Message, opts ...PublishOption) error {
|
||||
return DefaultClient.Publish(ctx, msg, opts...)
|
||||
}
|
||||
|
||||
// Creates a new message using the default client
|
||||
// Creates a new message using the default client.
|
||||
func NewMessage(topic string, payload interface{}, opts ...MessageOption) Message {
|
||||
return DefaultClient.NewMessage(topic, payload, opts...)
|
||||
}
|
||||
|
||||
// Creates a new request using the default client. Content Type will
|
||||
// be set to the default within options and use the appropriate codec
|
||||
// be set to the default within options and use the appropriate codec.
|
||||
func NewRequest(service, endpoint string, request interface{}, reqOpts ...RequestOption) Request {
|
||||
return DefaultClient.NewRequest(service, endpoint, request, reqOpts...)
|
||||
}
|
||||
|
||||
+115
-81
@@ -12,32 +12,38 @@ import (
|
||||
"go-micro.dev/v4/transport"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
// Used to select codec
|
||||
ContentType string
|
||||
var (
|
||||
// DefaultBackoff is the default backoff function for retries.
|
||||
DefaultBackoff = exponentialBackoff
|
||||
// DefaultRetry is the default check-for-retry function for retries.
|
||||
DefaultRetry = RetryOnError
|
||||
// DefaultRetries is the default number of times a request is tried.
|
||||
DefaultRetries = 5
|
||||
// DefaultRequestTimeout is the default request timeout.
|
||||
DefaultRequestTimeout = time.Second * 30
|
||||
// DefaultConnectionTimeout is the default connection timeout.
|
||||
DefaultConnectionTimeout = time.Second * 5
|
||||
// DefaultPoolSize sets the connection pool size.
|
||||
DefaultPoolSize = 100
|
||||
// DefaultPoolTTL sets the connection pool ttl.
|
||||
DefaultPoolTTL = time.Minute
|
||||
)
|
||||
|
||||
// Plugged interfaces
|
||||
Broker broker.Broker
|
||||
Codecs map[string]codec.NewCodec
|
||||
Registry registry.Registry
|
||||
Selector selector.Selector
|
||||
Transport transport.Transport
|
||||
// Options are the Client options.
|
||||
type Options struct {
|
||||
|
||||
// Default Call Options
|
||||
CallOptions CallOptions
|
||||
|
||||
// Router sets the router
|
||||
Router Router
|
||||
|
||||
// Connection Pool
|
||||
PoolSize int
|
||||
PoolTTL time.Duration
|
||||
Registry registry.Registry
|
||||
Selector selector.Selector
|
||||
Transport transport.Transport
|
||||
|
||||
// Response cache
|
||||
Cache *Cache
|
||||
|
||||
// Middleware for client
|
||||
Wrappers []Wrapper
|
||||
|
||||
// Default Call Options
|
||||
CallOptions CallOptions
|
||||
// Plugged interfaces
|
||||
Broker broker.Broker
|
||||
|
||||
// Logger is the underline logger
|
||||
Logger logger.Logger
|
||||
@@ -45,44 +51,65 @@ type Options struct {
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
Codecs map[string]codec.NewCodec
|
||||
|
||||
// Response cache
|
||||
Cache *Cache
|
||||
|
||||
// Used to select codec
|
||||
ContentType string
|
||||
|
||||
// Middleware for client
|
||||
Wrappers []Wrapper
|
||||
|
||||
// Connection Pool
|
||||
PoolSize int
|
||||
PoolTTL time.Duration
|
||||
}
|
||||
|
||||
// CallOptions are options used to make calls to a server.
|
||||
type CallOptions struct {
|
||||
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
// Backoff func
|
||||
Backoff BackoffFunc
|
||||
// Check if retriable func
|
||||
Retry RetryFunc
|
||||
SelectOptions []selector.SelectOption
|
||||
|
||||
// Address of remote hosts
|
||||
Address []string
|
||||
// Backoff func
|
||||
Backoff BackoffFunc
|
||||
// Check if retriable func
|
||||
Retry RetryFunc
|
||||
// Transport Dial Timeout
|
||||
DialTimeout time.Duration
|
||||
// Number of Call attempts
|
||||
Retries int
|
||||
// Request/Response timeout
|
||||
RequestTimeout time.Duration
|
||||
// Stream timeout for the stream
|
||||
StreamTimeout time.Duration
|
||||
// Use the services own auth token
|
||||
ServiceToken bool
|
||||
// Duration to cache the response for
|
||||
CacheExpiry time.Duration
|
||||
|
||||
// Middleware for low level call func
|
||||
CallWrappers []CallWrapper
|
||||
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
// ConnectionTimeout of one request to the server.
|
||||
// Set this lower than the RequestTimeout to enbale retries on connection timeout.
|
||||
ConnectionTimeout time.Duration
|
||||
// Request/Response timeout of entire srv.Call, for single request timeout set ConnectionTimeout.
|
||||
RequestTimeout time.Duration
|
||||
// Stream timeout for the stream
|
||||
StreamTimeout time.Duration
|
||||
// Duration to cache the response for
|
||||
CacheExpiry time.Duration
|
||||
// Transport Dial Timeout. Used for initial dial to establish a connection.
|
||||
DialTimeout time.Duration
|
||||
// Number of Call attempts
|
||||
Retries int
|
||||
// Use the services own auth token
|
||||
ServiceToken bool
|
||||
// ConnClose sets the Connection: close header.
|
||||
ConnClose bool
|
||||
}
|
||||
|
||||
type PublishOptions struct {
|
||||
// Exchange is the routing exchange for the message
|
||||
Exchange string
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
// Exchange is the routing exchange for the message
|
||||
Exchange string
|
||||
}
|
||||
|
||||
type MessageOptions struct {
|
||||
@@ -90,14 +117,15 @@ type MessageOptions struct {
|
||||
}
|
||||
|
||||
type RequestOptions struct {
|
||||
ContentType string
|
||||
Stream bool
|
||||
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
Context context.Context
|
||||
ContentType string
|
||||
Stream bool
|
||||
}
|
||||
|
||||
// NewOptions creates new Client options.
|
||||
func NewOptions(options ...Option) Options {
|
||||
opts := Options{
|
||||
Cache: NewCache(),
|
||||
@@ -105,11 +133,12 @@ func NewOptions(options ...Option) Options {
|
||||
ContentType: DefaultContentType,
|
||||
Codecs: make(map[string]codec.NewCodec),
|
||||
CallOptions: CallOptions{
|
||||
Backoff: DefaultBackoff,
|
||||
Retry: DefaultRetry,
|
||||
Retries: DefaultRetries,
|
||||
RequestTimeout: DefaultRequestTimeout,
|
||||
DialTimeout: transport.DefaultDialTimeout,
|
||||
Backoff: DefaultBackoff,
|
||||
Retry: DefaultRetry,
|
||||
Retries: DefaultRetries,
|
||||
RequestTimeout: DefaultRequestTimeout,
|
||||
ConnectionTimeout: DefaultConnectionTimeout,
|
||||
DialTimeout: transport.DefaultDialTimeout,
|
||||
},
|
||||
PoolSize: DefaultPoolSize,
|
||||
PoolTTL: DefaultPoolTTL,
|
||||
@@ -127,42 +156,42 @@ func NewOptions(options ...Option) Options {
|
||||
return opts
|
||||
}
|
||||
|
||||
// Broker to be used for pub/sub
|
||||
// Broker to be used for pub/sub.
|
||||
func Broker(b broker.Broker) Option {
|
||||
return func(o *Options) {
|
||||
o.Broker = b
|
||||
}
|
||||
}
|
||||
|
||||
// Codec to be used to encode/decode requests for a given content type
|
||||
// Codec to be used to encode/decode requests for a given content type.
|
||||
func Codec(contentType string, c codec.NewCodec) Option {
|
||||
return func(o *Options) {
|
||||
o.Codecs[contentType] = c
|
||||
}
|
||||
}
|
||||
|
||||
// Default content type of the client
|
||||
// ContentType sets the default content type of the client.
|
||||
func ContentType(ct string) Option {
|
||||
return func(o *Options) {
|
||||
o.ContentType = ct
|
||||
}
|
||||
}
|
||||
|
||||
// PoolSize sets the connection pool size
|
||||
// PoolSize sets the connection pool size.
|
||||
func PoolSize(d int) Option {
|
||||
return func(o *Options) {
|
||||
o.PoolSize = d
|
||||
}
|
||||
}
|
||||
|
||||
// PoolTTL sets the connection pool ttl
|
||||
// PoolTTL sets the connection pool ttl.
|
||||
func PoolTTL(d time.Duration) Option {
|
||||
return func(o *Options) {
|
||||
o.PoolTTL = d
|
||||
}
|
||||
}
|
||||
|
||||
// Registry to find nodes for a given service
|
||||
// Registry to find nodes for a given service.
|
||||
func Registry(r registry.Registry) Option {
|
||||
return func(o *Options) {
|
||||
o.Registry = r
|
||||
@@ -171,28 +200,28 @@ func Registry(r registry.Registry) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Transport to use for communication e.g http, rabbitmq, etc
|
||||
// Transport to use for communication e.g http, rabbitmq, etc.
|
||||
func Transport(t transport.Transport) Option {
|
||||
return func(o *Options) {
|
||||
o.Transport = t
|
||||
}
|
||||
}
|
||||
|
||||
// Select is used to select a node to route a request to
|
||||
// Select is used to select a node to route a request to.
|
||||
func Selector(s selector.Selector) Option {
|
||||
return func(o *Options) {
|
||||
o.Selector = s
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a Wrapper to a list of options passed into the client
|
||||
// Adds a Wrapper to a list of options passed into the client.
|
||||
func Wrap(w Wrapper) Option {
|
||||
return func(o *Options) {
|
||||
o.Wrappers = append(o.Wrappers, w)
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a Wrapper to the list of CallFunc wrappers
|
||||
// Adds a Wrapper to the list of CallFunc wrappers.
|
||||
func WrapCall(cw ...CallWrapper) Option {
|
||||
return func(o *Options) {
|
||||
o.CallOptions.CallWrappers = append(o.CallOptions.CallWrappers, cw...)
|
||||
@@ -200,15 +229,14 @@ func WrapCall(cw ...CallWrapper) Option {
|
||||
}
|
||||
|
||||
// Backoff is used to set the backoff function used
|
||||
// when retrying Calls
|
||||
// when retrying Calls.
|
||||
func Backoff(fn BackoffFunc) Option {
|
||||
return func(o *Options) {
|
||||
o.CallOptions.Backoff = fn
|
||||
}
|
||||
}
|
||||
|
||||
// Number of retries when making the request.
|
||||
// Should this be a Call Option?
|
||||
// Retries set the number of retries when making the request.
|
||||
func Retries(i int) Option {
|
||||
return func(o *Options) {
|
||||
o.CallOptions.Retries = i
|
||||
@@ -222,22 +250,21 @@ func Retry(fn RetryFunc) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// The request timeout.
|
||||
// Should this be a Call Option?
|
||||
// RequestTimeout set the request timeout.
|
||||
func RequestTimeout(d time.Duration) Option {
|
||||
return func(o *Options) {
|
||||
o.CallOptions.RequestTimeout = d
|
||||
}
|
||||
}
|
||||
|
||||
// StreamTimeout sets the stream timeout
|
||||
// StreamTimeout sets the stream timeout.
|
||||
func StreamTimeout(d time.Duration) Option {
|
||||
return func(o *Options) {
|
||||
o.CallOptions.StreamTimeout = d
|
||||
}
|
||||
}
|
||||
|
||||
// Transport dial timeout
|
||||
// DialTimeout sets the transport dial timeout.
|
||||
func DialTimeout(d time.Duration) Option {
|
||||
return func(o *Options) {
|
||||
o.CallOptions.DialTimeout = d
|
||||
@@ -246,21 +273,21 @@ func DialTimeout(d time.Duration) Option {
|
||||
|
||||
// Call Options
|
||||
|
||||
// WithExchange sets the exchange to route a message through
|
||||
// WithExchange sets the exchange to route a message through.
|
||||
func WithExchange(e string) PublishOption {
|
||||
return func(o *PublishOptions) {
|
||||
o.Exchange = e
|
||||
}
|
||||
}
|
||||
|
||||
// PublishContext sets the context in publish options
|
||||
// PublishContext sets the context in publish options.
|
||||
func PublishContext(ctx context.Context) PublishOption {
|
||||
return func(o *PublishOptions) {
|
||||
o.Context = ctx
|
||||
}
|
||||
}
|
||||
|
||||
// WithAddress sets the remote addresses to use rather than using service discovery
|
||||
// WithAddress sets the remote addresses to use rather than using service discovery.
|
||||
func WithAddress(a ...string) CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.Address = a
|
||||
@@ -273,7 +300,7 @@ func WithSelectOption(so ...selector.SelectOption) CallOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithCallWrapper is a CallOption which adds to the existing CallFunc wrappers
|
||||
// WithCallWrapper is a CallOption which adds to the existing CallFunc wrappers.
|
||||
func WithCallWrapper(cw ...CallWrapper) CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.CallWrappers = append(o.CallWrappers, cw...)
|
||||
@@ -281,7 +308,7 @@ func WithCallWrapper(cw ...CallWrapper) CallOption {
|
||||
}
|
||||
|
||||
// WithBackoff is a CallOption which overrides that which
|
||||
// set in Options.CallOptions
|
||||
// set in Options.CallOptions.
|
||||
func WithBackoff(fn BackoffFunc) CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.Backoff = fn
|
||||
@@ -289,15 +316,15 @@ func WithBackoff(fn BackoffFunc) CallOption {
|
||||
}
|
||||
|
||||
// WithRetry is a CallOption which overrides that which
|
||||
// set in Options.CallOptions
|
||||
// set in Options.CallOptions.
|
||||
func WithRetry(fn RetryFunc) CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.Retry = fn
|
||||
}
|
||||
}
|
||||
|
||||
// WithRetries is a CallOption which overrides that which
|
||||
// set in Options.CallOptions
|
||||
// WithRetries sets the number of tries for a call.
|
||||
// This CallOption overrides Options.CallOptions.
|
||||
func WithRetries(i int) CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.Retries = i
|
||||
@@ -305,14 +332,21 @@ func WithRetries(i int) CallOption {
|
||||
}
|
||||
|
||||
// WithRequestTimeout is a CallOption which overrides that which
|
||||
// set in Options.CallOptions
|
||||
// set in Options.CallOptions.
|
||||
func WithRequestTimeout(d time.Duration) CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.RequestTimeout = d
|
||||
}
|
||||
}
|
||||
|
||||
// WithStreamTimeout sets the stream timeout
|
||||
// WithConnClose sets the Connection header to close.
|
||||
func WithConnClose() CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.ConnClose = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithStreamTimeout sets the stream timeout.
|
||||
func WithStreamTimeout(d time.Duration) CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.StreamTimeout = d
|
||||
@@ -320,7 +354,7 @@ func WithStreamTimeout(d time.Duration) CallOption {
|
||||
}
|
||||
|
||||
// WithDialTimeout is a CallOption which overrides that which
|
||||
// set in Options.CallOptions
|
||||
// set in Options.CallOptions.
|
||||
func WithDialTimeout(d time.Duration) CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.DialTimeout = d
|
||||
@@ -328,7 +362,7 @@ func WithDialTimeout(d time.Duration) CallOption {
|
||||
}
|
||||
|
||||
// WithServiceToken is a CallOption which overrides the
|
||||
// authorization header with the services own auth token
|
||||
// authorization header with the services own auth token.
|
||||
func WithServiceToken() CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.ServiceToken = true
|
||||
@@ -336,7 +370,7 @@ func WithServiceToken() CallOption {
|
||||
}
|
||||
|
||||
// WithCache is a CallOption which sets the duration the response
|
||||
// shoull be cached for
|
||||
// shoull be cached for.
|
||||
func WithCache(c time.Duration) CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.CacheExpiry = c
|
||||
@@ -363,14 +397,14 @@ func StreamingRequest() RequestOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithRouter sets the client router
|
||||
// WithRouter sets the client router.
|
||||
func WithRouter(r Router) Option {
|
||||
return func(o *Options) {
|
||||
o.Router = r
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger sets the underline logger
|
||||
// WithLogger sets the underline logger.
|
||||
func WithLogger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
|
||||
+6
-5
@@ -6,15 +6,15 @@ import (
|
||||
"go-micro.dev/v4/errors"
|
||||
)
|
||||
|
||||
// note that returning either false or a non-nil error will result in the call not being retried
|
||||
// note that returning either false or a non-nil error will result in the call not being retried.
|
||||
type RetryFunc func(ctx context.Context, req Request, retryCount int, err error) (bool, error)
|
||||
|
||||
// RetryAlways always retry on error
|
||||
// RetryAlways always retry on error.
|
||||
func RetryAlways(ctx context.Context, req Request, retryCount int, err error) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// RetryOnError retries a request on a 500 or timeout error
|
||||
// RetryOnError retries a request on a 500 or timeout error.
|
||||
func RetryOnError(ctx context.Context, req Request, retryCount int, err error) (bool, error) {
|
||||
if err == nil {
|
||||
return false, nil
|
||||
@@ -26,8 +26,9 @@ func RetryOnError(ctx context.Context, req Request, retryCount int, err error) (
|
||||
}
|
||||
|
||||
switch e.Code {
|
||||
// retry on timeout or internal server error
|
||||
case 408, 500:
|
||||
// Retry on timeout, not on 500 internal server error, as that is a business
|
||||
// logic error that should be handled by the user.
|
||||
case 408:
|
||||
return true, nil
|
||||
default:
|
||||
return false, nil
|
||||
|
||||
+152
-64
@@ -3,31 +3,43 @@ package client
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"go-micro.dev/v4/broker"
|
||||
"go-micro.dev/v4/codec"
|
||||
raw "go-micro.dev/v4/codec/bytes"
|
||||
"go-micro.dev/v4/errors"
|
||||
merrors "go-micro.dev/v4/errors"
|
||||
log "go-micro.dev/v4/logger"
|
||||
"go-micro.dev/v4/metadata"
|
||||
"go-micro.dev/v4/registry"
|
||||
"go-micro.dev/v4/selector"
|
||||
"go-micro.dev/v4/transport"
|
||||
"go-micro.dev/v4/transport/headers"
|
||||
"go-micro.dev/v4/util/buf"
|
||||
"go-micro.dev/v4/util/net"
|
||||
"go-micro.dev/v4/util/pool"
|
||||
)
|
||||
|
||||
const (
|
||||
packageID = "go.micro.client"
|
||||
)
|
||||
|
||||
type rpcClient struct {
|
||||
seq uint64
|
||||
once atomic.Value
|
||||
opts Options
|
||||
once atomic.Value
|
||||
pool pool.Pool
|
||||
|
||||
seq uint64
|
||||
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func newRpcClient(opt ...Option) Client {
|
||||
func newRPCClient(opt ...Option) Client {
|
||||
opts := NewOptions(opt...)
|
||||
|
||||
p := pool.NewPool(
|
||||
@@ -57,14 +69,17 @@ func (r *rpcClient) newCodec(contentType string) (codec.NewCodec, error) {
|
||||
if c, ok := r.opts.Codecs[contentType]; ok {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
if cf, ok := DefaultCodecs[contentType]; ok {
|
||||
return cf, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported Content-Type: %s", contentType)
|
||||
}
|
||||
|
||||
func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request, resp interface{}, opts CallOptions) error {
|
||||
address := node.Address
|
||||
logger := r.Options().Logger
|
||||
|
||||
msg := &transport.Message{
|
||||
Header: make(map[string]string),
|
||||
@@ -73,31 +88,43 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
|
||||
md, ok := metadata.FromContext(ctx)
|
||||
if ok {
|
||||
for k, v := range md {
|
||||
// don't copy Micro-Topic header, that used for pub/sub
|
||||
// this fix case then client uses the same context that received in subscriber
|
||||
if k == "Micro-Topic" {
|
||||
// Don't copy Micro-Topic header, that is used for pub/sub
|
||||
// this is fixes the case when the client uses the same context that
|
||||
// is received in the subscriber.
|
||||
if k == headers.Message {
|
||||
continue
|
||||
}
|
||||
|
||||
msg.Header[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Set connection timeout for single requests to the server. Should be > 0
|
||||
// as otherwise requests can't be made.
|
||||
cTimeout := opts.ConnectionTimeout
|
||||
if cTimeout == 0 {
|
||||
logger.Log(log.DebugLevel, "connection timeout was set to 0, overridng to default connection timeout")
|
||||
|
||||
cTimeout = DefaultConnectionTimeout
|
||||
}
|
||||
|
||||
// set timeout in nanoseconds
|
||||
msg.Header["Timeout"] = fmt.Sprintf("%d", opts.RequestTimeout)
|
||||
msg.Header["Timeout"] = fmt.Sprintf("%d", cTimeout)
|
||||
// set the content type for the request
|
||||
msg.Header["Content-Type"] = req.ContentType()
|
||||
// set the accept header
|
||||
msg.Header["Accept"] = req.ContentType()
|
||||
|
||||
// setup old protocol
|
||||
cf := setupProtocol(msg, node)
|
||||
reqCodec := setupProtocol(msg, node)
|
||||
|
||||
// no codec specified
|
||||
if cf == nil {
|
||||
if reqCodec == nil {
|
||||
var err error
|
||||
cf, err = r.newCodec(req.ContentType())
|
||||
reqCodec, err = r.newCodec(req.ContentType())
|
||||
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
return merrors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,19 +136,29 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
|
||||
dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
|
||||
}
|
||||
|
||||
if opts.ConnClose {
|
||||
dOpts = append(dOpts, transport.WithConnClose())
|
||||
}
|
||||
|
||||
c, err := r.pool.Get(address, dOpts...)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", "connection error: %v", err)
|
||||
return merrors.InternalServerError("go.micro.client", "connection error: %v", err)
|
||||
}
|
||||
|
||||
seq := atomic.AddUint64(&r.seq, 1) - 1
|
||||
codec := newRpcCodec(msg, c, cf, "")
|
||||
codec := newRPCCodec(msg, c, reqCodec, "")
|
||||
|
||||
rsp := &rpcResponse{
|
||||
socket: c,
|
||||
codec: codec,
|
||||
}
|
||||
|
||||
releaseFunc := func(err error) {
|
||||
if err = r.pool.Release(c, err); err != nil {
|
||||
logger.Log(log.ErrorLevel, "failed to release pool", err)
|
||||
}
|
||||
}
|
||||
|
||||
stream := &rpcStream{
|
||||
id: fmt.Sprintf("%v", seq),
|
||||
context: ctx,
|
||||
@@ -129,11 +166,17 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
|
||||
response: rsp,
|
||||
codec: codec,
|
||||
closed: make(chan bool),
|
||||
release: func(err error) { r.pool.Release(c, err) },
|
||||
close: opts.ConnClose,
|
||||
release: releaseFunc,
|
||||
sendEOS: false,
|
||||
}
|
||||
|
||||
// close the stream on exiting this function
|
||||
defer stream.Close()
|
||||
defer func() {
|
||||
if err := stream.Close(); err != nil {
|
||||
logger.Log(log.ErrorLevel, "failed to close stream", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// wait for error response
|
||||
ch := make(chan error, 1)
|
||||
@@ -141,7 +184,7 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ch <- errors.InternalServerError("go.micro.client", "panic recovered: %v", r)
|
||||
ch <- merrors.InternalServerError("go.micro.client", "panic recovered: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -166,8 +209,8 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
|
||||
select {
|
||||
case err := <-ch:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
grr = errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
|
||||
case <-time.After(cTimeout):
|
||||
grr = merrors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
|
||||
}
|
||||
|
||||
// set the stream error
|
||||
@@ -184,6 +227,7 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
|
||||
|
||||
func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request, opts CallOptions) (Stream, error) {
|
||||
address := node.Address
|
||||
logger := r.Options().Logger
|
||||
|
||||
msg := &transport.Message{
|
||||
Header: make(map[string]string),
|
||||
@@ -206,14 +250,15 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
msg.Header["Accept"] = req.ContentType()
|
||||
|
||||
// set old codecs
|
||||
cf := setupProtocol(msg, node)
|
||||
nCodec := setupProtocol(msg, node)
|
||||
|
||||
// no codec specified
|
||||
if cf == nil {
|
||||
if nCodec == nil {
|
||||
var err error
|
||||
cf, err = r.newCodec(req.ContentType())
|
||||
|
||||
nCodec, err = r.newCodec(req.ContentType())
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", err.Error())
|
||||
return nil, merrors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +272,7 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
|
||||
c, err := r.opts.Transport.Dial(address, dOpts...)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", "connection error: %v", err)
|
||||
return nil, merrors.InternalServerError("go.micro.client", "connection error: %v", err)
|
||||
}
|
||||
|
||||
// increment the sequence number
|
||||
@@ -235,7 +280,7 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
id := fmt.Sprintf("%v", seq)
|
||||
|
||||
// create codec with stream id
|
||||
codec := newRpcCodec(msg, c, cf, id)
|
||||
codec := newRPCCodec(msg, c, nCodec, id)
|
||||
|
||||
rsp := &rpcResponse{
|
||||
socket: c,
|
||||
@@ -247,6 +292,12 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
r.codec = codec
|
||||
}
|
||||
|
||||
releaseFunc := func(_ error) {
|
||||
if err = c.Close(); err != nil {
|
||||
logger.Log(log.ErrorLevel, err)
|
||||
}
|
||||
}
|
||||
|
||||
stream := &rpcStream{
|
||||
id: id,
|
||||
context: ctx,
|
||||
@@ -257,8 +308,7 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
closed: make(chan bool),
|
||||
// signal the end of stream,
|
||||
sendEOS: true,
|
||||
// release func
|
||||
release: func(err error) { c.Close() },
|
||||
release: releaseFunc,
|
||||
}
|
||||
|
||||
// wait for error response
|
||||
@@ -275,7 +325,7 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
case err := <-ch:
|
||||
grr = err
|
||||
case <-ctx.Done():
|
||||
grr = errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
|
||||
grr = merrors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
|
||||
}
|
||||
|
||||
if grr != nil {
|
||||
@@ -285,7 +335,10 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
stream.Unlock()
|
||||
|
||||
// close the stream
|
||||
stream.Close()
|
||||
if err := stream.Close(); err != nil {
|
||||
logger.Logf(log.ErrorLevel, "failed to close stream: %v", err)
|
||||
}
|
||||
|
||||
return nil, grr
|
||||
}
|
||||
|
||||
@@ -293,6 +346,9 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
}
|
||||
|
||||
func (r *rpcClient) Init(opts ...Option) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
size := r.opts.PoolSize
|
||||
ttl := r.opts.PoolTTL
|
||||
tr := r.opts.Transport
|
||||
@@ -304,7 +360,10 @@ func (r *rpcClient) Init(opts ...Option) error {
|
||||
// update pool configuration if the options changed
|
||||
if size != r.opts.PoolSize || ttl != r.opts.PoolTTL || tr != r.opts.Transport {
|
||||
// close existing pool
|
||||
r.pool.Close()
|
||||
if err := r.pool.Close(); err != nil {
|
||||
return errors.Wrap(err, "failed to close pool")
|
||||
}
|
||||
|
||||
// create new pool
|
||||
r.pool = pool.NewPool(
|
||||
pool.Size(r.opts.PoolSize),
|
||||
@@ -316,11 +375,15 @@ func (r *rpcClient) Init(opts ...Option) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Options retrives the options.
|
||||
func (r *rpcClient) Options() Options {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
return r.opts
|
||||
}
|
||||
|
||||
// next returns an iterator for the next nodes to call
|
||||
// next returns an iterator for the next nodes to call.
|
||||
func (r *rpcClient) next(request Request, opts CallOptions) (selector.Next, error) {
|
||||
// try get the proxy
|
||||
service, address, _ := net.Proxy(request.Service(), opts.Address)
|
||||
@@ -348,16 +411,22 @@ func (r *rpcClient) next(request Request, opts CallOptions) (selector.Next, erro
|
||||
// get next nodes from the selector
|
||||
next, err := r.opts.Selector.Select(service, opts.SelectOptions...)
|
||||
if err != nil {
|
||||
if err == selector.ErrNotFound {
|
||||
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
if errors.Is(err, selector.ErrNotFound) {
|
||||
return nil, merrors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
}
|
||||
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
|
||||
|
||||
return nil, merrors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
|
||||
}
|
||||
|
||||
return next, nil
|
||||
}
|
||||
|
||||
func (r *rpcClient) Call(ctx context.Context, request Request, response interface{}, opts ...CallOption) error {
|
||||
// TODO: further validate these mutex locks. full lock would prevent
|
||||
// parallel calls. Maybe we can set individual locks for secctions.
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
// make a copy of call opts
|
||||
callOpts := r.opts.CallOptions
|
||||
for _, opt := range opts {
|
||||
@@ -375,6 +444,7 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
||||
// no deadline so we create a new one
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, callOpts.RequestTimeout)
|
||||
|
||||
defer cancel()
|
||||
} else {
|
||||
// got a deadline so no need to setup context
|
||||
@@ -386,7 +456,7 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
||||
// should we noop right here?
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
|
||||
return merrors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -403,7 +473,7 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
||||
// call backoff first. Someone may want an initial start delay
|
||||
t, err := callOpts.Backoff(ctx, request, i)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", "backoff error: %v", err.Error())
|
||||
return merrors.InternalServerError("go.micro.client", "backoff error: %v", err.Error())
|
||||
}
|
||||
|
||||
// only sleep if greater than 0
|
||||
@@ -414,16 +484,19 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
||||
// select next node
|
||||
node, err := next()
|
||||
service := request.Service()
|
||||
|
||||
if err != nil {
|
||||
if err == selector.ErrNotFound {
|
||||
return errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
if errors.Is(err, selector.ErrNotFound) {
|
||||
return merrors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
}
|
||||
return errors.InternalServerError("go.micro.client", "error getting next %s node: %s", service, err.Error())
|
||||
|
||||
return merrors.InternalServerError("go.micro.client", "error getting next %s node: %s", service, err.Error())
|
||||
}
|
||||
|
||||
// make the call
|
||||
err = rcall(ctx, node, request, response, callOpts)
|
||||
r.opts.Selector.Mark(service, node, err)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -431,11 +504,13 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
||||
retries := callOpts.Retries
|
||||
|
||||
// disable retries when using a proxy
|
||||
if _, _, ok := net.Proxy(request.Service(), callOpts.Address); ok {
|
||||
retries = 0
|
||||
}
|
||||
// Note: I don't see why we should disable retries for proxies, so commenting out.
|
||||
// if _, _, ok := net.Proxy(request.Service(), callOpts.Address); ok {
|
||||
// retries = 0
|
||||
// }
|
||||
|
||||
ch := make(chan error, retries+1)
|
||||
|
||||
var gerr error
|
||||
|
||||
for i := 0; i <= retries; i++ {
|
||||
@@ -445,7 +520,7 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err()))
|
||||
return merrors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err()))
|
||||
case err := <-ch:
|
||||
// if the call succeeded lets bail early
|
||||
if err == nil {
|
||||
@@ -461,6 +536,8 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
||||
return err
|
||||
}
|
||||
|
||||
r.opts.Logger.Logf(log.DebugLevel, "Retrying request. Previous attempt failed with: %v", err)
|
||||
|
||||
gerr = err
|
||||
}
|
||||
}
|
||||
@@ -469,6 +546,9 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
||||
}
|
||||
|
||||
func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOption) (Stream, error) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
// make a copy of call opts
|
||||
callOpts := r.opts.CallOptions
|
||||
for _, opt := range opts {
|
||||
@@ -480,10 +560,9 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOpt
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// should we noop right here?
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
|
||||
return nil, merrors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -491,7 +570,7 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOpt
|
||||
// call backoff first. Someone may want an initial start delay
|
||||
t, err := callOpts.Backoff(ctx, request, i)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", "backoff error: %v", err.Error())
|
||||
return nil, merrors.InternalServerError("go.micro.client", "backoff error: %v", err.Error())
|
||||
}
|
||||
|
||||
// only sleep if greater than 0
|
||||
@@ -501,15 +580,18 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOpt
|
||||
|
||||
node, err := next()
|
||||
service := request.Service()
|
||||
|
||||
if err != nil {
|
||||
if err == selector.ErrNotFound {
|
||||
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
if errors.Is(err, selector.ErrNotFound) {
|
||||
return nil, merrors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
}
|
||||
return nil, errors.InternalServerError("go.micro.client", "error getting next %s node: %s", service, err.Error())
|
||||
|
||||
return nil, merrors.InternalServerError("go.micro.client", "error getting next %s node: %s", service, err.Error())
|
||||
}
|
||||
|
||||
stream, err := r.stream(ctx, node, request, callOpts)
|
||||
r.opts.Selector.Mark(service, node, err)
|
||||
|
||||
return stream, err
|
||||
}
|
||||
|
||||
@@ -527,6 +609,7 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOpt
|
||||
}
|
||||
|
||||
ch := make(chan response, retries+1)
|
||||
|
||||
var grr error
|
||||
|
||||
for i := 0; i <= retries; i++ {
|
||||
@@ -537,7 +620,7 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOpt
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, errors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err()))
|
||||
return nil, merrors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err()))
|
||||
case rsp := <-ch:
|
||||
// if the call succeeded lets bail early
|
||||
if rsp.err == nil {
|
||||
@@ -568,15 +651,15 @@ func (r *rpcClient) Publish(ctx context.Context, msg Message, opts ...PublishOpt
|
||||
o(&options)
|
||||
}
|
||||
|
||||
md, ok := metadata.FromContext(ctx)
|
||||
metadata, ok := metadata.FromContext(ctx)
|
||||
if !ok {
|
||||
md = make(map[string]string)
|
||||
metadata = make(map[string]string)
|
||||
}
|
||||
|
||||
id := uuid.New().String()
|
||||
md["Content-Type"] = msg.ContentType()
|
||||
md["Micro-Topic"] = msg.Topic()
|
||||
md["Micro-Id"] = id
|
||||
metadata["Content-Type"] = msg.ContentType()
|
||||
metadata[headers.Message] = msg.Topic()
|
||||
metadata[headers.ID] = id
|
||||
|
||||
// set the topic
|
||||
topic := msg.Topic()
|
||||
@@ -589,7 +672,7 @@ func (r *rpcClient) Publish(ctx context.Context, msg Message, opts ...PublishOpt
|
||||
// encode message body
|
||||
cf, err := r.newCodec(msg.ContentType())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
return merrors.InternalServerError(packageID, err.Error())
|
||||
}
|
||||
|
||||
var body []byte
|
||||
@@ -598,33 +681,38 @@ func (r *rpcClient) Publish(ctx context.Context, msg Message, opts ...PublishOpt
|
||||
if d, ok := msg.Payload().(*raw.Frame); ok {
|
||||
body = d.Data
|
||||
} else {
|
||||
// new buffer
|
||||
b := buf.New(nil)
|
||||
|
||||
if err := cf(b).Write(&codec.Message{
|
||||
if err = cf(b).Write(&codec.Message{
|
||||
Target: topic,
|
||||
Type: codec.Event,
|
||||
Header: map[string]string{
|
||||
"Micro-Id": id,
|
||||
"Micro-Topic": msg.Topic(),
|
||||
headers.ID: id,
|
||||
headers.Message: msg.Topic(),
|
||||
},
|
||||
}, msg.Payload()); err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
return merrors.InternalServerError(packageID, err.Error())
|
||||
}
|
||||
|
||||
// set the body
|
||||
body = b.Bytes()
|
||||
}
|
||||
|
||||
if !r.once.Load().(bool) {
|
||||
l, ok := r.once.Load().(bool)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to cast to bool")
|
||||
}
|
||||
|
||||
if !l {
|
||||
if err = r.opts.Broker.Connect(); err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
return merrors.InternalServerError(packageID, err.Error())
|
||||
}
|
||||
|
||||
r.once.Store(true)
|
||||
}
|
||||
|
||||
return r.opts.Broker.Publish(topic, &broker.Message{
|
||||
Header: md,
|
||||
Header: metadata,
|
||||
Body: body,
|
||||
}, broker.PublishContext(options.Context))
|
||||
}
|
||||
|
||||
+47
-34
@@ -14,6 +14,7 @@ import (
|
||||
"go-micro.dev/v4/errors"
|
||||
"go-micro.dev/v4/registry"
|
||||
"go-micro.dev/v4/transport"
|
||||
"go-micro.dev/v4/transport/headers"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,7 +29,7 @@ func (e serverError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// errShutdown holds the specific error for closing/closed connections
|
||||
// errShutdown holds the specific error for closing/closed connections.
|
||||
var (
|
||||
errShutdown = errs.New("connection is shut down")
|
||||
)
|
||||
@@ -50,8 +51,10 @@ type readWriteCloser struct {
|
||||
}
|
||||
|
||||
var (
|
||||
// DefaultContentType header.
|
||||
DefaultContentType = "application/json"
|
||||
|
||||
// DefaultCodecs map.
|
||||
DefaultCodecs = map[string]codec.NewCodec{
|
||||
"application/grpc": grpc.NewCodec,
|
||||
"application/grpc+json": grpc.NewCodec,
|
||||
@@ -63,7 +66,7 @@ var (
|
||||
"application/octet-stream": raw.NewCodec,
|
||||
}
|
||||
|
||||
// TODO: remove legacy codec list
|
||||
// TODO: remove legacy codec list.
|
||||
defaultCodecs = map[string]codec.NewCodec{
|
||||
"application/json": jsonrpc.NewCodec,
|
||||
"application/json-rpc": jsonrpc.NewCodec,
|
||||
@@ -84,6 +87,7 @@ func (rwc *readWriteCloser) Write(p []byte) (n int, err error) {
|
||||
func (rwc *readWriteCloser) Close() error {
|
||||
rwc.rbuf.Reset()
|
||||
rwc.wbuf.Reset()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -92,20 +96,21 @@ func getHeaders(m *codec.Message) {
|
||||
if len(v) > 0 {
|
||||
return v
|
||||
}
|
||||
|
||||
return m.Header[hdr]
|
||||
}
|
||||
|
||||
// check error in header
|
||||
m.Error = set(m.Error, "Micro-Error")
|
||||
m.Error = set(m.Error, headers.Error)
|
||||
|
||||
// check endpoint in header
|
||||
m.Endpoint = set(m.Endpoint, "Micro-Endpoint")
|
||||
m.Endpoint = set(m.Endpoint, headers.Endpoint)
|
||||
|
||||
// check method in header
|
||||
m.Method = set(m.Method, "Micro-Method")
|
||||
m.Method = set(m.Method, headers.Method)
|
||||
|
||||
// set the request id
|
||||
m.Id = set(m.Id, "Micro-Id")
|
||||
m.Id = set(m.Id, headers.ID)
|
||||
}
|
||||
|
||||
func setHeaders(m *codec.Message, stream string) {
|
||||
@@ -113,21 +118,22 @@ func setHeaders(m *codec.Message, stream string) {
|
||||
if len(v) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
m.Header[hdr] = v
|
||||
}
|
||||
|
||||
set("Micro-Id", m.Id)
|
||||
set("Micro-Service", m.Target)
|
||||
set("Micro-Method", m.Method)
|
||||
set("Micro-Endpoint", m.Endpoint)
|
||||
set("Micro-Error", m.Error)
|
||||
set(headers.ID, m.Id)
|
||||
set(headers.Request, m.Target)
|
||||
set(headers.Method, m.Method)
|
||||
set(headers.Endpoint, m.Endpoint)
|
||||
set(headers.Error, m.Error)
|
||||
|
||||
if len(stream) > 0 {
|
||||
set("Micro-Stream", stream)
|
||||
set(headers.Stream, stream)
|
||||
}
|
||||
}
|
||||
|
||||
// setupProtocol sets up the old protocol
|
||||
// setupProtocol sets up the old protocol.
|
||||
func setupProtocol(msg *transport.Message, node *registry.Node) codec.NewCodec {
|
||||
protocol := node.Metadata["protocol"]
|
||||
|
||||
@@ -137,7 +143,7 @@ func setupProtocol(msg *transport.Message, node *registry.Node) codec.NewCodec {
|
||||
}
|
||||
|
||||
// processing topic publishing
|
||||
if len(msg.Header["Micro-Topic"]) > 0 {
|
||||
if len(msg.Header[headers.Message]) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -149,60 +155,59 @@ func setupProtocol(msg *transport.Message, node *registry.Node) codec.NewCodec {
|
||||
msg.Header["Content-Type"] = "application/proto-rpc"
|
||||
}
|
||||
|
||||
// now return codec
|
||||
return defaultCodecs[msg.Header["Content-Type"]]
|
||||
}
|
||||
|
||||
func newRpcCodec(req *transport.Message, client transport.Client, c codec.NewCodec, stream string) codec.Codec {
|
||||
func newRPCCodec(req *transport.Message, client transport.Client, c codec.NewCodec, stream string) codec.Codec {
|
||||
rwc := &readWriteCloser{
|
||||
wbuf: bytes.NewBuffer(nil),
|
||||
rbuf: bytes.NewBuffer(nil),
|
||||
}
|
||||
r := &rpcCodec{
|
||||
|
||||
return &rpcCodec{
|
||||
buf: rwc,
|
||||
client: client,
|
||||
codec: c(rwc),
|
||||
req: req,
|
||||
stream: stream,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *rpcCodec) Write(m *codec.Message, body interface{}) error {
|
||||
func (c *rpcCodec) Write(message *codec.Message, body interface{}) error {
|
||||
c.buf.wbuf.Reset()
|
||||
|
||||
// create header
|
||||
if m.Header == nil {
|
||||
m.Header = map[string]string{}
|
||||
if message.Header == nil {
|
||||
message.Header = map[string]string{}
|
||||
}
|
||||
|
||||
// copy original header
|
||||
for k, v := range c.req.Header {
|
||||
m.Header[k] = v
|
||||
message.Header[k] = v
|
||||
}
|
||||
|
||||
// set the mucp headers
|
||||
setHeaders(m, c.stream)
|
||||
setHeaders(message, c.stream)
|
||||
|
||||
// if body is bytes Frame don't encode
|
||||
if body != nil {
|
||||
if b, ok := body.(*raw.Frame); ok {
|
||||
// set body
|
||||
m.Body = b.Data
|
||||
message.Body = b.Data
|
||||
} else {
|
||||
// write to codec
|
||||
if err := c.codec.Write(m, body); err != nil {
|
||||
if err := c.codec.Write(message, body); err != nil {
|
||||
return errors.InternalServerError("go.micro.client.codec", err.Error())
|
||||
}
|
||||
// set body
|
||||
m.Body = c.buf.wbuf.Bytes()
|
||||
message.Body = c.buf.wbuf.Bytes()
|
||||
}
|
||||
}
|
||||
|
||||
// create new transport message
|
||||
msg := transport.Message{
|
||||
Header: m.Header,
|
||||
Body: m.Body,
|
||||
Header: message.Header,
|
||||
Body: message.Body,
|
||||
}
|
||||
|
||||
// send the request
|
||||
@@ -213,7 +218,7 @@ func (c *rpcCodec) Write(m *codec.Message, body interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *rpcCodec) ReadHeader(m *codec.Message, r codec.MessageType) error {
|
||||
func (c *rpcCodec) ReadHeader(msg *codec.Message, r codec.MessageType) error {
|
||||
var tm transport.Message
|
||||
|
||||
// read message from transport
|
||||
@@ -225,13 +230,13 @@ func (c *rpcCodec) ReadHeader(m *codec.Message, r codec.MessageType) error {
|
||||
c.buf.rbuf.Write(tm.Body)
|
||||
|
||||
// set headers from transport
|
||||
m.Header = tm.Header
|
||||
msg.Header = tm.Header
|
||||
|
||||
// read header
|
||||
err := c.codec.ReadHeader(m, r)
|
||||
err := c.codec.ReadHeader(msg, r)
|
||||
|
||||
// get headers
|
||||
getHeaders(m)
|
||||
getHeaders(msg)
|
||||
|
||||
// return header error
|
||||
if err != nil {
|
||||
@@ -252,15 +257,23 @@ func (c *rpcCodec) ReadBody(b interface{}) error {
|
||||
if err := c.codec.ReadBody(b); err != nil {
|
||||
return errors.InternalServerError("go.micro.client.codec", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *rpcCodec) Close() error {
|
||||
c.buf.Close()
|
||||
c.codec.Close()
|
||||
if err := c.buf.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.codec.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.client.Close(); err != nil {
|
||||
return errors.InternalServerError("go.micro.client.transport", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,9 +1,9 @@
|
||||
package client
|
||||
|
||||
type message struct {
|
||||
payload interface{}
|
||||
topic string
|
||||
contentType string
|
||||
payload interface{}
|
||||
}
|
||||
|
||||
func newMessage(topic string, payload interface{}, contentType string, opts ...MessageOption) Message {
|
||||
|
||||
+3
-3
@@ -5,13 +5,13 @@ import (
|
||||
)
|
||||
|
||||
type rpcRequest struct {
|
||||
opts RequestOptions
|
||||
codec codec.Codec
|
||||
body interface{}
|
||||
service string
|
||||
method string
|
||||
endpoint string
|
||||
contentType string
|
||||
codec codec.Codec
|
||||
body interface{}
|
||||
opts RequestOptions
|
||||
}
|
||||
|
||||
func newRequest(service, endpoint string, request interface{}, contentType string, reqOpts ...RequestOption) Request {
|
||||
|
||||
+2
-2
@@ -6,10 +6,10 @@ import (
|
||||
)
|
||||
|
||||
type rpcResponse struct {
|
||||
header map[string]string
|
||||
body []byte
|
||||
socket transport.Socket
|
||||
codec codec.Codec
|
||||
header map[string]string
|
||||
body []byte
|
||||
}
|
||||
|
||||
func (r *rpcResponse) Codec() codec.Reader {
|
||||
|
||||
+24
-10
@@ -9,22 +9,25 @@ import (
|
||||
"go-micro.dev/v4/codec"
|
||||
)
|
||||
|
||||
// Implements the streamer interface
|
||||
// Implements the streamer interface.
|
||||
type rpcStream struct {
|
||||
sync.RWMutex
|
||||
id string
|
||||
closed chan bool
|
||||
err error
|
||||
request Request
|
||||
response Response
|
||||
codec codec.Codec
|
||||
context context.Context
|
||||
|
||||
// signal whether we should send EOS
|
||||
sendEOS bool
|
||||
closed chan bool
|
||||
|
||||
// release releases the connection back to the pool
|
||||
release func(err error)
|
||||
id string
|
||||
sync.RWMutex
|
||||
// Indicates whether connection should be closed directly.
|
||||
close bool
|
||||
|
||||
// signal whether we should send EOS
|
||||
sendEOS bool
|
||||
}
|
||||
|
||||
func (r *rpcStream) isClosed() bool {
|
||||
@@ -79,6 +82,7 @@ func (r *rpcStream) Recv(msg interface{}) error {
|
||||
if r.isClosed() {
|
||||
r.err = errShutdown
|
||||
r.Unlock()
|
||||
|
||||
return errShutdown
|
||||
}
|
||||
|
||||
@@ -87,15 +91,19 @@ func (r *rpcStream) Recv(msg interface{}) error {
|
||||
r.Unlock()
|
||||
err := r.codec.ReadHeader(&resp, codec.Response)
|
||||
r.Lock()
|
||||
|
||||
if err != nil {
|
||||
if err == io.EOF && !r.isClosed() {
|
||||
if errors.Is(err, io.EOF) && !r.isClosed() {
|
||||
r.err = io.ErrUnexpectedEOF
|
||||
r.Unlock()
|
||||
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
r.err = err
|
||||
|
||||
r.Unlock()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -124,13 +132,15 @@ func (r *rpcStream) Recv(msg interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
r.Unlock()
|
||||
defer r.Unlock()
|
||||
|
||||
return r.err
|
||||
}
|
||||
|
||||
func (r *rpcStream) Error() error {
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
|
||||
return r.err
|
||||
}
|
||||
|
||||
@@ -152,6 +162,7 @@ func (r *rpcStream) Close() error {
|
||||
// send the end of stream message
|
||||
if r.sendEOS {
|
||||
// no need to check for error
|
||||
//nolint:errcheck,gosec
|
||||
r.codec.Write(&codec.Message{
|
||||
Id: r.id,
|
||||
Target: r.request.Service(),
|
||||
@@ -164,10 +175,13 @@ func (r *rpcStream) Close() error {
|
||||
|
||||
err := r.codec.Close()
|
||||
|
||||
rerr := r.Error()
|
||||
if r.close && rerr == nil {
|
||||
rerr = errors.New("connection header set to close")
|
||||
}
|
||||
// release the connection
|
||||
r.release(r.Error())
|
||||
r.release(rerr)
|
||||
|
||||
// return the codec error
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -6,14 +6,14 @@ import (
|
||||
"go-micro.dev/v4/registry"
|
||||
)
|
||||
|
||||
// CallFunc represents the individual call func
|
||||
// CallFunc represents the individual call func.
|
||||
type CallFunc func(ctx context.Context, node *registry.Node, req Request, rsp interface{}, opts CallOptions) error
|
||||
|
||||
// CallWrapper is a low level wrapper for the CallFunc
|
||||
// CallWrapper is a low level wrapper for the CallFunc.
|
||||
type CallWrapper func(CallFunc) CallFunc
|
||||
|
||||
// Wrapper wraps a client and returns a client
|
||||
// Wrapper wraps a client and returns a client.
|
||||
type Wrapper func(Client) Client
|
||||
|
||||
// StreamWrapper wraps a Stream and returns the equivalent
|
||||
// StreamWrapper wraps a Stream and returns the equivalent.
|
||||
type StreamWrapper func(Stream) Stream
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ type Codec struct {
|
||||
Conn io.ReadWriteCloser
|
||||
}
|
||||
|
||||
// Frame gives us the ability to define raw data to send over the pipes
|
||||
// Frame gives us the ability to define raw data to send over the pipes.
|
||||
type Frame struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
+6
-5
@@ -19,7 +19,7 @@ var (
|
||||
|
||||
type MessageType int
|
||||
|
||||
// Takes in a connection/buffer and returns a new Codec
|
||||
// Takes in a connection/buffer and returns a new Codec.
|
||||
type NewCodec func(io.ReadWriteCloser) Codec
|
||||
|
||||
// Codec encodes/decodes various types of messages used within go-micro.
|
||||
@@ -55,14 +55,15 @@ type Marshaler interface {
|
||||
// the communication, likely followed by the body.
|
||||
// In the case of an error, body may be nil.
|
||||
type Message struct {
|
||||
|
||||
// The values read from the socket
|
||||
Header map[string]string
|
||||
Id string
|
||||
Type MessageType
|
||||
Target string
|
||||
Method string
|
||||
Endpoint string
|
||||
Error string
|
||||
|
||||
// The values read from the socket
|
||||
Header map[string]string
|
||||
Body []byte
|
||||
Body []byte
|
||||
Type MessageType
|
||||
}
|
||||
|
||||
+4
-3
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"go-micro.dev/v4/codec"
|
||||
"go-micro.dev/v4/transport/headers"
|
||||
)
|
||||
|
||||
type Codec struct {
|
||||
@@ -29,8 +30,8 @@ func (c *Codec) ReadHeader(m *codec.Message, t codec.MessageType) error {
|
||||
// service method
|
||||
path := m.Header[":path"]
|
||||
if len(path) == 0 || path[0] != '/' {
|
||||
m.Target = m.Header["Micro-Service"]
|
||||
m.Endpoint = m.Header["Micro-Endpoint"]
|
||||
m.Target = m.Header[headers.Request]
|
||||
m.Endpoint = m.Header[headers.Endpoint]
|
||||
} else {
|
||||
// [ , a.package.Foo, Bar]
|
||||
parts := strings.Split(path, "/")
|
||||
@@ -89,7 +90,7 @@ func (c *Codec) Write(m *codec.Message, b interface{}) error {
|
||||
m.Header[":authority"] = m.Target
|
||||
m.Header["content-type"] = c.ContentType
|
||||
case codec.Response:
|
||||
m.Header["Trailer"] = "grpc-status" //, grpc-message"
|
||||
m.Header["Trailer"] = "grpc-status" // , grpc-message"
|
||||
m.Header["content-type"] = c.ContentType
|
||||
m.Header[":status"] = "200"
|
||||
m.Header["grpc-status"] = "0"
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
var jsonpbMarshaler = &jsonpb.Marshaler{}
|
||||
|
||||
// create buffer pool with 16 instances each preallocated with 256 bytes
|
||||
// create buffer pool with 16 instances each preallocated with 256 bytes.
|
||||
var bufferPool = bpool.NewSizedBufferPool(16, 256)
|
||||
|
||||
type Marshaler struct{}
|
||||
|
||||
+7
-5
@@ -10,22 +10,24 @@ import (
|
||||
)
|
||||
|
||||
type clientCodec struct {
|
||||
dec *json.Decoder // for reading JSON values
|
||||
enc *json.Encoder // for writing JSON values
|
||||
c io.Closer
|
||||
|
||||
// temporary work space
|
||||
req clientRequest
|
||||
resp clientResponse
|
||||
|
||||
sync.Mutex
|
||||
c io.Closer
|
||||
|
||||
dec *json.Decoder // for reading JSON values
|
||||
enc *json.Encoder // for writing JSON values
|
||||
pending map[interface{}]string
|
||||
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
type clientRequest struct {
|
||||
Method string `json:"method"`
|
||||
Params [1]interface{} `json:"params"`
|
||||
ID interface{} `json:"id"`
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
type clientResponse struct {
|
||||
|
||||
+5
-5
@@ -11,11 +11,11 @@ import (
|
||||
)
|
||||
|
||||
type jsonCodec struct {
|
||||
buf *bytes.Buffer
|
||||
mt codec.MessageType
|
||||
rwc io.ReadWriteCloser
|
||||
buf *bytes.Buffer
|
||||
c *clientCodec
|
||||
s *serverCodec
|
||||
mt codec.MessageType
|
||||
}
|
||||
|
||||
func (j *jsonCodec) Close() error {
|
||||
@@ -41,7 +41,7 @@ func (j *jsonCodec) Write(m *codec.Message, b interface{}) error {
|
||||
_, err = j.rwc.Write(data)
|
||||
return err
|
||||
default:
|
||||
return fmt.Errorf("Unrecognised message type: %v", m.Type)
|
||||
return fmt.Errorf("Unrecognized message type: %v", m.Type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func (j *jsonCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error {
|
||||
_, err := io.Copy(j.buf, j.rwc)
|
||||
return err
|
||||
default:
|
||||
return fmt.Errorf("Unrecognised message type: %v", mt)
|
||||
return fmt.Errorf("Unrecognized message type: %v", mt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (j *jsonCodec) ReadBody(b interface{}) error {
|
||||
return json.Unmarshal(j.buf.Bytes(), b)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Unrecognised message type: %v", j.mt)
|
||||
return fmt.Errorf("Unrecognized message type: %v", j.mt)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+2
-2
@@ -19,9 +19,9 @@ type serverCodec struct {
|
||||
}
|
||||
|
||||
type serverRequest struct {
|
||||
Method string `json:"method"`
|
||||
Params *json.RawMessage `json:"params"`
|
||||
ID interface{} `json:"id"`
|
||||
Params *json.RawMessage `json:"params"`
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
type serverResponse struct {
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"go-micro.dev/v4/codec"
|
||||
)
|
||||
|
||||
// create buffer pool with 16 instances each preallocated with 256 bytes
|
||||
// create buffer pool with 16 instances each preallocated with 256 bytes.
|
||||
var bufferPool = bpool.NewSizedBufferPool(16, 256)
|
||||
|
||||
type Marshaler struct{}
|
||||
|
||||
+5
-5
@@ -17,10 +17,10 @@ type flusher interface {
|
||||
}
|
||||
|
||||
type protoCodec struct {
|
||||
sync.Mutex
|
||||
rwc io.ReadWriteCloser
|
||||
mt codec.MessageType
|
||||
buf *bytes.Buffer
|
||||
mt codec.MessageType
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (c *protoCodec) Close() error {
|
||||
@@ -114,7 +114,7 @@ func (c *protoCodec) Write(m *codec.Message, b interface{}) error {
|
||||
}
|
||||
c.rwc.Write(data)
|
||||
default:
|
||||
return fmt.Errorf("Unrecognised message type: %v", m.Type)
|
||||
return fmt.Errorf("Unrecognized message type: %v", m.Type)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -153,7 +153,7 @@ func (c *protoCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error {
|
||||
_, err := io.Copy(c.buf, c.rwc)
|
||||
return err
|
||||
default:
|
||||
return fmt.Errorf("Unrecognised message type: %v", mt)
|
||||
return fmt.Errorf("Unrecognized message type: %v", mt)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -170,7 +170,7 @@ func (c *protoCodec) ReadBody(b interface{}) error {
|
||||
case codec.Event:
|
||||
data = c.buf.Bytes()
|
||||
default:
|
||||
return fmt.Errorf("Unrecognised message type: %v", c.mt)
|
||||
return fmt.Errorf("Unrecognized message type: %v", c.mt)
|
||||
}
|
||||
if b != nil {
|
||||
return proto.Unmarshal(data, b.(proto.Message))
|
||||
|
||||
+12
-12
@@ -2,23 +2,23 @@
|
||||
|
||||
Config is a pluggable dynamic config package
|
||||
|
||||
Most config in applications are statically configured or include complex logic to load from multiple sources.
|
||||
Most config in applications are statically configured or include complex logic to load from multiple sources.
|
||||
Go Config makes this easy, pluggable and mergeable. You'll never have to deal with config in the same way again.
|
||||
|
||||
## Features
|
||||
|
||||
- **Dynamic Loading** - Load configuration from multiple source as and when needed. Go Config manages watching config sources
|
||||
in the background and automatically merges and updates an in memory view.
|
||||
- **Dynamic Loading** - Load configuration from multiple source as and when needed. Go Config manages watching config sources
|
||||
in the background and automatically merges and updates an in memory view.
|
||||
|
||||
- **Pluggable Sources** - Choose from any number of sources to load and merge config. The backend source is abstracted away into
|
||||
a standard format consumed internally and decoded via encoders. Sources can be env vars, flags, file, etcd, k8s configmap, etc.
|
||||
- **Pluggable Sources** - Choose from any number of sources to load and merge config. The backend source is abstracted away into
|
||||
a standard format consumed internally and decoded via encoders. Sources can be env vars, flags, file, etcd, k8s configmap, etc.
|
||||
|
||||
- **Mergeable Config** - If you specify multiple sources of config, regardless of format, they will be merged and presented in
|
||||
a single view. This massively simplifies priority order loading and changes based on environment.
|
||||
- **Mergeable Config** - If you specify multiple sources of config, regardless of format, they will be merged and presented in
|
||||
a single view. This massively simplifies priority order loading and changes based on environment.
|
||||
|
||||
- **Observe Changes** - Optionally watch the config for changes to specific values. Hot reload your app using Go Config's watcher.
|
||||
You don't have to handle ad-hoc hup reloading or whatever else, just keep reading the config and watch for changes if you need
|
||||
to be notified.
|
||||
- **Observe Changes** - Optionally watch the config for changes to specific values. Hot reload your app using Go Config's watcher.
|
||||
You don't have to handle ad-hoc hup reloading or whatever else, just keep reading the config and watch for changes if you need
|
||||
to be notified.
|
||||
|
||||
- **Sane Defaults** - In case config loads badly or is completely wiped away for some unknown reason, you can specify fallback
|
||||
values when accessing any config values directly. This ensures you'll always be reading some sane default in the event of a problem.
|
||||
- **Sane Defaults** - In case config loads badly or is completely wiped away for some unknown reason, you can specify fallback
|
||||
values when accessing any config values directly. This ensures you'll always be reading some sane default in the event of a problem.
|
||||
|
||||
+14
-13
@@ -10,7 +10,7 @@ import (
|
||||
"go-micro.dev/v4/config/source/file"
|
||||
)
|
||||
|
||||
// Config is an interface abstraction for dynamic configuration
|
||||
// Config is an interface abstraction for dynamic configuration.
|
||||
type Config interface {
|
||||
// provide the reader.Values interface
|
||||
reader.Values
|
||||
@@ -28,7 +28,7 @@ type Config interface {
|
||||
Watch(path ...string) (Watcher, error)
|
||||
}
|
||||
|
||||
// Watcher is the config watcher
|
||||
// Watcher is the config watcher.
|
||||
type Watcher interface {
|
||||
Next() (reader.Value, error)
|
||||
Stop() error
|
||||
@@ -37,62 +37,63 @@ type Watcher interface {
|
||||
type Options struct {
|
||||
Loader loader.Loader
|
||||
Reader reader.Reader
|
||||
Source []source.Source
|
||||
|
||||
// for alternative data
|
||||
Context context.Context
|
||||
|
||||
Source []source.Source
|
||||
|
||||
WithWatcherDisabled bool
|
||||
}
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
var (
|
||||
// Default Config Manager
|
||||
// Default Config Manager.
|
||||
DefaultConfig, _ = NewConfig()
|
||||
)
|
||||
|
||||
// NewConfig returns new config
|
||||
// NewConfig returns new config.
|
||||
func NewConfig(opts ...Option) (Config, error) {
|
||||
return newConfig(opts...)
|
||||
}
|
||||
|
||||
// Return config as raw json
|
||||
// Return config as raw json.
|
||||
func Bytes() []byte {
|
||||
return DefaultConfig.Bytes()
|
||||
}
|
||||
|
||||
// Return config as a map
|
||||
// Return config as a map.
|
||||
func Map() map[string]interface{} {
|
||||
return DefaultConfig.Map()
|
||||
}
|
||||
|
||||
// Scan values to a go type
|
||||
// Scan values to a go type.
|
||||
func Scan(v interface{}) error {
|
||||
return DefaultConfig.Scan(v)
|
||||
}
|
||||
|
||||
// Force a source changeset sync
|
||||
// Force a source changeset sync.
|
||||
func Sync() error {
|
||||
return DefaultConfig.Sync()
|
||||
}
|
||||
|
||||
// Get a value from the config
|
||||
// Get a value from the config.
|
||||
func Get(path ...string) reader.Value {
|
||||
return DefaultConfig.Get(path...)
|
||||
}
|
||||
|
||||
// Load config sources
|
||||
// Load config sources.
|
||||
func Load(source ...source.Source) error {
|
||||
return DefaultConfig.Load(source...)
|
||||
}
|
||||
|
||||
// Watch a value for changes
|
||||
// Watch a value for changes.
|
||||
func Watch(path ...string) (Watcher, error) {
|
||||
return DefaultConfig.Watch(path...)
|
||||
}
|
||||
|
||||
// LoadFile is short hand for creating a file source and loading it
|
||||
// LoadFile is short hand for creating a file source and loading it.
|
||||
func LoadFile(path string) error {
|
||||
return Load(file.NewSource(
|
||||
file.WithPath(path),
|
||||
|
||||
+6
-6
@@ -13,21 +13,21 @@ import (
|
||||
)
|
||||
|
||||
type config struct {
|
||||
// the current values
|
||||
vals reader.Values
|
||||
exit chan bool
|
||||
// the current snapshot
|
||||
snap *loader.Snapshot
|
||||
opts Options
|
||||
|
||||
sync.RWMutex
|
||||
// the current snapshot
|
||||
snap *loader.Snapshot
|
||||
// the current values
|
||||
vals reader.Values
|
||||
}
|
||||
|
||||
type watcher struct {
|
||||
lw loader.Watcher
|
||||
rd reader.Reader
|
||||
path []string
|
||||
value reader.Value
|
||||
path []string
|
||||
}
|
||||
|
||||
func newConfig(opts ...Option) (Config, error) {
|
||||
@@ -159,7 +159,7 @@ func (c *config) Scan(v interface{}) error {
|
||||
return c.vals.Scan(v)
|
||||
}
|
||||
|
||||
// sync loads all the sources, calls the parser and updates the config
|
||||
// sync loads all the sources, calls the parser and updates the config.
|
||||
func (c *config) Sync() error {
|
||||
if err := c.opts.Loader.Sync(); err != nil {
|
||||
return err
|
||||
|
||||
+9
-6
@@ -1,4 +1,4 @@
|
||||
// package loader manages loading from multiple sources
|
||||
// Package loader manages loading from multiple sources
|
||||
package loader
|
||||
|
||||
import (
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"go-micro.dev/v4/config/source"
|
||||
)
|
||||
|
||||
// Loader manages loading sources
|
||||
// Loader manages loading sources.
|
||||
type Loader interface {
|
||||
// Stop the loader
|
||||
Close() error
|
||||
@@ -24,7 +24,7 @@ type Loader interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// Watcher lets you watch sources and returns a merged ChangeSet
|
||||
// Watcher lets you watch sources and returns a merged ChangeSet.
|
||||
type Watcher interface {
|
||||
// First call to next may return the current Snapshot
|
||||
// If you are watching a path then only the data from
|
||||
@@ -34,7 +34,7 @@ type Watcher interface {
|
||||
Stop() error
|
||||
}
|
||||
|
||||
// Snapshot is a merged ChangeSet
|
||||
// Snapshot is a merged ChangeSet.
|
||||
type Snapshot struct {
|
||||
// The merged ChangeSet
|
||||
ChangeSet *source.ChangeSet
|
||||
@@ -42,19 +42,22 @@ type Snapshot struct {
|
||||
Version string
|
||||
}
|
||||
|
||||
// Options contains all options for a config loader.
|
||||
type Options struct {
|
||||
Reader reader.Reader
|
||||
Source []source.Source
|
||||
|
||||
// for alternative data
|
||||
Context context.Context
|
||||
|
||||
Source []source.Source
|
||||
|
||||
WithWatcherDisabled bool
|
||||
}
|
||||
|
||||
// Option is a helper for a single option.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Copy snapshot
|
||||
// Copy snapshot.
|
||||
func Copy(s *Snapshot) *Snapshot {
|
||||
cs := *(s.ChangeSet)
|
||||
|
||||
|
||||
+25
-20
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go-micro.dev/v4/config/loader"
|
||||
@@ -16,34 +17,39 @@ import (
|
||||
)
|
||||
|
||||
type memory struct {
|
||||
exit chan bool
|
||||
opts loader.Options
|
||||
|
||||
sync.RWMutex
|
||||
// the current snapshot
|
||||
snap *loader.Snapshot
|
||||
// the current values
|
||||
vals reader.Values
|
||||
exit chan bool
|
||||
// the current snapshot
|
||||
snap *loader.Snapshot
|
||||
|
||||
watchers *list.List
|
||||
opts loader.Options
|
||||
|
||||
// all the sets
|
||||
sets []*source.ChangeSet
|
||||
// all the sources
|
||||
sources []source.Source
|
||||
|
||||
watchers *list.List
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type updateValue struct {
|
||||
version string
|
||||
value reader.Value
|
||||
version string
|
||||
}
|
||||
|
||||
type watcher struct {
|
||||
exit chan bool
|
||||
path []string
|
||||
value reader.Value
|
||||
reader reader.Reader
|
||||
version string
|
||||
version atomic.Value
|
||||
exit chan bool
|
||||
updates chan updateValue
|
||||
path []string
|
||||
}
|
||||
|
||||
func (w *watcher) getVersion() string {
|
||||
return w.version.Load().(string)
|
||||
}
|
||||
|
||||
func (m *memory) watch(idx int, s source.Source) {
|
||||
@@ -128,7 +134,7 @@ func (m *memory) loaded() bool {
|
||||
return loaded
|
||||
}
|
||||
|
||||
// reload reads the sets and creates new values
|
||||
// reload reads the sets and creates new values.
|
||||
func (m *memory) reload() error {
|
||||
m.Lock()
|
||||
|
||||
@@ -169,7 +175,7 @@ func (m *memory) update() {
|
||||
m.RUnlock()
|
||||
|
||||
for _, w := range watchers {
|
||||
if w.version >= snap.Version {
|
||||
if w.getVersion() >= snap.Version {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -185,7 +191,7 @@ func (m *memory) update() {
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot returns a snapshot of the current loaded config
|
||||
// Snapshot returns a snapshot of the current loaded config.
|
||||
func (m *memory) Snapshot() (*loader.Snapshot, error) {
|
||||
if m.loaded() {
|
||||
m.RLock()
|
||||
@@ -207,7 +213,7 @@ func (m *memory) Snapshot() (*loader.Snapshot, error) {
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
// Sync loads all the sources, calls the parser and updates the config
|
||||
// Sync loads all the sources, calls the parser and updates the config.
|
||||
func (m *memory) Sync() error {
|
||||
//nolint:prealloc
|
||||
var sets []*source.ChangeSet
|
||||
@@ -357,8 +363,8 @@ func (m *memory) Watch(path ...string) (loader.Watcher, error) {
|
||||
value: value,
|
||||
reader: m.opts.Reader,
|
||||
updates: make(chan updateValue, 1),
|
||||
version: m.snap.Version,
|
||||
}
|
||||
w.version.Store(m.snap.Version)
|
||||
|
||||
e := m.watchers.PushBack(w)
|
||||
|
||||
@@ -392,9 +398,8 @@ func (w *watcher) Next() (*loader.Snapshot, error) {
|
||||
|
||||
return &loader.Snapshot{
|
||||
ChangeSet: cs,
|
||||
Version: w.version,
|
||||
Version: w.getVersion(),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for {
|
||||
@@ -403,13 +408,13 @@ func (w *watcher) Next() (*loader.Snapshot, error) {
|
||||
return nil, errors.New("watcher stopped")
|
||||
|
||||
case uv := <-w.updates:
|
||||
if uv.version <= w.version {
|
||||
if uv.version <= w.getVersion() {
|
||||
continue
|
||||
}
|
||||
|
||||
v := uv.value
|
||||
|
||||
w.version = uv.version
|
||||
w.version.Store(uv.version)
|
||||
|
||||
if bytes.Equal(w.value.Bytes(), v.Bytes()) {
|
||||
continue
|
||||
|
||||
+2
-2
@@ -6,14 +6,14 @@ import (
|
||||
"go-micro.dev/v4/config/source"
|
||||
)
|
||||
|
||||
// WithSource appends a source to list of sources
|
||||
// WithSource appends a source to list of sources.
|
||||
func WithSource(s source.Source) loader.Option {
|
||||
return func(o *loader.Options) {
|
||||
o.Source = append(o.Source, s)
|
||||
}
|
||||
}
|
||||
|
||||
// WithReader sets the config reader
|
||||
// WithReader sets the config reader.
|
||||
func WithReader(r reader.Reader) loader.Option {
|
||||
return func(o *loader.Options) {
|
||||
o.Reader = r
|
||||
|
||||
+3
-3
@@ -6,21 +6,21 @@ import (
|
||||
"go-micro.dev/v4/config/source"
|
||||
)
|
||||
|
||||
// WithLoader sets the loader for manager config
|
||||
// WithLoader sets the loader for manager config.
|
||||
func WithLoader(l loader.Loader) Option {
|
||||
return func(o *Options) {
|
||||
o.Loader = l
|
||||
}
|
||||
}
|
||||
|
||||
// WithSource appends a source to list of sources
|
||||
// WithSource appends a source to list of sources.
|
||||
func WithSource(s source.Source) Option {
|
||||
return func(o *Options) {
|
||||
o.Source = append(o.Source, s)
|
||||
}
|
||||
}
|
||||
|
||||
// WithReader sets the config reader
|
||||
// WithReader sets the config reader.
|
||||
func WithReader(r reader.Reader) Option {
|
||||
return func(o *Options) {
|
||||
o.Reader = r
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ func (j *jsonReader) String() string {
|
||||
return "json"
|
||||
}
|
||||
|
||||
// NewReader creates a json reader
|
||||
// NewReader creates a json reader.
|
||||
func NewReader(opts ...reader.Option) reader.Reader {
|
||||
options := reader.NewOptions(opts...)
|
||||
return &jsonReader{
|
||||
|
||||
+1
-1
@@ -190,7 +190,7 @@ func (j *jsonValue) Scan(v interface{}) error {
|
||||
func (j *jsonValue) Bytes() []byte {
|
||||
b, err := j.Json.Bytes()
|
||||
if err != nil {
|
||||
// try return marshalled
|
||||
// try return marshaled
|
||||
b, err = j.Json.MarshalJSON()
|
||||
if err != nil {
|
||||
return []byte{}
|
||||
|
||||
+3
-3
@@ -7,14 +7,14 @@ import (
|
||||
"go-micro.dev/v4/config/source"
|
||||
)
|
||||
|
||||
// Reader is an interface for merging changesets
|
||||
// Reader is an interface for merging changesets.
|
||||
type Reader interface {
|
||||
Merge(...*source.ChangeSet) (*source.ChangeSet, error)
|
||||
Values(*source.ChangeSet) (Values, error)
|
||||
String() string
|
||||
}
|
||||
|
||||
// Values is returned by the reader
|
||||
// Values is returned by the reader.
|
||||
type Values interface {
|
||||
Bytes() []byte
|
||||
Get(path ...string) Value
|
||||
@@ -24,7 +24,7 @@ type Values interface {
|
||||
Scan(v interface{}) error
|
||||
}
|
||||
|
||||
// Value represents a value of any type
|
||||
// Value represents a value of any type.
|
||||
type Value interface {
|
||||
Bool(def bool) bool
|
||||
Int(def int) int
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Sum returns the md5 checksum of the ChangeSet data
|
||||
// Sum returns the md5 checksum of the ChangeSet data.
|
||||
func (c *ChangeSet) Sum() string {
|
||||
h := md5.New()
|
||||
h.Write(c.Data)
|
||||
|
||||
+15
-16
@@ -1,9 +1,9 @@
|
||||
# File Source
|
||||
|
||||
The file source reads config from a file.
|
||||
The file source reads config from a file.
|
||||
|
||||
It uses the File extension to determine the Format e.g `config.yaml` has the yaml format.
|
||||
It does not make use of encoders or interpet the file data. If a file extension is not present
|
||||
It uses the File extension to determine the Format e.g `config.yaml` has the yaml format.
|
||||
It does not make use of encoders or interpet the file data. If a file extension is not present
|
||||
the source Format will default to the Encoder in options.
|
||||
|
||||
## Example
|
||||
@@ -12,16 +12,16 @@ A config file format in json
|
||||
|
||||
```json
|
||||
{
|
||||
"hosts": {
|
||||
"database": {
|
||||
"address": "10.0.0.1",
|
||||
"port": 3306
|
||||
},
|
||||
"cache": {
|
||||
"address": "10.0.0.2",
|
||||
"port": 6379
|
||||
}
|
||||
"hosts": {
|
||||
"database": {
|
||||
"address": "10.0.0.1",
|
||||
"port": 3306
|
||||
},
|
||||
"cache": {
|
||||
"address": "10.0.0.2",
|
||||
"port": 6379
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -39,7 +39,7 @@ fileSource := file.NewSource(
|
||||
|
||||
To load different file formats e.g yaml, toml, xml simply specify them with their extension
|
||||
|
||||
```
|
||||
```go
|
||||
fileSource := file.NewSource(
|
||||
file.WithPath("/tmp/config.yaml"),
|
||||
)
|
||||
@@ -47,12 +47,12 @@ fileSource := file.NewSource(
|
||||
|
||||
If you want to specify a file without extension, ensure you set the encoder to the same format
|
||||
|
||||
```
|
||||
```go
|
||||
e := toml.NewEncoder()
|
||||
|
||||
fileSource := file.NewSource(
|
||||
file.WithPath("/tmp/config"),
|
||||
source.WithEncoder(e),
|
||||
source.WithEncoder(e),
|
||||
)
|
||||
```
|
||||
|
||||
@@ -67,4 +67,3 @@ conf := config.NewConfig()
|
||||
// Load file source
|
||||
conf.Load(fileSource)
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -10,9 +10,9 @@ import (
|
||||
)
|
||||
|
||||
type file struct {
|
||||
opts source.Options
|
||||
fs fs.FS
|
||||
path string
|
||||
opts source.Options
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ import (
|
||||
type filePathKey struct{}
|
||||
type fsKey struct{}
|
||||
|
||||
// WithPath sets the path to file
|
||||
// WithPath sets the path to file.
|
||||
func WithPath(p string) source.Option {
|
||||
return func(o *source.Options) {
|
||||
if o.Context == nil {
|
||||
@@ -20,7 +20,7 @@ func WithPath(p string) source.Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithFS sets the underlying filesystem to lookup file from (default os.FS)
|
||||
// WithFS sets the underlying filesystem to lookup file from (default os.FS).
|
||||
func WithFS(fs fs.FS) source.Option {
|
||||
return func(o *source.Options) {
|
||||
if o.Context == nil {
|
||||
|
||||
+17
-17
@@ -13,8 +13,7 @@ import (
|
||||
type watcher struct {
|
||||
f *file
|
||||
|
||||
fw *fsnotify.Watcher
|
||||
exit chan bool
|
||||
fw *fsnotify.Watcher
|
||||
}
|
||||
|
||||
func newWatcher(f *file) (source.Watcher, error) {
|
||||
@@ -26,24 +25,21 @@ func newWatcher(f *file) (source.Watcher, error) {
|
||||
fw.Add(f.path)
|
||||
|
||||
return &watcher{
|
||||
f: f,
|
||||
fw: fw,
|
||||
exit: make(chan bool),
|
||||
f: f,
|
||||
fw: fw,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w *watcher) Next() (*source.ChangeSet, error) {
|
||||
// is it closed?
|
||||
select {
|
||||
case <-w.exit:
|
||||
return nil, source.ErrWatcherStopped
|
||||
default:
|
||||
}
|
||||
|
||||
// try get the event
|
||||
select {
|
||||
case event, _ := <-w.fw.Events:
|
||||
if event.Op == fsnotify.Rename {
|
||||
case event, ok := <-w.fw.Events:
|
||||
// check if channel was closed (i.e. Watcher.Close() was called).
|
||||
if !ok {
|
||||
return nil, source.ErrWatcherStopped
|
||||
}
|
||||
|
||||
if event.Has(fsnotify.Rename) {
|
||||
// check existence of file, and add watch again
|
||||
_, err := os.Stat(event.Name)
|
||||
if err == nil || os.IsExist(err) {
|
||||
@@ -55,11 +51,15 @@ func (w *watcher) Next() (*source.ChangeSet, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
case err := <-w.fw.Errors:
|
||||
case err, ok := <-w.fw.Errors:
|
||||
// check if channel was closed (i.e. Watcher.Close() was called).
|
||||
if !ok {
|
||||
return nil, source.ErrWatcherStopped
|
||||
}
|
||||
|
||||
return nil, err
|
||||
case <-w.exit:
|
||||
return nil, source.ErrWatcherStopped
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-17
@@ -13,8 +13,7 @@ import (
|
||||
type watcher struct {
|
||||
f *file
|
||||
|
||||
fw *fsnotify.Watcher
|
||||
exit chan bool
|
||||
fw *fsnotify.Watcher
|
||||
}
|
||||
|
||||
func newWatcher(f *file) (source.Watcher, error) {
|
||||
@@ -26,24 +25,21 @@ func newWatcher(f *file) (source.Watcher, error) {
|
||||
fw.Add(f.path)
|
||||
|
||||
return &watcher{
|
||||
f: f,
|
||||
fw: fw,
|
||||
exit: make(chan bool),
|
||||
f: f,
|
||||
fw: fw,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w *watcher) Next() (*source.ChangeSet, error) {
|
||||
// is it closed?
|
||||
select {
|
||||
case <-w.exit:
|
||||
return nil, source.ErrWatcherStopped
|
||||
default:
|
||||
}
|
||||
|
||||
// try get the event
|
||||
select {
|
||||
case event := <-w.fw.Events:
|
||||
if event.Op == fsnotify.Rename {
|
||||
case event, ok := <-w.fw.Events:
|
||||
// check if channel was closed (i.e. Watcher.Close() was called).
|
||||
if !ok {
|
||||
return nil, source.ErrWatcherStopped
|
||||
}
|
||||
|
||||
if event.Has(fsnotify.Rename) {
|
||||
// check existence of file, and add watch again
|
||||
_, err := os.Stat(event.Name)
|
||||
if err == nil || os.IsExist(err) {
|
||||
@@ -60,10 +56,13 @@ func (w *watcher) Next() (*source.ChangeSet, error) {
|
||||
w.fw.Add(w.f.path)
|
||||
|
||||
return c, nil
|
||||
case err := <-w.fw.Errors:
|
||||
case err, ok := <-w.fw.Errors:
|
||||
// check if channel was closed (i.e. Watcher.Close() was called).
|
||||
if !ok {
|
||||
return nil, source.ErrWatcherStopped
|
||||
}
|
||||
|
||||
return nil, err
|
||||
case <-w.exit:
|
||||
return nil, source.ErrWatcherStopped
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -35,14 +35,14 @@ func NewOptions(opts ...Option) Options {
|
||||
return options
|
||||
}
|
||||
|
||||
// WithEncoder sets the source encoder
|
||||
// WithEncoder sets the source encoder.
|
||||
func WithEncoder(e encoder.Encoder) Option {
|
||||
return func(o *Options) {
|
||||
o.Encoder = e
|
||||
}
|
||||
}
|
||||
|
||||
// WithClient sets the source client
|
||||
// WithClient sets the source client.
|
||||
func WithClient(c client.Client) Option {
|
||||
return func(o *Options) {
|
||||
o.Client = c
|
||||
|
||||
+6
-6
@@ -7,11 +7,11 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrWatcherStopped is returned when source watcher has been stopped
|
||||
// ErrWatcherStopped is returned when source watcher has been stopped.
|
||||
ErrWatcherStopped = errors.New("watcher stopped")
|
||||
)
|
||||
|
||||
// Source is the source from which config is loaded
|
||||
// Source is the source from which config is loaded.
|
||||
type Source interface {
|
||||
Read() (*ChangeSet, error)
|
||||
Write(*ChangeSet) error
|
||||
@@ -19,16 +19,16 @@ type Source interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// ChangeSet represents a set of changes from a source
|
||||
// ChangeSet represents a set of changes from a source.
|
||||
type ChangeSet struct {
|
||||
Data []byte
|
||||
Timestamp time.Time
|
||||
Checksum string
|
||||
Format string
|
||||
Source string
|
||||
Timestamp time.Time
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Watcher watches a source for changes
|
||||
// Watcher watches a source for changes.
|
||||
type Watcher interface {
|
||||
Next() (*ChangeSet, error)
|
||||
Stop() error
|
||||
|
||||
+9
-9
@@ -8,15 +8,15 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// Default buffer size if any
|
||||
// Default buffer size if any.
|
||||
DefaultSize = 1024
|
||||
// DefaultLog logger
|
||||
// DefaultLog logger.
|
||||
DefaultLog = NewLog()
|
||||
// Default formatter
|
||||
// Default formatter.
|
||||
DefaultFormat = TextFormat
|
||||
)
|
||||
|
||||
// Log is debug log interface for reading and writing logs
|
||||
// Log is debug log interface for reading and writing logs.
|
||||
type Log interface {
|
||||
// Read reads log entries from the logger
|
||||
Read(...ReadOption) ([]Record, error)
|
||||
@@ -26,7 +26,7 @@ type Log interface {
|
||||
Stream() (Stream, error)
|
||||
}
|
||||
|
||||
// Record is log record entry
|
||||
// Record is log record entry.
|
||||
type Record struct {
|
||||
// Timestamp of logged event
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
@@ -36,22 +36,22 @@ type Record struct {
|
||||
Message interface{} `json:"message"`
|
||||
}
|
||||
|
||||
// Stream returns a log stream
|
||||
// Stream returns a log stream.
|
||||
type Stream interface {
|
||||
Chan() <-chan Record
|
||||
Stop() error
|
||||
}
|
||||
|
||||
// Format is a function which formats the output
|
||||
// Format is a function which formats the output.
|
||||
type FormatFunc func(Record) string
|
||||
|
||||
// TextFormat returns text format
|
||||
// TextFormat returns text format.
|
||||
func TextFormat(r Record) string {
|
||||
t := r.Timestamp.Format("2006-01-02 15:04:05")
|
||||
return fmt.Sprintf("%s %v", t, r.Message)
|
||||
}
|
||||
|
||||
// JSONFormat is a json Format func
|
||||
// JSONFormat is a json Format func.
|
||||
func JSONFormat(r Record) string {
|
||||
b, _ := json.Marshal(r)
|
||||
return string(b)
|
||||
|
||||
+11
-11
@@ -2,27 +2,27 @@ package log
|
||||
|
||||
import "time"
|
||||
|
||||
// Option used by the logger
|
||||
// Option used by the logger.
|
||||
type Option func(*Options)
|
||||
|
||||
// Options are logger options
|
||||
// Options are logger options.
|
||||
type Options struct {
|
||||
// Format specifies the output format
|
||||
Format FormatFunc
|
||||
// Name of the log
|
||||
Name string
|
||||
// Size is the size of ring buffer
|
||||
Size int
|
||||
// Format specifies the output format
|
||||
Format FormatFunc
|
||||
}
|
||||
|
||||
// Name of the log
|
||||
// Name of the log.
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
||||
// Size sets the size of the ring buffer
|
||||
// Size sets the size of the ring buffer.
|
||||
func Size(s int) Option {
|
||||
return func(o *Options) {
|
||||
o.Size = s
|
||||
@@ -35,14 +35,14 @@ func Format(f FormatFunc) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultOptions returns default options
|
||||
// DefaultOptions returns default options.
|
||||
func DefaultOptions() Options {
|
||||
return Options{
|
||||
Size: DefaultSize,
|
||||
}
|
||||
}
|
||||
|
||||
// ReadOptions for querying the logs
|
||||
// ReadOptions for querying the logs.
|
||||
type ReadOptions struct {
|
||||
// Since what time in past to return the logs
|
||||
Since time.Time
|
||||
@@ -52,17 +52,17 @@ type ReadOptions struct {
|
||||
Stream bool
|
||||
}
|
||||
|
||||
// ReadOption used for reading the logs
|
||||
// ReadOption used for reading the logs.
|
||||
type ReadOption func(*ReadOptions)
|
||||
|
||||
// Since sets the time since which to return the log records
|
||||
// Since sets the time since which to return the log records.
|
||||
func Since(s time.Time) ReadOption {
|
||||
return func(o *ReadOptions) {
|
||||
o.Since = s
|
||||
}
|
||||
}
|
||||
|
||||
// Count sets the number of log records to return
|
||||
// Count sets the number of log records to return.
|
||||
func Count(c int) ReadOption {
|
||||
return func(o *ReadOptions) {
|
||||
o.Count = c
|
||||
|
||||
+7
-7
@@ -7,21 +7,21 @@ import (
|
||||
"go-micro.dev/v4/util/ring"
|
||||
)
|
||||
|
||||
// Should stream from OS
|
||||
// Should stream from OS.
|
||||
type osLog struct {
|
||||
format FormatFunc
|
||||
once sync.Once
|
||||
|
||||
sync.RWMutex
|
||||
buffer *ring.Buffer
|
||||
subs map[string]*osStream
|
||||
|
||||
sync.RWMutex
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
type osStream struct {
|
||||
stream chan Record
|
||||
}
|
||||
|
||||
// Read reads log entries from the logger
|
||||
// Read reads log entries from the logger.
|
||||
func (o *osLog) Read(...ReadOption) ([]Record, error) {
|
||||
var records []Record
|
||||
|
||||
@@ -33,13 +33,13 @@ func (o *osLog) Read(...ReadOption) ([]Record, error) {
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// Write writes records to log
|
||||
// Write writes records to log.
|
||||
func (o *osLog) Write(r Record) error {
|
||||
o.buffer.Put(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stream log records
|
||||
// Stream log records.
|
||||
func (o *osLog) Stream() (Stream, error) {
|
||||
o.Lock()
|
||||
defer o.Unlock()
|
||||
|
||||
+3
-3
@@ -11,16 +11,16 @@ import (
|
||||
)
|
||||
|
||||
type httpProfile struct {
|
||||
server *http.Server
|
||||
sync.Mutex
|
||||
running bool
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultAddress = ":6060"
|
||||
)
|
||||
|
||||
// Start the profiler
|
||||
// Start the profiler.
|
||||
func (h *httpProfile) Start() error {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
@@ -42,7 +42,7 @@ func (h *httpProfile) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop the profiler
|
||||
// Stop the profiler.
|
||||
func (h *httpProfile) Stop() error {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
+4
-4
@@ -12,15 +12,15 @@ import (
|
||||
)
|
||||
|
||||
type profiler struct {
|
||||
opts profile.Options
|
||||
|
||||
sync.Mutex
|
||||
running bool
|
||||
|
||||
// where the cpu profile is written
|
||||
cpuFile *os.File
|
||||
// where the mem profile is written
|
||||
memFile *os.File
|
||||
opts profile.Options
|
||||
|
||||
sync.Mutex
|
||||
running bool
|
||||
}
|
||||
|
||||
func (p *profiler) Start() error {
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ type Options struct {
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
// Name of the profile
|
||||
// Name of the profile.
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
|
||||
+1
-1
@@ -9,10 +9,10 @@ import (
|
||||
)
|
||||
|
||||
type memTracer struct {
|
||||
opts Options
|
||||
|
||||
// ring buffer of traces
|
||||
buffer *ring.Buffer
|
||||
opts Options
|
||||
}
|
||||
|
||||
func (t *memTracer) Read(opts ...ReadOption) ([]*Span, error) {
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package trace
|
||||
|
||||
import "context"
|
||||
|
||||
type noop struct{}
|
||||
|
||||
func (n *noop) Init(...Option) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *noop) Start(ctx context.Context, name string) (context.Context, *Span) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *noop) Finish(*Span) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *noop) Read(...ReadOption) ([]*Span, error) {
|
||||
return nil, nil
|
||||
}
|
||||
+3
-3
@@ -14,7 +14,7 @@ type ReadOptions struct {
|
||||
|
||||
type ReadOption func(o *ReadOptions)
|
||||
|
||||
// Read the given trace
|
||||
// Read the given trace.
|
||||
func ReadTrace(t string) ReadOption {
|
||||
return func(o *ReadOptions) {
|
||||
o.Trace = t
|
||||
@@ -22,11 +22,11 @@ func ReadTrace(t string) ReadOption {
|
||||
}
|
||||
|
||||
const (
|
||||
// DefaultSize of the buffer
|
||||
// DefaultSize of the buffer.
|
||||
DefaultSize = 64
|
||||
)
|
||||
|
||||
// DefaultOptions returns default options
|
||||
// DefaultOptions returns default options.
|
||||
func DefaultOptions() Options {
|
||||
return Options{
|
||||
Size: DefaultSize,
|
||||
|
||||
+26
-43
@@ -6,9 +6,15 @@ import (
|
||||
"time"
|
||||
|
||||
"go-micro.dev/v4/metadata"
|
||||
"go-micro.dev/v4/transport/headers"
|
||||
)
|
||||
|
||||
// Tracer is an interface for distributed tracing
|
||||
var (
|
||||
// DefaultTracer is the default tracer.
|
||||
DefaultTracer = NewTracer()
|
||||
)
|
||||
|
||||
// Tracer is an interface for distributed tracing.
|
||||
type Tracer interface {
|
||||
// Start a trace
|
||||
Start(ctx context.Context, name string) (context.Context, *Span)
|
||||
@@ -18,18 +24,22 @@ type Tracer interface {
|
||||
Read(...ReadOption) ([]*Span, error)
|
||||
}
|
||||
|
||||
// SpanType describe the nature of the trace span
|
||||
// SpanType describe the nature of the trace span.
|
||||
type SpanType int
|
||||
|
||||
const (
|
||||
// SpanTypeRequestInbound is a span created when serving a request
|
||||
// SpanTypeRequestInbound is a span created when serving a request.
|
||||
SpanTypeRequestInbound SpanType = iota
|
||||
// SpanTypeRequestOutbound is a span created when making a service call
|
||||
// SpanTypeRequestOutbound is a span created when making a service call.
|
||||
SpanTypeRequestOutbound
|
||||
)
|
||||
|
||||
// Span is used to record an entry
|
||||
// Span is used to record an entry.
|
||||
type Span struct {
|
||||
// Start time
|
||||
Started time.Time
|
||||
// associated data
|
||||
Metadata map[string]string
|
||||
// Id of the trace
|
||||
Trace string
|
||||
// name of the span
|
||||
@@ -38,62 +48,35 @@ type Span struct {
|
||||
Id string
|
||||
// parent span id
|
||||
Parent string
|
||||
// Start time
|
||||
Started time.Time
|
||||
// Duration in nano seconds
|
||||
Duration time.Duration
|
||||
// associated data
|
||||
Metadata map[string]string
|
||||
// Type
|
||||
Type SpanType
|
||||
}
|
||||
|
||||
const (
|
||||
traceIDKey = "Micro-Trace-Id"
|
||||
spanIDKey = "Micro-Span-Id"
|
||||
)
|
||||
|
||||
// FromContext returns a span from context
|
||||
// FromContext returns a span from context.
|
||||
func FromContext(ctx context.Context) (traceID string, parentSpanID string, isFound bool) {
|
||||
traceID, traceOk := metadata.Get(ctx, traceIDKey)
|
||||
microID, microOk := metadata.Get(ctx, "Micro-Id")
|
||||
traceID, traceOk := metadata.Get(ctx, headers.TraceIDKey)
|
||||
microID, microOk := metadata.Get(ctx, headers.ID)
|
||||
|
||||
if !traceOk && !microOk {
|
||||
isFound = false
|
||||
return
|
||||
}
|
||||
|
||||
if !traceOk {
|
||||
traceID = microID
|
||||
}
|
||||
parentSpanID, ok := metadata.Get(ctx, spanIDKey)
|
||||
|
||||
parentSpanID, ok := metadata.Get(ctx, headers.SpanID)
|
||||
|
||||
return traceID, parentSpanID, ok
|
||||
}
|
||||
|
||||
// ToContext saves the trace and span ids in the context
|
||||
// ToContext saves the trace and span ids in the context.
|
||||
func ToContext(ctx context.Context, traceID, parentSpanID string) context.Context {
|
||||
return metadata.MergeContext(ctx, map[string]string{
|
||||
traceIDKey: traceID,
|
||||
spanIDKey: parentSpanID,
|
||||
headers.TraceIDKey: traceID,
|
||||
headers.SpanID: parentSpanID,
|
||||
}, true)
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultTracer Tracer = NewTracer()
|
||||
)
|
||||
|
||||
type noop struct{}
|
||||
|
||||
func (n *noop) Init(...Option) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *noop) Start(ctx context.Context, name string) (context.Context, *Span) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *noop) Finish(*Span) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *noop) Read(...ReadOption) ([]*Span, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
+5
-5
@@ -117,7 +117,7 @@ func InternalServerError(id, format string, a ...interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Equal tries to compare errors
|
||||
// Equal tries to compare errors.
|
||||
func Equal(err1 error, err2 error) bool {
|
||||
verr1, ok1 := err1.(*Error)
|
||||
verr2, ok2 := err2.(*Error)
|
||||
@@ -137,7 +137,7 @@ func Equal(err1 error, err2 error) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// FromError try to convert go error to *Error
|
||||
// FromError try to convert go error to *Error.
|
||||
func FromError(err error) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
@@ -149,7 +149,7 @@ func FromError(err error) *Error {
|
||||
return Parse(err.Error())
|
||||
}
|
||||
|
||||
// As finds the first error in err's chain that matches *Error
|
||||
// As finds the first error in err's chain that matches *Error.
|
||||
func As(err error) (*Error, bool) {
|
||||
if err == nil {
|
||||
return nil, false
|
||||
@@ -167,8 +167,8 @@ func NewMultiError() *MultiError {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *MultiError) Append(err *Error) {
|
||||
e.Errors = append(e.Errors, err)
|
||||
func (e *MultiError) Append(err ...*Error) {
|
||||
e.Errors = append(e.Errors, err...)
|
||||
}
|
||||
|
||||
func (e *MultiError) HasErrors() bool {
|
||||
|
||||
+19
-19
@@ -8,26 +8,26 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultStream is the default events stream implementation
|
||||
// DefaultStream is the default events stream implementation.
|
||||
DefaultStream Stream
|
||||
// DefaultStore is the default events store implementation
|
||||
// DefaultStore is the default events store implementation.
|
||||
DefaultStore Store
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrMissingTopic is returned if a blank topic was provided to publish
|
||||
// ErrMissingTopic is returned if a blank topic was provided to publish.
|
||||
ErrMissingTopic = errors.New("Missing topic")
|
||||
// ErrEncodingMessage is returned from publish if there was an error encoding the message option
|
||||
// ErrEncodingMessage is returned from publish if there was an error encoding the message option.
|
||||
ErrEncodingMessage = errors.New("Error encoding message")
|
||||
)
|
||||
|
||||
// Stream is an event streaming interface
|
||||
// Stream is an event streaming interface.
|
||||
type Stream interface {
|
||||
Publish(topic string, msg interface{}, opts ...PublishOption) error
|
||||
Consume(topic string, opts ...ConsumeOption) (<-chan Event, error)
|
||||
}
|
||||
|
||||
// Store is an event store interface
|
||||
// Store is an event store interface.
|
||||
type Store interface {
|
||||
Read(topic string, opts ...ReadOption) ([]*Event, error)
|
||||
Write(event *Event, opts ...WriteOption) error
|
||||
@@ -36,29 +36,29 @@ type Store interface {
|
||||
type AckFunc func() error
|
||||
type NackFunc func() error
|
||||
|
||||
// Event is the object returned by the broker when you subscribe to a topic
|
||||
// Event is the object returned by the broker when you subscribe to a topic.
|
||||
type Event struct {
|
||||
// ID to uniquely identify the event
|
||||
ID string
|
||||
// Topic of event, e.g. "registry.service.created"
|
||||
Topic string
|
||||
// Timestamp of the event
|
||||
Timestamp time.Time
|
||||
// Metadata contains the values the event was indexed by
|
||||
Metadata map[string]string
|
||||
// Payload contains the encoded message
|
||||
Payload []byte
|
||||
|
||||
ackFunc AckFunc
|
||||
nackFunc NackFunc
|
||||
// ID to uniquely identify the event
|
||||
ID string
|
||||
// Topic of event, e.g. "registry.service.created"
|
||||
Topic string
|
||||
// Payload contains the encoded message
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// Unmarshal the events message into an object
|
||||
// Unmarshal the events message into an object.
|
||||
func (e *Event) Unmarshal(v interface{}) error {
|
||||
return json.Unmarshal(e.Payload, v)
|
||||
}
|
||||
|
||||
// Ack acknowledges successful processing of the event in ManualAck mode
|
||||
// Ack acknowledges successful processing of the event in ManualAck mode.
|
||||
func (e *Event) Ack() error {
|
||||
return e.ackFunc()
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func (e *Event) SetAckFunc(f AckFunc) {
|
||||
e.ackFunc = f
|
||||
}
|
||||
|
||||
// Nack negatively acknowledges processing of the event (i.e. failure) in ManualAck mode
|
||||
// Nack negatively acknowledges processing of the event (i.e. failure) in ManualAck mode.
|
||||
func (e *Event) Nack() error {
|
||||
return e.nackFunc()
|
||||
}
|
||||
@@ -76,17 +76,17 @@ func (e *Event) SetNackFunc(f NackFunc) {
|
||||
e.nackFunc = f
|
||||
}
|
||||
|
||||
// Publish an event to a topic
|
||||
// Publish an event to a topic.
|
||||
func Publish(topic string, msg interface{}, opts ...PublishOption) error {
|
||||
return DefaultStream.Publish(topic, msg, opts...)
|
||||
}
|
||||
|
||||
// Consume to events
|
||||
// Consume to events.
|
||||
func Consume(topic string, opts ...ConsumeOption) (<-chan Event, error) {
|
||||
return DefaultStream.Consume(topic, opts...)
|
||||
}
|
||||
|
||||
// Read events for a topic
|
||||
// Read events for a topic.
|
||||
func Read(topic string, opts ...ReadOption) ([]*Event, error) {
|
||||
return DefaultStore.Read(topic, opts...)
|
||||
}
|
||||
|
||||
+7
-7
@@ -8,12 +8,11 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
log "go-micro.dev/v4/logger"
|
||||
"go-micro.dev/v4/store"
|
||||
)
|
||||
|
||||
// NewStream returns an initialized memory stream
|
||||
// NewStream returns an initialized memory stream.
|
||||
func NewStream(opts ...Option) (Stream, error) {
|
||||
// parse the options
|
||||
options := NewOptions(opts...)
|
||||
@@ -22,15 +21,16 @@ func NewStream(opts ...Option) (Stream, error) {
|
||||
}
|
||||
|
||||
type subscriber struct {
|
||||
Group string
|
||||
Topic string
|
||||
Channel chan Event
|
||||
|
||||
sync.RWMutex
|
||||
retryMap map[string]int
|
||||
Group string
|
||||
Topic string
|
||||
retryLimit int
|
||||
autoAck bool
|
||||
ackWait time.Duration
|
||||
|
||||
sync.RWMutex
|
||||
autoAck bool
|
||||
}
|
||||
|
||||
type mem struct {
|
||||
@@ -143,7 +143,7 @@ func (m *mem) Consume(topic string, opts ...ConsumeOption) (<-chan Event, error)
|
||||
}
|
||||
|
||||
// lookupPreviousEvents finds events for a subscriber which occurred before a given time and sends
|
||||
// them into the subscribers channel
|
||||
// them into the subscribers channel.
|
||||
func (m *mem) lookupPreviousEvents(sub *subscriber, startTime time.Time) {
|
||||
// lookup all events which match the topic (a blank topic will return all results)
|
||||
recs, err := m.store.Read(sub.Topic+"/", store.ReadPrefix())
|
||||
|
||||
+26
-26
@@ -25,21 +25,21 @@ func NewOptions(opts ...Option) *Options {
|
||||
}
|
||||
|
||||
type StoreOptions struct {
|
||||
TTL time.Duration
|
||||
Backup Backup
|
||||
Logger logger.Logger
|
||||
TTL time.Duration
|
||||
}
|
||||
|
||||
type StoreOption func(o *StoreOptions)
|
||||
|
||||
// WithLogger sets the underline logger
|
||||
// WithLogger sets the underline logger.
|
||||
func WithLogger(l logger.Logger) StoreOption {
|
||||
return func(o *StoreOptions) {
|
||||
o.Logger = l
|
||||
}
|
||||
}
|
||||
|
||||
// PublishOptions contains all the options which can be provided when publishing an event
|
||||
// PublishOptions contains all the options which can be provided when publishing an event.
|
||||
type PublishOptions struct {
|
||||
// Metadata contains any keys which can be used to query the data, for example a customer id
|
||||
Metadata map[string]string
|
||||
@@ -47,55 +47,55 @@ type PublishOptions struct {
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
// PublishOption sets attributes on PublishOptions
|
||||
// PublishOption sets attributes on PublishOptions.
|
||||
type PublishOption func(o *PublishOptions)
|
||||
|
||||
// WithMetadata sets the Metadata field on PublishOptions
|
||||
// WithMetadata sets the Metadata field on PublishOptions.
|
||||
func WithMetadata(md map[string]string) PublishOption {
|
||||
return func(o *PublishOptions) {
|
||||
o.Metadata = md
|
||||
}
|
||||
}
|
||||
|
||||
// WithTimestamp sets the timestamp field on PublishOptions
|
||||
// WithTimestamp sets the timestamp field on PublishOptions.
|
||||
func WithTimestamp(t time.Time) PublishOption {
|
||||
return func(o *PublishOptions) {
|
||||
o.Timestamp = t
|
||||
}
|
||||
}
|
||||
|
||||
// ConsumeOptions contains all the options which can be provided when subscribing to a topic
|
||||
// ConsumeOptions contains all the options which can be provided when subscribing to a topic.
|
||||
type ConsumeOptions struct {
|
||||
// Group is the name of the consumer group, if two consumers have the same group the events
|
||||
// are distributed between them
|
||||
Group string
|
||||
// Offset is the time from which the messages should be consumed from. If not provided then
|
||||
// the messages will be consumed starting from the moment the Subscription starts.
|
||||
Offset time.Time
|
||||
// Group is the name of the consumer group, if two consumers have the same group the events
|
||||
// are distributed between them
|
||||
Group string
|
||||
AckWait time.Duration
|
||||
// RetryLimit indicates number of times a message is retried
|
||||
RetryLimit int
|
||||
// AutoAck if true (default true), automatically acknowledges every message so it will not be redelivered.
|
||||
// If false specifies that each message need ts to be manually acknowledged by the subscriber.
|
||||
// If processing is successful the message should be ack'ed to remove the message from the stream.
|
||||
// If processing is unsuccessful the message should be nack'ed (negative acknowledgement) which will mean it will
|
||||
// remain on the stream to be processed again.
|
||||
AutoAck bool
|
||||
AckWait time.Duration
|
||||
// RetryLimit indicates number of times a message is retried
|
||||
RetryLimit int
|
||||
// CustomRetries indicates whether to use RetryLimit
|
||||
CustomRetries bool
|
||||
}
|
||||
|
||||
// ConsumeOption sets attributes on ConsumeOptions
|
||||
// ConsumeOption sets attributes on ConsumeOptions.
|
||||
type ConsumeOption func(o *ConsumeOptions)
|
||||
|
||||
// WithGroup sets the consumer group to be part of when consuming events
|
||||
// WithGroup sets the consumer group to be part of when consuming events.
|
||||
func WithGroup(q string) ConsumeOption {
|
||||
return func(o *ConsumeOptions) {
|
||||
o.Group = q
|
||||
}
|
||||
}
|
||||
|
||||
// WithOffset sets the offset time at which to start consuming events
|
||||
// WithOffset sets the offset time at which to start consuming events.
|
||||
func WithOffset(t time.Time) ConsumeOption {
|
||||
return func(o *ConsumeOptions) {
|
||||
o.Offset = t
|
||||
@@ -103,7 +103,7 @@ func WithOffset(t time.Time) ConsumeOption {
|
||||
}
|
||||
|
||||
// WithAutoAck sets the AutoAck field on ConsumeOptions and an ackWait duration after which if no ack is received
|
||||
// the message is requeued in case auto ack is turned off
|
||||
// the message is requeued in case auto ack is turned off.
|
||||
func WithAutoAck(ack bool, ackWait time.Duration) ConsumeOption {
|
||||
return func(o *ConsumeOptions) {
|
||||
o.AutoAck = ack
|
||||
@@ -112,7 +112,7 @@ func WithAutoAck(ack bool, ackWait time.Duration) ConsumeOption {
|
||||
}
|
||||
|
||||
// WithRetryLimit sets the RetryLimit field on ConsumeOptions.
|
||||
// Set to -1 for infinite retries (default)
|
||||
// Set to -1 for infinite retries (default).
|
||||
func WithRetryLimit(retries int) ConsumeOption {
|
||||
return func(o *ConsumeOptions) {
|
||||
o.RetryLimit = retries
|
||||
@@ -127,24 +127,24 @@ func (s ConsumeOptions) GetRetryLimit() int {
|
||||
return s.RetryLimit
|
||||
}
|
||||
|
||||
// WriteOptions contains all the options which can be provided when writing an event to a store
|
||||
// WriteOptions contains all the options which can be provided when writing an event to a store.
|
||||
type WriteOptions struct {
|
||||
// TTL is the duration the event should be recorded for, a zero value TTL indicates the event should
|
||||
// be stored indefinately
|
||||
// be stored indefinitely
|
||||
TTL time.Duration
|
||||
}
|
||||
|
||||
// WriteOption sets attributes on WriteOptions
|
||||
// WriteOption sets attributes on WriteOptions.
|
||||
type WriteOption func(o *WriteOptions)
|
||||
|
||||
// WithTTL sets the TTL attribute on WriteOptions
|
||||
// WithTTL sets the TTL attribute on WriteOptions.
|
||||
func WithTTL(d time.Duration) WriteOption {
|
||||
return func(o *WriteOptions) {
|
||||
o.TTL = d
|
||||
}
|
||||
}
|
||||
|
||||
// ReadOptions contains all the options which can be provided when reading events from a store
|
||||
// ReadOptions contains all the options which can be provided when reading events from a store.
|
||||
type ReadOptions struct {
|
||||
// Limit the number of results to return
|
||||
Limit uint
|
||||
@@ -152,17 +152,17 @@ type ReadOptions struct {
|
||||
Offset uint
|
||||
}
|
||||
|
||||
// ReadOption sets attributes on ReadOptions
|
||||
// ReadOption sets attributes on ReadOptions.
|
||||
type ReadOption func(o *ReadOptions)
|
||||
|
||||
// ReadLimit sets the limit attribute on ReadOptions
|
||||
// ReadLimit sets the limit attribute on ReadOptions.
|
||||
func ReadLimit(l uint) ReadOption {
|
||||
return func(o *ReadOptions) {
|
||||
o.Limit = 1
|
||||
}
|
||||
}
|
||||
|
||||
// ReadOffset sets the offset attribute on ReadOptions
|
||||
// ReadOffset sets the offset attribute on ReadOptions.
|
||||
func ReadOffset(l uint) ReadOption {
|
||||
return func(o *ReadOptions) {
|
||||
o.Offset = 1
|
||||
|
||||
+4
-5
@@ -5,14 +5,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
log "go-micro.dev/v4/logger"
|
||||
"go-micro.dev/v4/store"
|
||||
)
|
||||
|
||||
const joinKey = "/"
|
||||
|
||||
// NewStore returns an initialized events store
|
||||
// NewStore returns an initialized events store.
|
||||
func NewStore(opts ...StoreOption) Store {
|
||||
// parse the options
|
||||
var options StoreOptions
|
||||
@@ -41,7 +40,7 @@ type evStore struct {
|
||||
store store.Store
|
||||
}
|
||||
|
||||
// Read events for a topic
|
||||
// Read events for a topic.
|
||||
func (s *evStore) Read(topic string, opts ...ReadOption) ([]*Event, error) {
|
||||
// validate the topic
|
||||
if len(topic) == 0 {
|
||||
@@ -80,7 +79,7 @@ func (s *evStore) Read(topic string, opts ...ReadOption) ([]*Event, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Write an event to the store
|
||||
// Write an event to the store.
|
||||
func (s *evStore) Write(event *Event, opts ...WriteOption) error {
|
||||
// parse the options
|
||||
options := WriteOptions{
|
||||
@@ -124,7 +123,7 @@ func (s *evStore) backupLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
// Backup is an interface for snapshotting the events store to long term storage
|
||||
// Backup is an interface for snapshotting the events store to long term storage.
|
||||
type Backup interface {
|
||||
Snapshot(st store.Store) error
|
||||
}
|
||||
|
||||
+14
-4
@@ -23,15 +23,16 @@ func init() {
|
||||
}
|
||||
|
||||
type defaultLogger struct {
|
||||
sync.RWMutex
|
||||
opts Options
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// Init (opts...) should only overwrite provided options
|
||||
// Init (opts...) should only overwrite provided options.
|
||||
func (l *defaultLogger) Init(opts ...Option) error {
|
||||
for _, o := range opts {
|
||||
o(&l.opts)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -42,6 +43,7 @@ func (l *defaultLogger) String() string {
|
||||
func (l *defaultLogger) Fields(fields map[string]interface{}) Logger {
|
||||
l.Lock()
|
||||
nfields := make(map[string]interface{}, len(l.opts.Fields))
|
||||
|
||||
for k, v := range l.opts.Fields {
|
||||
nfields[k] = v
|
||||
}
|
||||
@@ -65,6 +67,7 @@ func copyFields(src map[string]interface{}) map[string]interface{} {
|
||||
for k, v := range src {
|
||||
dst[k] = v
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
@@ -85,10 +88,13 @@ func logCallerfilePath(loggingFilePath string) string {
|
||||
if idx == -1 {
|
||||
return loggingFilePath
|
||||
}
|
||||
|
||||
idx = strings.LastIndexByte(loggingFilePath[:idx], '/')
|
||||
|
||||
if idx == -1 {
|
||||
return loggingFilePath
|
||||
}
|
||||
|
||||
return loggingFilePath[idx+1:]
|
||||
}
|
||||
|
||||
@@ -121,6 +127,7 @@ func (l *defaultLogger) Log(level Level, v ...interface{}) {
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
|
||||
metadata := ""
|
||||
|
||||
for _, k := range keys {
|
||||
@@ -162,6 +169,7 @@ func (l *defaultLogger) Logf(level Level, format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
|
||||
metadata := ""
|
||||
|
||||
for _, k := range keys {
|
||||
@@ -177,13 +185,15 @@ func (l *defaultLogger) Logf(level Level, format string, v ...interface{}) {
|
||||
func (l *defaultLogger) Options() Options {
|
||||
// not guard against options Context values
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
|
||||
opts := l.opts
|
||||
opts.Fields = copyFields(l.opts.Fields)
|
||||
l.RUnlock()
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
// NewLogger builds a new logger based on options
|
||||
// NewLogger builds a new logger based on options.
|
||||
func NewLogger(opts ...Option) Logger {
|
||||
// Default options
|
||||
options := Options{
|
||||
|
||||
+1
-1
@@ -116,7 +116,7 @@ func Fatalf(template string, args ...interface{}) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Returns true if the given level is at or lower the current logger level
|
||||
// Returns true if the given level is at or lower the current logger level.
|
||||
func V(lvl Level, log Logger) bool {
|
||||
l := DefaultLogger
|
||||
if log != nil {
|
||||
|
||||
+4
-4
@@ -2,16 +2,16 @@
|
||||
package logger
|
||||
|
||||
var (
|
||||
// Default logger
|
||||
// Default logger.
|
||||
DefaultLogger Logger = NewLogger()
|
||||
|
||||
// Default logger helper
|
||||
// Default logger helper.
|
||||
DefaultHelper *Helper = NewHelper(DefaultLogger)
|
||||
)
|
||||
|
||||
// Logger is a generic logging interface
|
||||
// Logger is a generic logging interface.
|
||||
type Logger interface {
|
||||
// Init initialises options
|
||||
// Init initializes options
|
||||
Init(options ...Option) error
|
||||
// The Logger options
|
||||
Options() Options
|
||||
|
||||
+10
-10
@@ -8,40 +8,40 @@ import (
|
||||
type Option func(*Options)
|
||||
|
||||
type Options struct {
|
||||
// The logging level the logger should log at. default is `InfoLevel`
|
||||
Level Level
|
||||
// fields to always be logged
|
||||
Fields map[string]interface{}
|
||||
// It's common to set this to a file, or leave it default which is `os.Stderr`
|
||||
Out io.Writer
|
||||
// Caller skip frame count for file:line info
|
||||
CallerSkipCount int
|
||||
// Alternative options
|
||||
Context context.Context
|
||||
// fields to always be logged
|
||||
Fields map[string]interface{}
|
||||
// Caller skip frame count for file:line info
|
||||
CallerSkipCount int
|
||||
// The logging level the logger should log at. default is `InfoLevel`
|
||||
Level Level
|
||||
}
|
||||
|
||||
// WithFields set default fields for the logger
|
||||
// WithFields set default fields for the logger.
|
||||
func WithFields(fields map[string]interface{}) Option {
|
||||
return func(args *Options) {
|
||||
args.Fields = fields
|
||||
}
|
||||
}
|
||||
|
||||
// WithLevel set default level for the logger
|
||||
// WithLevel set default level for the logger.
|
||||
func WithLevel(level Level) Option {
|
||||
return func(args *Options) {
|
||||
args.Level = level
|
||||
}
|
||||
}
|
||||
|
||||
// WithOutput set default output writer for the logger
|
||||
// WithOutput set default output writer for the logger.
|
||||
func WithOutput(out io.Writer) Option {
|
||||
return func(args *Options) {
|
||||
args.Out = out
|
||||
}
|
||||
}
|
||||
|
||||
// WithCallerSkipCount set frame count to skip
|
||||
// WithCallerSkipCount set frame count to skip.
|
||||
func WithCallerSkipCount(c int) Option {
|
||||
return func(args *Options) {
|
||||
args.CallerSkipCount = c
|
||||
|
||||
Vendored
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 8.5 KiB |
+7
-7
@@ -36,7 +36,7 @@ func (md Metadata) Delete(key string) {
|
||||
delete(md, strings.Title(key))
|
||||
}
|
||||
|
||||
// Copy makes a copy of the metadata
|
||||
// Copy makes a copy of the metadata.
|
||||
func Copy(md Metadata) Metadata {
|
||||
cmd := make(Metadata, len(md))
|
||||
for k, v := range md {
|
||||
@@ -45,12 +45,12 @@ func Copy(md Metadata) Metadata {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Delete key from metadata
|
||||
// Delete key from metadata.
|
||||
func Delete(ctx context.Context, k string) context.Context {
|
||||
return Set(ctx, k, "")
|
||||
}
|
||||
|
||||
// Set add key with val to metadata
|
||||
// Set add key with val to metadata.
|
||||
func Set(ctx context.Context, k, v string) context.Context {
|
||||
md, ok := FromContext(ctx)
|
||||
if !ok {
|
||||
@@ -64,7 +64,7 @@ func Set(ctx context.Context, k, v string) context.Context {
|
||||
return context.WithValue(ctx, metadataKey{}, md)
|
||||
}
|
||||
|
||||
// Get returns a single value from metadata in the context
|
||||
// Get returns a single value from metadata in the context.
|
||||
func Get(ctx context.Context, key string) (string, bool) {
|
||||
md, ok := FromContext(ctx)
|
||||
if !ok {
|
||||
@@ -82,7 +82,7 @@ func Get(ctx context.Context, key string) (string, bool) {
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// FromContext returns metadata from the given context
|
||||
// FromContext returns metadata from the given context.
|
||||
func FromContext(ctx context.Context) (Metadata, bool) {
|
||||
md, ok := ctx.Value(metadataKey{}).(Metadata)
|
||||
if !ok {
|
||||
@@ -98,12 +98,12 @@ func FromContext(ctx context.Context) (Metadata, bool) {
|
||||
return newMD, ok
|
||||
}
|
||||
|
||||
// NewContext creates a new context with the given metadata
|
||||
// NewContext creates a new context with the given metadata.
|
||||
func NewContext(ctx context.Context, md Metadata) context.Context {
|
||||
return context.WithValue(ctx, metadataKey{}, md)
|
||||
}
|
||||
|
||||
// MergeContext merges metadata to existing metadata, overwriting if specified
|
||||
// MergeContext merges metadata to existing metadata, overwriting if specified.
|
||||
func MergeContext(ctx context.Context, patchMd Metadata, overwrite bool) context.Context {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
|
||||
Vendored
+8
-7
@@ -12,11 +12,11 @@ type serviceKey struct{}
|
||||
|
||||
// Service is an interface that wraps the lower level libraries
|
||||
// within go-micro. Its a convenience method for building
|
||||
// and initialising services.
|
||||
// and initializing services.
|
||||
type Service interface {
|
||||
// The service name
|
||||
Name() string
|
||||
// Init initialises options
|
||||
// Init initializes options
|
||||
Init(...Option)
|
||||
// Options returns the current options
|
||||
Options() Options
|
||||
@@ -30,13 +30,13 @@ type Service interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// Event is used to publish messages to a topic
|
||||
// Event is used to publish messages to a topic.
|
||||
type Event interface {
|
||||
// Publish publishes a message to the event topic
|
||||
Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error
|
||||
}
|
||||
|
||||
// Type alias to satisfy the deprecation
|
||||
// Type alias to satisfy the deprecation.
|
||||
type Publisher = Event
|
||||
|
||||
type Option func(*Options)
|
||||
@@ -57,20 +57,21 @@ func NewContext(ctx context.Context, s Service) context.Context {
|
||||
return context.WithValue(ctx, serviceKey{}, s)
|
||||
}
|
||||
|
||||
// NewEvent creates a new event publisher
|
||||
// NewEvent creates a new event publisher.
|
||||
func NewEvent(topic string, c client.Client) Event {
|
||||
if c == nil {
|
||||
c = client.NewClient()
|
||||
}
|
||||
|
||||
return &event{c, topic}
|
||||
}
|
||||
|
||||
// RegisterHandler is syntactic sugar for registering a handler
|
||||
// RegisterHandler is syntactic sugar for registering a handler.
|
||||
func RegisterHandler(s server.Server, h interface{}, opts ...server.HandlerOption) error {
|
||||
return s.Handle(s.NewHandler(h, opts...))
|
||||
}
|
||||
|
||||
// RegisterSubscriber is syntactic sugar for registering a subscriber
|
||||
// RegisterSubscriber is syntactic sugar for registering a subscriber.
|
||||
func RegisterSubscriber(topic string, s server.Server, h interface{}, opts ...server.SubscriberOption) error {
|
||||
return s.Subscribe(s.NewSubscriber(topic, h, opts...))
|
||||
}
|
||||
|
||||
Vendored
+59
-48
@@ -22,31 +22,33 @@ import (
|
||||
"go-micro.dev/v4/util/cmd"
|
||||
)
|
||||
|
||||
// Options for micro service
|
||||
// Options for micro service.
|
||||
type Options struct {
|
||||
Auth auth.Auth
|
||||
Broker broker.Broker
|
||||
Cache cache.Cache
|
||||
Cmd cmd.Cmd
|
||||
Config config.Config
|
||||
Client client.Client
|
||||
Server server.Server
|
||||
Store store.Store
|
||||
Registry registry.Registry
|
||||
Runtime runtime.Runtime
|
||||
Transport transport.Transport
|
||||
Profile profile.Profile
|
||||
Logger logger.Logger
|
||||
// Before and After funcs
|
||||
BeforeStart []func() error
|
||||
BeforeStop []func() error
|
||||
AfterStart []func() error
|
||||
AfterStop []func() error
|
||||
Registry registry.Registry
|
||||
Store store.Store
|
||||
Auth auth.Auth
|
||||
Cmd cmd.Cmd
|
||||
Config config.Config
|
||||
Client client.Client
|
||||
Server server.Server
|
||||
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
|
||||
Cache cache.Cache
|
||||
Runtime runtime.Runtime
|
||||
Profile profile.Profile
|
||||
Transport transport.Transport
|
||||
Logger logger.Logger
|
||||
Broker broker.Broker
|
||||
// Before and After funcs
|
||||
BeforeStart []func() error
|
||||
AfterStart []func() error
|
||||
AfterStop []func() error
|
||||
|
||||
BeforeStop []func() error
|
||||
|
||||
Signal bool
|
||||
}
|
||||
|
||||
@@ -75,7 +77,7 @@ func newOptions(opts ...Option) Options {
|
||||
return opt
|
||||
}
|
||||
|
||||
// Broker to be used for service
|
||||
// Broker to be used for service.
|
||||
func Broker(b broker.Broker) Option {
|
||||
return func(o *Options) {
|
||||
o.Broker = b
|
||||
@@ -97,7 +99,7 @@ func Cmd(c cmd.Cmd) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Client to be used for service
|
||||
// Client to be used for service.
|
||||
func Client(c client.Client) Option {
|
||||
return func(o *Options) {
|
||||
o.Client = c
|
||||
@@ -112,6 +114,15 @@ func Context(ctx context.Context) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle will register a handler without any fuss
|
||||
func Handle(v interface{}) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Handle(
|
||||
o.Server.NewHandler(v),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSignal toggles automatic installation of the signal handler that
|
||||
// traps TERM, INT, and QUIT. Users of this feature to disable the signal
|
||||
// handler, should control liveness of the service through the context.
|
||||
@@ -121,21 +132,21 @@ func HandleSignal(b bool) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Profile to be used for debug profile
|
||||
// Profile to be used for debug profile.
|
||||
func Profile(p profile.Profile) Option {
|
||||
return func(o *Options) {
|
||||
o.Profile = p
|
||||
}
|
||||
}
|
||||
|
||||
// Server to be used for service
|
||||
// Server to be used for service.
|
||||
func Server(s server.Server) Option {
|
||||
return func(o *Options) {
|
||||
o.Server = s
|
||||
}
|
||||
}
|
||||
|
||||
// Store sets the store to use
|
||||
// Store sets the store to use.
|
||||
func Store(s store.Store) Option {
|
||||
return func(o *Options) {
|
||||
o.Store = s
|
||||
@@ -143,7 +154,7 @@ func Store(s store.Store) Option {
|
||||
}
|
||||
|
||||
// Registry sets the registry for the service
|
||||
// and the underlying components
|
||||
// and the underlying components.
|
||||
func Registry(r registry.Registry) Option {
|
||||
return func(o *Options) {
|
||||
o.Registry = r
|
||||
@@ -155,28 +166,28 @@ func Registry(r registry.Registry) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Tracer sets the tracer for the service
|
||||
// Tracer sets the tracer for the service.
|
||||
func Tracer(t trace.Tracer) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.Tracer(t))
|
||||
}
|
||||
}
|
||||
|
||||
// Auth sets the auth for the service
|
||||
// Auth sets the auth for the service.
|
||||
func Auth(a auth.Auth) Option {
|
||||
return func(o *Options) {
|
||||
o.Auth = a
|
||||
}
|
||||
}
|
||||
|
||||
// Config sets the config for the service
|
||||
// Config sets the config for the service.
|
||||
func Config(c config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = c
|
||||
}
|
||||
}
|
||||
|
||||
// Selector sets the selector for the service client
|
||||
// Selector sets the selector for the service client.
|
||||
func Selector(s selector.Selector) Option {
|
||||
return func(o *Options) {
|
||||
o.Client.Init(client.Selector(s))
|
||||
@@ -184,7 +195,7 @@ func Selector(s selector.Selector) Option {
|
||||
}
|
||||
|
||||
// Transport sets the transport for the service
|
||||
// and the underlying components
|
||||
// and the underlying components.
|
||||
func Transport(t transport.Transport) Option {
|
||||
return func(o *Options) {
|
||||
o.Transport = t
|
||||
@@ -194,7 +205,7 @@ func Transport(t transport.Transport) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime sets the runtime
|
||||
// Runtime sets the runtime.
|
||||
func Runtime(r runtime.Runtime) Option {
|
||||
return func(o *Options) {
|
||||
o.Runtime = r
|
||||
@@ -203,56 +214,56 @@ func Runtime(r runtime.Runtime) Option {
|
||||
|
||||
// Convenience options
|
||||
|
||||
// Address sets the address of the server
|
||||
// Address sets the address of the server.
|
||||
func Address(addr string) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.Address(addr))
|
||||
}
|
||||
}
|
||||
|
||||
// Name of the service
|
||||
// Name of the service.
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.Name(n))
|
||||
}
|
||||
}
|
||||
|
||||
// Version of the service
|
||||
// Version of the service.
|
||||
func Version(v string) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.Version(v))
|
||||
}
|
||||
}
|
||||
|
||||
// Metadata associated with the service
|
||||
// Metadata associated with the service.
|
||||
func Metadata(md map[string]string) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.Metadata(md))
|
||||
}
|
||||
}
|
||||
|
||||
// Flags that can be passed to service
|
||||
// Flags that can be passed to service.
|
||||
func Flags(flags ...cli.Flag) Option {
|
||||
return func(o *Options) {
|
||||
o.Cmd.App().Flags = append(o.Cmd.App().Flags, flags...)
|
||||
}
|
||||
}
|
||||
|
||||
// Action can be used to parse user provided cli options
|
||||
// Action can be used to parse user provided cli options.
|
||||
func Action(a func(*cli.Context) error) Option {
|
||||
return func(o *Options) {
|
||||
o.Cmd.App().Action = a
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterTTL specifies the TTL to use when registering the service
|
||||
// RegisterTTL specifies the TTL to use when registering the service.
|
||||
func RegisterTTL(t time.Duration) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.RegisterTTL(t))
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterInterval specifies the interval on which to re-register
|
||||
// RegisterInterval specifies the interval on which to re-register.
|
||||
func RegisterInterval(t time.Duration) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.RegisterInterval(t))
|
||||
@@ -271,14 +282,14 @@ func WrapClient(w ...client.Wrapper) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WrapCall is a convenience method for wrapping a Client CallFunc
|
||||
// WrapCall is a convenience method for wrapping a Client CallFunc.
|
||||
func WrapCall(w ...client.CallWrapper) Option {
|
||||
return func(o *Options) {
|
||||
o.Client.Init(client.WrapCall(w...))
|
||||
}
|
||||
}
|
||||
|
||||
// WrapHandler adds a handler Wrapper to a list of options passed into the server
|
||||
// WrapHandler adds a handler Wrapper to a list of options passed into the server.
|
||||
func WrapHandler(w ...server.HandlerWrapper) Option {
|
||||
return func(o *Options) {
|
||||
var wrappers []server.Option
|
||||
@@ -292,7 +303,7 @@ func WrapHandler(w ...server.HandlerWrapper) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WrapSubscriber adds a subscriber Wrapper to a list of options passed into the server
|
||||
// WrapSubscriber adds a subscriber Wrapper to a list of options passed into the server.
|
||||
func WrapSubscriber(w ...server.SubscriberWrapper) Option {
|
||||
return func(o *Options) {
|
||||
var wrappers []server.Option
|
||||
@@ -306,7 +317,7 @@ func WrapSubscriber(w ...server.SubscriberWrapper) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Add opt to server option
|
||||
// Add opt to server option.
|
||||
func AddListenOption(option server.Option) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(option)
|
||||
@@ -315,35 +326,35 @@ func AddListenOption(option server.Option) Option {
|
||||
|
||||
// Before and Afters
|
||||
|
||||
// BeforeStart run funcs before service starts
|
||||
// BeforeStart run funcs before service starts.
|
||||
func BeforeStart(fn func() error) Option {
|
||||
return func(o *Options) {
|
||||
o.BeforeStart = append(o.BeforeStart, fn)
|
||||
}
|
||||
}
|
||||
|
||||
// BeforeStop run funcs before service stops
|
||||
// BeforeStop run funcs before service stops.
|
||||
func BeforeStop(fn func() error) Option {
|
||||
return func(o *Options) {
|
||||
o.BeforeStop = append(o.BeforeStop, fn)
|
||||
}
|
||||
}
|
||||
|
||||
// AfterStart run funcs after service starts
|
||||
// AfterStart run funcs after service starts.
|
||||
func AfterStart(fn func() error) Option {
|
||||
return func(o *Options) {
|
||||
o.AfterStart = append(o.AfterStart, fn)
|
||||
}
|
||||
}
|
||||
|
||||
// AfterStop run funcs after service stops
|
||||
// AfterStop run funcs after service stops.
|
||||
func AfterStop(fn func() error) Option {
|
||||
return func(o *Options) {
|
||||
o.AfterStop = append(o.AfterStop, fn)
|
||||
}
|
||||
}
|
||||
|
||||
// Logger sets the logger for the service
|
||||
// Logger sets the logger for the service.
|
||||
func Logger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user