Compare commits
	
		
			71 Commits
		
	
	
		
			v3.0.0-gam
			...
			v3.0.0-del
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6dc7e792c8 | |||
| 81649d51e1 | |||
| 23f5d10ccb | |||
| e3f235acc1 | |||
| fa9ef1c816 | |||
| 77dab8ee15 | |||
| 51fbff3e4a | |||
| bd6493327f | |||
|  | 2141e9631c | ||
| be8d09c663 | |||
| 72bbbe3817 | |||
| c92add984c | |||
| 3542d6c824 | |||
| dc63d96e0b | |||
| 7c9a7e84c7 | |||
| 31180758b4 | |||
| ce25a41fe1 | |||
| 8fa8afdfa4 | |||
| e127547799 | |||
| 1fbf8b2e20 | |||
| e41bb5ebc5 | |||
| 7c311aea19 | |||
| 8a2b122015 | |||
| 40b0870cf8 | |||
| e6ab6d50eb | |||
| a9eff06976 | |||
| 416fe5e4c8 | |||
| ddb53bf8e4 | |||
| 0e6efda528 | |||
| f2413a7789 | |||
| 9553f46cf4 | |||
| 14c97d59c1 | |||
| a59aae760f | |||
| 0a5b34a07b | |||
| 62502ad720 | |||
| 6e43ae7190 | |||
| 0e1f744fcc | |||
| 2fc47782cf | |||
| 34d93306d6 | |||
| 336868ed0d | |||
| 2682f15b8e | |||
| 4c12e38c01 | |||
| 62bfe9c06e | |||
| 24be220f91 | |||
| cacd33e84f | |||
| 9475003059 | |||
| 8532ccebba | |||
| 9c55b1d06a | |||
| efd9075d9b | |||
| 4c4fa00a5d | |||
| 21d5ca1cdd | |||
| ec3c1a02fc | |||
| dc5dc6ab5b | |||
| 1cbd1d2bad | |||
| aa667728a1 | |||
| 9b11ea527a | |||
| 5787a1afb8 | |||
| 74c10f1139 | |||
| 7e3fac8937 | |||
| 6021edc855 | |||
| 8817c110d0 | |||
| d59db9df16 | |||
| 2d1e6db9fd | |||
| 5bfca99627 | |||
| 9ea3149b60 | |||
| 8f03480ed2 | |||
| caec730248 | |||
| f1fde75567 | |||
| 5fe3a46732 | |||
| e7d418183b | |||
| c576749b57 | 
							
								
								
									
										26
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| name: build | name: build | ||||||
| on: | on: | ||||||
|  push: |   push: | ||||||
|     branches: |     branches: | ||||||
|     - master |     - master | ||||||
| jobs: | jobs: | ||||||
| @@ -12,20 +12,36 @@ jobs: | |||||||
|       uses: actions/setup-go@v1 |       uses: actions/setup-go@v1 | ||||||
|       with: |       with: | ||||||
|         go-version: 1.15 |         go-version: 1.15 | ||||||
|     - name: checkout |  | ||||||
|       uses: actions/checkout@v2 |  | ||||||
|     - name: cache |     - name: cache | ||||||
|       uses: actions/cache@v2 |       uses: actions/cache@v2 | ||||||
|       with: |       with: | ||||||
|         path: ~/go/pkg/mod |         path: ~/go/pkg/mod | ||||||
|         key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} |         key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | ||||||
|         restore-keys: ${{ runner.os }}-go- |         restore-keys: ${{ runner.os }}-go- | ||||||
|     - name: deps |     - name: sdk checkout | ||||||
|  |       uses: actions/checkout@v2 | ||||||
|  |     - name: sdk deps | ||||||
|       run: go get -v -t -d ./... |       run: go get -v -t -d ./... | ||||||
|     - name: test |     - name: sdk test | ||||||
|       env: |       env: | ||||||
|         INTEGRATION_TESTS: yes |         INTEGRATION_TESTS: yes | ||||||
|       run: go test -mod readonly -v ./... |       run: go test -mod readonly -v ./... | ||||||
|  |     - name: tests checkout | ||||||
|  |       uses: actions/checkout@v2 | ||||||
|  |       with: | ||||||
|  |         repository: unistack-org/micro-tests | ||||||
|  |         ref: refs/heads/master | ||||||
|  |         path: micro-tests | ||||||
|  |         fetch-depth: 1 | ||||||
|  |     - name: tests deps | ||||||
|  |       run: | | ||||||
|  |         cd micro-tests | ||||||
|  |         go mod edit -replace="github.com/unistack-org/micro/v3=../" | ||||||
|  |         go get -v -t -d ./... | ||||||
|  |     - name: tests test | ||||||
|  |       env: | ||||||
|  |         INTEGRATION_TESTS: yes | ||||||
|  |       run: cd micro-tests && go test -mod readonly -v ./... | ||||||
|   lint: |   lint: | ||||||
|     name: lint |     name: lint | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								.github/workflows/pr.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/pr.yml
									
									
									
									
										vendored
									
									
								
							| @@ -12,20 +12,36 @@ jobs: | |||||||
|       uses: actions/setup-go@v1 |       uses: actions/setup-go@v1 | ||||||
|       with: |       with: | ||||||
|         go-version: 1.15 |         go-version: 1.15 | ||||||
|     - name: checkout |  | ||||||
|       uses: actions/checkout@v2 |  | ||||||
|     - name: cache |     - name: cache | ||||||
|       uses: actions/cache@v2 |       uses: actions/cache@v2 | ||||||
|       with: |       with: | ||||||
|         path: ~/go/pkg/mod |         path: ~/go/pkg/mod | ||||||
|         key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} |         key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | ||||||
|         restore-keys: ${{ runner.os }}-go- |         restore-keys: ${{ runner.os }}-go- | ||||||
|     - name: deps |     - name: sdk checkout | ||||||
|  |       uses: actions/checkout@v2 | ||||||
|  |     - name: sdk deps | ||||||
|       run: go get -v -t -d ./... |       run: go get -v -t -d ./... | ||||||
|     - name: test |     - name: sdk test | ||||||
|       env: |       env: | ||||||
|         INTEGRATION_TESTS: yes |         INTEGRATION_TESTS: yes | ||||||
|       run: go test -mod readonly -v ./... |       run: go test -mod readonly -v ./... | ||||||
|  |     - name: tests checkout | ||||||
|  |       uses: actions/checkout@v2 | ||||||
|  |       with: | ||||||
|  |         repository: unistack-org/micro-tests | ||||||
|  |         ref: refs/heads/master | ||||||
|  |         path: micro-tests | ||||||
|  |         fetch-depth: 1 | ||||||
|  |     - name: tests deps | ||||||
|  |       run: | | ||||||
|  |         cd micro-tests | ||||||
|  |         go mod edit -replace="github.com/unistack-org/micro/v3=../" | ||||||
|  |         go get -v -t -d ./... | ||||||
|  |     - name: tests test | ||||||
|  |       env: | ||||||
|  |         INTEGRATION_TESTS: yes | ||||||
|  |       run: cd micro-tests && go test -mod readonly -v ./... | ||||||
|   lint: |   lint: | ||||||
|     name: lint |     name: lint | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| # Micro [](https://opensource.org/licenses/Apache-2.0) [](https://pkg.go.dev/github.com/unistack-org/micro/v3?tab=overview) [](https://github.com/unistack-org/micro/actions?query=workflow%3Abuild+branch%3Amaster+event%3Apush) [](https://goreportcard.com/report/github.com/unistack-org/micro) | # Micro [](https://opensource.org/licenses/Apache-2.0) [](https://pkg.go.dev/github.com/unistack-org/micro/v3?tab=overview) [](https://github.com/unistack-org/micro/actions?query=workflow%3Abuild+branch%3Amaster+event%3Apush) [](https://goreportcard.com/report/github.com/unistack-org/micro) [](https://unistack-org.slack.com/messages/default) | ||||||
|  |  | ||||||
| Micro is a standard library for microservices. | Micro is a standard library for microservices. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,13 +10,13 @@ import ( | |||||||
|  |  | ||||||
| 	jsonpatch "github.com/evanphx/json-patch/v5" | 	jsonpatch "github.com/evanphx/json-patch/v5" | ||||||
| 	"github.com/oxtoacart/bpool" | 	"github.com/oxtoacart/bpool" | ||||||
|  | 	jsonrpc "github.com/unistack-org/micro-codec-jsonrpc" | ||||||
|  | 	protorpc "github.com/unistack-org/micro-codec-protorpc" | ||||||
| 	"github.com/unistack-org/micro/v3/api" | 	"github.com/unistack-org/micro/v3/api" | ||||||
| 	"github.com/unistack-org/micro/v3/api/handler" | 	"github.com/unistack-org/micro/v3/api/handler" | ||||||
| 	"github.com/unistack-org/micro/v3/api/internal/proto" | 	"github.com/unistack-org/micro/v3/api/internal/proto" | ||||||
| 	"github.com/unistack-org/micro/v3/client" | 	"github.com/unistack-org/micro/v3/client" | ||||||
| 	"github.com/unistack-org/micro/v3/codec" | 	"github.com/unistack-org/micro/v3/codec" | ||||||
| 	"github.com/unistack-org/micro/v3/codec/jsonrpc" |  | ||||||
| 	"github.com/unistack-org/micro/v3/codec/protorpc" |  | ||||||
| 	"github.com/unistack-org/micro/v3/errors" | 	"github.com/unistack-org/micro/v3/errors" | ||||||
| 	"github.com/unistack-org/micro/v3/logger" | 	"github.com/unistack-org/micro/v3/logger" | ||||||
| 	"github.com/unistack-org/micro/v3/metadata" | 	"github.com/unistack-org/micro/v3/metadata" | ||||||
| @@ -224,7 +224,7 @@ func requestPayload(r *http.Request) ([]byte, error) { | |||||||
| 	case strings.Contains(ct, "application/json-rpc"): | 	case strings.Contains(ct, "application/json-rpc"): | ||||||
| 		msg := codec.Message{ | 		msg := codec.Message{ | ||||||
| 			Type:   codec.Request, | 			Type:   codec.Request, | ||||||
| 			Header: make(map[string]string), | 			Header: metadata.New(0), | ||||||
| 		} | 		} | ||||||
| 		c := jsonrpc.NewCodec(&buffer{r.Body}) | 		c := jsonrpc.NewCodec(&buffer{r.Body}) | ||||||
| 		if err = c.ReadHeader(&msg, codec.Request); err != nil { | 		if err = c.ReadHeader(&msg, codec.Request); err != nil { | ||||||
| @@ -238,7 +238,7 @@ func requestPayload(r *http.Request) ([]byte, error) { | |||||||
| 	case strings.Contains(ct, "application/proto-rpc"), strings.Contains(ct, "application/octet-stream"): | 	case strings.Contains(ct, "application/proto-rpc"), strings.Contains(ct, "application/octet-stream"): | ||||||
| 		msg := codec.Message{ | 		msg := codec.Message{ | ||||||
| 			Type:   codec.Request, | 			Type:   codec.Request, | ||||||
| 			Header: make(map[string]string), | 			Header: metadata.New(0), | ||||||
| 		} | 		} | ||||||
| 		c := protorpc.NewCodec(&buffer{r.Body}) | 		c := protorpc.NewCodec(&buffer{r.Body}) | ||||||
| 		if err = c.ReadHeader(&msg, codec.Request); err != nil { | 		if err = c.ReadHeader(&msg, codec.Request); err != nil { | ||||||
| @@ -250,10 +250,12 @@ func requestPayload(r *http.Request) ([]byte, error) { | |||||||
| 		} | 		} | ||||||
| 		return raw.Marshal() | 		return raw.Marshal() | ||||||
| 	case strings.Contains(ct, "application/www-x-form-urlencoded"): | 	case strings.Contains(ct, "application/www-x-form-urlencoded"): | ||||||
| 		r.ParseForm() | 		if err = r.ParseForm(); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		// generate a new set of values from the form | 		// generate a new set of values from the form | ||||||
| 		vals := make(map[string]string) | 		vals := make(map[string]string, len(r.Form)) | ||||||
| 		for k, v := range r.Form { | 		for k, v := range r.Form { | ||||||
| 			vals[k] = strings.Join(v, ",") | 			vals[k] = strings.Join(v, ",") | ||||||
| 		} | 		} | ||||||
| @@ -268,7 +270,7 @@ func requestPayload(r *http.Request) ([]byte, error) { | |||||||
| 	// dont user metadata.FromContext as it mangles names | 	// dont user metadata.FromContext as it mangles names | ||||||
| 	md, ok := metadata.FromContext(ctx) | 	md, ok := metadata.FromContext(ctx) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		md = make(map[string]string) | 		md = metadata.New(0) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// allocate maximum | 	// allocate maximum | ||||||
| @@ -444,8 +446,8 @@ func writeError(w http.ResponseWriter, r *http.Request, err error) { | |||||||
|  |  | ||||||
| 	_, werr := w.Write([]byte(ce.Error())) | 	_, werr := w.Write([]byte(ce.Error())) | ||||||
| 	if werr != nil { | 	if werr != nil { | ||||||
| 		if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 		if logger.V(logger.ErrorLevel) { | ||||||
| 			logger.Error(werr) | 			logger.Error(werr.Error()) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -470,8 +472,8 @@ func writeResponse(w http.ResponseWriter, r *http.Request, rsp []byte) { | |||||||
| 	// write response | 	// write response | ||||||
| 	_, err := w.Write(rsp) | 	_, err := w.Write(rsp) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 		if logger.V(logger.ErrorLevel) { | ||||||
| 			logger.Error(err) | 			logger.Error(err.Error()) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,12 +2,12 @@ package rpc | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"encoding/json" |  | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"github.com/golang/protobuf/proto" |  | ||||||
| 	go_api "github.com/unistack-org/micro/v3/api/proto" | 	go_api "github.com/unistack-org/micro/v3/api/proto" | ||||||
|  | 	jsonpb "google.golang.org/protobuf/encoding/protojson" | ||||||
|  | 	"google.golang.org/protobuf/proto" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestRequestPayloadFromRequest(t *testing.T) { | func TestRequestPayloadFromRequest(t *testing.T) { | ||||||
| @@ -22,7 +22,7 @@ func TestRequestPayloadFromRequest(t *testing.T) { | |||||||
| 		t.Fatal("Failed to marshal proto", err) | 		t.Fatal("Failed to marshal proto", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	jsonBytes, err := json.Marshal(protoEvent) | 	jsonBytes, err := jsonpb.Marshal(&protoEvent) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal("Failed to marshal proto to JSON ", err) | 		t.Fatal("Failed to marshal proto to JSON ", err) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -44,13 +44,15 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request, | |||||||
| 			case "binary": | 			case "binary": | ||||||
| 				hdr["Sec-WebSocket-Protocol"] = []string{"binary"} | 				hdr["Sec-WebSocket-Protocol"] = []string{"binary"} | ||||||
| 				op = ws.OpBinary | 				op = ws.OpBinary | ||||||
|  | 			default: | ||||||
|  | 				op = ws.OpBinary | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	payload, err := requestPayload(r) | 	payload, err := requestPayload(r) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 		if logger.V(logger.ErrorLevel) { | ||||||
| 			logger.Error(err) | 			logger.Error(err.Error()) | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -72,16 +74,16 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request, | |||||||
|  |  | ||||||
| 	conn, rw, _, err := upgrader.Upgrade(r, w) | 	conn, rw, _, err := upgrader.Upgrade(r, w) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 		if logger.V(logger.ErrorLevel) { | ||||||
| 			logger.Error(err) | 			logger.Error(err.Error()) | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		if err := conn.Close(); err != nil { | 		if err := conn.Close(); err != nil { | ||||||
| 			if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 			if logger.V(logger.ErrorLevel) { | ||||||
| 				logger.Error(err) | 				logger.Error(err.Error()) | ||||||
| 			} | 			} | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @@ -116,16 +118,16 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request, | |||||||
| 	// create a new stream | 	// create a new stream | ||||||
| 	stream, err := c.Stream(ctx, req, callOpt) | 	stream, err := c.Stream(ctx, req, callOpt) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 		if logger.V(logger.ErrorLevel) { | ||||||
| 			logger.Error(err) | 			logger.Error(err.Error()) | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if request != nil { | 	if request != nil { | ||||||
| 		if err = stream.Send(request); err != nil { | 		if err = stream.Send(request); err != nil { | ||||||
| 			if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 			if logger.V(logger.ErrorLevel) { | ||||||
| 				logger.Error(err) | 				logger.Error(err.Error()) | ||||||
| 			} | 			} | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @@ -150,22 +152,22 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request, | |||||||
| 				if strings.Contains(err.Error(), "context canceled") { | 				if strings.Contains(err.Error(), "context canceled") { | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 				if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 				if logger.V(logger.ErrorLevel) { | ||||||
| 					logger.Error(err) | 					logger.Error(err.Error()) | ||||||
| 				} | 				} | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// write the response | 			// write the response | ||||||
| 			if err := wsutil.WriteServerMessage(rw, op, buf); err != nil { | 			if err := wsutil.WriteServerMessage(rw, op, buf); err != nil { | ||||||
| 				if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 				if logger.V(logger.ErrorLevel) { | ||||||
| 					logger.Error(err) | 					logger.Error(err.Error()) | ||||||
| 				} | 				} | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			if err = rw.Flush(); err != nil { | 			if err = rw.Flush(); err != nil { | ||||||
| 				if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 				if logger.V(logger.ErrorLevel) { | ||||||
| 					logger.Error(err) | 					logger.Error(err.Error()) | ||||||
| 				} | 				} | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| @@ -195,8 +197,8 @@ func writeLoop(rw io.ReadWriter, stream client.Stream) { | |||||||
| 						return | 						return | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 				if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 				if logger.V(logger.ErrorLevel) { | ||||||
| 					logger.Error(err) | 					logger.Error(err.Error()) | ||||||
| 				} | 				} | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| @@ -212,8 +214,8 @@ func writeLoop(rw io.ReadWriter, stream client.Stream) { | |||||||
| 			// if the extracted payload isn't empty lets use it | 			// if the extracted payload isn't empty lets use it | ||||||
| 			request := &raw.Frame{Data: buf} | 			request := &raw.Frame{Data: buf} | ||||||
| 			if err := stream.Send(request); err != nil { | 			if err := stream.Send(request); err != nil { | ||||||
| 				if logger.V(logger.ErrorLevel, logger.DefaultLogger) { | 				if logger.V(logger.ErrorLevel) { | ||||||
| 					logger.Error(err) | 					logger.Error(err.Error()) | ||||||
| 				} | 				} | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|   | |||||||
| @@ -9,10 +9,12 @@ import ( | |||||||
| 	"github.com/unistack-org/micro/v3/api/resolver" | 	"github.com/unistack-org/micro/v3/api/resolver" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // Resolver struct | ||||||
| type Resolver struct { | type Resolver struct { | ||||||
| 	opts resolver.Options | 	opts resolver.Options | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Resolve func to resolve enndpoint | ||||||
| func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) { | func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) { | ||||||
| 	// parse options | 	// parse options | ||||||
| 	options := resolver.NewResolveOptions(opts...) | 	options := resolver.NewResolveOptions(opts...) | ||||||
| @@ -39,6 +41,7 @@ func (r *Resolver) String() string { | |||||||
| 	return "grpc" | 	return "grpc" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // NewResolver is used to create new Resolver | ||||||
| func NewResolver(opts ...resolver.Option) resolver.Resolver { | func NewResolver(opts ...resolver.Option) resolver.Resolver { | ||||||
| 	return &Resolver{opts: resolver.NewOptions(opts...)} | 	return &Resolver{opts: resolver.NewOptions(opts...)} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,11 +4,13 @@ import ( | |||||||
| 	"github.com/unistack-org/micro/v3/registry" | 	"github.com/unistack-org/micro/v3/registry" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // Options struct | ||||||
| type Options struct { | type Options struct { | ||||||
| 	Handler       string | 	Handler       string | ||||||
| 	ServicePrefix string | 	ServicePrefix string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Option func | ||||||
| type Option func(o *Options) | type Option func(o *Options) | ||||||
|  |  | ||||||
| // WithHandler sets the handler being used | // WithHandler sets the handler being used | ||||||
| @@ -27,7 +29,7 @@ func WithServicePrefix(p string) Option { | |||||||
|  |  | ||||||
| // NewOptions returns new initialised options | // NewOptions returns new initialised options | ||||||
| func NewOptions(opts ...Option) Options { | func NewOptions(opts ...Option) Options { | ||||||
| 	var options Options | 	options := Options{} | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&options) | 		o(&options) | ||||||
| 	} | 	} | ||||||
| @@ -51,13 +53,10 @@ func Domain(n string) ResolveOption { | |||||||
|  |  | ||||||
| // NewResolveOptions returns new initialised resolve options | // NewResolveOptions returns new initialised resolve options | ||||||
| func NewResolveOptions(opts ...ResolveOption) ResolveOptions { | func NewResolveOptions(opts ...ResolveOption) ResolveOptions { | ||||||
| 	var options ResolveOptions | 	options := ResolveOptions{Domain: registry.DefaultDomain} | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&options) | 		o(&options) | ||||||
| 	} | 	} | ||||||
| 	if len(options.Domain) == 0 { |  | ||||||
| 		options.Domain = registry.DefaultDomain |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return options | 	return options | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,7 +7,9 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	ErrNotFound    = errors.New("not found") | 	// ErrNotFound returned when endpoint is not found | ||||||
|  | 	ErrNotFound = errors.New("not found") | ||||||
|  | 	// ErrInvalidPath returned on invalid path | ||||||
| 	ErrInvalidPath = errors.New("invalid path") | 	ErrInvalidPath = errors.New("invalid path") | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -19,7 +21,7 @@ type Resolver interface { | |||||||
|  |  | ||||||
| // Endpoint is the endpoint for a http request | // Endpoint is the endpoint for a http request | ||||||
| type Endpoint struct { | type Endpoint struct { | ||||||
| 	// e.g greeter | 	// Endpoint name e.g greeter | ||||||
| 	Name string | 	Name string | ||||||
| 	// HTTP Host e.g example.com | 	// HTTP Host e.g example.com | ||||||
| 	Host string | 	Host string | ||||||
|   | |||||||
| @@ -54,7 +54,9 @@ func (r *Resolver) Domain(req *http.Request) string { | |||||||
| 	// extract the top level domain plus one (e.g. 'myapp.com') | 	// extract the top level domain plus one (e.g. 'myapp.com') | ||||||
| 	domain, err := publicsuffix.EffectiveTLDPlusOne(host) | 	domain, err := publicsuffix.EffectiveTLDPlusOne(host) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		logger.Debugf("Unable to extract domain from %v", host) | 		if logger.V(logger.DebugLevel) { | ||||||
|  | 			logger.Debug("Unable to extract domain from %v", host) | ||||||
|  | 		} | ||||||
| 		return "" | 		return "" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| package router | package router | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/api/resolver" | 	"github.com/unistack-org/micro/v3/api/resolver" | ||||||
| 	"github.com/unistack-org/micro/v3/api/resolver/vpath" | 	"github.com/unistack-org/micro/v3/api/resolver/vpath" | ||||||
| 	"github.com/unistack-org/micro/v3/registry" | 	"github.com/unistack-org/micro/v3/registry" | ||||||
| @@ -10,12 +12,14 @@ type Options struct { | |||||||
| 	Handler  string | 	Handler  string | ||||||
| 	Registry registry.Registry | 	Registry registry.Registry | ||||||
| 	Resolver resolver.Resolver | 	Resolver resolver.Resolver | ||||||
|  | 	Context  context.Context | ||||||
| } | } | ||||||
|  |  | ||||||
| type Option func(o *Options) | type Option func(o *Options) | ||||||
|  |  | ||||||
| func NewOptions(opts ...Option) Options { | func NewOptions(opts ...Option) Options { | ||||||
| 	options := Options{ | 	options := Options{ | ||||||
|  | 		Context: context.Background(), | ||||||
| 		Handler: "meta", | 		Handler: "meta", | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -32,18 +36,28 @@ func NewOptions(opts ...Option) Options { | |||||||
| 	return options | 	return options | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // WithContext sets the context | ||||||
|  | func WithContext(ctx context.Context) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.Context = ctx | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithHandler sets the handler | ||||||
| func WithHandler(h string) Option { | func WithHandler(h string) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Handler = h | 		o.Handler = h | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // WithRegistry sets the registry | ||||||
| func WithRegistry(r registry.Registry) Option { | func WithRegistry(r registry.Registry) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Registry = r | 		o.Registry = r | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // WithResolver sets the resolver | ||||||
| func WithResolver(r resolver.Resolver) Option { | func WithResolver(r resolver.Resolver) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Resolver = r | 		o.Resolver = r | ||||||
|   | |||||||
| @@ -1,498 +0,0 @@ | |||||||
| // Package registry provides a dynamic api service router |  | ||||||
| package registry |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"net/http" |  | ||||||
| 	"regexp" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/api" |  | ||||||
| 	"github.com/unistack-org/micro/v3/api/router" |  | ||||||
| 	"github.com/unistack-org/micro/v3/logger" |  | ||||||
| 	"github.com/unistack-org/micro/v3/metadata" |  | ||||||
| 	"github.com/unistack-org/micro/v3/registry" |  | ||||||
| 	util "github.com/unistack-org/micro/v3/util/router" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // endpoint struct, that holds compiled pcre |  | ||||||
| type endpoint struct { |  | ||||||
| 	hostregs []*regexp.Regexp |  | ||||||
| 	pathregs []util.Pattern |  | ||||||
| 	pcreregs []*regexp.Regexp |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // router is the default router |  | ||||||
| type registryRouter struct { |  | ||||||
| 	exit chan bool |  | ||||||
| 	opts router.Options |  | ||||||
|  |  | ||||||
| 	sync.RWMutex |  | ||||||
| 	eps map[string]*api.Service |  | ||||||
| 	// compiled regexp for host and path |  | ||||||
| 	ceps map[string]*endpoint |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *registryRouter) isClosed() bool { |  | ||||||
| 	select { |  | ||||||
| 	case <-r.exit: |  | ||||||
| 		return true |  | ||||||
| 	default: |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // refresh list of api services |  | ||||||
| func (r *registryRouter) refresh() { |  | ||||||
| 	var attempts int |  | ||||||
|  |  | ||||||
| 	for { |  | ||||||
| 		services, err := r.opts.Registry.ListServices() |  | ||||||
| 		if err != nil { |  | ||||||
| 			attempts++ |  | ||||||
| 			if logger.V(logger.ErrorLevel, logger.DefaultLogger) { |  | ||||||
| 				logger.Errorf("unable to list services: %v", err) |  | ||||||
| 			} |  | ||||||
| 			time.Sleep(time.Duration(attempts) * time.Second) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		attempts = 0 |  | ||||||
|  |  | ||||||
| 		// for each service, get service and store endpoints |  | ||||||
| 		for _, s := range services { |  | ||||||
| 			service, err := r.opts.Registry.GetService(s.Name) |  | ||||||
| 			if err != nil { |  | ||||||
| 				if logger.V(logger.ErrorLevel, logger.DefaultLogger) { |  | ||||||
| 					logger.Errorf("unable to get service: %v", err) |  | ||||||
| 				} |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			r.store(service) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// refresh list in 10 minutes... cruft |  | ||||||
| 		// use registry watching |  | ||||||
| 		select { |  | ||||||
| 		case <-time.After(time.Minute * 10): |  | ||||||
| 		case <-r.exit: |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // process watch event |  | ||||||
| func (r *registryRouter) process(res *registry.Result) { |  | ||||||
| 	// skip these things |  | ||||||
| 	if res == nil || res.Service == nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// get entry from cache |  | ||||||
| 	service, err := r.opts.Registry.GetService(res.Service.Name) |  | ||||||
| 	if err != nil { |  | ||||||
| 		if logger.V(logger.ErrorLevel, logger.DefaultLogger) { |  | ||||||
| 			logger.Errorf("unable to get %v service: %v", res.Service.Name, err) |  | ||||||
| 		} |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// update our local endpoints |  | ||||||
| 	r.store(service) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // store local endpoint cache |  | ||||||
| func (r *registryRouter) store(services []*registry.Service) { |  | ||||||
| 	// endpoints |  | ||||||
| 	eps := map[string]*api.Service{} |  | ||||||
|  |  | ||||||
| 	// services |  | ||||||
| 	names := map[string]bool{} |  | ||||||
|  |  | ||||||
| 	// create a new endpoint mapping |  | ||||||
| 	for _, service := range services { |  | ||||||
| 		// set names we need later |  | ||||||
| 		names[service.Name] = true |  | ||||||
|  |  | ||||||
| 		// map per endpoint |  | ||||||
| 		for _, sep := range service.Endpoints { |  | ||||||
| 			// create a key service:endpoint_name |  | ||||||
| 			key := fmt.Sprintf("%s.%s", service.Name, sep.Name) |  | ||||||
| 			// decode endpoint |  | ||||||
| 			end := api.Decode(sep.Metadata) |  | ||||||
| 			// no endpoint or no name |  | ||||||
| 			if end == nil || len(end.Name) == 0 { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			// if we got nothing skip |  | ||||||
| 			if err := api.Validate(end); err != nil { |  | ||||||
| 				if logger.V(logger.TraceLevel, logger.DefaultLogger) { |  | ||||||
| 					logger.Tracef("endpoint validation failed: %v", err) |  | ||||||
| 				} |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			// try get endpoint |  | ||||||
| 			ep, ok := eps[key] |  | ||||||
| 			if !ok { |  | ||||||
| 				ep = &api.Service{Name: service.Name} |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			// overwrite the endpoint |  | ||||||
| 			ep.Endpoint = end |  | ||||||
| 			// append services |  | ||||||
| 			ep.Services = append(ep.Services, service) |  | ||||||
| 			// store it |  | ||||||
| 			eps[key] = ep |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	r.Lock() |  | ||||||
| 	defer r.Unlock() |  | ||||||
|  |  | ||||||
| 	// delete any existing eps for services we know |  | ||||||
| 	for key, service := range r.eps { |  | ||||||
| 		// skip what we don't care about |  | ||||||
| 		if !names[service.Name] { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// ok we know this thing |  | ||||||
| 		// delete delete delete |  | ||||||
| 		delete(r.eps, key) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// now set the eps we have |  | ||||||
| 	for name, ep := range eps { |  | ||||||
| 		r.eps[name] = ep |  | ||||||
| 		cep := &endpoint{} |  | ||||||
|  |  | ||||||
| 		for _, h := range ep.Endpoint.Host { |  | ||||||
| 			if h == "" || h == "*" { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			hostreg, err := regexp.CompilePOSIX(h) |  | ||||||
| 			if err != nil { |  | ||||||
| 				if logger.V(logger.TraceLevel, logger.DefaultLogger) { |  | ||||||
| 					logger.Tracef("endpoint have invalid host regexp: %v", err) |  | ||||||
| 				} |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			cep.hostregs = append(cep.hostregs, hostreg) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for _, p := range ep.Endpoint.Path { |  | ||||||
| 			var pcreok bool |  | ||||||
|  |  | ||||||
| 			if p[0] == '^' && p[len(p)-1] == '$' { |  | ||||||
| 				pcrereg, err := regexp.CompilePOSIX(p) |  | ||||||
| 				if err == nil { |  | ||||||
| 					cep.pcreregs = append(cep.pcreregs, pcrereg) |  | ||||||
| 					pcreok = true |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			rule, err := util.Parse(p) |  | ||||||
| 			if err != nil && !pcreok { |  | ||||||
| 				if logger.V(logger.TraceLevel, logger.DefaultLogger) { |  | ||||||
| 					logger.Tracef("endpoint have invalid path pattern: %v", err) |  | ||||||
| 				} |  | ||||||
| 				continue |  | ||||||
| 			} else if err != nil && pcreok { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			tpl := rule.Compile() |  | ||||||
| 			pathreg, err := util.NewPattern(tpl.Version, tpl.OpCodes, tpl.Pool, "") |  | ||||||
| 			if err != nil { |  | ||||||
| 				if logger.V(logger.TraceLevel, logger.DefaultLogger) { |  | ||||||
| 					logger.Tracef("endpoint have invalid path pattern: %v", err) |  | ||||||
| 				} |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			cep.pathregs = append(cep.pathregs, pathreg) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		r.ceps[name] = cep |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // watch for endpoint changes |  | ||||||
| func (r *registryRouter) watch() { |  | ||||||
| 	var attempts int |  | ||||||
|  |  | ||||||
| 	for { |  | ||||||
| 		if r.isClosed() { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// watch for changes |  | ||||||
| 		w, err := r.opts.Registry.Watch() |  | ||||||
| 		if err != nil { |  | ||||||
| 			attempts++ |  | ||||||
| 			if logger.V(logger.ErrorLevel, logger.DefaultLogger) { |  | ||||||
| 				logger.Errorf("error watching endpoints: %v", err) |  | ||||||
| 			} |  | ||||||
| 			time.Sleep(time.Duration(attempts) * time.Second) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		ch := make(chan bool) |  | ||||||
|  |  | ||||||
| 		go func() { |  | ||||||
| 			select { |  | ||||||
| 			case <-ch: |  | ||||||
| 				w.Stop() |  | ||||||
| 			case <-r.exit: |  | ||||||
| 				w.Stop() |  | ||||||
| 			} |  | ||||||
| 		}() |  | ||||||
|  |  | ||||||
| 		// reset if we get here |  | ||||||
| 		attempts = 0 |  | ||||||
|  |  | ||||||
| 		for { |  | ||||||
| 			// process next event |  | ||||||
| 			res, err := w.Next() |  | ||||||
| 			if err != nil { |  | ||||||
| 				if logger.V(logger.ErrorLevel, logger.DefaultLogger) { |  | ||||||
| 					logger.Errorf("error getting next endpoint: %v", err) |  | ||||||
| 				} |  | ||||||
| 				close(ch) |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 			r.process(res) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *registryRouter) Options() router.Options { |  | ||||||
| 	return r.opts |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *registryRouter) Close() error { |  | ||||||
| 	select { |  | ||||||
| 	case <-r.exit: |  | ||||||
| 		return nil |  | ||||||
| 	default: |  | ||||||
| 		close(r.exit) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *registryRouter) Register(ep *api.Endpoint) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *registryRouter) Deregister(ep *api.Endpoint) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) { |  | ||||||
| 	if r.isClosed() { |  | ||||||
| 		return nil, errors.New("router closed") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	r.RLock() |  | ||||||
| 	defer r.RUnlock() |  | ||||||
|  |  | ||||||
| 	var idx int |  | ||||||
| 	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 { |  | ||||||
| 		cep, ok := r.ceps[n] |  | ||||||
| 		if !ok { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		ep := e.Endpoint |  | ||||||
| 		var mMatch, hMatch, pMatch bool |  | ||||||
| 		// 1. try method |  | ||||||
| 		for _, m := range ep.Method { |  | ||||||
| 			if m == req.Method { |  | ||||||
| 				mMatch = true |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !mMatch { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		if logger.V(logger.DebugLevel, logger.DefaultLogger) { |  | ||||||
| 			logger.Debugf("api method match %s", req.Method) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// 2. try host |  | ||||||
| 		if len(ep.Host) == 0 { |  | ||||||
| 			hMatch = true |  | ||||||
| 		} else { |  | ||||||
| 			for idx, h := range ep.Host { |  | ||||||
| 				if h == "" || h == "*" { |  | ||||||
| 					hMatch = true |  | ||||||
| 					break |  | ||||||
| 				} else { |  | ||||||
| 					if cep.hostregs[idx].MatchString(req.URL.Host) { |  | ||||||
| 						hMatch = true |  | ||||||
| 						break |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !hMatch { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		if logger.V(logger.DebugLevel, logger.DefaultLogger) { |  | ||||||
| 			logger.Debugf("api host match %s", req.URL.Host) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// 3. try path via google.api path matching |  | ||||||
| 		for _, pathreg := range cep.pathregs { |  | ||||||
| 			matches, err := pathreg.Match(path, "") |  | ||||||
| 			if err != nil { |  | ||||||
| 				if logger.V(logger.DebugLevel, logger.DefaultLogger) { |  | ||||||
| 					logger.Debugf("api gpath not match %s != %v", path, pathreg) |  | ||||||
| 				} |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			if logger.V(logger.DebugLevel, logger.DefaultLogger) { |  | ||||||
| 				logger.Debugf("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 |  | ||||||
| 			} |  | ||||||
| 			md["x-api-body"] = ep.Body |  | ||||||
| 			*req = *req.Clone(metadata.NewContext(ctx, md)) |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if !pMatch { |  | ||||||
| 			// 4. try path via pcre path matching |  | ||||||
| 			for _, pathreg := range cep.pcreregs { |  | ||||||
| 				if !pathreg.MatchString(req.URL.Path) { |  | ||||||
| 					if logger.V(logger.DebugLevel, logger.DefaultLogger) { |  | ||||||
| 						logger.Debugf("api pcre path not match %s != %v", path, pathreg) |  | ||||||
| 					} |  | ||||||
| 					continue |  | ||||||
| 				} |  | ||||||
| 				if logger.V(logger.DebugLevel, logger.DefaultLogger) { |  | ||||||
| 					logger.Debugf("api pcre path match %s != %v", path, pathreg) |  | ||||||
| 				} |  | ||||||
| 				pMatch = true |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if !pMatch { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// TODO: Percentage traffic |  | ||||||
| 		// we got here, so its a match |  | ||||||
| 		return e, nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// no match |  | ||||||
| 	return nil, errors.New("not found") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *registryRouter) Route(req *http.Request) (*api.Service, error) { |  | ||||||
| 	if r.isClosed() { |  | ||||||
| 		return nil, errors.New("router closed") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// try get an endpoint |  | ||||||
| 	ep, err := r.Endpoint(req) |  | ||||||
| 	if err == nil { |  | ||||||
| 		return ep, nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// error not nil |  | ||||||
| 	// ignore that shit |  | ||||||
| 	// TODO: don't ignore that shit |  | ||||||
|  |  | ||||||
| 	// get the service name |  | ||||||
| 	rp, err := r.opts.Resolver.Resolve(req) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// service name |  | ||||||
| 	name := rp.Name |  | ||||||
|  |  | ||||||
| 	// get service |  | ||||||
| 	services, err := r.opts.Registry.GetService(name, registry.GetDomain(rp.Domain)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// only use endpoint matching when the meta handler is set aka api.Default |  | ||||||
| 	switch r.opts.Handler { |  | ||||||
| 	// rpc handlers |  | ||||||
| 	case "meta", "api", "rpc": |  | ||||||
| 		handler := r.opts.Handler |  | ||||||
|  |  | ||||||
| 		// set default handler to api |  | ||||||
| 		if r.opts.Handler == "meta" { |  | ||||||
| 			handler = "rpc" |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// construct api service |  | ||||||
| 		return &api.Service{ |  | ||||||
| 			Name: name, |  | ||||||
| 			Endpoint: &api.Endpoint{ |  | ||||||
| 				Name:    rp.Method, |  | ||||||
| 				Handler: handler, |  | ||||||
| 			}, |  | ||||||
| 			Services: services, |  | ||||||
| 		}, nil |  | ||||||
| 	// http handler |  | ||||||
| 	case "http", "proxy", "web": |  | ||||||
| 		// construct api service |  | ||||||
| 		return &api.Service{ |  | ||||||
| 			Name: name, |  | ||||||
| 			Endpoint: &api.Endpoint{ |  | ||||||
| 				Name:    req.URL.String(), |  | ||||||
| 				Handler: r.opts.Handler, |  | ||||||
| 				Host:    []string{req.Host}, |  | ||||||
| 				Method:  []string{req.Method}, |  | ||||||
| 				Path:    []string{req.URL.Path}, |  | ||||||
| 			}, |  | ||||||
| 			Services: services, |  | ||||||
| 		}, nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil, errors.New("unknown handler") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newRouter(opts ...router.Option) (*registryRouter, error) { |  | ||||||
| 	options := router.NewOptions(opts...) |  | ||||||
| 	if options.Registry == nil { |  | ||||||
| 		return nil, fmt.Errorf("registry is not set") |  | ||||||
| 	} |  | ||||||
| 	r := ®istryRouter{ |  | ||||||
| 		exit: make(chan bool), |  | ||||||
| 		opts: options, |  | ||||||
| 		eps:  make(map[string]*api.Service), |  | ||||||
| 		ceps: make(map[string]*endpoint), |  | ||||||
| 	} |  | ||||||
| 	go r.watch() |  | ||||||
| 	go r.refresh() |  | ||||||
| 	return r, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewRouter returns the default router |  | ||||||
| func NewRouter(opts ...router.Option) (router.Router, error) { |  | ||||||
| 	return newRouter(opts...) |  | ||||||
| } |  | ||||||
| @@ -1,38 +0,0 @@ | |||||||
| package registry |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| 	"github.com/unistack-org/micro/v3/registry" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestStoreRegex(t *testing.T) { |  | ||||||
| 	t.Skip() |  | ||||||
| 	router, err := newRouter() |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	router.store([]*registry.Service{ |  | ||||||
| 		{ |  | ||||||
| 			Name:    "Foobar", |  | ||||||
| 			Version: "latest", |  | ||||||
| 			Endpoints: []*registry.Endpoint{ |  | ||||||
| 				{ |  | ||||||
| 					Name: "foo", |  | ||||||
| 					Metadata: map[string]string{ |  | ||||||
| 						"endpoint":    "FooEndpoint", |  | ||||||
| 						"description": "Some description", |  | ||||||
| 						"method":      "POST", |  | ||||||
| 						"path":        "^/foo/$", |  | ||||||
| 						"handler":     "rpc", |  | ||||||
| 					}, |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 			Metadata: map[string]string{}, |  | ||||||
| 		}, |  | ||||||
| 	}, |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	assert.Len(t, router.ceps["Foobar.foo"].pcreregs, 1) |  | ||||||
| } |  | ||||||
| @@ -1,257 +0,0 @@ | |||||||
| // +build ignore |  | ||||||
|  |  | ||||||
| package router_test |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"log" |  | ||||||
| 	"net/http" |  | ||||||
| 	"testing" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/api" |  | ||||||
| 	"github.com/unistack-org/micro/v3/api/handler" |  | ||||||
| 	"github.com/unistack-org/micro/v3/api/handler/rpc" |  | ||||||
| 	"github.com/unistack-org/micro/v3/api/router" |  | ||||||
| 	rregistry "github.com/unistack-org/micro/v3/api/router/registry" |  | ||||||
| 	rstatic "github.com/unistack-org/micro/v3/api/router/static" |  | ||||||
| 	"github.com/unistack-org/micro/v3/broker" |  | ||||||
| 	bmemory "github.com/unistack-org/micro/v3/broker/memory" |  | ||||||
| 	"github.com/unistack-org/micro/v3/client" |  | ||||||
| 	gcli "github.com/unistack-org/micro/v3/client/grpc" |  | ||||||
| 	rmemory "github.com/unistack-org/micro/v3/registry/memory" |  | ||||||
| 	rt "github.com/unistack-org/micro/v3/router" |  | ||||||
| 	regRouter "github.com/unistack-org/micro/v3/router/registry" |  | ||||||
| 	"github.com/unistack-org/micro/v3/server" |  | ||||||
| 	gsrv "github.com/unistack-org/micro/v3/server/grpc" |  | ||||||
| 	pb "github.com/unistack-org/micro/v3/server/grpc/proto" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // server is used to implement helloworld.GreeterServer. |  | ||||||
| type testServer struct { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // TestHello implements helloworld.GreeterServer |  | ||||||
| func (s *testServer) Call(ctx context.Context, req *pb.Request, rsp *pb.Response) error { |  | ||||||
| 	rsp.Msg = "Hello " + req.Uuid |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // TestHello implements helloworld.GreeterServer |  | ||||||
| func (s *testServer) CallPcre(ctx context.Context, req *pb.Request, rsp *pb.Response) error { |  | ||||||
| 	rsp.Msg = "Hello " + req.Uuid |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // TestHello implements helloworld.GreeterServer |  | ||||||
| func (s *testServer) CallPcreInvalid(ctx context.Context, req *pb.Request, rsp *pb.Response) error { |  | ||||||
| 	rsp.Msg = "Hello " + req.Uuid |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func initial(t *testing.T) (server.Server, client.Client) { |  | ||||||
| 	r := rmemory.NewRegistry() |  | ||||||
| 	b := bmemory.NewBroker(broker.Registry(r)) |  | ||||||
|  |  | ||||||
| 	// create a new client |  | ||||||
| 	s := gsrv.NewServer( |  | ||||||
| 		server.Name("foo"), |  | ||||||
| 		server.Broker(b), |  | ||||||
| 		server.Registry(r), |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	rtr := regRouter.NewRouter( |  | ||||||
| 		rt.Registry(r), |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	// create a new server |  | ||||||
| 	c := gcli.NewClient( |  | ||||||
| 		client.Router(rtr), |  | ||||||
| 		client.Broker(b), |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	h := &testServer{} |  | ||||||
| 	pb.RegisterTestHandler(s, h) |  | ||||||
|  |  | ||||||
| 	if err := s.Start(); err != nil { |  | ||||||
| 		t.Fatalf("failed to start: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return s, c |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func check(t *testing.T, addr string, path string, expected string) { |  | ||||||
| 	req, err := http.NewRequest("POST", fmt.Sprintf(path, addr), nil) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("Failed to created http.Request: %v", err) |  | ||||||
| 	} |  | ||||||
| 	req.Header.Set("Content-Type", "application/json") |  | ||||||
| 	rsp, err := (&http.Client{}).Do(req) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("Failed to created http.Request: %v", err) |  | ||||||
| 	} |  | ||||||
| 	defer rsp.Body.Close() |  | ||||||
|  |  | ||||||
| 	buf, err := ioutil.ReadAll(rsp.Body) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatal(err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	jsonMsg := expected |  | ||||||
| 	if string(buf) != jsonMsg { |  | ||||||
| 		t.Fatalf("invalid message received, parsing error %s != %s", buf, jsonMsg) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRouterRegistryPcre(t *testing.T) { |  | ||||||
| 	s, c := initial(t) |  | ||||||
| 	defer s.Stop() |  | ||||||
|  |  | ||||||
| 	router := rregistry.NewRouter( |  | ||||||
| 		router.WithHandler(rpc.Handler), |  | ||||||
| 		router.WithRegistry(s.Options().Registry), |  | ||||||
| 	) |  | ||||||
| 	hrpc := rpc.NewHandler( |  | ||||||
| 		handler.WithClient(c), |  | ||||||
| 		handler.WithRouter(router), |  | ||||||
| 	) |  | ||||||
| 	hsrv := &http.Server{ |  | ||||||
| 		Handler:        hrpc, |  | ||||||
| 		Addr:           "127.0.0.1:6543", |  | ||||||
| 		WriteTimeout:   15 * time.Second, |  | ||||||
| 		ReadTimeout:    15 * time.Second, |  | ||||||
| 		IdleTimeout:    20 * time.Second, |  | ||||||
| 		MaxHeaderBytes: 1024 * 1024 * 1, // 1Mb |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	go func() { |  | ||||||
| 		log.Println(hsrv.ListenAndServe()) |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	defer hsrv.Close() |  | ||||||
| 	time.Sleep(1 * time.Second) |  | ||||||
| 	check(t, hsrv.Addr, "http://%s/api/v0/test/call/TEST", `{"msg":"Hello TEST"}`) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRouterStaticPcre(t *testing.T) { |  | ||||||
| 	s, c := initial(t) |  | ||||||
| 	defer s.Stop() |  | ||||||
|  |  | ||||||
| 	router := rstatic.NewRouter( |  | ||||||
| 		router.WithHandler(rpc.Handler), |  | ||||||
| 		router.WithRegistry(s.Options().Registry), |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	err := router.Register(&api.Endpoint{ |  | ||||||
| 		Name:    "foo.Test.Call", |  | ||||||
| 		Method:  []string{"POST"}, |  | ||||||
| 		Path:    []string{"^/api/v0/test/call/?$"}, |  | ||||||
| 		Handler: "rpc", |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatal(err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	hrpc := rpc.NewHandler( |  | ||||||
| 		handler.WithClient(c), |  | ||||||
| 		handler.WithRouter(router), |  | ||||||
| 	) |  | ||||||
| 	hsrv := &http.Server{ |  | ||||||
| 		Handler:        hrpc, |  | ||||||
| 		Addr:           "127.0.0.1:6543", |  | ||||||
| 		WriteTimeout:   15 * time.Second, |  | ||||||
| 		ReadTimeout:    15 * time.Second, |  | ||||||
| 		IdleTimeout:    20 * time.Second, |  | ||||||
| 		MaxHeaderBytes: 1024 * 1024 * 1, // 1Mb |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	go func() { |  | ||||||
| 		log.Println(hsrv.ListenAndServe()) |  | ||||||
| 	}() |  | ||||||
| 	defer hsrv.Close() |  | ||||||
|  |  | ||||||
| 	time.Sleep(1 * time.Second) |  | ||||||
| 	check(t, hsrv.Addr, "http://%s/api/v0/test/call", `{"msg":"Hello "}`) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRouterStaticGpath(t *testing.T) { |  | ||||||
| 	s, c := initial(t) |  | ||||||
| 	defer s.Stop() |  | ||||||
|  |  | ||||||
| 	router := rstatic.NewRouter( |  | ||||||
| 		router.WithHandler(rpc.Handler), |  | ||||||
| 		router.WithRegistry(s.Options().Registry), |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	err := router.Register(&api.Endpoint{ |  | ||||||
| 		Name:    "foo.Test.Call", |  | ||||||
| 		Method:  []string{"POST"}, |  | ||||||
| 		Path:    []string{"/api/v0/test/call/{uuid}"}, |  | ||||||
| 		Handler: "rpc", |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatal(err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	hrpc := rpc.NewHandler( |  | ||||||
| 		handler.WithClient(c), |  | ||||||
| 		handler.WithRouter(router), |  | ||||||
| 	) |  | ||||||
| 	hsrv := &http.Server{ |  | ||||||
| 		Handler:        hrpc, |  | ||||||
| 		Addr:           "127.0.0.1:6543", |  | ||||||
| 		WriteTimeout:   15 * time.Second, |  | ||||||
| 		ReadTimeout:    15 * time.Second, |  | ||||||
| 		IdleTimeout:    20 * time.Second, |  | ||||||
| 		MaxHeaderBytes: 1024 * 1024 * 1, // 1Mb |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	go func() { |  | ||||||
| 		log.Println(hsrv.ListenAndServe()) |  | ||||||
| 	}() |  | ||||||
| 	defer hsrv.Close() |  | ||||||
|  |  | ||||||
| 	time.Sleep(1 * time.Second) |  | ||||||
| 	check(t, hsrv.Addr, "http://%s/api/v0/test/call/TEST", `{"msg":"Hello TEST"}`) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRouterStaticPcreInvalid(t *testing.T) { |  | ||||||
| 	var ep *api.Endpoint |  | ||||||
| 	var err error |  | ||||||
|  |  | ||||||
| 	s, c := initial(t) |  | ||||||
| 	defer s.Stop() |  | ||||||
|  |  | ||||||
| 	router := rstatic.NewRouter( |  | ||||||
| 		router.WithHandler(rpc.Handler), |  | ||||||
| 		router.WithRegistry(s.Options().Registry), |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	ep = &api.Endpoint{ |  | ||||||
| 		Name:    "foo.Test.Call", |  | ||||||
| 		Method:  []string{"POST"}, |  | ||||||
| 		Path:    []string{"^/api/v0/test/call/?"}, |  | ||||||
| 		Handler: "rpc", |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = router.Register(ep) |  | ||||||
| 	if err == nil { |  | ||||||
| 		t.Fatalf("invalid endpoint %v", ep) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ep = &api.Endpoint{ |  | ||||||
| 		Name:    "foo.Test.Call", |  | ||||||
| 		Method:  []string{"POST"}, |  | ||||||
| 		Path:    []string{"/api/v0/test/call/?$"}, |  | ||||||
| 		Handler: "rpc", |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = router.Register(ep) |  | ||||||
| 	if err == nil { |  | ||||||
| 		t.Fatalf("invalid endpoint %v", ep) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	_ = c |  | ||||||
| } |  | ||||||
| @@ -1,356 +0,0 @@ | |||||||
| package static |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"net/http" |  | ||||||
| 	"regexp" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/api" |  | ||||||
| 	"github.com/unistack-org/micro/v3/api/router" |  | ||||||
| 	"github.com/unistack-org/micro/v3/logger" |  | ||||||
| 	"github.com/unistack-org/micro/v3/metadata" |  | ||||||
| 	"github.com/unistack-org/micro/v3/registry" |  | ||||||
| 	rutil "github.com/unistack-org/micro/v3/util/registry" |  | ||||||
| 	util "github.com/unistack-org/micro/v3/util/router" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type endpoint struct { |  | ||||||
| 	apiep    *api.Endpoint |  | ||||||
| 	hostregs []*regexp.Regexp |  | ||||||
| 	pathregs []util.Pattern |  | ||||||
| 	pcreregs []*regexp.Regexp |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // router is the default router |  | ||||||
| type staticRouter struct { |  | ||||||
| 	exit chan bool |  | ||||||
| 	opts router.Options |  | ||||||
| 	sync.RWMutex |  | ||||||
| 	eps map[string]*endpoint |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *staticRouter) isClosed() bool { |  | ||||||
| 	select { |  | ||||||
| 	case <-r.exit: |  | ||||||
| 		return true |  | ||||||
| 	default: |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* |  | ||||||
| // watch for endpoint changes |  | ||||||
| func (r *staticRouter) watch() { |  | ||||||
| 	var attempts int |  | ||||||
|  |  | ||||||
| 	for { |  | ||||||
| 		if r.isClosed() { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// watch for changes |  | ||||||
| 		w, err := r.opts.Registry.Watch() |  | ||||||
| 		if err != nil { |  | ||||||
| 			attempts++ |  | ||||||
| 			log.Println("Error watching endpoints", err) |  | ||||||
| 			time.Sleep(time.Duration(attempts) * time.Second) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		ch := make(chan bool) |  | ||||||
|  |  | ||||||
| 		go func() { |  | ||||||
| 			select { |  | ||||||
| 			case <-ch: |  | ||||||
| 				w.Stop() |  | ||||||
| 			case <-r.exit: |  | ||||||
| 				w.Stop() |  | ||||||
| 			} |  | ||||||
| 		}() |  | ||||||
|  |  | ||||||
| 		// reset if we get here |  | ||||||
| 		attempts = 0 |  | ||||||
|  |  | ||||||
| 		for { |  | ||||||
| 			// process next event |  | ||||||
| 			res, err := w.Next() |  | ||||||
| 			if err != nil { |  | ||||||
| 				log.Println("Error getting next endpoint", err) |  | ||||||
| 				close(ch) |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 			r.process(res) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| func (r *staticRouter) Register(ep *api.Endpoint) error { |  | ||||||
| 	if err := api.Validate(ep); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var pathregs []util.Pattern |  | ||||||
| 	var hostregs []*regexp.Regexp |  | ||||||
| 	var pcreregs []*regexp.Regexp |  | ||||||
|  |  | ||||||
| 	for _, h := range ep.Host { |  | ||||||
| 		if h == "" || h == "*" { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		hostreg, err := regexp.CompilePOSIX(h) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		hostregs = append(hostregs, hostreg) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, p := range ep.Path { |  | ||||||
| 		var pcreok bool |  | ||||||
|  |  | ||||||
| 		// pcre only when we have start and end markers |  | ||||||
| 		if p[0] == '^' && p[len(p)-1] == '$' { |  | ||||||
| 			pcrereg, err := regexp.CompilePOSIX(p) |  | ||||||
| 			if err == nil { |  | ||||||
| 				pcreregs = append(pcreregs, pcrereg) |  | ||||||
| 				pcreok = true |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		rule, err := util.Parse(p) |  | ||||||
| 		if err != nil && !pcreok { |  | ||||||
| 			return err |  | ||||||
| 		} else if err != nil && pcreok { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		tpl := rule.Compile() |  | ||||||
| 		pathreg, err := util.NewPattern(tpl.Version, tpl.OpCodes, tpl.Pool, "") |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		pathregs = append(pathregs, pathreg) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	r.Lock() |  | ||||||
| 	r.eps[ep.Name] = &endpoint{ |  | ||||||
| 		apiep:    ep, |  | ||||||
| 		pcreregs: pcreregs, |  | ||||||
| 		pathregs: pathregs, |  | ||||||
| 		hostregs: hostregs, |  | ||||||
| 	} |  | ||||||
| 	r.Unlock() |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *staticRouter) Deregister(ep *api.Endpoint) error { |  | ||||||
| 	if err := api.Validate(ep); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	r.Lock() |  | ||||||
| 	delete(r.eps, ep.Name) |  | ||||||
| 	r.Unlock() |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *staticRouter) Options() router.Options { |  | ||||||
| 	return r.opts |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *staticRouter) Close() error { |  | ||||||
| 	select { |  | ||||||
| 	case <-r.exit: |  | ||||||
| 		return nil |  | ||||||
| 	default: |  | ||||||
| 		close(r.exit) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *staticRouter) Endpoint(req *http.Request) (*api.Service, error) { |  | ||||||
| 	ep, err := r.endpoint(req) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	epf := strings.Split(ep.apiep.Name, ".") |  | ||||||
| 	services, err := r.opts.Registry.GetService(epf[0]) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// hack for stream endpoint |  | ||||||
| 	if ep.apiep.Stream { |  | ||||||
| 		svcs := rutil.Copy(services) |  | ||||||
| 		for _, svc := range svcs { |  | ||||||
| 			if len(svc.Endpoints) == 0 { |  | ||||||
| 				e := ®istry.Endpoint{} |  | ||||||
| 				e.Name = strings.Join(epf[1:], ".") |  | ||||||
| 				e.Metadata = make(map[string]string) |  | ||||||
| 				e.Metadata["stream"] = "true" |  | ||||||
| 				svc.Endpoints = append(svc.Endpoints, e) |  | ||||||
| 			} |  | ||||||
| 			for _, e := range svc.Endpoints { |  | ||||||
| 				e.Name = strings.Join(epf[1:], ".") |  | ||||||
| 				e.Metadata = make(map[string]string) |  | ||||||
| 				e.Metadata["stream"] = "true" |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		services = svcs |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	svc := &api.Service{ |  | ||||||
| 		Name: epf[0], |  | ||||||
| 		Endpoint: &api.Endpoint{ |  | ||||||
| 			Name:    strings.Join(epf[1:], "."), |  | ||||||
| 			Handler: "rpc", |  | ||||||
| 			Host:    ep.apiep.Host, |  | ||||||
| 			Method:  ep.apiep.Method, |  | ||||||
| 			Path:    ep.apiep.Path, |  | ||||||
| 			Body:    ep.apiep.Body, |  | ||||||
| 			Stream:  ep.apiep.Stream, |  | ||||||
| 		}, |  | ||||||
| 		Services: services, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return svc, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *staticRouter) endpoint(req *http.Request) (*endpoint, error) { |  | ||||||
| 	if r.isClosed() { |  | ||||||
| 		return nil, errors.New("router closed") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	r.RLock() |  | ||||||
| 	defer r.RUnlock() |  | ||||||
|  |  | ||||||
| 	var idx int |  | ||||||
| 	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 _, ep := range r.eps { |  | ||||||
| 		var mMatch, hMatch, pMatch bool |  | ||||||
|  |  | ||||||
| 		// 1. try method |  | ||||||
| 		for _, m := range ep.apiep.Method { |  | ||||||
| 			if m == req.Method { |  | ||||||
| 				mMatch = true |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !mMatch { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		if logger.V(logger.DebugLevel, logger.DefaultLogger) { |  | ||||||
| 			logger.Debugf("api method match %s", req.Method) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// 2. try host |  | ||||||
| 		if len(ep.apiep.Host) == 0 { |  | ||||||
| 			hMatch = true |  | ||||||
| 		} else { |  | ||||||
| 			for idx, h := range ep.apiep.Host { |  | ||||||
| 				if h == "" || h == "*" { |  | ||||||
| 					hMatch = true |  | ||||||
| 					break |  | ||||||
| 				} else { |  | ||||||
| 					if ep.hostregs[idx].MatchString(req.URL.Host) { |  | ||||||
| 						hMatch = true |  | ||||||
| 						break |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !hMatch { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		if logger.V(logger.DebugLevel, logger.DefaultLogger) { |  | ||||||
| 			logger.Debugf("api host match %s", req.URL.Host) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// 3. try google.api path |  | ||||||
| 		for _, pathreg := range ep.pathregs { |  | ||||||
| 			matches, err := pathreg.Match(path, "") |  | ||||||
| 			if err != nil { |  | ||||||
| 				if logger.V(logger.DebugLevel, logger.DefaultLogger) { |  | ||||||
| 					logger.Debugf("api gpath not match %s != %v", path, pathreg) |  | ||||||
| 				} |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			if logger.V(logger.DebugLevel, logger.DefaultLogger) { |  | ||||||
| 				logger.Debugf("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 |  | ||||||
| 			} |  | ||||||
| 			md["x-api-body"] = ep.apiep.Body |  | ||||||
| 			*req = *req.Clone(metadata.NewContext(ctx, md)) |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if !pMatch { |  | ||||||
| 			// 4. try path via pcre path matching |  | ||||||
| 			for _, pathreg := range ep.pcreregs { |  | ||||||
| 				if !pathreg.MatchString(req.URL.Path) { |  | ||||||
| 					if logger.V(logger.DebugLevel, logger.DefaultLogger) { |  | ||||||
| 						logger.Debugf("api pcre path not match %s != %v", req.URL.Path, pathreg) |  | ||||||
| 					} |  | ||||||
| 					continue |  | ||||||
| 				} |  | ||||||
| 				pMatch = true |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if !pMatch { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		// TODO: Percentage traffic |  | ||||||
|  |  | ||||||
| 		// we got here, so its a match |  | ||||||
| 		return ep, nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// no match |  | ||||||
| 	return nil, fmt.Errorf("endpoint not found for %v", req.URL) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *staticRouter) Route(req *http.Request) (*api.Service, error) { |  | ||||||
| 	if r.isClosed() { |  | ||||||
| 		return nil, errors.New("router closed") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// try get an endpoint |  | ||||||
| 	ep, err := r.Endpoint(req) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return ep, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewRouter(opts ...router.Option) *staticRouter { |  | ||||||
| 	options := router.NewOptions(opts...) |  | ||||||
| 	r := &staticRouter{ |  | ||||||
| 		exit: make(chan bool), |  | ||||||
| 		opts: options, |  | ||||||
| 		eps:  make(map[string]*endpoint), |  | ||||||
| 	} |  | ||||||
| 	//go r.watch() |  | ||||||
| 	//go r.refresh() |  | ||||||
| 	return r |  | ||||||
| } |  | ||||||
| @@ -15,6 +15,7 @@ var ( | |||||||
|  |  | ||||||
| // Provider is a ACME provider interface | // Provider is a ACME provider interface | ||||||
| type Provider interface { | type Provider interface { | ||||||
|  | 	Init(...Option) error | ||||||
| 	// Listen returns a new listener | 	// Listen returns a new listener | ||||||
| 	Listen(...string) (net.Listener, error) | 	Listen(...string) (net.Listener, error) | ||||||
| 	// TLSConfig returns a tls config | 	// TLSConfig returns a tls config | ||||||
|   | |||||||
| @@ -15,6 +15,10 @@ import ( | |||||||
| // autoCertACME is the ACME provider from golang.org/x/crypto/acme/autocert | // autoCertACME is the ACME provider from golang.org/x/crypto/acme/autocert | ||||||
| type autocertProvider struct{} | type autocertProvider struct{} | ||||||
|  |  | ||||||
|  | func (a *autocertProvider) Init(opts ...acme.Option) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // Listen implements acme.Provider | // Listen implements acme.Provider | ||||||
| func (a *autocertProvider) Listen(hosts ...string) (net.Listener, error) { | func (a *autocertProvider) Listen(hosts ...string) (net.Listener, error) { | ||||||
| 	return autocert.NewListener(hosts...), nil | 	return autocert.NewListener(hosts...), nil | ||||||
| @@ -31,8 +35,8 @@ func (a *autocertProvider) TLSConfig(hosts ...string) (*tls.Config, error) { | |||||||
| 	} | 	} | ||||||
| 	dir := cacheDir() | 	dir := cacheDir() | ||||||
| 	if err := os.MkdirAll(dir, 0700); err != nil { | 	if err := os.MkdirAll(dir, 0700); err != nil { | ||||||
| 		if logger.V(logger.InfoLevel, logger.DefaultLogger) { | 		if logger.V(logger.InfoLevel) { | ||||||
| 			logger.Infof("warning: autocert not using a cache: %v", err) | 			logger.Info("warning: autocert not using a cache: %v", err) | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		m.Cache = autocert.DirCache(dir) | 		m.Cache = autocert.DirCache(dir) | ||||||
|   | |||||||
| @@ -3,13 +3,13 @@ package certmagic | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"crypto/tls" | 	"crypto/tls" | ||||||
|  | 	"fmt" | ||||||
| 	"math/rand" | 	"math/rand" | ||||||
| 	"net" | 	"net" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/caddyserver/certmagic" | 	"github.com/caddyserver/certmagic" | ||||||
| 	"github.com/unistack-org/micro/v3/api/server/acme" | 	"github.com/unistack-org/micro/v3/api/server/acme" | ||||||
| 	"github.com/unistack-org/micro/v3/logger" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type certmagicProvider struct { | type certmagicProvider struct { | ||||||
| @@ -48,6 +48,15 @@ func (c *certmagicProvider) TLSConfig(hosts ...string) (*tls.Config, error) { | |||||||
| 	return certmagic.TLS(hosts) | 	return certmagic.TLS(hosts) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (p *certmagicProvider) Init(opts ...acme.Option) error { | ||||||
|  | 	if p.opts.Cache != nil { | ||||||
|  | 		if _, ok := p.opts.Cache.(certmagic.Storage); !ok { | ||||||
|  | 			return fmt.Errorf("ACME: cache provided doesn't implement certmagic's Storage interface") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // NewProvider returns a certmagic provider | // NewProvider returns a certmagic provider | ||||||
| func NewProvider(options ...acme.Option) acme.Provider { | func NewProvider(options ...acme.Option) acme.Provider { | ||||||
| 	opts := acme.DefaultOptions() | 	opts := acme.DefaultOptions() | ||||||
| @@ -56,12 +65,6 @@ func NewProvider(options ...acme.Option) acme.Provider { | |||||||
| 		o(&opts) | 		o(&opts) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if opts.Cache != nil { |  | ||||||
| 		if _, ok := opts.Cache.(certmagic.Storage); !ok { |  | ||||||
| 			logger.Fatal("ACME: cache provided doesn't implement certmagic's Storage interface") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &certmagicProvider{ | 	return &certmagicProvider{ | ||||||
| 		opts: opts, | 		opts: opts, | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -52,14 +52,14 @@ func (s *storage) Store(key string, value []byte) error { | |||||||
| 		Key:   key, | 		Key:   key, | ||||||
| 		Value: buf.Bytes(), | 		Value: buf.Bytes(), | ||||||
| 	} | 	} | ||||||
| 	return s.store.Write(r) | 	return s.store.Write(s.store.Options().Context, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *storage) Load(key string) ([]byte, error) { | func (s *storage) Load(key string) ([]byte, error) { | ||||||
| 	if !s.Exists(key) { | 	if !s.Exists(key) { | ||||||
| 		return nil, certmagic.ErrNotExist(errors.New(key + " doesn't exist")) | 		return nil, certmagic.ErrNotExist(errors.New(key + " doesn't exist")) | ||||||
| 	} | 	} | ||||||
| 	records, err := s.store.Read(key) | 	records, err := s.store.Read(s.store.Options().Context, key) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -77,18 +77,18 @@ func (s *storage) Load(key string) ([]byte, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (s *storage) Delete(key string) error { | func (s *storage) Delete(key string) error { | ||||||
| 	return s.store.Delete(key) | 	return s.store.Delete(s.store.Options().Context, key) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *storage) Exists(key string) bool { | func (s *storage) Exists(key string) bool { | ||||||
| 	if _, err := s.store.Read(key); err != nil { | 	if _, err := s.store.Read(s.store.Options().Context, key); err != nil { | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *storage) List(prefix string, recursive bool) ([]string, error) { | func (s *storage) List(prefix string, recursive bool) ([]string, error) { | ||||||
| 	keys, err := s.store.List() | 	keys, err := s.store.List(s.store.Options().Context) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -116,7 +116,7 @@ func (s *storage) List(prefix string, recursive bool) ([]string, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (s *storage) Stat(key string) (certmagic.KeyInfo, error) { | func (s *storage) Stat(key string) (certmagic.KeyInfo, error) { | ||||||
| 	records, err := s.store.Read(key) | 	records, err := s.store.Read(s.store.Options().Context, key) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return certmagic.KeyInfo{}, err | 		return certmagic.KeyInfo{}, err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -15,19 +15,14 @@ type httpServer struct { | |||||||
| 	mux  *http.ServeMux | 	mux  *http.ServeMux | ||||||
| 	opts server.Options | 	opts server.Options | ||||||
|  |  | ||||||
| 	mtx     sync.RWMutex | 	sync.RWMutex | ||||||
| 	address string | 	address string | ||||||
| 	exit    chan chan error | 	exit    chan chan error | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewServer(address string, opts ...server.Option) server.Server { | func NewServer(address string, opts ...server.Option) server.Server { | ||||||
| 	var options server.Options |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&options) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &httpServer{ | 	return &httpServer{ | ||||||
| 		opts:    options, | 		opts:    server.NewOptions(opts...), | ||||||
| 		mux:     http.NewServeMux(), | 		mux:     http.NewServeMux(), | ||||||
| 		address: address, | 		address: address, | ||||||
| 		exit:    make(chan chan error), | 		exit:    make(chan chan error), | ||||||
| @@ -35,8 +30,8 @@ func NewServer(address string, opts ...server.Option) server.Server { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (s *httpServer) Address() string { | func (s *httpServer) Address() string { | ||||||
| 	s.mtx.RLock() | 	s.RLock() | ||||||
| 	defer s.mtx.RUnlock() | 	defer s.RUnlock() | ||||||
| 	return s.address | 	return s.address | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -62,6 +57,9 @@ func (s *httpServer) Start() error { | |||||||
| 	var l net.Listener | 	var l net.Listener | ||||||
| 	var err error | 	var err error | ||||||
|  |  | ||||||
|  | 	s.RLock() | ||||||
|  | 	config := s.opts | ||||||
|  | 	s.RUnlock() | ||||||
| 	if s.opts.EnableACME && s.opts.ACMEProvider != nil { | 	if s.opts.EnableACME && s.opts.ACMEProvider != nil { | ||||||
| 		// should we check the address to make sure its using :443? | 		// should we check the address to make sure its using :443? | ||||||
| 		l, err = s.opts.ACMEProvider.Listen(s.opts.ACMEHosts...) | 		l, err = s.opts.ACMEProvider.Listen(s.opts.ACMEHosts...) | ||||||
| @@ -75,18 +73,21 @@ func (s *httpServer) Start() error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if logger.V(logger.InfoLevel, logger.DefaultLogger) { | 	if config.Logger.V(logger.InfoLevel) { | ||||||
| 		logger.Infof("HTTP API Listening on %s", l.Addr().String()) | 		config.Logger.Info("HTTP API Listening on %s", l.Addr().String()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	s.mtx.Lock() | 	s.Lock() | ||||||
| 	s.address = l.Addr().String() | 	s.address = l.Addr().String() | ||||||
| 	s.mtx.Unlock() | 	s.Unlock() | ||||||
|  |  | ||||||
| 	go func() { | 	go func() { | ||||||
| 		if err := http.Serve(l, s.mux); err != nil { | 		if err := http.Serve(l, s.mux); err != nil { | ||||||
| 			// temporary fix | 			// temporary fix | ||||||
| 			logger.Error(err) | 			if config.Logger.V(logger.ErrorLevel) { | ||||||
|  | 				config.Logger.Error("serve err: %v", err) | ||||||
|  | 			} | ||||||
|  | 			s.Stop() | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,10 +6,13 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/api/resolver" | 	"github.com/unistack-org/micro/v3/api/resolver" | ||||||
| 	"github.com/unistack-org/micro/v3/api/server/acme" | 	"github.com/unistack-org/micro/v3/api/server/acme" | ||||||
|  | 	"github.com/unistack-org/micro/v3/logger" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // Option func | ||||||
| type Option func(o *Options) | type Option func(o *Options) | ||||||
|  |  | ||||||
|  | // Options for api server | ||||||
| type Options struct { | type Options struct { | ||||||
| 	EnableACME   bool | 	EnableACME   bool | ||||||
| 	EnableCORS   bool | 	EnableCORS   bool | ||||||
| @@ -19,6 +22,18 @@ type Options struct { | |||||||
| 	TLSConfig    *tls.Config | 	TLSConfig    *tls.Config | ||||||
| 	Resolver     resolver.Resolver | 	Resolver     resolver.Resolver | ||||||
| 	Wrappers     []Wrapper | 	Wrappers     []Wrapper | ||||||
|  | 	Logger       logger.Logger | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewOptions returns new Options | ||||||
|  | func NewOptions(opts ...Option) Options { | ||||||
|  | 	options := Options{ | ||||||
|  | 		Logger: logger.DefaultLogger, | ||||||
|  | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
| } | } | ||||||
|  |  | ||||||
| type Wrapper func(h http.Handler) http.Handler | type Wrapper func(h http.Handler) http.Handler | ||||||
| @@ -70,3 +85,9 @@ func Resolver(r resolver.Resolver) Option { | |||||||
| 		o.Resolver = r | 		o.Resolver = r | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func Logger(l logger.Logger) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.Logger = l | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ const ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	DefaultAuth Auth | 	DefaultAuth Auth = &NoopAuth{opts: NewOptions()} | ||||||
| 	// ErrInvalidToken is when the token provided is not valid | 	// ErrInvalidToken is when the token provided is not valid | ||||||
| 	ErrInvalidToken = errors.New("invalid token provided") | 	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 | ||||||
|   | |||||||
							
								
								
									
										151
									
								
								auth/jwt/jwt.go
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								auth/jwt/jwt.go
									
									
									
									
									
								
							| @@ -1,151 +0,0 @@ | |||||||
| // Package jwt is a jwt implementation of the auth interface |  | ||||||
| package jwt |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/auth" |  | ||||||
| 	"github.com/unistack-org/micro/v3/util/token" |  | ||||||
| 	"github.com/unistack-org/micro/v3/util/token/jwt" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // NewAuth returns a new instance of the Auth service |  | ||||||
| func NewAuth(opts ...auth.Option) auth.Auth { |  | ||||||
| 	j := new(jwtAuth) |  | ||||||
| 	j.Init(opts...) |  | ||||||
| 	return j |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type jwtAuth struct { |  | ||||||
| 	options auth.Options |  | ||||||
| 	token   token.Provider |  | ||||||
| 	rules   []*auth.Rule |  | ||||||
|  |  | ||||||
| 	sync.Mutex |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jwtAuth) String() string { |  | ||||||
| 	return "jwt" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jwtAuth) Init(opts ...auth.Option) { |  | ||||||
| 	j.Lock() |  | ||||||
| 	defer j.Unlock() |  | ||||||
|  |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&j.options) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	j.token = jwt.NewTokenProvider( |  | ||||||
| 		token.WithPrivateKey(j.options.PrivateKey), |  | ||||||
| 		token.WithPublicKey(j.options.PublicKey), |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jwtAuth) Options() auth.Options { |  | ||||||
| 	j.Lock() |  | ||||||
| 	defer j.Unlock() |  | ||||||
| 	return j.options |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jwtAuth) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) { |  | ||||||
| 	options := auth.NewGenerateOptions(opts...) |  | ||||||
| 	if len(options.Issuer) == 0 { |  | ||||||
| 		options.Issuer = j.Options().Issuer |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	account := &auth.Account{ |  | ||||||
| 		ID:       id, |  | ||||||
| 		Type:     options.Type, |  | ||||||
| 		Scopes:   options.Scopes, |  | ||||||
| 		Metadata: options.Metadata, |  | ||||||
| 		Issuer:   options.Issuer, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// generate a JWT secret which can be provided to the Token() method |  | ||||||
| 	// and exchanged for an access token |  | ||||||
| 	secret, err := j.token.Generate(account, token.WithExpiry(time.Hour*24*365)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	account.Secret = secret.Token |  | ||||||
|  |  | ||||||
| 	// return the account |  | ||||||
| 	return account, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jwtAuth) Grant(rule *auth.Rule) error { |  | ||||||
| 	j.Lock() |  | ||||||
| 	defer j.Unlock() |  | ||||||
| 	j.rules = append(j.rules, rule) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jwtAuth) Revoke(rule *auth.Rule) error { |  | ||||||
| 	j.Lock() |  | ||||||
| 	defer j.Unlock() |  | ||||||
|  |  | ||||||
| 	rules := []*auth.Rule{} |  | ||||||
| 	for _, r := range j.rules { |  | ||||||
| 		if r.ID != rule.ID { |  | ||||||
| 			rules = append(rules, r) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	j.rules = rules |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jwtAuth) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error { |  | ||||||
| 	j.Lock() |  | ||||||
| 	defer j.Unlock() |  | ||||||
|  |  | ||||||
| 	var options auth.VerifyOptions |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&options) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return auth.VerifyAccess(j.rules, acc, res) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jwtAuth) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) { |  | ||||||
| 	j.Lock() |  | ||||||
| 	defer j.Unlock() |  | ||||||
| 	return j.rules, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jwtAuth) Inspect(token string) (*auth.Account, error) { |  | ||||||
| 	return j.token.Inspect(token) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jwtAuth) Token(opts ...auth.TokenOption) (*auth.Token, error) { |  | ||||||
| 	options := auth.NewTokenOptions(opts...) |  | ||||||
|  |  | ||||||
| 	secret := options.RefreshToken |  | ||||||
| 	if len(options.Secret) > 0 { |  | ||||||
| 		secret = options.Secret |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	account, err := j.token.Inspect(secret) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	access, err := j.token.Generate(account, token.WithExpiry(options.Expiry)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	refresh, err := j.token.Generate(account, token.WithExpiry(options.Expiry+time.Hour)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &auth.Token{ |  | ||||||
| 		Created:      access.Created, |  | ||||||
| 		Expiry:       access.Expiry, |  | ||||||
| 		AccessToken:  access.Token, |  | ||||||
| 		RefreshToken: refresh.Token, |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
							
								
								
									
										73
									
								
								auth/noop.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								auth/noop.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | |||||||
|  | package auth | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/google/uuid" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type NoopAuth struct { | ||||||
|  | 	opts Options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // String returns the name of the implementation | ||||||
|  | func (n *NoopAuth) String() string { | ||||||
|  | 	return "noop" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Init the auth | ||||||
|  | func (n *NoopAuth) Init(opts ...Option) { | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&n.opts) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Options set for auth | ||||||
|  | func (n *NoopAuth) Options() Options { | ||||||
|  | 	return n.opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Generate a new account | ||||||
|  | func (n *NoopAuth) Generate(id string, opts ...GenerateOption) (*Account, error) { | ||||||
|  | 	options := NewGenerateOptions(opts...) | ||||||
|  |  | ||||||
|  | 	return &Account{ | ||||||
|  | 		ID:       id, | ||||||
|  | 		Secret:   options.Secret, | ||||||
|  | 		Metadata: options.Metadata, | ||||||
|  | 		Scopes:   options.Scopes, | ||||||
|  | 		Issuer:   n.Options().Issuer, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Grant access to a resource | ||||||
|  | func (n *NoopAuth) Grant(rule *Rule) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Revoke access to a resource | ||||||
|  | func (n *NoopAuth) Revoke(rule *Rule) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Rules used to verify requests | ||||||
|  | func (n *NoopAuth) Rules(opts ...RulesOption) ([]*Rule, error) { | ||||||
|  | 	return []*Rule{}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Verify an account has access to a resource | ||||||
|  | func (n *NoopAuth) Verify(acc *Account, res *Resource, opts ...VerifyOption) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Inspect a token | ||||||
|  | func (n *NoopAuth) Inspect(token string) (*Account, error) { | ||||||
|  | 	uid, err := uuid.NewRandom() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return &Account{ID: uid.String(), Issuer: n.Options().Issuer}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Token generation using an account id and secret | ||||||
|  | func (n *NoopAuth) Token(opts ...TokenOption) (*Token, error) { | ||||||
|  | 	return &Token{}, nil | ||||||
|  | } | ||||||
| @@ -1,81 +0,0 @@ | |||||||
| package noop |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/google/uuid" |  | ||||||
| 	"github.com/unistack-org/micro/v3/auth" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func NewAuth(opts ...auth.Option) auth.Auth { |  | ||||||
| 	var options auth.Options |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&options) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &noop{ |  | ||||||
| 		opts: options, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type noop struct { |  | ||||||
| 	opts auth.Options |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // String returns the name of the implementation |  | ||||||
| func (n *noop) String() string { |  | ||||||
| 	return "noop" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Init the auth |  | ||||||
| func (n *noop) Init(opts ...auth.Option) { |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&n.opts) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Options set for auth |  | ||||||
| func (n *noop) Options() auth.Options { |  | ||||||
| 	return n.opts |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Generate a new account |  | ||||||
| func (n *noop) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) { |  | ||||||
| 	options := auth.NewGenerateOptions(opts...) |  | ||||||
|  |  | ||||||
| 	return &auth.Account{ |  | ||||||
| 		ID:       id, |  | ||||||
| 		Secret:   options.Secret, |  | ||||||
| 		Metadata: options.Metadata, |  | ||||||
| 		Scopes:   options.Scopes, |  | ||||||
| 		Issuer:   n.Options().Issuer, |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Grant access to a resource |  | ||||||
| func (n *noop) Grant(rule *auth.Rule) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Revoke access to a resource |  | ||||||
| func (n *noop) Revoke(rule *auth.Rule) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Rules used to verify requests |  | ||||||
| func (n *noop) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) { |  | ||||||
| 	return []*auth.Rule{}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Verify an account has access to a resource |  | ||||||
| func (n *noop) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Inspect a token |  | ||||||
| func (n *noop) Inspect(token string) (*auth.Account, error) { |  | ||||||
| 	return &auth.Account{ID: uuid.New().String(), Issuer: n.Options().Issuer}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Token generation using an account id and secret |  | ||||||
| func (n *noop) Token(opts ...auth.TokenOption) (*auth.Token, error) { |  | ||||||
| 	return &auth.Token{}, nil |  | ||||||
| } |  | ||||||
| @@ -1,8 +1,10 @@ | |||||||
| // Package broker is an interface used for asynchronous messaging | // Package broker is an interface used for asynchronous messaging | ||||||
| package broker | package broker | ||||||
|  |  | ||||||
|  | import "context" | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	DefaultBroker Broker = newBroker() | 	DefaultBroker Broker = NewBroker() | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Broker is an interface used for asynchronous messaging. | // Broker is an interface used for asynchronous messaging. | ||||||
| @@ -10,10 +12,10 @@ type Broker interface { | |||||||
| 	Init(...Option) error | 	Init(...Option) error | ||||||
| 	Options() Options | 	Options() Options | ||||||
| 	Address() string | 	Address() string | ||||||
| 	Connect() error | 	Connect(context.Context) error | ||||||
| 	Disconnect() error | 	Disconnect(context.Context) error | ||||||
| 	Publish(topic string, m *Message, opts ...PublishOption) error | 	Publish(context.Context, string, *Message, ...PublishOption) error | ||||||
| 	Subscribe(topic string, h Handler, opts ...SubscribeOption) (Subscriber, error) | 	Subscribe(context.Context, string, Handler, ...SubscribeOption) (Subscriber, error) | ||||||
| 	String() string | 	String() string | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -30,14 +32,13 @@ type Event interface { | |||||||
|  |  | ||||||
| // Message is used to transfer data | // Message is used to transfer data | ||||||
| type Message struct { | type Message struct { | ||||||
| 	Header map[string]string | 	Header map[string]string // contains message metadata | ||||||
| 	Body   []byte | 	Body   []byte            // contains message body | ||||||
| 	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 { | type Subscriber interface { | ||||||
| 	Options() SubscribeOptions | 	Options() SubscribeOptions | ||||||
| 	Topic() string | 	Topic() string | ||||||
| 	Unsubscribe() error | 	Unsubscribe(context.Context) error | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								broker/context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								broker/context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | package broker | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type brokerKey struct{} | ||||||
|  |  | ||||||
|  | func FromContext(ctx context.Context) (Broker, bool) { | ||||||
|  | 	c, ok := ctx.Value(brokerKey{}).(Broker) | ||||||
|  | 	return c, ok | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewContext(ctx context.Context, s Broker) context.Context { | ||||||
|  | 	return context.WithValue(ctx, brokerKey{}, s) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetSubscribeOption returns a function to setup a context with given value | ||||||
|  | func SetSubscribeOption(k, v interface{}) SubscribeOption { | ||||||
|  | 	return func(o *SubscribeOptions) { | ||||||
|  | 		if o.Context == nil { | ||||||
|  | 			o.Context = context.Background() | ||||||
|  | 		} | ||||||
|  | 		o.Context = context.WithValue(o.Context, k, v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetOption returns a function to setup a context with given value | ||||||
|  | func SetOption(k, v interface{}) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		if o.Context == nil { | ||||||
|  | 			o.Context = context.Background() | ||||||
|  | 		} | ||||||
|  | 		o.Context = context.WithValue(o.Context, k, v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetPublishOption returns a function to setup a context with given value | ||||||
|  | func SetPublishOption(k, v interface{}) PublishOption { | ||||||
|  | 	return func(o *PublishOptions) { | ||||||
|  | 		if o.Context == nil { | ||||||
|  | 			o.Context = context.Background() | ||||||
|  | 		} | ||||||
|  | 		o.Context = context.WithValue(o.Context, k, v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -1,5 +1,7 @@ | |||||||
| package broker | package broker | ||||||
|  |  | ||||||
|  | import "context" | ||||||
|  |  | ||||||
| type noopBroker struct { | type noopBroker struct { | ||||||
| 	opts Options | 	opts Options | ||||||
| } | } | ||||||
| @@ -9,6 +11,12 @@ type noopSubscriber struct { | |||||||
| 	opts  SubscribeOptions | 	opts  SubscribeOptions | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // NewBroker returns new noop broker | ||||||
|  | func NewBroker(opts ...Option) Broker { | ||||||
|  | 	return &noopBroker{opts: NewOptions(opts...)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Init initialize broker | ||||||
| func (n *noopBroker) Init(opts ...Option) error { | func (n *noopBroker) Init(opts ...Option) error { | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&n.opts) | 		o(&n.opts) | ||||||
| @@ -17,58 +25,53 @@ func (n *noopBroker) Init(opts ...Option) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Options returns broker Options | ||||||
| func (n *noopBroker) Options() Options { | func (n *noopBroker) Options() Options { | ||||||
| 	return n.opts | 	return n.opts | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Address returns broker address | ||||||
| func (n *noopBroker) Address() string { | func (n *noopBroker) Address() string { | ||||||
| 	return "" | 	return "" | ||||||
| } | } | ||||||
|  |  | ||||||
| func (n *noopBroker) Connect() error { | // Connect connects to broker | ||||||
|  | func (n *noopBroker) Connect(ctx context.Context) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (n *noopBroker) Disconnect() error { | // Disconnect disconnects from broker | ||||||
|  | func (n *noopBroker) Disconnect(ctx context.Context) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (n *noopBroker) Publish(topic string, m *Message, opts ...PublishOption) error { | // Publish publishes message to broker | ||||||
|  | func (n *noopBroker) Publish(ctx context.Context, topic string, m *Message, opts ...PublishOption) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (n *noopBroker) Subscribe(topic string, h Handler, opts ...SubscribeOption) (Subscriber, error) { | // Subscribe subscribes to broker topic | ||||||
| 	options := NewSubscribeOptions() | func (n *noopBroker) Subscribe(ctx context.Context, topic string, h Handler, opts ...SubscribeOption) (Subscriber, error) { | ||||||
|  | 	options := NewSubscribeOptions(opts...) | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&options) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &noopSubscriber{topic: topic, opts: options}, nil | 	return &noopSubscriber{topic: topic, opts: options}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // String return broker string representation | ||||||
| func (n *noopBroker) String() string { | func (n *noopBroker) String() string { | ||||||
| 	return "noop" | 	return "noop" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Options returns subscriber options | ||||||
| func (n *noopSubscriber) Options() SubscribeOptions { | func (n *noopSubscriber) Options() SubscribeOptions { | ||||||
| 	return n.opts | 	return n.opts | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // TOpic returns subscriber topic | ||||||
| func (n *noopSubscriber) Topic() string { | func (n *noopSubscriber) Topic() string { | ||||||
| 	return n.topic | 	return n.topic | ||||||
| } | } | ||||||
|  |  | ||||||
| func (n *noopSubscriber) Unsubscribe() error { | // Unsubscribe unsbscribes from broker topic | ||||||
|  | func (n *noopSubscriber) Unsubscribe(ctx context.Context) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // newBroker returns a new noop broker |  | ||||||
| func newBroker(opts ...Option) Broker { |  | ||||||
| 	options := NewOptions() |  | ||||||
|  |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&options) |  | ||||||
| 	} |  | ||||||
| 	return &noopBroker{opts: options} |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"github.com/unistack-org/micro/v3/registry" | 	"github.com/unistack-org/micro/v3/registry" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // Options struct | ||||||
| type Options struct { | type Options struct { | ||||||
| 	Addrs  []string | 	Addrs  []string | ||||||
| 	Secure bool | 	Secure bool | ||||||
| @@ -27,18 +28,47 @@ type Options struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewOptions() Options { | // NewOptions create new Options | ||||||
| 	return Options{ | func NewOptions(opts ...Option) Options { | ||||||
| 		Context: context.Background(), | 	options := Options{ | ||||||
|  | 		Registry: registry.DefaultRegistry, | ||||||
|  | 		Logger:   logger.DefaultLogger, | ||||||
|  | 		Context:  context.Background(), | ||||||
|  | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Context sets the context option | ||||||
|  | func Context(ctx context.Context) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.Context = ctx | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // PublishOptions struct | ||||||
| type PublishOptions struct { | type PublishOptions struct { | ||||||
| 	// Other options for implementations of the interface | 	// Other options for implementations of the interface | ||||||
| 	// can be stored in a context | 	// can be stored in a context | ||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // NewPublishOptions creates PublishOptions struct | ||||||
|  | func NewPublishOptions(opts ...PublishOption) PublishOptions { | ||||||
|  | 	options := PublishOptions{ | ||||||
|  | 		Context: context.Background(), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SubscribeOptions struct | ||||||
| type SubscribeOptions struct { | type SubscribeOptions struct { | ||||||
| 	// AutoAck ack messages if handler returns nil err | 	// AutoAck ack messages if handler returns nil err | ||||||
| 	AutoAck bool | 	AutoAck bool | ||||||
| @@ -56,30 +86,34 @@ type SubscribeOptions struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Option func | ||||||
| type Option func(*Options) | type Option func(*Options) | ||||||
|  |  | ||||||
|  | // PublishOption func | ||||||
| type PublishOption func(*PublishOptions) | type PublishOption func(*PublishOptions) | ||||||
|  |  | ||||||
| // PublishContext set context | // PublishContext sets the context | ||||||
| func PublishContext(ctx context.Context) PublishOption { | func PublishContext(ctx context.Context) PublishOption { | ||||||
| 	return func(o *PublishOptions) { | 	return func(o *PublishOptions) { | ||||||
| 		o.Context = ctx | 		o.Context = ctx | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // SubscribeOption func | ||||||
| type SubscribeOption func(*SubscribeOptions) | type SubscribeOption func(*SubscribeOptions) | ||||||
|  |  | ||||||
|  | // NewSubscribeOptions creates new SubscribeOptions | ||||||
| func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions { | func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions { | ||||||
| 	opt := SubscribeOptions{ | 	options := SubscribeOptions{ | ||||||
| 		AutoAck: true, | 		AutoAck: true, | ||||||
| 		Context: context.Background(), | 		Context: context.Background(), | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&opt) | 		o(&options) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return opt | 	return options | ||||||
| } | } | ||||||
|  |  | ||||||
| // Addrs sets the host addresses to be used by the broker | // Addrs sets the host addresses to be used by the broker | ||||||
| @@ -97,6 +131,7 @@ func Codec(c codec.Marshaler) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // DisableAutoAck disables auto ack | ||||||
| func DisableAutoAck() SubscribeOption { | func DisableAutoAck() SubscribeOption { | ||||||
| 	return func(o *SubscribeOptions) { | 	return func(o *SubscribeOptions) { | ||||||
| 		o.AutoAck = false | 		o.AutoAck = false | ||||||
| @@ -127,6 +162,7 @@ func SubscribeErrorHandler(h Handler) SubscribeOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Queue sets the subscribers sueue | ||||||
| func Queue(name string) SubscribeOption { | func Queue(name string) SubscribeOption { | ||||||
| 	return func(o *SubscribeOptions) { | 	return func(o *SubscribeOptions) { | ||||||
| 		o.Group = name | 		o.Group = name | ||||||
| @@ -140,6 +176,7 @@ func SubscribeGroup(name string) SubscribeOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Registry sets registry option | ||||||
| func Registry(r registry.Registry) Option { | func Registry(r registry.Registry) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Registry = r | 		o.Registry = r | ||||||
| @@ -153,7 +190,7 @@ func Secure(b bool) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Specify TLS Config | // TLSConfig sets the TLS Config | ||||||
| func TLSConfig(t *tls.Config) Option { | func TLSConfig(t *tls.Config) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.TLSConfig = t | 		o.TLSConfig = t | ||||||
|   | |||||||
| @@ -1,13 +1,15 @@ | |||||||
| package build | package build | ||||||
|  |  | ||||||
|  | // Options struct | ||||||
| type Options struct { | type Options struct { | ||||||
| 	// local path to download source | 	// local path to download source | ||||||
| 	Path string | 	Path string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Option func | ||||||
| type Option func(o *Options) | type Option func(o *Options) | ||||||
|  |  | ||||||
| // Local path for repository | // Path is the Local path for repository | ||||||
| func Path(p string) Option { | func Path(p string) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Path = p | 		o.Path = p | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								cache/cache.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								cache/cache.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,29 +0,0 @@ | |||||||
| // Package cache is a caching interface |  | ||||||
| package cache |  | ||||||
|  |  | ||||||
| // Cache is an interface for caching |  | ||||||
| type Cache interface { |  | ||||||
| 	// Initialise options |  | ||||||
| 	Init(...Option) error |  | ||||||
| 	// Get a value |  | ||||||
| 	Get(key string) (interface{}, error) |  | ||||||
| 	// Set a value |  | ||||||
| 	Set(key string, val interface{}) error |  | ||||||
| 	// Delete a value |  | ||||||
| 	Delete(key string) error |  | ||||||
| 	// Name of the implementation |  | ||||||
| 	String() string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Options struct { |  | ||||||
| 	Nodes []string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Option func(o *Options) |  | ||||||
|  |  | ||||||
| // Nodes sets the nodes for the cache |  | ||||||
| func Nodes(v ...string) Option { |  | ||||||
| 	return func(o *Options) { |  | ||||||
| 		o.Nodes = v |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -7,6 +7,7 @@ import ( | |||||||
| 	"github.com/unistack-org/micro/v3/util/backoff" | 	"github.com/unistack-org/micro/v3/util/backoff" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // BackoffFunc is the backoff call func | ||||||
| type BackoffFunc func(ctx context.Context, req Request, attempts int) (time.Duration, error) | type BackoffFunc func(ctx context.Context, req Request, attempts int) (time.Duration, error) | ||||||
|  |  | ||||||
| func exponentialBackoff(ctx context.Context, req Request, attempts int) (time.Duration, error) { | func exponentialBackoff(ctx context.Context, req Request, attempts int) (time.Duration, error) { | ||||||
|   | |||||||
| @@ -1,66 +0,0 @@ | |||||||
| package client |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"hash/fnv" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	cache "github.com/patrickmn/go-cache" |  | ||||||
| 	"github.com/unistack-org/micro/v3/metadata" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // NewCache returns an initialised cache. |  | ||||||
| func NewCache() *Cache { |  | ||||||
| 	return &Cache{ |  | ||||||
| 		cache: cache.New(cache.NoExpiration, 30*time.Second), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Cache for responses |  | ||||||
| type Cache struct { |  | ||||||
| 	cache *cache.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 |  | ||||||
| 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 |  | ||||||
| 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) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return rsp |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // key returns a hash for the context and request |  | ||||||
| func key(ctx context.Context, req Request) string { |  | ||||||
| 	ns, _ := metadata.Get(ctx, "Micro-Namespace") |  | ||||||
|  |  | ||||||
| 	bytes, _ := json.Marshal(map[string]interface{}{ |  | ||||||
| 		"namespace": ns, |  | ||||||
| 		"request": map[string]interface{}{ |  | ||||||
| 			"service":  req.Service(), |  | ||||||
| 			"endpoint": req.Endpoint(), |  | ||||||
| 			"method":   req.Method(), |  | ||||||
| 			"body":     req.Body(), |  | ||||||
| 		}, |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	h := fnv.New64() |  | ||||||
| 	h.Write(bytes) |  | ||||||
| 	return fmt.Sprintf("%x", h.Sum(nil)) |  | ||||||
| } |  | ||||||
| @@ -1,77 +0,0 @@ | |||||||
| package client |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"testing" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/metadata" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestCache(t *testing.T) { |  | ||||||
| 	ctx := context.TODO() |  | ||||||
| 	req := &testRequest{service: "go.micro.service.foo", method: "Foo.Bar"} |  | ||||||
|  |  | ||||||
| 	t.Run("CacheMiss", func(t *testing.T) { |  | ||||||
| 		if _, ok := NewCache().Get(ctx, req); ok { |  | ||||||
| 			t.Errorf("Expected to get no result from Get") |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	t.Run("CacheHit", func(t *testing.T) { |  | ||||||
| 		c := NewCache() |  | ||||||
|  |  | ||||||
| 		rsp := "theresponse" |  | ||||||
| 		c.Set(ctx, req, rsp, time.Minute) |  | ||||||
|  |  | ||||||
| 		if res, ok := c.Get(ctx, req); !ok { |  | ||||||
| 			t.Errorf("Expected a result, got nothing") |  | ||||||
| 		} else if res != rsp { |  | ||||||
| 			t.Errorf("Expected '%v' result, got '%v'", rsp, res) |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestCacheKey(t *testing.T) { |  | ||||||
| 	ctx := context.TODO() |  | ||||||
|  |  | ||||||
| 	req1 := &testRequest{service: "go.micro.service.foo", method: "Foo.Bar"} |  | ||||||
| 	req2 := &testRequest{service: "go.micro.service.foo", method: "Foo.Baz"} |  | ||||||
| 	req3 := &testRequest{service: "go.micro.service.foo", method: "Foo.Bar", body: "customquery"} |  | ||||||
|  |  | ||||||
| 	t.Run("IdenticalRequests", func(t *testing.T) { |  | ||||||
| 		key1 := key(ctx, req1) |  | ||||||
| 		key2 := key(ctx, req1) |  | ||||||
| 		if key1 != key2 { |  | ||||||
| 			t.Errorf("Expected the keys to match for identical requests and context") |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	t.Run("DifferentRequestEndpoints", func(t *testing.T) { |  | ||||||
| 		key1 := key(ctx, req1) |  | ||||||
| 		key2 := key(ctx, req2) |  | ||||||
|  |  | ||||||
| 		if key1 == key2 { |  | ||||||
| 			t.Errorf("Expected the keys to differ for different request endpoints") |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	t.Run("DifferentRequestBody", func(t *testing.T) { |  | ||||||
| 		key1 := key(ctx, req2) |  | ||||||
| 		key2 := key(ctx, req3) |  | ||||||
|  |  | ||||||
| 		if key1 == key2 { |  | ||||||
| 			t.Errorf("Expected the keys to differ for different request bodies") |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	t.Run("DifferentMetadata", func(t *testing.T) { |  | ||||||
| 		mdCtx := metadata.Set(context.TODO(), "Micro-Namespace", "bar") |  | ||||||
| 		key1 := key(mdCtx, req1) |  | ||||||
| 		key2 := key(ctx, req1) |  | ||||||
|  |  | ||||||
| 		if key1 == key2 { |  | ||||||
| 			t.Errorf("Expected the keys to differ for different metadata") |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| @@ -9,7 +9,8 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	DefaultClient Client | 	// DefaultClient is the global default client | ||||||
|  | 	DefaultClient Client = NewClient() | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Client is the interface used to make requests to services. | // Client is the interface used to make requests to services. | ||||||
|   | |||||||
| @@ -14,3 +14,13 @@ func FromContext(ctx context.Context) (Client, bool) { | |||||||
| func NewContext(ctx context.Context, c Client) context.Context { | func NewContext(ctx context.Context, c Client) context.Context { | ||||||
| 	return context.WithValue(ctx, clientKey{}, c) | 	return context.WithValue(ctx, clientKey{}, c) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // SetPublishOption returns a function to setup a context with given value | ||||||
|  | func SetPublishOption(k, v interface{}) PublishOption { | ||||||
|  | 	return func(o *PublishOptions) { | ||||||
|  | 		if o.Context == nil { | ||||||
|  | 			o.Context = context.Background() | ||||||
|  | 		} | ||||||
|  | 		o.Context = context.WithValue(o.Context, k, v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										205
									
								
								client/noop.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								client/noop.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | |||||||
|  | package client | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
|  | 	raw "github.com/unistack-org/micro-codec-bytes" | ||||||
|  | 	json "github.com/unistack-org/micro-codec-json" | ||||||
|  | 	"github.com/unistack-org/micro/v3/broker" | ||||||
|  | 	"github.com/unistack-org/micro/v3/codec" | ||||||
|  | 	"github.com/unistack-org/micro/v3/errors" | ||||||
|  | 	"github.com/unistack-org/micro/v3/metadata" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type noopClient struct { | ||||||
|  | 	opts Options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type noopMessage struct { | ||||||
|  | 	topic   string | ||||||
|  | 	payload interface{} | ||||||
|  | 	opts    MessageOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type noopRequest struct { | ||||||
|  | 	service     string | ||||||
|  | 	method      string | ||||||
|  | 	endpoint    string | ||||||
|  | 	contentType string | ||||||
|  | 	body        interface{} | ||||||
|  | 	codec       codec.Writer | ||||||
|  | 	stream      bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewClient returns new noop client | ||||||
|  | func NewClient(opts ...Option) Client { | ||||||
|  | 	return &noopClient{opts: NewOptions(opts...)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopRequest) Service() string { | ||||||
|  | 	return n.service | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopRequest) Method() string { | ||||||
|  | 	return n.method | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopRequest) Endpoint() string { | ||||||
|  | 	return n.endpoint | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopRequest) ContentType() string { | ||||||
|  | 	return n.contentType | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopRequest) Body() interface{} { | ||||||
|  | 	return n.body | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopRequest) Codec() codec.Writer { | ||||||
|  | 	return n.codec | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopRequest) Stream() bool { | ||||||
|  | 	return n.stream | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type noopResponse struct { | ||||||
|  | 	codec  codec.Reader | ||||||
|  | 	header map[string]string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopResponse) Codec() codec.Reader { | ||||||
|  | 	return n.codec | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopResponse) Header() map[string]string { | ||||||
|  | 	return n.header | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopResponse) Read() ([]byte, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type noopStream struct{} | ||||||
|  |  | ||||||
|  | func (n *noopStream) Context() context.Context { | ||||||
|  | 	return context.Background() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopStream) Request() Request { | ||||||
|  | 	return &noopRequest{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopStream) Response() Response { | ||||||
|  | 	return &noopResponse{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopStream) Send(interface{}) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopStream) Recv(interface{}) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopStream) Error() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopStream) Close() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopMessage) Topic() string { | ||||||
|  | 	return n.topic | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopMessage) Payload() interface{} { | ||||||
|  | 	return n.payload | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopMessage) ContentType() string { | ||||||
|  | 	return n.opts.ContentType | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopClient) Init(opts ...Option) error { | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&n.opts) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopClient) Options() Options { | ||||||
|  | 	return n.opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopClient) String() string { | ||||||
|  | 	return "noop" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopClient) NewRequest(service, endpoint string, req interface{}, opts ...RequestOption) Request { | ||||||
|  | 	return &noopRequest{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopClient) NewMessage(topic string, msg interface{}, opts ...MessageOption) Message { | ||||||
|  | 	options := NewMessageOptions(opts...) | ||||||
|  | 	return &noopMessage{topic: topic, payload: msg, opts: options} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) { | ||||||
|  | 	return &noopStream{}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOption) error { | ||||||
|  | 	var body []byte | ||||||
|  |  | ||||||
|  | 	options := NewPublishOptions(opts...) | ||||||
|  |  | ||||||
|  | 	md, ok := metadata.FromContext(ctx) | ||||||
|  | 	if !ok { | ||||||
|  | 		md = metadata.New(0) | ||||||
|  | 	} | ||||||
|  | 	md["Content-Type"] = p.ContentType() | ||||||
|  | 	md["Micro-Topic"] = p.Topic() | ||||||
|  |  | ||||||
|  | 	// passed in raw data | ||||||
|  | 	if d, ok := p.Payload().(*raw.Frame); ok { | ||||||
|  | 		body = d.Data | ||||||
|  | 	} else { | ||||||
|  | 		cf := n.opts.Broker.Options().Codec | ||||||
|  | 		if cf == nil { | ||||||
|  | 			cf = json.Marshaler{} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* | ||||||
|  | 			// use codec for payload | ||||||
|  | 			cf, err := n.opts.Codecs[p.ContentType()] | ||||||
|  | 			if err != nil { | ||||||
|  | 				return errors.InternalServerError("go.micro.client", err.Error()) | ||||||
|  | 			} | ||||||
|  | 		*/ | ||||||
|  | 		// set the body | ||||||
|  | 		b, err := cf.Marshal(p.Payload()) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return errors.InternalServerError("go.micro.client", err.Error()) | ||||||
|  | 		} | ||||||
|  | 		body = b | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	topic := p.Topic() | ||||||
|  |  | ||||||
|  | 	// get the exchange | ||||||
|  | 	if len(options.Exchange) > 0 { | ||||||
|  | 		topic = options.Exchange | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return n.opts.Broker.Publish(ctx, topic, &broker.Message{ | ||||||
|  | 		Header: md, | ||||||
|  | 		Body:   body, | ||||||
|  | 	}, broker.PublishContext(options.Context)) | ||||||
|  | } | ||||||
| @@ -7,11 +7,11 @@ import ( | |||||||
| 	"github.com/unistack-org/micro/v3/broker" | 	"github.com/unistack-org/micro/v3/broker" | ||||||
| 	"github.com/unistack-org/micro/v3/codec" | 	"github.com/unistack-org/micro/v3/codec" | ||||||
| 	"github.com/unistack-org/micro/v3/logger" | 	"github.com/unistack-org/micro/v3/logger" | ||||||
|  | 	"github.com/unistack-org/micro/v3/network/transport" | ||||||
| 	"github.com/unistack-org/micro/v3/registry" | 	"github.com/unistack-org/micro/v3/registry" | ||||||
| 	"github.com/unistack-org/micro/v3/router" | 	"github.com/unistack-org/micro/v3/router" | ||||||
| 	"github.com/unistack-org/micro/v3/selector" | 	"github.com/unistack-org/micro/v3/selector" | ||||||
| 	"github.com/unistack-org/micro/v3/selector/random" | 	"github.com/unistack-org/micro/v3/selector/random" | ||||||
| 	"github.com/unistack-org/micro/v3/transport" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Options struct { | type Options struct { | ||||||
| @@ -34,9 +34,6 @@ type Options struct { | |||||||
| 	PoolSize int | 	PoolSize int | ||||||
| 	PoolTTL  time.Duration | 	PoolTTL  time.Duration | ||||||
|  |  | ||||||
| 	// Response cache |  | ||||||
| 	Cache *Cache |  | ||||||
|  |  | ||||||
| 	// Middleware for client | 	// Middleware for client | ||||||
| 	Wrappers []Wrapper | 	Wrappers []Wrapper | ||||||
|  |  | ||||||
| @@ -48,13 +45,19 @@ type Options struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NewCallOptions(opts ...CallOption) CallOptions { | ||||||
|  | 	options := CallOptions{} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  |  | ||||||
| type CallOptions struct { | type CallOptions struct { | ||||||
| 	// Address of remote hosts | 	// Address of remote hosts | ||||||
| 	Address []string | 	Address []string | ||||||
| 	// Backoff func | 	// Backoff func | ||||||
| 	Backoff BackoffFunc | 	Backoff BackoffFunc | ||||||
| 	// Duration to cache the response for |  | ||||||
| 	CacheExpiry time.Duration |  | ||||||
| 	// Transport Dial Timeout | 	// Transport Dial Timeout | ||||||
| 	DialTimeout time.Duration | 	DialTimeout time.Duration | ||||||
| 	// Number of Call attempts | 	// Number of Call attempts | ||||||
| @@ -84,6 +87,20 @@ type CallOptions struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func Context(ctx context.Context) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.Context = ctx | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewPublishOptions(opts ...PublishOption) PublishOptions { | ||||||
|  | 	options := PublishOptions{} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  |  | ||||||
| type PublishOptions struct { | type PublishOptions struct { | ||||||
| 	// Exchange is the routing exchange for the message | 	// Exchange is the routing exchange for the message | ||||||
| 	Exchange string | 	Exchange string | ||||||
| @@ -92,10 +109,26 @@ type PublishOptions struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NewMessageOptions(opts ...MessageOption) MessageOptions { | ||||||
|  | 	options := MessageOptions{} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  |  | ||||||
| type MessageOptions struct { | type MessageOptions struct { | ||||||
| 	ContentType string | 	ContentType string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NewRequestOptions(opts ...RequestOption) RequestOptions { | ||||||
|  | 	options := RequestOptions{} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  |  | ||||||
| type RequestOptions struct { | type RequestOptions struct { | ||||||
| 	ContentType string | 	ContentType string | ||||||
| 	Stream      bool | 	Stream      bool | ||||||
| @@ -105,9 +138,8 @@ type RequestOptions struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewOptions(options ...Option) Options { | func NewOptions(opts ...Option) Options { | ||||||
| 	opts := Options{ | 	options := Options{ | ||||||
| 		Cache:       NewCache(), |  | ||||||
| 		Context:     context.Background(), | 		Context:     context.Background(), | ||||||
| 		ContentType: "application/protobuf", | 		ContentType: "application/protobuf", | ||||||
| 		Codecs:      make(map[string]codec.NewCodec), | 		Codecs:      make(map[string]codec.NewCodec), | ||||||
| @@ -122,13 +154,15 @@ func NewOptions(options ...Option) Options { | |||||||
| 		PoolSize: DefaultPoolSize, | 		PoolSize: DefaultPoolSize, | ||||||
| 		PoolTTL:  DefaultPoolTTL, | 		PoolTTL:  DefaultPoolTTL, | ||||||
| 		Selector: random.NewSelector(), | 		Selector: random.NewSelector(), | ||||||
|  | 		Logger:   logger.DefaultLogger, | ||||||
|  | 		Broker:   broker.DefaultBroker, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, o := range options { | 	for _, o := range opts { | ||||||
| 		o(&opts) | 		o(&options) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return opts | 	return options | ||||||
| } | } | ||||||
|  |  | ||||||
| // Broker to be used for pub/sub | // Broker to be used for pub/sub | ||||||
| @@ -360,14 +394,6 @@ func WithAuthToken() CallOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // WithCache is a CallOption which sets the duration the response |  | ||||||
| // shoull be cached for |  | ||||||
| func WithCache(c time.Duration) CallOption { |  | ||||||
| 	return func(o *CallOptions) { |  | ||||||
| 		o.CacheExpiry = c |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // WithNetwork is a CallOption which sets the network attribute | // WithNetwork is a CallOption which sets the network attribute | ||||||
| func WithNetwork(n string) CallOption { | func WithNetwork(n string) CallOption { | ||||||
| 	return func(o *CallOptions) { | 	return func(o *CallOptions) { | ||||||
|   | |||||||
| @@ -14,28 +14,6 @@ type testRequest struct { | |||||||
| 	opts        RequestOptions | 	opts        RequestOptions | ||||||
| } | } | ||||||
|  |  | ||||||
| func newRequest(service, endpoint string, request interface{}, contentType string, reqOpts ...RequestOption) Request { |  | ||||||
| 	var opts RequestOptions |  | ||||||
|  |  | ||||||
| 	for _, o := range reqOpts { |  | ||||||
| 		o(&opts) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// set the content-type specified |  | ||||||
| 	if len(opts.ContentType) > 0 { |  | ||||||
| 		contentType = opts.ContentType |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &testRequest{ |  | ||||||
| 		service:     service, |  | ||||||
| 		method:      endpoint, |  | ||||||
| 		endpoint:    endpoint, |  | ||||||
| 		body:        request, |  | ||||||
| 		contentType: contentType, |  | ||||||
| 		opts:        opts, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *testRequest) ContentType() string { | func (r *testRequest) ContentType() string { | ||||||
| 	return r.contentType | 	return r.contentType | ||||||
| } | } | ||||||
|   | |||||||
| @@ -236,7 +236,7 @@ func (g *micro) generateService(file *generator.FileDescriptor, service *pb.Serv | |||||||
| 		outType := g.typeName(method.GetOutputType()) | 		outType := g.typeName(method.GetOutputType()) | ||||||
|  |  | ||||||
| 		if !method.GetServerStreaming() && !method.GetClientStreaming() { | 		if !method.GetServerStreaming() && !method.GetClientStreaming() { | ||||||
| 			g.P(methName, "(ctx ", contextPkg, ".Context, in *", inType, ", out *", outType, ") error") | 			g.P(methName, "(ctx ", contextPkg, ".Context, req *", inType, ", rsp *", outType, ") error") | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		g.P(methName, "(ctx ", contextPkg, ".Context, stream server.Stream) error") | 		g.P(methName, "(ctx ", contextPkg, ".Context, stream server.Stream) error") | ||||||
| @@ -322,7 +322,7 @@ func (g *micro) generateClientSignature(servName string, method *pb.MethodDescri | |||||||
| 	if reservedClientName[methName] { | 	if reservedClientName[methName] { | ||||||
| 		methName += "_" | 		methName += "_" | ||||||
| 	} | 	} | ||||||
| 	reqArg := ", in *" + g.typeName(method.GetInputType()) | 	reqArg := ", req *" + g.typeName(method.GetInputType()) | ||||||
| 	if method.GetClientStreaming() { | 	if method.GetClientStreaming() { | ||||||
| 		reqArg = "" | 		reqArg = "" | ||||||
| 	} | 	} | ||||||
| @@ -335,8 +335,8 @@ func (g *micro) generateClientSignature(servName string, method *pb.MethodDescri | |||||||
| } | } | ||||||
|  |  | ||||||
| func (g *micro) generateClientMethod(reqServ, servName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) { | func (g *micro) generateClientMethod(reqServ, servName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) { | ||||||
| 	reqMethod := fmt.Sprintf("%s.%s", servName, method.GetName()) |  | ||||||
| 	methName := generator.CamelCase(method.GetName()) | 	methName := generator.CamelCase(method.GetName()) | ||||||
|  | 	reqMethod := fmt.Sprintf("%s.%s", servName, methName) | ||||||
| 	inType := g.typeName(method.GetInputType()) | 	inType := g.typeName(method.GetInputType()) | ||||||
| 	outType := g.typeName(method.GetOutputType()) | 	outType := g.typeName(method.GetOutputType()) | ||||||
|  |  | ||||||
| @@ -349,23 +349,21 @@ func (g *micro) generateClientMethod(reqServ, servName, serviceDescVar string, m | |||||||
|  |  | ||||||
| 	g.P("func (c *", unexport(servAlias), ") ", g.generateClientSignature(servName, method), "{") | 	g.P("func (c *", unexport(servAlias), ") ", g.generateClientSignature(servName, method), "{") | ||||||
| 	if !method.GetServerStreaming() && !method.GetClientStreaming() { | 	if !method.GetServerStreaming() && !method.GetClientStreaming() { | ||||||
| 		g.P(`req := c.c.NewRequest(c.name, "`, reqMethod, `", in)`) | 		g.P("rsp := &", outType, "{}") | ||||||
| 		g.P("out := new(", outType, ")") |  | ||||||
| 		// TODO: Pass descExpr to Invoke. | 		// TODO: Pass descExpr to Invoke. | ||||||
| 		g.P("err := ", `c.c.Call(ctx, req, out, opts...)`) | 		g.P(`err := c.c.Call(ctx, c.c.NewRequest(c.name, "`, reqMethod, `", req), rsp, opts...)`) | ||||||
| 		g.P("if err != nil { return nil, err }") | 		g.P("if err != nil { return nil, err }") | ||||||
| 		g.P("return out, nil") | 		g.P("return rsp, nil") | ||||||
| 		g.P("}") | 		g.P("}") | ||||||
| 		g.P() | 		g.P() | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	streamType := unexport(servAlias) + methName | 	streamType := unexport(servAlias) + methName | ||||||
| 	g.P(`req := c.c.NewRequest(c.name, "`, reqMethod, `", &`, inType, `{})`) | 	g.P(`stream, err := c.c.Stream(ctx, c.c.NewRequest(c.name, "`, reqMethod, `", &`, inType, `{}), opts...)`) | ||||||
| 	g.P("stream, err := c.c.Stream(ctx, req, opts...)") |  | ||||||
| 	g.P("if err != nil { return nil, err }") | 	g.P("if err != nil { return nil, err }") | ||||||
|  |  | ||||||
| 	if !method.GetClientStreaming() { | 	if !method.GetClientStreaming() { | ||||||
| 		g.P("if err := stream.Send(in); err != nil { return nil, err }") | 		g.P("if err := stream.Send(req); err != nil { return nil, err }") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	g.P("return &", streamType, "{stream}, nil") | 	g.P("return &", streamType, "{stream}, nil") | ||||||
| @@ -380,6 +378,11 @@ func (g *micro) generateClientMethod(reqServ, servName, serviceDescVar string, m | |||||||
| 	g.P("Context() context.Context") | 	g.P("Context() context.Context") | ||||||
| 	g.P("SendMsg(interface{}) error") | 	g.P("SendMsg(interface{}) error") | ||||||
| 	g.P("RecvMsg(interface{}) error") | 	g.P("RecvMsg(interface{}) error") | ||||||
|  |  | ||||||
|  | 	if genSend && !genRecv { | ||||||
|  | 		// client streaming, the server will send a response upon close | ||||||
|  | 		g.P("CloseAndRecv() (*", outType, ", error)") | ||||||
|  | 	} | ||||||
| 	g.P("Close() error") | 	g.P("Close() error") | ||||||
|  |  | ||||||
| 	if genSend { | 	if genSend { | ||||||
| @@ -396,6 +399,18 @@ func (g *micro) generateClientMethod(reqServ, servName, serviceDescVar string, m | |||||||
| 	g.P("}") | 	g.P("}") | ||||||
| 	g.P() | 	g.P() | ||||||
|  |  | ||||||
|  | 	if genSend && !genRecv { | ||||||
|  | 		// client streaming, the server will send a response upon close | ||||||
|  | 		g.P("func (x *", streamType, ") CloseAndRecv() (*", outType, ", error) {") | ||||||
|  | 		g.P("if err := x.stream.Close(); err != nil {") | ||||||
|  | 		g.P("return nil, err") | ||||||
|  | 		g.P("}") | ||||||
|  | 		g.P("r := new(", outType, ")") | ||||||
|  | 		g.P("err := x.RecvMsg(r)") | ||||||
|  | 		g.P("return r, err") | ||||||
|  | 		g.P("}") | ||||||
|  | 		g.P() | ||||||
|  | 	} | ||||||
| 	g.P("func (x *", streamType, ") Close() error {") | 	g.P("func (x *", streamType, ") Close() error {") | ||||||
| 	g.P("return x.stream.Close()") | 	g.P("return x.stream.Close()") | ||||||
| 	g.P("}") | 	g.P("}") | ||||||
| @@ -426,7 +441,7 @@ func (g *micro) generateClientMethod(reqServ, servName, serviceDescVar string, m | |||||||
|  |  | ||||||
| 	if genRecv { | 	if genRecv { | ||||||
| 		g.P("func (x *", streamType, ") Recv() (*", outType, ", error) {") | 		g.P("func (x *", streamType, ") Recv() (*", outType, ", error) {") | ||||||
| 		g.P("m := new(", outType, ")") | 		g.P("m := &", outType, "{}") | ||||||
| 		g.P("err := x.stream.Recv(m)") | 		g.P("err := x.stream.Recv(m)") | ||||||
| 		g.P("if err != nil {") | 		g.P("if err != nil {") | ||||||
| 		g.P("return nil, err") | 		g.P("return nil, err") | ||||||
| @@ -469,8 +484,8 @@ func (g *micro) generateServerMethod(servName string, method *pb.MethodDescripto | |||||||
| 	outType := g.typeName(method.GetOutputType()) | 	outType := g.typeName(method.GetOutputType()) | ||||||
|  |  | ||||||
| 	if !method.GetServerStreaming() && !method.GetClientStreaming() { | 	if !method.GetServerStreaming() && !method.GetClientStreaming() { | ||||||
| 		g.P("func (h *", unexport(servName), "Handler) ", methName, "(ctx ", contextPkg, ".Context, in *", inType, ", out *", outType, ") error {") | 		g.P("func (h *", unexport(servName), "Handler) ", methName, "(ctx ", contextPkg, ".Context, req *", inType, ", rsp *", outType, ") error {") | ||||||
| 		g.P("return h.", serveType, ".", methName, "(ctx, in, out)") | 		g.P("return h.", serveType, ".", methName, "(ctx, req, rsp)") | ||||||
| 		g.P("}") | 		g.P("}") | ||||||
| 		g.P() | 		g.P() | ||||||
| 		return hname | 		return hname | ||||||
| @@ -478,7 +493,7 @@ func (g *micro) generateServerMethod(servName string, method *pb.MethodDescripto | |||||||
| 	streamType := unexport(servName) + methName + "Stream" | 	streamType := unexport(servName) + methName + "Stream" | ||||||
| 	g.P("func (h *", unexport(servName), "Handler) ", methName, "(ctx ", contextPkg, ".Context, stream server.Stream) error {") | 	g.P("func (h *", unexport(servName), "Handler) ", methName, "(ctx ", contextPkg, ".Context, stream server.Stream) error {") | ||||||
| 	if !method.GetClientStreaming() { | 	if !method.GetClientStreaming() { | ||||||
| 		g.P("m := new(", inType, ")") | 		g.P("m := &", inType, "{}") | ||||||
| 		g.P("if err := stream.Recv(m); err != nil { return err }") | 		g.P("if err := stream.Recv(m); err != nil { return err }") | ||||||
| 		g.P("return h.", serveType, ".", methName, "(ctx, m, &", streamType, "{stream})") | 		g.P("return h.", serveType, ".", methName, "(ctx, m, &", streamType, "{stream})") | ||||||
| 	} else { | 	} else { | ||||||
| @@ -495,6 +510,10 @@ func (g *micro) generateServerMethod(servName string, method *pb.MethodDescripto | |||||||
| 	g.P("Context() context.Context") | 	g.P("Context() context.Context") | ||||||
| 	g.P("SendMsg(interface{}) error") | 	g.P("SendMsg(interface{}) error") | ||||||
| 	g.P("RecvMsg(interface{}) error") | 	g.P("RecvMsg(interface{}) error") | ||||||
|  | 	if !genSend { | ||||||
|  | 		// client streaming, the server will send a response upon close | ||||||
|  | 		g.P("SendAndClose(*", outType, ")  error") | ||||||
|  | 	} | ||||||
| 	g.P("Close() error") | 	g.P("Close() error") | ||||||
|  |  | ||||||
| 	if genSend { | 	if genSend { | ||||||
| @@ -513,6 +532,17 @@ func (g *micro) generateServerMethod(servName string, method *pb.MethodDescripto | |||||||
| 	g.P("}") | 	g.P("}") | ||||||
| 	g.P() | 	g.P() | ||||||
|  |  | ||||||
|  | 	if !genSend { | ||||||
|  | 		// client streaming, the server will send a response upon close | ||||||
|  | 		g.P("func (x *", streamType, ") SendAndClose(in *", outType, ") error {") | ||||||
|  | 		g.P("if err := x.SendMsg(in); err != nil {") | ||||||
|  | 		g.P("return err") | ||||||
|  | 		g.P("}") | ||||||
|  | 		g.P("return x.stream.Close()") | ||||||
|  | 		g.P("}") | ||||||
|  | 		g.P() | ||||||
|  | 	} | ||||||
|  | 	// other types of rpc don't send a response when the stream closes | ||||||
| 	g.P("func (x *", streamType, ") Close() error {") | 	g.P("func (x *", streamType, ") Close() error {") | ||||||
| 	g.P("return x.stream.Close()") | 	g.P("return x.stream.Close()") | ||||||
| 	g.P("}") | 	g.P("}") | ||||||
| @@ -542,7 +572,7 @@ func (g *micro) generateServerMethod(servName string, method *pb.MethodDescripto | |||||||
|  |  | ||||||
| 	if genRecv { | 	if genRecv { | ||||||
| 		g.P("func (x *", streamType, ") Recv() (*", inType, ", error) {") | 		g.P("func (x *", streamType, ") Recv() (*", inType, ", error) {") | ||||||
| 		g.P("m := new(", inType, ")") | 		g.P("m := &", inType, "{}") | ||||||
| 		g.P("if err := x.stream.Recv(m); err != nil { return nil, err }") | 		g.P("if err := x.stream.Recv(m); err != nil { return nil, err }") | ||||||
| 		g.P("return m, nil") | 		g.P("return m, nil") | ||||||
| 		g.P("}") | 		g.P("}") | ||||||
|   | |||||||
| @@ -14,12 +14,14 @@ const ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
|  | 	// ErrInvalidMessage returned when invalid messge passed to codec | ||||||
| 	ErrInvalidMessage = errors.New("invalid message") | 	ErrInvalidMessage = errors.New("invalid message") | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // MessageType | ||||||
| type MessageType int | type MessageType int | ||||||
|  |  | ||||||
| // Takes in a connection/buffer and returns a new Codec | // NewCodec takes in a connection/buffer and returns a new Codec | ||||||
| type NewCodec func(io.ReadWriteCloser) Codec | type NewCodec func(io.ReadWriteCloser) Codec | ||||||
|  |  | ||||||
| // Codec encodes/decodes various types of messages used within go-micro. | // Codec encodes/decodes various types of messages used within go-micro. | ||||||
| @@ -34,11 +36,13 @@ type Codec interface { | |||||||
| 	String() string | 	String() string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Reader interface | ||||||
| type Reader interface { | type Reader interface { | ||||||
| 	ReadHeader(*Message, MessageType) error | 	ReadHeader(*Message, MessageType) error | ||||||
| 	ReadBody(interface{}) error | 	ReadBody(interface{}) error | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Writer interface | ||||||
| type Writer interface { | type Writer interface { | ||||||
| 	Write(*Message, interface{}) error | 	Write(*Message, interface{}) error | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,59 +0,0 @@ | |||||||
| // Package json provides a json codec |  | ||||||
| package json |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"io" |  | ||||||
| 	"io/ioutil" |  | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/codec" |  | ||||||
| 	jsonpb "google.golang.org/protobuf/encoding/protojson" |  | ||||||
| 	"google.golang.org/protobuf/proto" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type Codec struct { |  | ||||||
| 	Conn    io.ReadWriteCloser |  | ||||||
| 	Encoder *json.Encoder |  | ||||||
| 	Decoder *json.Decoder |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) ReadHeader(m *codec.Message, t codec.MessageType) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) ReadBody(b interface{}) error { |  | ||||||
| 	if b == nil { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	if pb, ok := b.(proto.Message); ok { |  | ||||||
| 		buf, err := ioutil.ReadAll(c.Conn) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		return jsonpb.Unmarshal(buf, pb) |  | ||||||
| 	} |  | ||||||
| 	return c.Decoder.Decode(b) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) Write(m *codec.Message, b interface{}) error { |  | ||||||
| 	if b == nil { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	return c.Encoder.Encode(b) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) Close() error { |  | ||||||
| 	return c.Conn.Close() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) String() string { |  | ||||||
| 	return "json" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewCodec(c io.ReadWriteCloser) codec.Codec { |  | ||||||
| 	return &Codec{ |  | ||||||
| 		Conn:    c, |  | ||||||
| 		Decoder: json.NewDecoder(c), |  | ||||||
| 		Encoder: json.NewEncoder(c), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,38 +0,0 @@ | |||||||
| package json |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
|  |  | ||||||
| 	"github.com/oxtoacart/bpool" |  | ||||||
| 	jsonpb "google.golang.org/protobuf/encoding/protojson" |  | ||||||
| 	"google.golang.org/protobuf/proto" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var jsonpbMarshaler = &jsonpb.MarshalOptions{} |  | ||||||
|  |  | ||||||
| // create buffer pool with 16 instances each preallocated with 256 bytes |  | ||||||
| var bufferPool = bpool.NewSizedBufferPool(16, 256) |  | ||||||
|  |  | ||||||
| type Marshaler struct{} |  | ||||||
|  |  | ||||||
| func (j Marshaler) Marshal(v interface{}) ([]byte, error) { |  | ||||||
| 	if pb, ok := v.(proto.Message); ok { |  | ||||||
| 		buf, err := jsonpbMarshaler.Marshal(pb) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		return buf, nil |  | ||||||
| 	} |  | ||||||
| 	return json.Marshal(v) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j Marshaler) Unmarshal(d []byte, v interface{}) error { |  | ||||||
| 	if pb, ok := v.(proto.Message); ok { |  | ||||||
| 		return jsonpb.Unmarshal(d, pb) |  | ||||||
| 	} |  | ||||||
| 	return json.Unmarshal(d, v) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j Marshaler) String() string { |  | ||||||
| 	return "json" |  | ||||||
| } |  | ||||||
| @@ -1,97 +0,0 @@ | |||||||
| package jsonrpc |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/codec" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| 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 |  | ||||||
| 	pending map[interface{}]string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type clientRequest struct { |  | ||||||
| 	Method string         `json:"method"` |  | ||||||
| 	Params [1]interface{} `json:"params"` |  | ||||||
| 	ID     interface{}    `json:"id"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type clientResponse struct { |  | ||||||
| 	ID     interface{}      `json:"id"` |  | ||||||
| 	Result *json.RawMessage `json:"result"` |  | ||||||
| 	Error  interface{}      `json:"error"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newClientCodec(conn io.ReadWriteCloser) *clientCodec { |  | ||||||
| 	return &clientCodec{ |  | ||||||
| 		dec:     json.NewDecoder(conn), |  | ||||||
| 		enc:     json.NewEncoder(conn), |  | ||||||
| 		c:       conn, |  | ||||||
| 		pending: make(map[interface{}]string), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *clientCodec) Write(m *codec.Message, b interface{}) error { |  | ||||||
| 	c.Lock() |  | ||||||
| 	c.pending[m.Id] = m.Method |  | ||||||
| 	c.Unlock() |  | ||||||
| 	c.req.Method = m.Method |  | ||||||
| 	c.req.Params[0] = b |  | ||||||
| 	c.req.ID = m.Id |  | ||||||
| 	return c.enc.Encode(&c.req) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *clientResponse) reset() { |  | ||||||
| 	r.ID = 0 |  | ||||||
| 	r.Result = nil |  | ||||||
| 	r.Error = nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *clientCodec) ReadHeader(m *codec.Message) error { |  | ||||||
| 	c.resp.reset() |  | ||||||
| 	if err := c.dec.Decode(&c.resp); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	c.Lock() |  | ||||||
| 	m.Method = c.pending[c.resp.ID] |  | ||||||
| 	delete(c.pending, c.resp.ID) |  | ||||||
| 	c.Unlock() |  | ||||||
|  |  | ||||||
| 	m.Error = "" |  | ||||||
| 	m.Id = fmt.Sprintf("%v", c.resp.ID) |  | ||||||
| 	if c.resp.Error != nil { |  | ||||||
| 		x, ok := c.resp.Error.(string) |  | ||||||
| 		if !ok { |  | ||||||
| 			return fmt.Errorf("invalid error %v", c.resp.Error) |  | ||||||
| 		} |  | ||||||
| 		if x == "" { |  | ||||||
| 			x = "unspecified error" |  | ||||||
| 		} |  | ||||||
| 		m.Error = x |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *clientCodec) ReadBody(x interface{}) error { |  | ||||||
| 	if x == nil || c.resp.Result == nil { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	return json.Unmarshal(*c.resp.Result, x) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *clientCodec) Close() error { |  | ||||||
| 	return c.c.Close() |  | ||||||
| } |  | ||||||
| @@ -1,88 +0,0 @@ | |||||||
| // Package jsonrpc provides a json-rpc 1.0 codec |  | ||||||
| package jsonrpc |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/codec" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type jsonCodec struct { |  | ||||||
| 	buf *bytes.Buffer |  | ||||||
| 	mt  codec.MessageType |  | ||||||
| 	rwc io.ReadWriteCloser |  | ||||||
| 	c   *clientCodec |  | ||||||
| 	s   *serverCodec |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jsonCodec) Close() error { |  | ||||||
| 	j.buf.Reset() |  | ||||||
| 	return j.rwc.Close() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jsonCodec) String() string { |  | ||||||
| 	return "json-rpc" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jsonCodec) Write(m *codec.Message, b interface{}) error { |  | ||||||
| 	switch m.Type { |  | ||||||
| 	case codec.Request: |  | ||||||
| 		return j.c.Write(m, b) |  | ||||||
| 	case codec.Response, codec.Error: |  | ||||||
| 		return j.s.Write(m, b) |  | ||||||
| 	case codec.Event: |  | ||||||
| 		data, err := json.Marshal(b) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		_, err = j.rwc.Write(data) |  | ||||||
| 		return err |  | ||||||
| 	default: |  | ||||||
| 		return fmt.Errorf("Unrecognised message type: %v", m.Type) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jsonCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error { |  | ||||||
| 	j.buf.Reset() |  | ||||||
| 	j.mt = mt |  | ||||||
|  |  | ||||||
| 	switch mt { |  | ||||||
| 	case codec.Request: |  | ||||||
| 		return j.s.ReadHeader(m) |  | ||||||
| 	case codec.Response: |  | ||||||
| 		return j.c.ReadHeader(m) |  | ||||||
| 	case codec.Event: |  | ||||||
| 		_, err := io.Copy(j.buf, j.rwc) |  | ||||||
| 		return err |  | ||||||
| 	default: |  | ||||||
| 		return fmt.Errorf("Unrecognised message type: %v", mt) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (j *jsonCodec) ReadBody(b interface{}) error { |  | ||||||
| 	switch j.mt { |  | ||||||
| 	case codec.Request: |  | ||||||
| 		return j.s.ReadBody(b) |  | ||||||
| 	case codec.Response: |  | ||||||
| 		return j.c.ReadBody(b) |  | ||||||
| 	case codec.Event: |  | ||||||
| 		if b != nil { |  | ||||||
| 			return json.Unmarshal(j.buf.Bytes(), b) |  | ||||||
| 		} |  | ||||||
| 	default: |  | ||||||
| 		return fmt.Errorf("Unrecognised message type: %v", j.mt) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewCodec(rwc io.ReadWriteCloser) codec.Codec { |  | ||||||
| 	return &jsonCodec{ |  | ||||||
| 		buf: bytes.NewBuffer(nil), |  | ||||||
| 		rwc: rwc, |  | ||||||
| 		c:   newClientCodec(rwc), |  | ||||||
| 		s:   newServerCodec(rwc), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,84 +0,0 @@ | |||||||
| package jsonrpc |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/codec" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type serverCodec struct { |  | ||||||
| 	dec *json.Decoder // for reading JSON values |  | ||||||
| 	enc *json.Encoder // for writing JSON values |  | ||||||
| 	c   io.Closer |  | ||||||
|  |  | ||||||
| 	// temporary work space |  | ||||||
| 	req serverRequest |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type serverRequest struct { |  | ||||||
| 	Method string           `json:"method"` |  | ||||||
| 	Params *json.RawMessage `json:"params"` |  | ||||||
| 	ID     interface{}      `json:"id"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type serverResponse struct { |  | ||||||
| 	ID     interface{} `json:"id"` |  | ||||||
| 	Result interface{} `json:"result"` |  | ||||||
| 	Error  interface{} `json:"error"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newServerCodec(conn io.ReadWriteCloser) *serverCodec { |  | ||||||
| 	return &serverCodec{ |  | ||||||
| 		dec: json.NewDecoder(conn), |  | ||||||
| 		enc: json.NewEncoder(conn), |  | ||||||
| 		c:   conn, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *serverRequest) reset() { |  | ||||||
| 	r.Method = "" |  | ||||||
| 	if r.Params != nil { |  | ||||||
| 		*r.Params = (*r.Params)[0:0] |  | ||||||
| 	} |  | ||||||
| 	if r.ID != nil { |  | ||||||
| 		r.ID = nil |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *serverCodec) ReadHeader(m *codec.Message) error { |  | ||||||
| 	c.req.reset() |  | ||||||
| 	if err := c.dec.Decode(&c.req); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	m.Method = c.req.Method |  | ||||||
| 	m.Id = fmt.Sprintf("%v", c.req.ID) |  | ||||||
| 	c.req.ID = nil |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *serverCodec) ReadBody(x interface{}) error { |  | ||||||
| 	if x == nil { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	var params [1]interface{} |  | ||||||
| 	params[0] = x |  | ||||||
| 	return json.Unmarshal(*c.req.Params, ¶ms) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *serverCodec) Write(m *codec.Message, x interface{}) error { |  | ||||||
| 	var resp serverResponse |  | ||||||
| 	resp.ID = m.Id |  | ||||||
| 	resp.Result = x |  | ||||||
| 	if m.Error == "" { |  | ||||||
| 		resp.Error = nil |  | ||||||
| 	} else { |  | ||||||
| 		resp.Error = m.Error |  | ||||||
| 	} |  | ||||||
| 	return c.enc.Encode(resp) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *serverCodec) Close() error { |  | ||||||
| 	return c.c.Close() |  | ||||||
| } |  | ||||||
| @@ -1,47 +0,0 @@ | |||||||
| package proto |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
|  |  | ||||||
| 	"github.com/golang/protobuf/proto" |  | ||||||
| 	"github.com/oxtoacart/bpool" |  | ||||||
| 	"github.com/unistack-org/micro/v3/codec" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // create buffer pool with 16 instances each preallocated with 256 bytes |  | ||||||
| var bufferPool = bpool.NewSizedBufferPool(16, 256) |  | ||||||
|  |  | ||||||
| type Marshaler struct{} |  | ||||||
|  |  | ||||||
| func (Marshaler) Marshal(v interface{}) ([]byte, error) { |  | ||||||
| 	pb, ok := v.(proto.Message) |  | ||||||
| 	if !ok { |  | ||||||
| 		return nil, codec.ErrInvalidMessage |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// looks not good, but allows to reuse underlining bytes |  | ||||||
| 	buf := bufferPool.Get() |  | ||||||
| 	pbuf := proto.NewBuffer(buf.Bytes()) |  | ||||||
| 	defer func() { |  | ||||||
| 		bufferPool.Put(bytes.NewBuffer(pbuf.Bytes())) |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	if err := pbuf.Marshal(pb); err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return pbuf.Bytes(), nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (Marshaler) Unmarshal(data []byte, v interface{}) error { |  | ||||||
| 	pb, ok := v.(proto.Message) |  | ||||||
| 	if !ok { |  | ||||||
| 		return codec.ErrInvalidMessage |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return proto.Unmarshal(data, pb) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (Marshaler) String() string { |  | ||||||
| 	return "proto" |  | ||||||
| } |  | ||||||
| @@ -1,37 +0,0 @@ | |||||||
| package proto |  | ||||||
|  |  | ||||||
| type Message struct { |  | ||||||
| 	Data []byte |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Message) MarshalJSON() ([]byte, error) { |  | ||||||
| 	return m.Data, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Message) UnmarshalJSON(data []byte) error { |  | ||||||
| 	m.Data = data |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Message) ProtoMessage() {} |  | ||||||
|  |  | ||||||
| func (m *Message) Reset() { |  | ||||||
| 	*m = Message{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Message) String() string { |  | ||||||
| 	return string(m.Data) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Message) Marshal() ([]byte, error) { |  | ||||||
| 	return m.Data, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *Message) Unmarshal(data []byte) error { |  | ||||||
| 	m.Data = data |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewMessage(data []byte) *Message { |  | ||||||
| 	return &Message{data} |  | ||||||
| } |  | ||||||
| @@ -1,64 +0,0 @@ | |||||||
| // Package proto provides a proto codec |  | ||||||
| package proto |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"io" |  | ||||||
| 	"io/ioutil" |  | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/codec" |  | ||||||
| 	"google.golang.org/protobuf/proto" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type Codec struct { |  | ||||||
| 	Conn io.ReadWriteCloser |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) ReadHeader(m *codec.Message, t codec.MessageType) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) ReadBody(b interface{}) error { |  | ||||||
| 	if b == nil { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	buf, err := ioutil.ReadAll(c.Conn) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	m, ok := b.(proto.Message) |  | ||||||
| 	if !ok { |  | ||||||
| 		return codec.ErrInvalidMessage |  | ||||||
| 	} |  | ||||||
| 	return proto.Unmarshal(buf, m) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) Write(m *codec.Message, b interface{}) error { |  | ||||||
| 	if b == nil { |  | ||||||
| 		// Nothing to write |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	p, ok := b.(proto.Message) |  | ||||||
| 	if !ok { |  | ||||||
| 		return codec.ErrInvalidMessage |  | ||||||
| 	} |  | ||||||
| 	buf, err := proto.Marshal(p) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	_, err = c.Conn.Write(buf) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) Close() error { |  | ||||||
| 	return c.Conn.Close() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) String() string { |  | ||||||
| 	return "proto" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewCodec(c io.ReadWriteCloser) codec.Codec { |  | ||||||
| 	return &Codec{ |  | ||||||
| 		Conn: c, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,238 +0,0 @@ | |||||||
| // Code generated by protoc-gen-go. DO NOT EDIT. |  | ||||||
| // versions: |  | ||||||
| // 	protoc-gen-go v1.25.0 |  | ||||||
| // 	protoc        v3.6.1 |  | ||||||
| // source: codec/protorpc/envelope.proto |  | ||||||
|  |  | ||||||
| package protorpc |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	proto "github.com/golang/protobuf/proto" |  | ||||||
| 	protoreflect "google.golang.org/protobuf/reflect/protoreflect" |  | ||||||
| 	protoimpl "google.golang.org/protobuf/runtime/protoimpl" |  | ||||||
| 	reflect "reflect" |  | ||||||
| 	sync "sync" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	// Verify that this generated code is sufficiently up-to-date. |  | ||||||
| 	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) |  | ||||||
| 	// Verify that runtime/protoimpl is sufficiently up-to-date. |  | ||||||
| 	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // This is a compile-time assertion that a sufficiently up-to-date version |  | ||||||
| // of the legacy proto package is being used. |  | ||||||
| const _ = proto.ProtoPackageIsVersion4 |  | ||||||
|  |  | ||||||
| type Request struct { |  | ||||||
| 	state         protoimpl.MessageState |  | ||||||
| 	sizeCache     protoimpl.SizeCache |  | ||||||
| 	unknownFields protoimpl.UnknownFields |  | ||||||
|  |  | ||||||
| 	ServiceMethod string `protobuf:"bytes,1,opt,name=service_method,json=serviceMethod,proto3" json:"service_method,omitempty"` |  | ||||||
| 	Seq           uint64 `protobuf:"fixed64,2,opt,name=seq,proto3" json:"seq,omitempty"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (x *Request) Reset() { |  | ||||||
| 	*x = Request{} |  | ||||||
| 	if protoimpl.UnsafeEnabled { |  | ||||||
| 		mi := &file_codec_protorpc_envelope_proto_msgTypes[0] |  | ||||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |  | ||||||
| 		ms.StoreMessageInfo(mi) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (x *Request) String() string { |  | ||||||
| 	return protoimpl.X.MessageStringOf(x) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (*Request) ProtoMessage() {} |  | ||||||
|  |  | ||||||
| func (x *Request) ProtoReflect() protoreflect.Message { |  | ||||||
| 	mi := &file_codec_protorpc_envelope_proto_msgTypes[0] |  | ||||||
| 	if protoimpl.UnsafeEnabled && x != nil { |  | ||||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |  | ||||||
| 		if ms.LoadMessageInfo() == nil { |  | ||||||
| 			ms.StoreMessageInfo(mi) |  | ||||||
| 		} |  | ||||||
| 		return ms |  | ||||||
| 	} |  | ||||||
| 	return mi.MessageOf(x) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Deprecated: Use Request.ProtoReflect.Descriptor instead. |  | ||||||
| func (*Request) Descriptor() ([]byte, []int) { |  | ||||||
| 	return file_codec_protorpc_envelope_proto_rawDescGZIP(), []int{0} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (x *Request) GetServiceMethod() string { |  | ||||||
| 	if x != nil { |  | ||||||
| 		return x.ServiceMethod |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (x *Request) GetSeq() uint64 { |  | ||||||
| 	if x != nil { |  | ||||||
| 		return x.Seq |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Response struct { |  | ||||||
| 	state         protoimpl.MessageState |  | ||||||
| 	sizeCache     protoimpl.SizeCache |  | ||||||
| 	unknownFields protoimpl.UnknownFields |  | ||||||
|  |  | ||||||
| 	ServiceMethod string `protobuf:"bytes,1,opt,name=service_method,json=serviceMethod,proto3" json:"service_method,omitempty"` |  | ||||||
| 	Seq           uint64 `protobuf:"fixed64,2,opt,name=seq,proto3" json:"seq,omitempty"` |  | ||||||
| 	Error         string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (x *Response) Reset() { |  | ||||||
| 	*x = Response{} |  | ||||||
| 	if protoimpl.UnsafeEnabled { |  | ||||||
| 		mi := &file_codec_protorpc_envelope_proto_msgTypes[1] |  | ||||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |  | ||||||
| 		ms.StoreMessageInfo(mi) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (x *Response) String() string { |  | ||||||
| 	return protoimpl.X.MessageStringOf(x) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (*Response) ProtoMessage() {} |  | ||||||
|  |  | ||||||
| func (x *Response) ProtoReflect() protoreflect.Message { |  | ||||||
| 	mi := &file_codec_protorpc_envelope_proto_msgTypes[1] |  | ||||||
| 	if protoimpl.UnsafeEnabled && x != nil { |  | ||||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |  | ||||||
| 		if ms.LoadMessageInfo() == nil { |  | ||||||
| 			ms.StoreMessageInfo(mi) |  | ||||||
| 		} |  | ||||||
| 		return ms |  | ||||||
| 	} |  | ||||||
| 	return mi.MessageOf(x) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Deprecated: Use Response.ProtoReflect.Descriptor instead. |  | ||||||
| func (*Response) Descriptor() ([]byte, []int) { |  | ||||||
| 	return file_codec_protorpc_envelope_proto_rawDescGZIP(), []int{1} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (x *Response) GetServiceMethod() string { |  | ||||||
| 	if x != nil { |  | ||||||
| 		return x.ServiceMethod |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (x *Response) GetSeq() uint64 { |  | ||||||
| 	if x != nil { |  | ||||||
| 		return x.Seq |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (x *Response) GetError() string { |  | ||||||
| 	if x != nil { |  | ||||||
| 		return x.Error |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var File_codec_protorpc_envelope_proto protoreflect.FileDescriptor |  | ||||||
|  |  | ||||||
| var file_codec_protorpc_envelope_proto_rawDesc = []byte{ |  | ||||||
| 	0x0a, 0x1d, 0x63, 0x6f, 0x64, 0x65, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x72, 0x70, 0x63, |  | ||||||
| 	0x2f, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, |  | ||||||
| 	0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x72, 0x70, 0x63, 0x22, 0x42, 0x0a, 0x07, 0x52, 0x65, 0x71, |  | ||||||
| 	0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, |  | ||||||
| 	0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, |  | ||||||
| 	0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, |  | ||||||
| 	0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x06, 0x52, 0x03, 0x73, 0x65, 0x71, 0x22, 0x59, 0x0a, |  | ||||||
| 	0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, |  | ||||||
| 	0x76, 0x69, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, |  | ||||||
| 	0x09, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, |  | ||||||
| 	0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x06, 0x52, 0x03, 0x73, |  | ||||||
| 	0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, |  | ||||||
| 	0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	file_codec_protorpc_envelope_proto_rawDescOnce sync.Once |  | ||||||
| 	file_codec_protorpc_envelope_proto_rawDescData = file_codec_protorpc_envelope_proto_rawDesc |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func file_codec_protorpc_envelope_proto_rawDescGZIP() []byte { |  | ||||||
| 	file_codec_protorpc_envelope_proto_rawDescOnce.Do(func() { |  | ||||||
| 		file_codec_protorpc_envelope_proto_rawDescData = protoimpl.X.CompressGZIP(file_codec_protorpc_envelope_proto_rawDescData) |  | ||||||
| 	}) |  | ||||||
| 	return file_codec_protorpc_envelope_proto_rawDescData |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var file_codec_protorpc_envelope_proto_msgTypes = make([]protoimpl.MessageInfo, 2) |  | ||||||
| var file_codec_protorpc_envelope_proto_goTypes = []interface{}{ |  | ||||||
| 	(*Request)(nil),  // 0: protorpc.Request |  | ||||||
| 	(*Response)(nil), // 1: protorpc.Response |  | ||||||
| } |  | ||||||
| var file_codec_protorpc_envelope_proto_depIdxs = []int32{ |  | ||||||
| 	0, // [0:0] is the sub-list for method output_type |  | ||||||
| 	0, // [0:0] is the sub-list for method input_type |  | ||||||
| 	0, // [0:0] is the sub-list for extension type_name |  | ||||||
| 	0, // [0:0] is the sub-list for extension extendee |  | ||||||
| 	0, // [0:0] is the sub-list for field type_name |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func init() { file_codec_protorpc_envelope_proto_init() } |  | ||||||
| func file_codec_protorpc_envelope_proto_init() { |  | ||||||
| 	if File_codec_protorpc_envelope_proto != nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if !protoimpl.UnsafeEnabled { |  | ||||||
| 		file_codec_protorpc_envelope_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { |  | ||||||
| 			switch v := v.(*Request); i { |  | ||||||
| 			case 0: |  | ||||||
| 				return &v.state |  | ||||||
| 			case 1: |  | ||||||
| 				return &v.sizeCache |  | ||||||
| 			case 2: |  | ||||||
| 				return &v.unknownFields |  | ||||||
| 			default: |  | ||||||
| 				return nil |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		file_codec_protorpc_envelope_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { |  | ||||||
| 			switch v := v.(*Response); i { |  | ||||||
| 			case 0: |  | ||||||
| 				return &v.state |  | ||||||
| 			case 1: |  | ||||||
| 				return &v.sizeCache |  | ||||||
| 			case 2: |  | ||||||
| 				return &v.unknownFields |  | ||||||
| 			default: |  | ||||||
| 				return nil |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	type x struct{} |  | ||||||
| 	out := protoimpl.TypeBuilder{ |  | ||||||
| 		File: protoimpl.DescBuilder{ |  | ||||||
| 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(), |  | ||||||
| 			RawDescriptor: file_codec_protorpc_envelope_proto_rawDesc, |  | ||||||
| 			NumEnums:      0, |  | ||||||
| 			NumMessages:   2, |  | ||||||
| 			NumExtensions: 0, |  | ||||||
| 			NumServices:   0, |  | ||||||
| 		}, |  | ||||||
| 		GoTypes:           file_codec_protorpc_envelope_proto_goTypes, |  | ||||||
| 		DependencyIndexes: file_codec_protorpc_envelope_proto_depIdxs, |  | ||||||
| 		MessageInfos:      file_codec_protorpc_envelope_proto_msgTypes, |  | ||||||
| 	}.Build() |  | ||||||
| 	File_codec_protorpc_envelope_proto = out.File |  | ||||||
| 	file_codec_protorpc_envelope_proto_rawDesc = nil |  | ||||||
| 	file_codec_protorpc_envelope_proto_goTypes = nil |  | ||||||
| 	file_codec_protorpc_envelope_proto_depIdxs = nil |  | ||||||
| } |  | ||||||
| @@ -1,21 +0,0 @@ | |||||||
| // Code generated by protoc-gen-micro. DO NOT EDIT. |  | ||||||
| // source: codec/protorpc/envelope.proto |  | ||||||
|  |  | ||||||
| package protorpc |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	fmt "fmt" |  | ||||||
| 	proto "github.com/golang/protobuf/proto" |  | ||||||
| 	math "math" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Reference imports to suppress errors if they are not otherwise used. |  | ||||||
| var _ = proto.Marshal |  | ||||||
| var _ = fmt.Errorf |  | ||||||
| var _ = math.Inf |  | ||||||
|  |  | ||||||
| // This is a compile-time assertion to ensure that this generated file |  | ||||||
| // is compatible with the proto package it is being compiled against. |  | ||||||
| // A compilation error at this line likely means your copy of the |  | ||||||
| // proto package needs to be updated. |  | ||||||
| const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package |  | ||||||
| @@ -1,14 +0,0 @@ | |||||||
| syntax = "proto3"; |  | ||||||
|  |  | ||||||
| package protorpc; |  | ||||||
|  |  | ||||||
| message Request { |  | ||||||
| 	string service_method = 1; |  | ||||||
| 	fixed64 seq = 2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| message Response { |  | ||||||
| 	string service_method = 1; |  | ||||||
| 	fixed64 seq = 2; |  | ||||||
| 	string error = 3; |  | ||||||
| } |  | ||||||
| @@ -1,36 +0,0 @@ | |||||||
| package protorpc |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/binary" |  | ||||||
| 	"io" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // WriteNetString writes data to a big-endian netstring on a Writer. |  | ||||||
| // Size is always a 32-bit unsigned int. |  | ||||||
| func WriteNetString(w io.Writer, data []byte) (written int, err error) { |  | ||||||
| 	size := make([]byte, 4) |  | ||||||
| 	binary.BigEndian.PutUint32(size, uint32(len(data))) |  | ||||||
| 	if written, err = w.Write(size); err != nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	return w.Write(data) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ReadNetString reads data from a big-endian netstring. |  | ||||||
| func ReadNetString(r io.Reader) (data []byte, err error) { |  | ||||||
| 	sizeBuf := make([]byte, 4) |  | ||||||
| 	_, err = r.Read(sizeBuf) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	size := binary.BigEndian.Uint32(sizeBuf) |  | ||||||
| 	if size == 0 { |  | ||||||
| 		return nil, nil |  | ||||||
| 	} |  | ||||||
| 	data = make([]byte, size) |  | ||||||
| 	_, err = r.Read(data) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| @@ -1,186 +0,0 @@ | |||||||
| // Protorpc provides a net/rpc proto-rpc codec. See envelope.proto for the format. |  | ||||||
| package protorpc |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"strconv" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"github.com/golang/protobuf/proto" |  | ||||||
| 	"github.com/unistack-org/micro/v3/codec" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type flusher interface { |  | ||||||
| 	Flush() error |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type protoCodec struct { |  | ||||||
| 	sync.Mutex |  | ||||||
| 	rwc io.ReadWriteCloser |  | ||||||
| 	mt  codec.MessageType |  | ||||||
| 	buf *bytes.Buffer |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *protoCodec) Close() error { |  | ||||||
| 	c.buf.Reset() |  | ||||||
| 	return c.rwc.Close() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *protoCodec) String() string { |  | ||||||
| 	return "proto-rpc" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func id(id string) uint64 { |  | ||||||
| 	p, err := strconv.ParseInt(id, 10, 64) |  | ||||||
| 	if err != nil { |  | ||||||
| 		p = 0 |  | ||||||
| 	} |  | ||||||
| 	i := uint64(p) |  | ||||||
| 	return i |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *protoCodec) Write(m *codec.Message, b interface{}) error { |  | ||||||
| 	switch m.Type { |  | ||||||
| 	case codec.Request: |  | ||||||
| 		c.Lock() |  | ||||||
| 		defer c.Unlock() |  | ||||||
| 		// This is protobuf, of course we copy it. |  | ||||||
| 		pbr := &Request{ServiceMethod: m.Method, Seq: id(m.Id)} |  | ||||||
| 		data, err := proto.Marshal(pbr) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		_, err = WriteNetString(c.rwc, data) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		// dont trust or incoming message |  | ||||||
| 		m, ok := b.(proto.Message) |  | ||||||
| 		if !ok { |  | ||||||
| 			return codec.ErrInvalidMessage |  | ||||||
| 		} |  | ||||||
| 		data, err = proto.Marshal(m) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		_, err = WriteNetString(c.rwc, data) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		if flusher, ok := c.rwc.(flusher); ok { |  | ||||||
| 			if err = flusher.Flush(); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	case codec.Response, codec.Error: |  | ||||||
| 		c.Lock() |  | ||||||
| 		defer c.Unlock() |  | ||||||
| 		rtmp := &Response{ServiceMethod: m.Method, Seq: id(m.Id), Error: m.Error} |  | ||||||
| 		data, err := proto.Marshal(rtmp) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		_, err = WriteNetString(c.rwc, data) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		if pb, ok := b.(proto.Message); ok { |  | ||||||
| 			data, err = proto.Marshal(pb) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			data = nil |  | ||||||
| 		} |  | ||||||
| 		_, err = WriteNetString(c.rwc, data) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		if flusher, ok := c.rwc.(flusher); ok { |  | ||||||
| 			if err = flusher.Flush(); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	case codec.Event: |  | ||||||
| 		m, ok := b.(proto.Message) |  | ||||||
| 		if !ok { |  | ||||||
| 			return codec.ErrInvalidMessage |  | ||||||
| 		} |  | ||||||
| 		data, err := proto.Marshal(m) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		c.rwc.Write(data) |  | ||||||
| 	default: |  | ||||||
| 		return fmt.Errorf("Unrecognised message type: %v", m.Type) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *protoCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error { |  | ||||||
| 	c.buf.Reset() |  | ||||||
| 	c.mt = mt |  | ||||||
|  |  | ||||||
| 	switch mt { |  | ||||||
| 	case codec.Request: |  | ||||||
| 		data, err := ReadNetString(c.rwc) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		rtmp := new(Request) |  | ||||||
| 		err = proto.Unmarshal(data, rtmp) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		m.Method = rtmp.GetServiceMethod() |  | ||||||
| 		m.Id = fmt.Sprintf("%d", rtmp.GetSeq()) |  | ||||||
| 	case codec.Response: |  | ||||||
| 		data, err := ReadNetString(c.rwc) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		rtmp := new(Response) |  | ||||||
| 		err = proto.Unmarshal(data, rtmp) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		m.Method = rtmp.GetServiceMethod() |  | ||||||
| 		m.Id = fmt.Sprintf("%d", rtmp.GetSeq()) |  | ||||||
| 		m.Error = rtmp.GetError() |  | ||||||
| 	case codec.Event: |  | ||||||
| 		_, err := io.Copy(c.buf, c.rwc) |  | ||||||
| 		return err |  | ||||||
| 	default: |  | ||||||
| 		return fmt.Errorf("Unrecognised message type: %v", mt) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *protoCodec) ReadBody(b interface{}) error { |  | ||||||
| 	var data []byte |  | ||||||
| 	switch c.mt { |  | ||||||
| 	case codec.Request, codec.Response: |  | ||||||
| 		var err error |  | ||||||
| 		data, err = ReadNetString(c.rwc) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	case codec.Event: |  | ||||||
| 		data = c.buf.Bytes() |  | ||||||
| 	default: |  | ||||||
| 		return fmt.Errorf("Unrecognised message type: %v", c.mt) |  | ||||||
| 	} |  | ||||||
| 	if b != nil { |  | ||||||
| 		return proto.Unmarshal(data, b.(proto.Message)) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewCodec(rwc io.ReadWriteCloser) codec.Codec { |  | ||||||
| 	return &protoCodec{ |  | ||||||
| 		buf: bytes.NewBuffer(nil), |  | ||||||
| 		rwc: rwc, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,80 +0,0 @@ | |||||||
| // Package text reads any text/* content-type |  | ||||||
| package text |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"io/ioutil" |  | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/codec" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type Codec struct { |  | ||||||
| 	Conn io.ReadWriteCloser |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Frame gives us the ability to define raw data to send over the pipes |  | ||||||
| type Frame struct { |  | ||||||
| 	Data []byte |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) ReadHeader(m *codec.Message, t codec.MessageType) error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) ReadBody(b interface{}) error { |  | ||||||
| 	// read bytes |  | ||||||
| 	buf, err := ioutil.ReadAll(c.Conn) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	switch v := b.(type) { |  | ||||||
| 	case *string: |  | ||||||
| 		*v = string(buf) |  | ||||||
| 	case *[]byte: |  | ||||||
| 		*v = buf |  | ||||||
| 	case *Frame: |  | ||||||
| 		v.Data = buf |  | ||||||
| 	default: |  | ||||||
| 		return fmt.Errorf("failed to read body: %v is not type of *[]byte", b) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) Write(m *codec.Message, b interface{}) error { |  | ||||||
| 	var v []byte |  | ||||||
| 	switch ve := b.(type) { |  | ||||||
| 	case nil: |  | ||||||
| 		return nil |  | ||||||
| 	case *Frame: |  | ||||||
| 		v = ve.Data |  | ||||||
| 	case *[]byte: |  | ||||||
| 		v = *ve |  | ||||||
| 	case *string: |  | ||||||
| 		v = []byte(*ve) |  | ||||||
| 	case string: |  | ||||||
| 		v = []byte(ve) |  | ||||||
| 	case []byte: |  | ||||||
| 		v = ve |  | ||||||
| 	default: |  | ||||||
| 		return fmt.Errorf("failed to write: %v is not type of *[]byte or []byte", b) |  | ||||||
| 	} |  | ||||||
| 	_, err := c.Conn.Write(v) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) Close() error { |  | ||||||
| 	return c.Conn.Close() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Codec) String() string { |  | ||||||
| 	return "text" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewCodec(c io.ReadWriteCloser) codec.Codec { |  | ||||||
| 	return &Codec{ |  | ||||||
| 		Conn: c, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -5,6 +5,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/debug/log" | 	"github.com/unistack-org/micro/v3/debug/log" | ||||||
|  | 	"github.com/unistack-org/micro/v3/metadata" | ||||||
| 	"github.com/unistack-org/micro/v3/util/ring" | 	"github.com/unistack-org/micro/v3/util/ring" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -94,7 +95,7 @@ func (l *memoryLog) Stream() (log.Stream, error) { | |||||||
| 			records <- log.Record{ | 			records <- log.Record{ | ||||||
| 				Timestamp: entry.Timestamp, | 				Timestamp: entry.Timestamp, | ||||||
| 				Message:   entry.Value, | 				Message:   entry.Value, | ||||||
| 				Metadata:  make(map[string]string), | 				Metadata:  metadata.New(0), | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		// now stream continuously | 		// now stream continuously | ||||||
| @@ -102,7 +103,7 @@ func (l *memoryLog) Stream() (log.Stream, error) { | |||||||
| 			records <- log.Record{ | 			records <- log.Record{ | ||||||
| 				Timestamp: entry.Timestamp, | 				Timestamp: entry.Timestamp, | ||||||
| 				Message:   entry.Value, | 				Message:   entry.Value, | ||||||
| 				Metadata:  make(map[string]string), | 				Metadata:  metadata.New(0), | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
|   | |||||||
| @@ -11,20 +11,20 @@ type Profile interface { | |||||||
| } | } | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	DefaultProfile Profile = new(noop) | 	DefaultProfile Profile = &NoopProfile{} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type noop struct{} | type NoopProfile struct{} | ||||||
|  |  | ||||||
| func (p *noop) Start() error { | func (p *NoopProfile) Start() error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (p *noop) Stop() error { | func (p *NoopProfile) Stop() error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (p *noop) String() string { | func (p *NoopProfile) String() string { | ||||||
| 	return "noop" | 	return "noop" | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,90 +0,0 @@ | |||||||
| package memory |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/google/uuid" |  | ||||||
| 	"github.com/unistack-org/micro/v3/debug/trace" |  | ||||||
| 	"github.com/unistack-org/micro/v3/util/ring" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type Tracer struct { |  | ||||||
| 	opts trace.Options |  | ||||||
|  |  | ||||||
| 	// ring buffer of traces |  | ||||||
| 	buffer *ring.Buffer |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *Tracer) Read(opts ...trace.ReadOption) ([]*trace.Span, error) { |  | ||||||
| 	var options trace.ReadOptions |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&options) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	sp := t.buffer.Get(t.buffer.Size()) |  | ||||||
|  |  | ||||||
| 	spans := make([]*trace.Span, 0, len(sp)) |  | ||||||
|  |  | ||||||
| 	for _, span := range sp { |  | ||||||
| 		val := span.Value.(*trace.Span) |  | ||||||
| 		// skip if trace id is specified and doesn't match |  | ||||||
| 		if len(options.Trace) > 0 && val.Trace != options.Trace { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		spans = append(spans, val) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return spans, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *Tracer) Start(ctx context.Context, name string) (context.Context, *trace.Span) { |  | ||||||
| 	span := &trace.Span{ |  | ||||||
| 		Name:     name, |  | ||||||
| 		Trace:    uuid.New().String(), |  | ||||||
| 		Id:       uuid.New().String(), |  | ||||||
| 		Started:  time.Now(), |  | ||||||
| 		Metadata: make(map[string]string), |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// return span if no context |  | ||||||
| 	if ctx == nil { |  | ||||||
| 		return trace.ToContext(context.Background(), span.Trace, span.Id), span |  | ||||||
| 	} |  | ||||||
| 	traceID, parentSpanID, ok := trace.FromContext(ctx) |  | ||||||
| 	// If the trace can not be found in the header, |  | ||||||
| 	// that means this is where the trace is created. |  | ||||||
| 	if !ok { |  | ||||||
| 		return trace.ToContext(ctx, span.Trace, span.Id), span |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// set trace id |  | ||||||
| 	span.Trace = traceID |  | ||||||
| 	// set parent |  | ||||||
| 	span.Parent = parentSpanID |  | ||||||
|  |  | ||||||
| 	// return the span |  | ||||||
| 	return trace.ToContext(ctx, span.Trace, span.Id), span |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *Tracer) Finish(s *trace.Span) error { |  | ||||||
| 	// set finished time |  | ||||||
| 	s.Duration = time.Since(s.Started) |  | ||||||
| 	// save the span |  | ||||||
| 	t.buffer.Put(s) |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewTracer(opts ...trace.Option) trace.Tracer { |  | ||||||
| 	var options trace.Options |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&options) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &Tracer{ |  | ||||||
| 		opts: options, |  | ||||||
| 		// the last 256 requests |  | ||||||
| 		buffer: ring.New(256), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,99 +0,0 @@ | |||||||
| // Package trace provides an interface for distributed tracing |  | ||||||
| package trace |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/metadata" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Tracer is an interface for distributed tracing |  | ||||||
| type Tracer interface { |  | ||||||
| 	// Start a trace |  | ||||||
| 	Start(ctx context.Context, name string) (context.Context, *Span) |  | ||||||
| 	// Finish the trace |  | ||||||
| 	Finish(*Span) error |  | ||||||
| 	// Read the traces |  | ||||||
| 	Read(...ReadOption) ([]*Span, error) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // SpanType describe the nature of the trace span |  | ||||||
| type SpanType int |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	// SpanTypeRequestInbound is a span created when serving a request |  | ||||||
| 	SpanTypeRequestInbound SpanType = iota |  | ||||||
| 	// SpanTypeRequestOutbound is a span created when making a service call |  | ||||||
| 	SpanTypeRequestOutbound |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Span is used to record an entry |  | ||||||
| type Span struct { |  | ||||||
| 	// Id of the trace |  | ||||||
| 	Trace string |  | ||||||
| 	// name of the span |  | ||||||
| 	Name string |  | ||||||
| 	// id of the span |  | ||||||
| 	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 |  | ||||||
| func FromContext(ctx context.Context) (traceID string, parentSpanID string, isFound bool) { |  | ||||||
| 	traceID, traceOk := metadata.Get(ctx, traceIDKey) |  | ||||||
| 	microID, microOk := metadata.Get(ctx, "Micro-Id") |  | ||||||
| 	if !traceOk && !microOk { |  | ||||||
| 		isFound = false |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if !traceOk { |  | ||||||
| 		traceID = microID |  | ||||||
| 	} |  | ||||||
| 	parentSpanID, ok := metadata.Get(ctx, spanIDKey) |  | ||||||
| 	return traceID, parentSpanID, ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 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, |  | ||||||
| 	}, true) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	DefaultTracer Tracer = new(noop) |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| 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 |  | ||||||
| } |  | ||||||
| @@ -8,6 +8,22 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	ErrBadRequest          = &Error{Code: 400} | ||||||
|  | 	ErrUnauthorized        = &Error{Code: 401} | ||||||
|  | 	ErrForbidden           = &Error{Code: 403} | ||||||
|  | 	ErrNotFound            = &Error{Code: 404} | ||||||
|  | 	ErrMethodNotAllowed    = &Error{Code: 405} | ||||||
|  | 	ErrTimeout             = &Error{Code: 408} | ||||||
|  | 	ErrConflict            = &Error{Code: 409} | ||||||
|  | 	ErrInternalServerError = &Error{Code: 500} | ||||||
|  | 	ErNotImplemented       = &Error{Code: 501} | ||||||
|  | 	ErrBadGateway          = &Error{Code: 502} | ||||||
|  | 	ErrServiceUnavailable  = &Error{Code: 503} | ||||||
|  | 	ErrGatewayTimeout      = &Error{Code: 504} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Error tpye | ||||||
| type Error struct { | type Error struct { | ||||||
| 	Id     string | 	Id     string | ||||||
| 	Code   int32 | 	Code   int32 | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| package events | package events | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"time" | 	"time" | ||||||
| @@ -16,14 +17,14 @@ var ( | |||||||
|  |  | ||||||
| // Stream of events | // Stream of events | ||||||
| type Stream interface { | type Stream interface { | ||||||
| 	Publish(topic string, msg interface{}, opts ...PublishOption) error | 	Publish(ctx context.Context, topic string, msg interface{}, opts ...PublishOption) error | ||||||
| 	Subscribe(topic string, opts ...SubscribeOption) (<-chan Event, error) | 	Subscribe(ctx context.Context, topic string, opts ...SubscribeOption) (<-chan Event, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Store of events | // Store of events | ||||||
| type Store interface { | type Store interface { | ||||||
| 	Read(opts ...ReadOption) ([]*Event, error) | 	Read(ctx context.Context, opts ...ReadOption) ([]*Event, error) | ||||||
| 	Write(event *Event, opts ...WriteOption) error | 	Write(ctx context.Context, event *Event, opts ...WriteOption) 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 | ||||||
|   | |||||||
| @@ -8,14 +8,13 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	rmemory "github.com/unistack-org/micro-registry-memory" | 	rmemory "github.com/unistack-org/micro-registry-memory" | ||||||
| 	"github.com/unistack-org/micro/v3/util/test" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestFunction(t *testing.T) { | func TestFunction(t *testing.T) { | ||||||
| 	var wg sync.WaitGroup | 	var wg sync.WaitGroup | ||||||
| 	wg.Add(1) | 	wg.Add(1) | ||||||
|  |  | ||||||
| 	r := rmemory.NewRegistry(rmemory.Services(test.Data)) | 	r := rmemory.NewRegistry() | ||||||
|  |  | ||||||
| 	// create service | 	// create service | ||||||
| 	fn := NewFunction( | 	fn := NewFunction( | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,33 +1,33 @@ | |||||||
| module github.com/unistack-org/micro/v3 | module github.com/unistack-org/micro/v3 | ||||||
|  |  | ||||||
| go 1.15 | go 1.14 | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	github.com/BurntSushi/toml v0.3.1 | 	github.com/BurntSushi/toml v0.3.1 | ||||||
| 	github.com/caddyserver/certmagic v0.10.6 | 	github.com/caddyserver/certmagic v0.10.6 | ||||||
| 	github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect |  | ||||||
| 	github.com/dgrijalva/jwt-go v3.2.0+incompatible | 	github.com/dgrijalva/jwt-go v3.2.0+incompatible | ||||||
| 	github.com/ef-ds/deque v1.0.4-0.20190904040645-54cb57c252a1 | 	github.com/ef-ds/deque v1.0.4-0.20190904040645-54cb57c252a1 | ||||||
| 	github.com/evanphx/json-patch/v5 v5.0.0 | 	github.com/evanphx/json-patch/v5 v5.1.0 | ||||||
| 	github.com/ghodss/yaml v1.0.0 | 	github.com/ghodss/yaml v1.0.0 | ||||||
| 	github.com/go-acme/lego/v3 v3.4.0 | 	github.com/go-acme/lego/v3 v3.4.0 | ||||||
| 	github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee | 	github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee | ||||||
| 	github.com/gobwas/ws v1.0.3 | 	github.com/gobwas/ws v1.0.3 | ||||||
| 	github.com/golang/protobuf v1.4.2 | 	github.com/golang/protobuf v1.4.3 | ||||||
| 	github.com/google/uuid v1.1.1 | 	github.com/google/uuid v1.1.2 | ||||||
| 	github.com/hashicorp/hcl v1.0.0 | 	github.com/hashicorp/hcl v1.0.0 | ||||||
| 	github.com/kr/text v0.2.0 // indirect |  | ||||||
| 	github.com/micro/cli/v2 v2.1.2 | 	github.com/micro/cli/v2 v2.1.2 | ||||||
| 	github.com/miekg/dns v1.1.27 | 	github.com/miekg/dns v1.1.31 | ||||||
| 	github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect |  | ||||||
| 	github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c | 	github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c | ||||||
| 	github.com/patrickmn/go-cache v2.1.0+incompatible | 	github.com/patrickmn/go-cache v2.1.0+incompatible | ||||||
| 	github.com/stretchr/testify v1.5.1 | 	github.com/stretchr/testify v1.5.1 | ||||||
| 	github.com/unistack-org/micro-codec-bytes v0.0.0-20200827104921-3616a69473a6 | 	github.com/unistack-org/micro-codec-bytes v0.0.0-20200828083432-4e49e953d844 | ||||||
| 	github.com/unistack-org/micro-config-cmd v0.0.0-20200828075439-d859b9d7265b | 	github.com/unistack-org/micro-codec-json v0.0.0-20201102222734-a29c895ec05c | ||||||
| 	golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 | 	github.com/unistack-org/micro-codec-jsonrpc v0.0.0-20201102222451-ff6a69988bcd | ||||||
| 	golang.org/x/net v0.0.0-20200707034311-ab3426394381 | 	github.com/unistack-org/micro-codec-proto v0.0.0-20201102222202-769c2d6a4b92 | ||||||
| 	google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 | 	github.com/unistack-org/micro-codec-protorpc v0.0.0-20201102222610-3a343898c077 | ||||||
|  | 	github.com/unistack-org/micro-config-cmd v0.0.0-20201028144621-5a55f1aad70a | ||||||
|  | 	golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a | ||||||
|  | 	golang.org/x/net v0.0.0-20200904194848-62affa334b73 | ||||||
|  | 	google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d | ||||||
| 	google.golang.org/protobuf v1.25.0 | 	google.golang.org/protobuf v1.25.0 | ||||||
| 	gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect |  | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								go.sum
									
									
									
									
									
								
							| @@ -75,6 +75,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m | |||||||
| github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | ||||||
| github.com/evanphx/json-patch/v5 v5.0.0 h1:dKTrUeykyQwKb/kx7Z+4ukDs6l+4L41HqG1XHnhX7WE= | github.com/evanphx/json-patch/v5 v5.0.0 h1:dKTrUeykyQwKb/kx7Z+4ukDs6l+4L41HqG1XHnhX7WE= | ||||||
| github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= | github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= | ||||||
|  | github.com/evanphx/json-patch/v5 v5.1.0 h1:B0aXl1o/1cP8NbviYiBMkcHBtUjIJ1/Ccg6b+SwCLQg= | ||||||
|  | github.com/evanphx/json-patch/v5 v5.1.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= | ||||||
| github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= | github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= | ||||||
| github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= | ||||||
| github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= | ||||||
| @@ -119,6 +121,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq | |||||||
| github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= | ||||||
| github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= | ||||||
| github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||||
|  | github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= | ||||||
|  | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||||
| github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | ||||||
| github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||||
| github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= | github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= | ||||||
| @@ -139,6 +143,8 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI | |||||||
| github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= | ||||||
| github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= | ||||||
| github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||||
|  | github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= | ||||||
|  | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||||
| github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= | ||||||
| github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | ||||||
| github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= | github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= | ||||||
| @@ -197,6 +203,8 @@ github.com/micro/cli/v2 v2.1.2/go.mod h1:EguNh6DAoWKm9nmk+k/Rg0H3lQnDxqzu5x5srOt | |||||||
| github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= | github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= | ||||||
| github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= | github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= | ||||||
| github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= | github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= | ||||||
|  | github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo= | ||||||
|  | github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= | ||||||
| github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= | ||||||
| github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | ||||||
| github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= | github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= | ||||||
| @@ -275,11 +283,31 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 | |||||||
| github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= | github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= | ||||||
| github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY= | github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY= | ||||||
| github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= | github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= | ||||||
| github.com/unistack-org/micro-codec-bytes v0.0.0-20200827104921-3616a69473a6 h1:bBPX47ly/xhLhuCMIvybXKevCOCZCmxzvDwR61zu3cQ= |  | ||||||
| github.com/unistack-org/micro-codec-bytes v0.0.0-20200827104921-3616a69473a6/go.mod h1:g5sOI8TWgGZiVHe8zoUPdtz7+0oLnqTnfBoai6Qb7jE= | github.com/unistack-org/micro-codec-bytes v0.0.0-20200827104921-3616a69473a6/go.mod h1:g5sOI8TWgGZiVHe8zoUPdtz7+0oLnqTnfBoai6Qb7jE= | ||||||
| github.com/unistack-org/micro-config-cmd v0.0.0-20200828075439-d859b9d7265b h1:v5Ak+Sr780jZclFDnx82g5biF0N5HRVKphEpJhbnVUs= | github.com/unistack-org/micro-codec-bytes v0.0.0-20200828083432-4e49e953d844 h1:5b1yuSllbsMm/9fUIlIXSr8DbsKT/sAKSCgOx6+SAfI= | ||||||
|  | github.com/unistack-org/micro-codec-bytes v0.0.0-20200828083432-4e49e953d844/go.mod h1:g5sOI8TWgGZiVHe8zoUPdtz7+0oLnqTnfBoai6Qb7jE= | ||||||
|  | github.com/unistack-org/micro-codec-json v0.0.0-20201102222734-a29c895ec05c h1:RtcNaK8rQSl7xAoy1W437dvZLCVjSC6e4JcolepSQs0= | ||||||
|  | github.com/unistack-org/micro-codec-json v0.0.0-20201102222734-a29c895ec05c/go.mod h1:dG5aUyhBv+ebOl/UFW2Aj2GTfVxxXWi6AcynpePOAhQ= | ||||||
|  | github.com/unistack-org/micro-codec-jsonrpc v0.0.0-20201102222451-ff6a69988bcd h1:qXSiEfVnCgrwTHYvAnEPSHEai3+5EUH9ZYovLpxGDwg= | ||||||
|  | github.com/unistack-org/micro-codec-jsonrpc v0.0.0-20201102222451-ff6a69988bcd/go.mod h1:PFyvkGhavl+3tEPgOaLAhoJJX4/webVGW59BSOXDfNM= | ||||||
|  | github.com/unistack-org/micro-codec-proto v0.0.0-20201102222202-769c2d6a4b92 h1:1rPDBu7Nwo3ZL6r6H5rj7qNchHSdBF4zcewAeTUEMC4= | ||||||
|  | github.com/unistack-org/micro-codec-proto v0.0.0-20201102222202-769c2d6a4b92/go.mod h1:31JMo683bBQ+uN9YufpUU6ESHphyx3DFmTXEnjpJV9Y= | ||||||
|  | github.com/unistack-org/micro-codec-protorpc v0.0.0-20201102222610-3a343898c077 h1:uK7owL8TPSwoQiDM1V/0swmgCEepSQKXoi8GEnGxtlU= | ||||||
|  | github.com/unistack-org/micro-codec-protorpc v0.0.0-20201102222610-3a343898c077/go.mod h1:Ct4uAVZaDEyBZj9Q0poDkbzu6zKXUCcSqJkv/MWPpeI= | ||||||
| github.com/unistack-org/micro-config-cmd v0.0.0-20200828075439-d859b9d7265b/go.mod h1:6pm1cadbwsFcEW1ZbV5Fp0i3goR3TNfROMNSPih3I8k= | github.com/unistack-org/micro-config-cmd v0.0.0-20200828075439-d859b9d7265b/go.mod h1:6pm1cadbwsFcEW1ZbV5Fp0i3goR3TNfROMNSPih3I8k= | ||||||
|  | github.com/unistack-org/micro-config-cmd v0.0.0-20200909210346-ec89783dc46c h1:GbcjxyOyA9tnNoe4FcnzzLDa8JwEBnQKN/7Bhd8t47I= | ||||||
|  | github.com/unistack-org/micro-config-cmd v0.0.0-20200909210346-ec89783dc46c/go.mod h1:6pm1cadbwsFcEW1ZbV5Fp0i3goR3TNfROMNSPih3I8k= | ||||||
|  | github.com/unistack-org/micro-config-cmd v0.0.0-20200909210755-6e7e85eeab34 h1:VHc98t4SoiCF/jbkFu2e/j+IyJ/+MFQ1T+INNL7LubU= | ||||||
|  | github.com/unistack-org/micro-config-cmd v0.0.0-20200909210755-6e7e85eeab34/go.mod h1:fT1gYn+TtfVZZ5tNx56bZIncJjmlji66g7GKdWua5hE= | ||||||
|  | github.com/unistack-org/micro-config-cmd v0.0.0-20200920140133-0853deb2e5dc h1:hHAU3rgeiA0LaudfNdMLf9/jkOBeFxvJdnwXevviZF8= | ||||||
|  | github.com/unistack-org/micro-config-cmd v0.0.0-20200920140133-0853deb2e5dc/go.mod h1:il8nz4ZEcX3Usyfrtwy+YtQcb7xSUSFJdSe8PBJ9gOA= | ||||||
|  | github.com/unistack-org/micro-config-cmd v0.0.0-20201028144621-5a55f1aad70a h1:VjlqP1qZkjC0Chmx5MKFPIbtSCigeICFDf8vaLZGh9o= | ||||||
|  | github.com/unistack-org/micro-config-cmd v0.0.0-20201028144621-5a55f1aad70a/go.mod h1:MzMg+qh1wORZwYtg5AVgFkNFrXVVbdPKW7s/Is+A994= | ||||||
| github.com/unistack-org/micro/v3 v3.0.0-20200827083227-aa99378adc6e/go.mod h1:rPQbnry3nboAnMczj8B1Gzlcyv/HYoMZLgd3/3nttJ4= | github.com/unistack-org/micro/v3 v3.0.0-20200827083227-aa99378adc6e/go.mod h1:rPQbnry3nboAnMczj8B1Gzlcyv/HYoMZLgd3/3nttJ4= | ||||||
|  | github.com/unistack-org/micro/v3 v3.0.0-gamma/go.mod h1:iEtpu3wTYCRs3pQ3VsFEO7JBO4lOMpkOwMyrpZyIDPo= | ||||||
|  | github.com/unistack-org/micro/v3 v3.0.0-gamma.0.20200909210629-caec730248b1/go.mod h1:mmqHR9WelHUXqg2mELjsQ+FJHcWs6mNmXg+wEYO2T3c= | ||||||
|  | github.com/unistack-org/micro/v3 v3.0.0-gamma.0.20200920135754-1cbd1d2bad83/go.mod h1:HUzMG4Mcy97958VxWTg8zuazZgwQ/aoLZ8wtBVONwRE= | ||||||
|  | github.com/unistack-org/micro/v3 v3.0.0-gamma.0.20200922103357-4c4fa00a5d94/go.mod h1:aL+8VhSXpx0SuEeXPOWUo5BgS7kyvWYobeXFay90UUM= | ||||||
| github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= | ||||||
| github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= | github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= | ||||||
| github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= | ||||||
| @@ -303,6 +331,8 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh | |||||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
| golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= | golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= | ||||||
| golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
|  | golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= | ||||||
|  | golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||||
| golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||||
| golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | ||||||
| @@ -345,6 +375,8 @@ golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLL | |||||||
| golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
| golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= | golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= | ||||||
| golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||||
|  | golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= | ||||||
|  | golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||||
| golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= | ||||||
| @@ -405,6 +437,7 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn | |||||||
| golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
| golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
| golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||||
|  | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361 h1:RIIXAeV6GvDBuADKumTODatUqANFZ+5BPMnzsy4hulY= | ||||||
| golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
| @@ -432,6 +465,8 @@ google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBr | |||||||
| google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||||
| google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= | ||||||
| google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= | ||||||
|  | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d h1:92D1fum1bJLKSdr11OJ+54YeCMCGYIygTA7R/YZxH5M= | ||||||
|  | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||||
| google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= | ||||||
| google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||||
| google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||||
| @@ -448,6 +483,7 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi | |||||||
| google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||||
| google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||||
| google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||||
|  | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= | ||||||
| google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= | google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= | ||||||
| google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= | ||||||
| gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= | ||||||
|   | |||||||
| @@ -4,11 +4,13 @@ import "context" | |||||||
|  |  | ||||||
| type loggerKey struct{} | type loggerKey struct{} | ||||||
|  |  | ||||||
|  | // FromContext returns logger from passed context | ||||||
| func FromContext(ctx context.Context) (Logger, bool) { | func FromContext(ctx context.Context) (Logger, bool) { | ||||||
| 	l, ok := ctx.Value(loggerKey{}).(Logger) | 	l, ok := ctx.Value(loggerKey{}).(Logger) | ||||||
| 	return l, ok | 	return l, ok | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // NewContext stores logger into passed context | ||||||
| func NewContext(ctx context.Context, l Logger) context.Context { | func NewContext(ctx context.Context, l Logger) context.Context { | ||||||
| 	return context.WithValue(ctx, loggerKey{}, l) | 	return context.WithValue(ctx, loggerKey{}, l) | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										114
									
								
								logger/helper.go
									
									
									
									
									
								
							
							
						
						
									
										114
									
								
								logger/helper.go
									
									
									
									
									
								
							| @@ -1,114 +0,0 @@ | |||||||
| package logger |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"os" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type Helper struct { |  | ||||||
| 	Logger |  | ||||||
| 	fields map[string]interface{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewHelper(log Logger) *Helper { |  | ||||||
| 	return &Helper{Logger: log} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) Info(args ...interface{}) { |  | ||||||
| 	if !h.Logger.Options().Level.Enabled(InfoLevel) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	h.Logger.Fields(h.fields).Log(InfoLevel, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) Infof(template string, args ...interface{}) { |  | ||||||
| 	if !h.Logger.Options().Level.Enabled(InfoLevel) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	h.Logger.Fields(h.fields).Logf(InfoLevel, template, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) Trace(args ...interface{}) { |  | ||||||
| 	if !h.Logger.Options().Level.Enabled(TraceLevel) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	h.Logger.Fields(h.fields).Log(TraceLevel, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) Tracef(template string, args ...interface{}) { |  | ||||||
| 	if !h.Logger.Options().Level.Enabled(TraceLevel) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	h.Logger.Fields(h.fields).Logf(TraceLevel, template, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) Debug(args ...interface{}) { |  | ||||||
| 	if !h.Logger.Options().Level.Enabled(DebugLevel) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	h.Logger.Fields(h.fields).Log(DebugLevel, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) Debugf(template string, args ...interface{}) { |  | ||||||
| 	if !h.Logger.Options().Level.Enabled(DebugLevel) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	h.Logger.Fields(h.fields).Logf(DebugLevel, template, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) Warn(args ...interface{}) { |  | ||||||
| 	if !h.Logger.Options().Level.Enabled(WarnLevel) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	h.Logger.Fields(h.fields).Log(WarnLevel, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) Warnf(template string, args ...interface{}) { |  | ||||||
| 	if !h.Logger.Options().Level.Enabled(WarnLevel) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	h.Logger.Fields(h.fields).Logf(WarnLevel, template, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) Error(args ...interface{}) { |  | ||||||
| 	if !h.Logger.Options().Level.Enabled(ErrorLevel) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	h.Logger.Fields(h.fields).Log(ErrorLevel, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) Errorf(template string, args ...interface{}) { |  | ||||||
| 	if !h.Logger.Options().Level.Enabled(ErrorLevel) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	h.Logger.Fields(h.fields).Logf(ErrorLevel, template, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) Fatal(args ...interface{}) { |  | ||||||
| 	if !h.Logger.Options().Level.Enabled(FatalLevel) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	h.Logger.Fields(h.fields).Log(FatalLevel, args...) |  | ||||||
| 	os.Exit(1) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) Fatalf(template string, args ...interface{}) { |  | ||||||
| 	if !h.Logger.Options().Level.Enabled(FatalLevel) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	h.Logger.Fields(h.fields).Logf(FatalLevel, template, args...) |  | ||||||
| 	os.Exit(1) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) WithError(err error) *Helper { |  | ||||||
| 	fields := copyFields(h.fields) |  | ||||||
| 	fields["error"] = err |  | ||||||
| 	return &Helper{Logger: h.Logger, fields: fields} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (h *Helper) WithFields(fields map[string]interface{}) *Helper { |  | ||||||
| 	nfields := copyFields(fields) |  | ||||||
| 	for k, v := range h.fields { |  | ||||||
| 		nfields[k] = v |  | ||||||
| 	} |  | ||||||
| 	return &Helper{Logger: h.Logger, fields: nfields} |  | ||||||
| } |  | ||||||
| @@ -2,7 +2,6 @@ package logger | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Level int8 | type Level int8 | ||||||
| @@ -19,7 +18,7 @@ const ( | |||||||
| 	WarnLevel | 	WarnLevel | ||||||
| 	// ErrorLevel level. Logs. Used for errors that should definitely be noted. | 	// ErrorLevel level. Logs. Used for errors that should definitely be noted. | ||||||
| 	ErrorLevel | 	ErrorLevel | ||||||
| 	// FatalLevel level. Logs and then calls `logger.Exit(1)`. highest level of severity. | 	// FatalLevel level. Logs and then calls `os.Exit(1)`. highest level of severity. | ||||||
| 	FatalLevel | 	FatalLevel | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -63,64 +62,5 @@ func GetLevel(levelStr string) (Level, error) { | |||||||
| 	case FatalLevel.String(): | 	case FatalLevel.String(): | ||||||
| 		return FatalLevel, nil | 		return FatalLevel, nil | ||||||
| 	} | 	} | ||||||
| 	return InfoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to InfoLevel", levelStr) | 	return InfoLevel, fmt.Errorf("unknown Level String: '%s', use InfoLevel", levelStr) | ||||||
| } |  | ||||||
|  |  | ||||||
| func Info(args ...interface{}) { |  | ||||||
| 	DefaultLogger.Log(InfoLevel, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Infof(template string, args ...interface{}) { |  | ||||||
| 	DefaultLogger.Logf(InfoLevel, template, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Trace(args ...interface{}) { |  | ||||||
| 	DefaultLogger.Log(TraceLevel, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Tracef(template string, args ...interface{}) { |  | ||||||
| 	DefaultLogger.Logf(TraceLevel, template, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Debug(args ...interface{}) { |  | ||||||
| 	DefaultLogger.Log(DebugLevel, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Debugf(template string, args ...interface{}) { |  | ||||||
| 	DefaultLogger.Logf(DebugLevel, template, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Warn(args ...interface{}) { |  | ||||||
| 	DefaultLogger.Log(WarnLevel, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Warnf(template string, args ...interface{}) { |  | ||||||
| 	DefaultLogger.Logf(WarnLevel, template, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Error(args ...interface{}) { |  | ||||||
| 	DefaultLogger.Log(ErrorLevel, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Errorf(template string, args ...interface{}) { |  | ||||||
| 	DefaultLogger.Logf(ErrorLevel, template, args...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Fatal(args ...interface{}) { |  | ||||||
| 	DefaultLogger.Log(FatalLevel, args...) |  | ||||||
| 	os.Exit(1) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Fatalf(template string, args ...interface{}) { |  | ||||||
| 	DefaultLogger.Logf(FatalLevel, template, args...) |  | ||||||
| 	os.Exit(1) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 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 { |  | ||||||
| 		l = log |  | ||||||
| 	} |  | ||||||
| 	return l.Options().Level <= lvl |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										102
									
								
								logger/logger.go
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								logger/logger.go
									
									
									
									
									
								
							| @@ -1,45 +1,107 @@ | |||||||
| // Package log provides a log interface | // Package logger provides a log interface | ||||||
| package logger | package logger | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	// Default logger | 	// DefaultLogger variable | ||||||
| 	DefaultLogger Logger = NewHelper(NewLogger()) | 	DefaultLogger Logger = NewLogger() | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Logger is a generic logging interface | // Logger is a generic logging interface | ||||||
| type Logger interface { | type Logger interface { | ||||||
| 	// Init initialises options | 	// Init initialises options | ||||||
| 	Init(options ...Option) error | 	Init(opts ...Option) error | ||||||
| 	// V compare provided verbosity level with current log level | 	// V compare provided verbosity level with current log level | ||||||
| 	V(level Level) bool | 	V(level Level) bool | ||||||
| 	// The Logger options | 	// The Logger options | ||||||
| 	Options() Options | 	Options() Options | ||||||
| 	// Fields set fields to always be logged | 	// Fields set fields to always be logged | ||||||
| 	Fields(fields map[string]interface{}) Logger | 	Fields(fields map[string]interface{}) Logger | ||||||
| 	// Log writes a log entry | 	// Info level message | ||||||
| 	Log(level Level, v ...interface{}) | 	Info(args ...interface{}) | ||||||
| 	// Logf writes a formatted log entry | 	// Trace level message | ||||||
| 	Logf(level Level, format string, v ...interface{}) | 	Trace(args ...interface{}) | ||||||
|  | 	// Debug level message | ||||||
|  | 	Debug(args ...interface{}) | ||||||
|  | 	// Warn level message | ||||||
|  | 	Warn(args ...interface{}) | ||||||
|  | 	// Error level message | ||||||
|  | 	Error(args ...interface{}) | ||||||
|  | 	// Fatal level message | ||||||
|  | 	Fatal(args ...interface{}) | ||||||
|  | 	// Infof level message | ||||||
|  | 	Infof(msg string, args ...interface{}) | ||||||
|  | 	// Tracef level message | ||||||
|  | 	Tracef(msg string, args ...interface{}) | ||||||
|  | 	// Debug level message | ||||||
|  | 	Debugf(msg string, args ...interface{}) | ||||||
|  | 	// Warn level message | ||||||
|  | 	Warnf(msg string, args ...interface{}) | ||||||
|  | 	// Error level message | ||||||
|  | 	Errorf(msg string, args ...interface{}) | ||||||
|  | 	// Fatal level message | ||||||
|  | 	Fatalf(msg string, args ...interface{}) | ||||||
| 	// String returns the name of logger | 	// String returns the name of logger | ||||||
| 	String() string | 	String() string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func Info(args ...interface{}) { | ||||||
|  | 	DefaultLogger.Info(args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Error(args ...interface{}) { | ||||||
|  | 	DefaultLogger.Error(args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Debug(args ...interface{}) { | ||||||
|  | 	DefaultLogger.Debug(args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Warn(args ...interface{}) { | ||||||
|  | 	DefaultLogger.Warn(args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Trace(args ...interface{}) { | ||||||
|  | 	DefaultLogger.Trace(args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Fatal(args ...interface{}) { | ||||||
|  | 	DefaultLogger.Fatal(args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Infof(msg string, args ...interface{}) { | ||||||
|  | 	DefaultLogger.Infof(msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Errorf(msg string, args ...interface{}) { | ||||||
|  | 	DefaultLogger.Errorf(msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Debugf(msg string, args ...interface{}) { | ||||||
|  | 	DefaultLogger.Debugf(msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Warnf(msg string, args ...interface{}) { | ||||||
|  | 	DefaultLogger.Warnf(msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Tracef(msg string, args ...interface{}) { | ||||||
|  | 	DefaultLogger.Tracef(msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Fatalf(msg string, args ...interface{}) { | ||||||
|  | 	DefaultLogger.Fatalf(msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func V(level Level) bool { | ||||||
|  | 	return DefaultLogger.V(level) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Init initialize logger | ||||||
| func Init(opts ...Option) error { | func Init(opts ...Option) error { | ||||||
| 	return DefaultLogger.Init(opts...) | 	return DefaultLogger.Init(opts...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Fields create logger with specific fields | ||||||
| func Fields(fields map[string]interface{}) Logger { | func Fields(fields map[string]interface{}) Logger { | ||||||
| 	return DefaultLogger.Fields(fields) | 	return DefaultLogger.Fields(fields) | ||||||
| } | } | ||||||
|  |  | ||||||
| func Log(level Level, v ...interface{}) { |  | ||||||
| 	DefaultLogger.Log(level, v...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Logf(level Level, format string, v ...interface{}) { |  | ||||||
| 	DefaultLogger.Logf(level, format, v...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func String() string { |  | ||||||
| 	return DefaultLogger.String() |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -6,13 +6,11 @@ import ( | |||||||
|  |  | ||||||
| func TestLogger(t *testing.T) { | func TestLogger(t *testing.T) { | ||||||
| 	l := NewLogger(WithLevel(TraceLevel)) | 	l := NewLogger(WithLevel(TraceLevel)) | ||||||
| 	h1 := NewHelper(l).WithFields(map[string]interface{}{"key1": "val1"}) | 	if err := l.Init(); err != nil { | ||||||
| 	h1.Trace("trace_msg1") | 		t.Fatal(err) | ||||||
| 	h1.Warn("warn_msg1") | 	} | ||||||
|  | 	l.Trace("trace_msg1") | ||||||
| 	h2 := NewHelper(l).WithFields(map[string]interface{}{"key2": "val2"}) | 	l.Warn("warn_msg1") | ||||||
| 	h2.Trace("trace_msg2") | 	l.Fields(map[string]interface{}{"error": "test"}).Info("error message") | ||||||
| 	h2.Warn("warn_msg2") | 	l.Warn("first", " ", "second") | ||||||
|  |  | ||||||
| 	l.Fields(map[string]interface{}{"key3": "val4"}).Log(InfoLevel, "test_msg") |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,16 +1,13 @@ | |||||||
| package logger | package logger | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
| 	"runtime" | 	"runtime" | ||||||
| 	"sort" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| 
 |  | ||||||
| 	dlog "github.com/unistack-org/micro/v3/debug/log" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
| @@ -19,31 +16,33 @@ func init() { | |||||||
| 		lvl = InfoLevel | 		lvl = InfoLevel | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	DefaultLogger = NewHelper(NewLogger(WithLevel(lvl))) | 	DefaultLogger = NewLogger(WithLevel(lvl)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type defaultLogger struct { | type defaultLogger struct { | ||||||
| 	sync.RWMutex | 	sync.RWMutex | ||||||
| 	opts Options | 	opts Options | ||||||
|  | 	enc  *json.Encoder | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Init(opts...) should only overwrite provided options | // Init(opts...) should only overwrite provided options | ||||||
| func (l *defaultLogger) Init(opts ...Option) error { | func (l *defaultLogger) Init(opts ...Option) error { | ||||||
|  | 	l.Lock() | ||||||
|  | 	defer l.Unlock() | ||||||
|  | 
 | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&l.opts) | 		o(&l.opts) | ||||||
| 	} | 	} | ||||||
|  | 	l.enc = json.NewEncoder(l.opts.Out) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (l *defaultLogger) String() string { | func (l *defaultLogger) String() string { | ||||||
| 	return "default" | 	return "micro" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (l *defaultLogger) V(level Level) bool { | func (l *defaultLogger) V(level Level) bool { | ||||||
| 	if l.opts.Level.Enabled(level) { | 	return l.opts.Level.Enabled(level) | ||||||
| 		return true |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (l *defaultLogger) Fields(fields map[string]interface{}) Logger { | func (l *defaultLogger) Fields(fields map[string]interface{}) Logger { | ||||||
| @@ -85,45 +84,57 @@ func logCallerfilePath(loggingFilePath string) string { | |||||||
| 	return loggingFilePath[idx+1:] | 	return loggingFilePath[idx+1:] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (l *defaultLogger) Log(level Level, v ...interface{}) { | func (l *defaultLogger) Info(args ...interface{}) { | ||||||
| 	if !l.V(level) { | 	l.log(InfoLevel, args...) | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	l.RLock() |  | ||||||
| 	fields := copyFields(l.opts.Fields) |  | ||||||
| 	l.RUnlock() |  | ||||||
| 
 |  | ||||||
| 	fields["level"] = level.String() |  | ||||||
| 
 |  | ||||||
| 	if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { |  | ||||||
| 		fields["file"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	rec := dlog.Record{ |  | ||||||
| 		Timestamp: time.Now(), |  | ||||||
| 		Message:   fmt.Sprint(v...), |  | ||||||
| 		Metadata:  make(map[string]string, len(fields)), |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	keys := make([]string, 0, len(fields)) |  | ||||||
| 	for k, v := range fields { |  | ||||||
| 		keys = append(keys, k) |  | ||||||
| 		rec.Metadata[k] = fmt.Sprintf("%v", v) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	sort.Strings(keys) |  | ||||||
| 	metadata := "" |  | ||||||
| 
 |  | ||||||
| 	for _, k := range keys { |  | ||||||
| 		metadata += fmt.Sprintf(" %s=%v", k, fields[k]) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	t := rec.Timestamp.Format("2006-01-02 15:04:05") |  | ||||||
| 	fmt.Printf("%s %s %v\n", t, metadata, rec.Message) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (l *defaultLogger) Logf(level Level, format string, v ...interface{}) { | func (l *defaultLogger) Error(args ...interface{}) { | ||||||
|  | 	l.log(ErrorLevel, args...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *defaultLogger) Debug(args ...interface{}) { | ||||||
|  | 	l.log(DebugLevel, args...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *defaultLogger) Warn(args ...interface{}) { | ||||||
|  | 	l.log(WarnLevel, args...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *defaultLogger) Trace(args ...interface{}) { | ||||||
|  | 	l.log(TraceLevel, args...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *defaultLogger) Fatal(args ...interface{}) { | ||||||
|  | 	l.log(FatalLevel, args...) | ||||||
|  | 	os.Exit(1) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *defaultLogger) Infof(msg string, args ...interface{}) { | ||||||
|  | 	l.logf(InfoLevel, msg, args...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *defaultLogger) Errorf(msg string, args ...interface{}) { | ||||||
|  | 	l.logf(ErrorLevel, msg, args...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *defaultLogger) Debugf(msg string, args ...interface{}) { | ||||||
|  | 	l.logf(DebugLevel, msg, args...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *defaultLogger) Warnf(msg string, args ...interface{}) { | ||||||
|  | 	l.logf(WarnLevel, msg, args...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *defaultLogger) Tracef(msg string, args ...interface{}) { | ||||||
|  | 	l.logf(TraceLevel, msg, args...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *defaultLogger) Fatalf(msg string, args ...interface{}) { | ||||||
|  | 	l.logf(FatalLevel, msg, args...) | ||||||
|  | 	os.Exit(1) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *defaultLogger) log(level Level, args ...interface{}) { | ||||||
| 	if !l.V(level) { | 	if !l.V(level) { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -135,30 +146,41 @@ func (l *defaultLogger) Logf(level Level, format string, v ...interface{}) { | |||||||
| 	fields["level"] = level.String() | 	fields["level"] = level.String() | ||||||
| 
 | 
 | ||||||
| 	if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { | 	if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { | ||||||
| 		fields["file"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line) | 		fields["caller"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	rec := dlog.Record{ | 	fields["timestamp"] = time.Now().Format("2006-01-02 15:04:05") | ||||||
| 		Timestamp: time.Now(), | 	fields["msg"] = fmt.Sprint(args...) | ||||||
| 		Message:   fmt.Sprintf(format, v...), | 
 | ||||||
| 		Metadata:  make(map[string]string, len(fields)), | 	l.RLock() | ||||||
|  | 	_ = l.enc.Encode(fields) | ||||||
|  | 	l.RUnlock() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *defaultLogger) logf(level Level, msg string, args ...interface{}) { | ||||||
|  | 	if !l.V(level) { | ||||||
|  | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	keys := make([]string, 0, len(fields)) | 	l.RLock() | ||||||
| 	for k, v := range fields { | 	fields := copyFields(l.opts.Fields) | ||||||
| 		keys = append(keys, k) | 	l.RUnlock() | ||||||
| 		rec.Metadata[k] = fmt.Sprintf("%v", v) | 
 | ||||||
|  | 	fields["level"] = level.String() | ||||||
|  | 
 | ||||||
|  | 	if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { | ||||||
|  | 		fields["caller"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sort.Strings(keys) | 	fields["timestamp"] = time.Now().Format("2006-01-02 15:04:05") | ||||||
| 	metadata := "" | 	if len(args) > 0 { | ||||||
| 
 | 		fields["msg"] = fmt.Sprintf(msg, args...) | ||||||
| 	for _, k := range keys { | 	} else { | ||||||
| 		metadata += fmt.Sprintf(" %s=%v", k, fields[k]) | 		fields["msg"] = msg | ||||||
| 	} | 	} | ||||||
| 
 | 	l.RLock() | ||||||
| 	t := rec.Timestamp.Format("2006-01-02 15:04:05") | 	_ = l.enc.Encode(fields) | ||||||
| 	fmt.Printf("%s %s %v\n", t, metadata, rec.Message) | 	l.RUnlock() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (l *defaultLogger) Options() Options { | func (l *defaultLogger) Options() Options { | ||||||
| @@ -172,19 +194,7 @@ func (l *defaultLogger) Options() Options { | |||||||
| 
 | 
 | ||||||
| // NewLogger builds a new logger based on options | // NewLogger builds a new logger based on options | ||||||
| func NewLogger(opts ...Option) Logger { | func NewLogger(opts ...Option) Logger { | ||||||
| 	// Default options | 	l := &defaultLogger{opts: NewOptions(opts...)} | ||||||
| 	options := Options{ | 	l.enc = json.NewEncoder(l.opts.Out) | ||||||
| 		Level:           InfoLevel, |  | ||||||
| 		Fields:          make(map[string]interface{}), |  | ||||||
| 		Out:             os.Stderr, |  | ||||||
| 		CallerSkipCount: 2, |  | ||||||
| 		Context:         context.Background(), |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	l := &defaultLogger{opts: options} |  | ||||||
| 	if err := l.Init(opts...); err != nil { |  | ||||||
| 		l.Log(FatalLevel, err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return l | 	return l | ||||||
| } | } | ||||||
| @@ -3,6 +3,7 @@ package logger | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"os" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Option func(*Options) | type Option func(*Options) | ||||||
| @@ -20,6 +21,20 @@ type Options struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NewOptions(opts ...Option) Options { | ||||||
|  | 	options := Options{ | ||||||
|  | 		Level:           InfoLevel, | ||||||
|  | 		Fields:          make(map[string]interface{}), | ||||||
|  | 		Out:             os.Stderr, | ||||||
|  | 		CallerSkipCount: 2, | ||||||
|  | 		Context:         context.Background(), | ||||||
|  | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  |  | ||||||
| // WithFields set default fields for the logger | // WithFields set default fields for the logger | ||||||
| func WithFields(fields map[string]interface{}) Option { | func WithFields(fields map[string]interface{}) Option { | ||||||
| 	return func(args *Options) { | 	return func(args *Options) { | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ package metadata | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"strings" | 	"net/textproto" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type metadataKey struct{} | type metadataKey struct{} | ||||||
| @@ -13,54 +13,63 @@ type metadataKey struct{} | |||||||
| // from Transport headers. | // from Transport headers. | ||||||
| type Metadata map[string]string | type Metadata map[string]string | ||||||
|  |  | ||||||
| func (md Metadata) Get(key string) (string, bool) { | var ( | ||||||
| 	// attempt to get as is | 	// DefaultMetadataSize used when need to init new Metadata | ||||||
| 	val, ok := md[key] | 	DefaultMetadataSize = 6 | ||||||
| 	if ok { | ) | ||||||
| 		return val, ok |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// attempt to get lower case | // Get returns value from metadata by key | ||||||
| 	val, ok = md[strings.Title(key)] | func (md Metadata) Get(key string) (string, bool) { | ||||||
|  | 	// fast path | ||||||
|  | 	val, ok := md[key] | ||||||
|  | 	if !ok { | ||||||
|  | 		// slow path | ||||||
|  | 		val, ok = md[textproto.CanonicalMIMEHeaderKey(key)] | ||||||
|  | 	} | ||||||
| 	return val, ok | 	return val, ok | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Set is used to store value in metadata | ||||||
| func (md Metadata) Set(key, val string) { | func (md Metadata) Set(key, val string) { | ||||||
| 	md[key] = val | 	md[textproto.CanonicalMIMEHeaderKey(key)] = val | ||||||
| } | } | ||||||
|  |  | ||||||
| func (md Metadata) Delete(key string) { | // Del is used to remove value from metadata | ||||||
| 	// delete key as-is | func (md Metadata) Del(key string) { | ||||||
| 	delete(md, key) | 	// fast path | ||||||
| 	// delete also Title key | 	if _, ok := md[key]; ok { | ||||||
| 	delete(md, strings.Title(key)) | 		delete(md, key) | ||||||
|  | 	} else { | ||||||
|  | 		// slow path | ||||||
|  | 		delete(md, textproto.CanonicalMIMEHeaderKey(key)) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Copy makes a copy of the metadata | // Copy makes a copy of the metadata | ||||||
| func Copy(md Metadata) Metadata { | func Copy(md Metadata) Metadata { | ||||||
| 	cmd := make(Metadata, len(md)) | 	nmd := New(len(md)) | ||||||
| 	for k, v := range md { | 	for key, val := range md { | ||||||
| 		cmd[k] = v | 		nmd.Set(key, val) | ||||||
| 	} | 	} | ||||||
| 	return cmd | 	return nmd | ||||||
| } | } | ||||||
|  |  | ||||||
| // Delete key from metadata | func Del(ctx context.Context, key string) context.Context { | ||||||
| func Delete(ctx context.Context, k string) context.Context { | 	md, ok := FromContext(ctx) | ||||||
| 	return Set(ctx, k, "") | 	if !ok { | ||||||
|  | 		md = New(0) | ||||||
|  | 	} | ||||||
|  | 	md.Del(key) | ||||||
|  | 	return context.WithValue(ctx, metadataKey{}, md) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Set add key with val to metadata | // Set add key with val to metadata | ||||||
| func Set(ctx context.Context, k, v string) context.Context { | func Set(ctx context.Context, key, val string) context.Context { | ||||||
| 	md, ok := FromContext(ctx) | 	md, ok := FromContext(ctx) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		md = make(Metadata) | 		md = New(0) | ||||||
| 	} |  | ||||||
| 	if v == "" { |  | ||||||
| 		delete(md, k) |  | ||||||
| 	} else { |  | ||||||
| 		md[k] = v |  | ||||||
| 	} | 	} | ||||||
|  | 	md.Set(key, val) | ||||||
| 	return context.WithValue(ctx, metadataKey{}, md) | 	return context.WithValue(ctx, metadataKey{}, md) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -70,57 +79,56 @@ func Get(ctx context.Context, key string) (string, bool) { | |||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return "", ok | 		return "", ok | ||||||
| 	} | 	} | ||||||
| 	// attempt to get as is | 	return md.Get(key) | ||||||
| 	val, ok := md[key] |  | ||||||
| 	if ok { |  | ||||||
| 		return val, ok |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// attempt to get lower case |  | ||||||
| 	val, ok = md[strings.Title(key)] |  | ||||||
|  |  | ||||||
| 	return val, ok |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // FromContext returns metadata from the given context | // FromContext returns metadata from the given context | ||||||
| func FromContext(ctx context.Context) (Metadata, bool) { | func FromContext(ctx context.Context) (Metadata, bool) { | ||||||
|  | 	if ctx == nil { | ||||||
|  | 		return nil, false | ||||||
|  | 	} | ||||||
| 	md, ok := ctx.Value(metadataKey{}).(Metadata) | 	md, ok := ctx.Value(metadataKey{}).(Metadata) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return nil, ok | 		return nil, ok | ||||||
| 	} | 	} | ||||||
|  | 	nmd := Copy(md) | ||||||
|  | 	return nmd, ok | ||||||
|  | } | ||||||
|  |  | ||||||
| 	// capitalise all values | // New return new sized metadata | ||||||
| 	newMD := make(Metadata, len(md)) | func New(size int) Metadata { | ||||||
| 	for k, v := range md { | 	if size == 0 { | ||||||
| 		newMD[strings.Title(k)] = v | 		size = DefaultMetadataSize | ||||||
| 	} | 	} | ||||||
|  | 	return make(Metadata, size) | ||||||
| 	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 { | func NewContext(ctx context.Context, md Metadata) context.Context { | ||||||
| 	return context.WithValue(ctx, metadataKey{}, md) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // MergeContext merges metadata to existing metadata, overwriting if specified |  | ||||||
| func MergeContext(ctx context.Context, patchMd Metadata, overwrite bool) context.Context { |  | ||||||
| 	if ctx == nil { | 	if ctx == nil { | ||||||
| 		ctx = context.Background() | 		ctx = context.Background() | ||||||
| 	} | 	} | ||||||
| 	md, _ := ctx.Value(metadataKey{}).(Metadata) | 	return context.WithValue(ctx, metadataKey{}, Copy(md)) | ||||||
| 	cmd := make(Metadata, len(md)) | } | ||||||
| 	for k, v := range md { |  | ||||||
| 		cmd[k] = v | // MergeContext merges metadata to existing metadata, overwriting if specified | ||||||
|  | func MergeContext(ctx context.Context, pmd Metadata, overwrite bool) context.Context { | ||||||
|  | 	if ctx == nil { | ||||||
|  | 		ctx = context.Background() | ||||||
| 	} | 	} | ||||||
| 	for k, v := range patchMd { | 	md, ok := FromContext(ctx) | ||||||
| 		if _, ok := cmd[k]; ok && !overwrite { | 	if !ok { | ||||||
|  | 		return context.WithValue(ctx, metadataKey{}, Copy(pmd)) | ||||||
|  | 	} | ||||||
|  | 	nmd := Copy(md) | ||||||
|  | 	for key, val := range pmd { | ||||||
|  | 		if _, ok := nmd[key]; ok && !overwrite { | ||||||
| 			// skip | 			// skip | ||||||
| 		} else if v != "" { | 		} else if val != "" { | ||||||
| 			cmd[k] = v | 			nmd.Set(key, val) | ||||||
| 		} else { | 		} else { | ||||||
| 			delete(cmd, k) | 			nmd.Del(key) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return context.WithValue(ctx, metadataKey{}, cmd) | 	return context.WithValue(ctx, metadataKey{}, nmd) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,6 +6,30 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | func TestMedataCanonicalKey(t *testing.T) { | ||||||
|  | 	ctx := Set(context.TODO(), "x-request-id", "12345") | ||||||
|  | 	v, ok := Get(ctx, "x-request-id") | ||||||
|  | 	if !ok { | ||||||
|  | 		t.Fatalf("failed to get x-request-id") | ||||||
|  | 	} else if v != "12345" { | ||||||
|  | 		t.Fatalf("invalid metadata value: %s != %s", "12345", v) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	v, ok = Get(ctx, "X-Request-Id") | ||||||
|  | 	if !ok { | ||||||
|  | 		t.Fatalf("failed to get x-request-id") | ||||||
|  | 	} else if v != "12345" { | ||||||
|  | 		t.Fatalf("invalid metadata value: %s != %s", "12345", v) | ||||||
|  | 	} | ||||||
|  | 	v, ok = Get(ctx, "X-Request-ID") | ||||||
|  | 	if !ok { | ||||||
|  | 		t.Fatalf("failed to get x-request-id") | ||||||
|  | 	} else if v != "12345" { | ||||||
|  | 		t.Fatalf("invalid metadata value: %s != %s", "12345", v) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestMetadataSet(t *testing.T) { | func TestMetadataSet(t *testing.T) { | ||||||
| 	ctx := Set(context.TODO(), "Key", "val") | 	ctx := Set(context.TODO(), "Key", "val") | ||||||
|  |  | ||||||
| @@ -25,7 +49,7 @@ func TestMetadataDelete(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ctx := NewContext(context.TODO(), md) | 	ctx := NewContext(context.TODO(), md) | ||||||
| 	ctx = Delete(ctx, "Baz") | 	ctx = Del(ctx, "Baz") | ||||||
|  |  | ||||||
| 	emd, ok := FromContext(ctx) | 	emd, ok := FromContext(ctx) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| @@ -39,10 +63,19 @@ func TestMetadataDelete(t *testing.T) { | |||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestNilContext(t *testing.T) { | ||||||
|  | 	var ctx context.Context | ||||||
|  |  | ||||||
|  | 	_, ok := FromContext(ctx) | ||||||
|  | 	if ok { | ||||||
|  | 		t.Fatal("nil context") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestMetadataCopy(t *testing.T) { | func TestMetadataCopy(t *testing.T) { | ||||||
| 	md := Metadata{ | 	md := Metadata{ | ||||||
| 		"Foo": "bar", | 		"Foo": "bar", | ||||||
| 		"bar": "baz", | 		"Bar": "baz", | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	cp := Copy(md) | 	cp := Copy(md) | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ import "time" | |||||||
| type Tags map[string]string | type Tags map[string]string | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	Defaultreporter Reporter | 	DefaultReporter Reporter | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Reporter is an interface for collecting and instrumenting metrics | // Reporter is an interface for collecting and instrumenting metrics | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								micro.go
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								micro.go
									
									
									
									
									
								
							| @@ -4,6 +4,7 @@ package micro | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  |  | ||||||
|  | 	"github.com/unistack-org/micro/v3/broker" | ||||||
| 	"github.com/unistack-org/micro/v3/client" | 	"github.com/unistack-org/micro/v3/client" | ||||||
| 	"github.com/unistack-org/micro/v3/server" | 	"github.com/unistack-org/micro/v3/server" | ||||||
| ) | ) | ||||||
| @@ -17,13 +18,15 @@ type Service interface { | |||||||
| 	// The service name | 	// The service name | ||||||
| 	Name() string | 	Name() string | ||||||
| 	// Init initialises options | 	// Init initialises options | ||||||
| 	Init(...Option) | 	Init(...Option) error | ||||||
| 	// Options returns the current options | 	// Options returns the current options | ||||||
| 	Options() Options | 	Options() Options | ||||||
| 	// Client is used to call services | 	// Client is used to call services | ||||||
| 	Client() client.Client | 	Client() client.Client | ||||||
| 	// Server is for handling requests and events | 	// Server is for handling requests and events | ||||||
| 	Server() server.Server | 	Server() server.Server | ||||||
|  | 	// Broker is for broker usage | ||||||
|  | 	Broker() broker.Broker | ||||||
| 	// Run the service | 	// Run the service | ||||||
| 	Run() error | 	Run() error | ||||||
| 	// The service implementation | 	// The service implementation | ||||||
| @@ -68,10 +71,8 @@ type Event interface { | |||||||
| 	Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error | 	Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error | ||||||
| } | } | ||||||
|  |  | ||||||
| // Type alias to satisfy the deprecation |  | ||||||
| type Publisher = Event |  | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
|  | 	// HeaderPrefix for all headers passed | ||||||
| 	HeaderPrefix = "Micro-" | 	HeaderPrefix = "Micro-" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -101,11 +102,6 @@ func NewEvent(topic string, c client.Client) Event { | |||||||
| 	return &event{c, topic} | 	return &event{c, topic} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Deprecated: NewPublisher returns a new Publisher |  | ||||||
| func NewPublisher(topic string, c client.Client) Event { |  | ||||||
| 	return NewEvent(topic, c) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 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 { | func RegisterHandler(s server.Server, h interface{}, opts ...server.HandlerOption) error { | ||||||
| 	return s.Handle(s.NewHandler(h, opts...)) | 	return s.Handle(s.NewHandler(h, opts...)) | ||||||
|   | |||||||
| @@ -3,11 +3,12 @@ package network | |||||||
| import ( | import ( | ||||||
| 	"github.com/google/uuid" | 	"github.com/google/uuid" | ||||||
| 	"github.com/unistack-org/micro/v3/logger" | 	"github.com/unistack-org/micro/v3/logger" | ||||||
|  | 	"github.com/unistack-org/micro/v3/network/tunnel" | ||||||
| 	"github.com/unistack-org/micro/v3/proxy" | 	"github.com/unistack-org/micro/v3/proxy" | ||||||
| 	"github.com/unistack-org/micro/v3/router" | 	"github.com/unistack-org/micro/v3/router" | ||||||
| 	"github.com/unistack-org/micro/v3/tunnel" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // Option func | ||||||
| type Option func(*Options) | type Option func(*Options) | ||||||
|  |  | ||||||
| // Options configure network | // Options configure network | ||||||
|   | |||||||
							
								
								
									
										76
									
								
								network/transport/noop.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								network/transport/noop.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | |||||||
|  | package transport | ||||||
|  |  | ||||||
|  | import "context" | ||||||
|  |  | ||||||
|  | type noopTransport struct { | ||||||
|  | 	opts Options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewTransport(opts ...Option) Transport { | ||||||
|  | 	return &noopTransport{opts: NewOptions(opts...)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *noopTransport) Init(opts ...Option) error { | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&t.opts) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *noopTransport) Options() Options { | ||||||
|  | 	return t.opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *noopTransport) Dial(ctx context.Context, addr string, opts ...DialOption) (Client, error) { | ||||||
|  | 	options := NewDialOptions(opts...) | ||||||
|  | 	return &noopClient{opts: options}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *noopTransport) Listen(ctx context.Context, addr string, opts ...ListenOption) (Listener, error) { | ||||||
|  | 	options := NewListenOptions(opts...) | ||||||
|  | 	return &noopListener{opts: options}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *noopTransport) String() string { | ||||||
|  | 	return "noop" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type noopClient struct { | ||||||
|  | 	opts DialOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *noopClient) Close() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *noopClient) Local() string { | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *noopClient) Remote() string { | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *noopClient) Recv(*Message) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *noopClient) Send(*Message) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type noopListener struct { | ||||||
|  | 	opts ListenOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (l *noopListener) Addr() string { | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (l *noopListener) Accept(fn func(Socket)) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (l *noopListener) Close() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @@ -31,6 +31,21 @@ type Options struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // NewOptions returns new options | ||||||
|  | func NewOptions(opts ...Option) Options { | ||||||
|  | 	options := Options{ | ||||||
|  | 		Logger:  logger.DefaultLogger, | ||||||
|  | 		Context: context.Background(), | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DialOptions struct | ||||||
| type DialOptions struct { | type DialOptions struct { | ||||||
| 	// Tells the transport this is a streaming connection with | 	// Tells the transport this is a streaming connection with | ||||||
| 	// multiple calls to send/recv and that send may not even be called | 	// multiple calls to send/recv and that send may not even be called | ||||||
| @@ -46,6 +61,21 @@ type DialOptions struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // NewDialOptions returns new DialOptions | ||||||
|  | func NewDialOptions(opts ...DialOption) DialOptions { | ||||||
|  | 	options := DialOptions{ | ||||||
|  | 		Timeout: DefaultDialTimeout, | ||||||
|  | 		Context: context.Background(), | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ListenOptions struct | ||||||
| type ListenOptions struct { | type ListenOptions struct { | ||||||
| 	// TODO: add tls options when listening | 	// TODO: add tls options when listening | ||||||
| 	// Currently set in global options | 	// Currently set in global options | ||||||
| @@ -55,6 +85,19 @@ type ListenOptions struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // NewListenOptions returns new ListenOptions | ||||||
|  | func NewListenOptions(opts ...ListenOption) ListenOptions { | ||||||
|  | 	options := ListenOptions{ | ||||||
|  | 		Context: context.Background(), | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Addrs to use for transport | // Addrs to use for transport | ||||||
| func Addrs(addrs ...string) Option { | func Addrs(addrs ...string) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| @@ -69,6 +112,13 @@ func Logger(l logger.Logger) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Context sets the context | ||||||
|  | func Context(ctx context.Context) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.Context = ctx | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Codec sets the codec used for encoding where the transport | // Codec sets the codec used for encoding where the transport | ||||||
| // does not support message headers | // does not support message headers | ||||||
| func Codec(c codec.Marshaler) Option { | func Codec(c codec.Marshaler) Option { | ||||||
| @@ -99,14 +149,14 @@ func TLSConfig(t *tls.Config) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Indicates whether this is a streaming connection | // WithStream indicates whether this is a streaming connection | ||||||
| func WithStream() DialOption { | func WithStream() DialOption { | ||||||
| 	return func(o *DialOptions) { | 	return func(o *DialOptions) { | ||||||
| 		o.Stream = true | 		o.Stream = true | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Timeout used when dialling the remote side | // WithTimeout used when dialling the remote side | ||||||
| func WithTimeout(d time.Duration) DialOption { | func WithTimeout(d time.Duration) DialOption { | ||||||
| 	return func(o *DialOptions) { | 	return func(o *DialOptions) { | ||||||
| 		o.Timeout = d | 		o.Timeout = d | ||||||
| @@ -2,11 +2,15 @@ | |||||||
| package transport | package transport | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	DefaultTransport Transport | 	// DefaultTransport is the global default transport | ||||||
|  | 	DefaultTransport Transport = NewTransport() | ||||||
|  | 	// Default dial timeout | ||||||
|  | 	DefaultDialTimeout = time.Second * 5 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Transport is an interface which is used for communication between | // Transport is an interface which is used for communication between | ||||||
| @@ -15,8 +19,8 @@ var ( | |||||||
| type Transport interface { | type Transport interface { | ||||||
| 	Init(...Option) error | 	Init(...Option) error | ||||||
| 	Options() Options | 	Options() Options | ||||||
| 	Dial(addr string, opts ...DialOption) (Client, error) | 	Dial(ctx context.Context, addr string, opts ...DialOption) (Client, error) | ||||||
| 	Listen(addr string, opts ...ListenOption) (Listener, error) | 	Listen(ctx context.Context, addr string, opts ...ListenOption) (Listener, error) | ||||||
| 	String() string | 	String() string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -55,8 +59,3 @@ type DialOption func(*DialOptions) | |||||||
| 
 | 
 | ||||||
| // ListenOption is the option signature | // ListenOption is the option signature | ||||||
| type ListenOption func(*ListenOptions) | type ListenOption func(*ListenOptions) | ||||||
| 
 |  | ||||||
| var ( |  | ||||||
| 	// Default dial timeout |  | ||||||
| 	DefaultDialTimeout = time.Second * 5 |  | ||||||
| ) |  | ||||||
| @@ -7,8 +7,8 @@ import ( | |||||||
| 
 | 
 | ||||||
| 	"github.com/unistack-org/micro/v3/broker" | 	"github.com/unistack-org/micro/v3/broker" | ||||||
| 	"github.com/unistack-org/micro/v3/logger" | 	"github.com/unistack-org/micro/v3/logger" | ||||||
| 	"github.com/unistack-org/micro/v3/transport" | 	"github.com/unistack-org/micro/v3/network/transport" | ||||||
| 	"github.com/unistack-org/micro/v3/tunnel" | 	"github.com/unistack-org/micro/v3/network/tunnel" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type tunBroker struct { | type tunBroker struct { | ||||||
| @@ -49,18 +49,18 @@ func (t *tunBroker) Address() string { | |||||||
| 	return t.tunnel.Address() | 	return t.tunnel.Address() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *tunBroker) Connect() error { | func (t *tunBroker) Connect(ctx context.Context) error { | ||||||
| 	return t.tunnel.Connect() | 	return t.tunnel.Connect(ctx) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *tunBroker) Disconnect() error { | func (t *tunBroker) Disconnect(ctx context.Context) error { | ||||||
| 	return t.tunnel.Close() | 	return t.tunnel.Close(ctx) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *tunBroker) Publish(topic string, m *broker.Message, opts ...broker.PublishOption) error { | func (t *tunBroker) Publish(ctx context.Context, topic string, m *broker.Message, opts ...broker.PublishOption) error { | ||||||
| 	// TODO: this is probably inefficient, we might want to just maintain an open connection | 	// TODO: this is probably inefficient, we might want to just maintain an open connection | ||||||
| 	// it may be easier to add broadcast to the tunnel | 	// it may be easier to add broadcast to the tunnel | ||||||
| 	c, err := t.tunnel.Dial(topic, tunnel.DialMode(tunnel.Multicast)) | 	c, err := t.tunnel.Dial(ctx, topic, tunnel.DialMode(tunnel.Multicast)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -72,21 +72,16 @@ func (t *tunBroker) Publish(topic string, m *broker.Message, opts ...broker.Publ | |||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *tunBroker) Subscribe(topic string, h broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { | func (t *tunBroker) Subscribe(ctx context.Context, topic string, h broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { | ||||||
| 	l, err := t.tunnel.Listen(topic, tunnel.ListenMode(tunnel.Multicast)) | 	l, err := t.tunnel.Listen(ctx, topic, tunnel.ListenMode(tunnel.Multicast)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var options broker.SubscribeOptions |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&options) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	tunSub := &tunSubscriber{ | 	tunSub := &tunSubscriber{ | ||||||
| 		topic:    topic, | 		topic:    topic, | ||||||
| 		handler:  h, | 		handler:  h, | ||||||
| 		opts:     options, | 		opts:     broker.NewSubscribeOptions(opts...), | ||||||
| 		closed:   make(chan bool), | 		closed:   make(chan bool), | ||||||
| 		listener: l, | 		listener: l, | ||||||
| 	} | 	} | ||||||
| @@ -117,9 +112,13 @@ func (t *tunSubscriber) run() { | |||||||
| 		// receive message | 		// receive message | ||||||
| 		m := new(transport.Message) | 		m := new(transport.Message) | ||||||
| 		if err := c.Recv(m); err != nil { | 		if err := c.Recv(m); err != nil { | ||||||
| 			logger.Error(err) | 			if logger.V(logger.ErrorLevel) { | ||||||
|  | 				logger.Error(err.Error()) | ||||||
|  | 			} | ||||||
| 			if err = c.Close(); err != nil { | 			if err = c.Close(); err != nil { | ||||||
| 				logger.Error(err) | 				if logger.V(logger.ErrorLevel) { | ||||||
|  | 					logger.Error(err.Error()) | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| @@ -146,7 +145,7 @@ func (t *tunSubscriber) Topic() string { | |||||||
| 	return t.topic | 	return t.topic | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *tunSubscriber) Unsubscribe() error { | func (t *tunSubscriber) Unsubscribe(ctx context.Context) error { | ||||||
| 	select { | 	select { | ||||||
| 	case <-t.closed: | 	case <-t.closed: | ||||||
| 		return nil | 		return nil | ||||||
| @@ -173,12 +172,7 @@ func (t *tunEvent) Error() error { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewBroker(opts ...broker.Option) (broker.Broker, error) { | func NewBroker(opts ...broker.Option) (broker.Broker, error) { | ||||||
| 	options := broker.Options{ | 	options := broker.NewOptions(opts...) | ||||||
| 		Context: context.Background(), |  | ||||||
| 	} |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&options) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	t, ok := options.Context.Value(tunnelKey{}).(tunnel.Tunnel) | 	t, ok := options.Context.Value(tunnelKey{}).(tunnel.Tunnel) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| @@ -5,7 +5,7 @@ import ( | |||||||
| 
 | 
 | ||||||
| 	"github.com/google/uuid" | 	"github.com/google/uuid" | ||||||
| 	"github.com/unistack-org/micro/v3/logger" | 	"github.com/unistack-org/micro/v3/logger" | ||||||
| 	"github.com/unistack-org/micro/v3/transport" | 	"github.com/unistack-org/micro/v3/network/transport" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| @@ -15,6 +15,7 @@ var ( | |||||||
| 	DefaultToken = "go.micro.tunnel" | 	DefaultToken = "go.micro.tunnel" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // Option func | ||||||
| type Option func(*Options) | type Option func(*Options) | ||||||
| 
 | 
 | ||||||
| // Options provides network configuration options | // Options provides network configuration options | ||||||
| @@ -33,8 +34,10 @@ type Options struct { | |||||||
| 	Logger logger.Logger | 	Logger logger.Logger | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // DialOption func | ||||||
| type DialOption func(*DialOptions) | type DialOption func(*DialOptions) | ||||||
| 
 | 
 | ||||||
|  | // DialOptions provides dial options | ||||||
| type DialOptions struct { | type DialOptions struct { | ||||||
| 	// Link specifies the link to use | 	// Link specifies the link to use | ||||||
| 	Link string | 	Link string | ||||||
| @@ -46,8 +49,10 @@ type DialOptions struct { | |||||||
| 	Timeout time.Duration | 	Timeout time.Duration | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ListenOption func | ||||||
| type ListenOption func(*ListenOptions) | type ListenOption func(*ListenOptions) | ||||||
| 
 | 
 | ||||||
|  | // ListenOptions provides listen options | ||||||
| type ListenOptions struct { | type ListenOptions struct { | ||||||
| 	// specify mode of the session | 	// specify mode of the session | ||||||
| 	Mode Mode | 	Mode Mode | ||||||
| @@ -55,7 +60,7 @@ type ListenOptions struct { | |||||||
| 	Timeout time.Duration | 	Timeout time.Duration | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // The tunnel id | // Id sets the tunnel id | ||||||
| func Id(id string) Option { | func Id(id string) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Id = id | 		o.Id = id | ||||||
| @@ -69,7 +74,7 @@ func Logger(l logger.Logger) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // The tunnel address | // Address sets the tunnel address | ||||||
| func Address(a string) Option { | func Address(a string) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Address = a | 		o.Address = a | ||||||
| @@ -97,23 +102,21 @@ func Transport(t transport.Transport) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Listen options | // ListenMode option | ||||||
| func ListenMode(m Mode) ListenOption { | func ListenMode(m Mode) ListenOption { | ||||||
| 	return func(o *ListenOptions) { | 	return func(o *ListenOptions) { | ||||||
| 		o.Mode = m | 		o.Mode = m | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Timeout for reads and writes on the listener session | // ListenTimeout for reads and writes on the listener session | ||||||
| func ListenTimeout(t time.Duration) ListenOption { | func ListenTimeout(t time.Duration) ListenOption { | ||||||
| 	return func(o *ListenOptions) { | 	return func(o *ListenOptions) { | ||||||
| 		o.Timeout = t | 		o.Timeout = t | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Dial options | // DialMode multicast sets the multicast option to send only to those mapped | ||||||
| 
 |  | ||||||
| // Dial multicast sets the multicast option to send only to those mapped |  | ||||||
| func DialMode(m Mode) DialOption { | func DialMode(m Mode) DialOption { | ||||||
| 	return func(o *DialOptions) { | 	return func(o *DialOptions) { | ||||||
| 		o.Mode = m | 		o.Mode = m | ||||||
| @@ -144,10 +147,14 @@ func DialWait(b bool) DialOption { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // DefaultOptions returns router default options | // DefaultOptions returns router default options | ||||||
| func DefaultOptions() Options { | func NewOptions(opts ...Option) Options { | ||||||
| 	return Options{ | 	options := Options{ | ||||||
| 		Id:      uuid.New().String(), | 		Id:      uuid.New().String(), | ||||||
| 		Address: DefaultAddress, | 		Address: DefaultAddress, | ||||||
| 		Token:   DefaultToken, | 		Token:   DefaultToken, | ||||||
| 	} | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
| } | } | ||||||
| @@ -1,8 +1,8 @@ | |||||||
| package transport | package transport | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/unistack-org/micro/v3/transport" | 	"github.com/unistack-org/micro/v3/network/transport" | ||||||
| 	"github.com/unistack-org/micro/v3/tunnel" | 	"github.com/unistack-org/micro/v3/network/tunnel" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type tunListener struct { | type tunListener struct { | ||||||
| @@ -5,8 +5,8 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 
 | 
 | ||||||
| 	"github.com/unistack-org/micro/v3/transport" | 	"github.com/unistack-org/micro/v3/network/transport" | ||||||
| 	"github.com/unistack-org/micro/v3/tunnel" | 	"github.com/unistack-org/micro/v3/network/tunnel" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type tunTransport struct { | type tunTransport struct { | ||||||
| @@ -26,7 +26,7 @@ func (t *tunTransport) Init(opts ...transport.Option) error { | |||||||
| 
 | 
 | ||||||
| 	// close the existing tunnel | 	// close the existing tunnel | ||||||
| 	if t.tunnel != nil { | 	if t.tunnel != nil { | ||||||
| 		t.tunnel.Close() | 		t.tunnel.Close(context.TODO()) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// get the tunnel | 	// get the tunnel | ||||||
| @@ -47,12 +47,12 @@ func (t *tunTransport) Init(opts ...transport.Option) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *tunTransport) Dial(addr string, opts ...transport.DialOption) (transport.Client, error) { | func (t *tunTransport) Dial(ctx context.Context, addr string, opts ...transport.DialOption) (transport.Client, error) { | ||||||
| 	if err := t.tunnel.Connect(); err != nil { | 	if err := t.tunnel.Connect(ctx); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	c, err := t.tunnel.Dial(addr) | 	c, err := t.tunnel.Dial(ctx, addr) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -60,12 +60,12 @@ func (t *tunTransport) Dial(addr string, opts ...transport.DialOption) (transpor | |||||||
| 	return c, nil | 	return c, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *tunTransport) Listen(addr string, opts ...transport.ListenOption) (transport.Listener, error) { | func (t *tunTransport) Listen(ctx context.Context, addr string, opts ...transport.ListenOption) (transport.Listener, error) { | ||||||
| 	if err := t.tunnel.Connect(); err != nil { | 	if err := t.tunnel.Connect(ctx); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	l, err := t.tunnel.Listen(addr) | 	l, err := t.tunnel.Listen(ctx, addr) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -93,7 +93,7 @@ func NewTransport(opts ...transport.Option) transport.Transport { | |||||||
| 	return t | 	return t | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // WithTransport sets the internal tunnel | // WithTunnel sets the internal tunnel | ||||||
| func WithTunnel(t tunnel.Tunnel) transport.Option { | func WithTunnel(t tunnel.Tunnel) transport.Option { | ||||||
| 	return func(o *transport.Options) { | 	return func(o *transport.Options) { | ||||||
| 		if o.Context == nil { | 		if o.Context == nil { | ||||||
| @@ -2,10 +2,11 @@ | |||||||
| package tunnel | package tunnel | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/unistack-org/micro/v3/transport" | 	"github.com/unistack-org/micro/v3/network/transport" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| @@ -55,15 +56,15 @@ type Tunnel interface { | |||||||
| 	// Address returns the address the tunnel is listening on | 	// Address returns the address the tunnel is listening on | ||||||
| 	Address() string | 	Address() string | ||||||
| 	// Connect connects the tunnel | 	// Connect connects the tunnel | ||||||
| 	Connect() error | 	Connect(ctx context.Context) error | ||||||
| 	// Close closes the tunnel | 	// Close closes the tunnel | ||||||
| 	Close() error | 	Close(ctx context.Context) error | ||||||
| 	// Links returns all the links the tunnel is connected to | 	// Links returns all the links the tunnel is connected to | ||||||
| 	Links() []Link | 	Links() []Link | ||||||
| 	// Dial allows a client to connect to a channel | 	// Dial allows a client to connect to a channel | ||||||
| 	Dial(channel string, opts ...DialOption) (Session, error) | 	Dial(ctx context.Context, channel string, opts ...DialOption) (Session, error) | ||||||
| 	// Listen allows to accept connections on a channel | 	// Listen allows to accept connections on a channel | ||||||
| 	Listen(channel string, opts ...ListenOption) (Listener, error) | 	Listen(ctx context.Context, channel string, opts ...ListenOption) (Listener, error) | ||||||
| 	// String returns the name of the tunnel implementation | 	// String returns the name of the tunnel implementation | ||||||
| 	String() string | 	String() string | ||||||
| } | } | ||||||
							
								
								
									
										29
									
								
								options.go
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								options.go
									
									
									
									
									
								
							| @@ -11,15 +11,15 @@ import ( | |||||||
| 	"github.com/unistack-org/micro/v3/client" | 	"github.com/unistack-org/micro/v3/client" | ||||||
| 	"github.com/unistack-org/micro/v3/config" | 	"github.com/unistack-org/micro/v3/config" | ||||||
| 	"github.com/unistack-org/micro/v3/debug/profile" | 	"github.com/unistack-org/micro/v3/debug/profile" | ||||||
| 	"github.com/unistack-org/micro/v3/debug/trace" |  | ||||||
| 	"github.com/unistack-org/micro/v3/logger" | 	"github.com/unistack-org/micro/v3/logger" | ||||||
|  | 	"github.com/unistack-org/micro/v3/network/transport" | ||||||
| 	"github.com/unistack-org/micro/v3/registry" | 	"github.com/unistack-org/micro/v3/registry" | ||||||
| 	"github.com/unistack-org/micro/v3/router" | 	"github.com/unistack-org/micro/v3/router" | ||||||
| 	"github.com/unistack-org/micro/v3/runtime" | 	"github.com/unistack-org/micro/v3/runtime" | ||||||
| 	"github.com/unistack-org/micro/v3/selector" | 	"github.com/unistack-org/micro/v3/selector" | ||||||
| 	"github.com/unistack-org/micro/v3/server" | 	"github.com/unistack-org/micro/v3/server" | ||||||
| 	"github.com/unistack-org/micro/v3/store" | 	"github.com/unistack-org/micro/v3/store" | ||||||
| 	"github.com/unistack-org/micro/v3/transport" | 	"github.com/unistack-org/micro/v3/tracer" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Options for micro service | // Options for micro service | ||||||
| @@ -47,14 +47,12 @@ type Options struct { | |||||||
| 	// Other options for implementations of the interface | 	// Other options for implementations of the interface | ||||||
| 	// can be stored in a context | 	// can be stored in a context | ||||||
| 	Context context.Context | 	Context context.Context | ||||||
|  |  | ||||||
| 	Signal bool |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func newOptions(opts ...Option) Options { | // NewOptions returns new Options filled with defaults and overrided by provided opts | ||||||
| 	opt := Options{ | func NewOptions(opts ...Option) Options { | ||||||
|  | 	options := Options{ | ||||||
| 		Context:   context.Background(), | 		Context:   context.Background(), | ||||||
| 		Signal:    true, |  | ||||||
| 		Server:    server.DefaultServer, | 		Server:    server.DefaultServer, | ||||||
| 		Client:    client.DefaultClient, | 		Client:    client.DefaultClient, | ||||||
| 		Broker:    broker.DefaultBroker, | 		Broker:    broker.DefaultBroker, | ||||||
| @@ -70,12 +68,13 @@ func newOptions(opts ...Option) Options { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&opt) | 		o(&options) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return opt | 	return options | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Option func | ||||||
| type Option func(*Options) | type Option func(*Options) | ||||||
|  |  | ||||||
| // Broker to be used for service | // Broker to be used for service | ||||||
| @@ -92,6 +91,7 @@ func Broker(b broker.Broker) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Cmd to be used for service | ||||||
| func Cmd(c cmd.Cmd) Option { | func Cmd(c cmd.Cmd) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Cmd = c | 		o.Cmd = c | ||||||
| @@ -113,15 +113,6 @@ func Context(ctx context.Context) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // 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. |  | ||||||
| func HandleSignal(b bool) Option { |  | ||||||
| 	return func(o *Options) { |  | ||||||
| 		o.Signal = b |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Profile to be used for debug profile | // Profile to be used for debug profile | ||||||
| func Profile(p profile.Profile) Option { | func Profile(p profile.Profile) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| @@ -171,7 +162,7 @@ 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 { | func Tracer(t tracer.Tracer) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		if o.Server != nil { | 		if o.Server != nil { | ||||||
| 			//todo client trace | 			//todo client trace | ||||||
|   | |||||||
							
								
								
									
										125
									
								
								registry/extractor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								registry/extractor.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | |||||||
|  | package registry | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"reflect" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"github.com/unistack-org/micro/v3/metadata" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Extract *Value from reflect.Type | ||||||
|  | func ExtractValue(v reflect.Type, d int) *Value { | ||||||
|  | 	if d == 3 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	if v == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if v.Kind() == reflect.Ptr { | ||||||
|  | 		v = v.Elem() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	arg := &Value{ | ||||||
|  | 		Name: v.Name(), | ||||||
|  | 		Type: v.Name(), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch v.Kind() { | ||||||
|  | 	case reflect.Struct: | ||||||
|  | 		for i := 0; i < v.NumField(); i++ { | ||||||
|  | 			f := v.Field(i) | ||||||
|  | 			val := ExtractValue(f.Type, d+1) | ||||||
|  | 			if val == nil { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// if we can find a json tag use it | ||||||
|  | 			if tags := f.Tag.Get("json"); len(tags) > 0 { | ||||||
|  | 				parts := strings.Split(tags, ",") | ||||||
|  | 				if parts[0] == "-" || parts[0] == "omitempty" { | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				val.Name = parts[0] | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// if there's no name default it | ||||||
|  | 			if len(val.Name) == 0 { | ||||||
|  | 				val.Name = v.Field(i).Name | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			arg.Values = append(arg.Values, val) | ||||||
|  | 		} | ||||||
|  | 	case reflect.Slice: | ||||||
|  | 		p := v.Elem() | ||||||
|  | 		if p.Kind() == reflect.Ptr { | ||||||
|  | 			p = p.Elem() | ||||||
|  | 		} | ||||||
|  | 		arg.Type = "[]" + p.Name() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return arg | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ExtractEndpoint extract *Endpoint from reflect.Method | ||||||
|  | func ExtractEndpoint(method reflect.Method) *Endpoint { | ||||||
|  | 	if method.PkgPath != "" { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var rspType, reqType reflect.Type | ||||||
|  | 	var stream bool | ||||||
|  | 	mt := method.Type | ||||||
|  |  | ||||||
|  | 	switch mt.NumIn() { | ||||||
|  | 	case 3: | ||||||
|  | 		reqType = mt.In(1) | ||||||
|  | 		rspType = mt.In(2) | ||||||
|  | 	case 4: | ||||||
|  | 		reqType = mt.In(2) | ||||||
|  | 		rspType = mt.In(3) | ||||||
|  | 	default: | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// are we dealing with a stream? | ||||||
|  | 	switch rspType.Kind() { | ||||||
|  | 	case reflect.Func, reflect.Interface: | ||||||
|  | 		stream = true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	request := ExtractValue(reqType, 0) | ||||||
|  | 	response := ExtractValue(rspType, 0) | ||||||
|  |  | ||||||
|  | 	ep := &Endpoint{ | ||||||
|  | 		Name:     method.Name, | ||||||
|  | 		Request:  request, | ||||||
|  | 		Response: response, | ||||||
|  | 		Metadata: metadata.New(0), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if stream { | ||||||
|  | 		ep.Metadata = map[string]string{ | ||||||
|  | 			"stream": fmt.Sprintf("%v", stream), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return ep | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ExtractSubValue exctact *Value from reflect.Type | ||||||
|  | func ExtractSubValue(typ reflect.Type) *Value { | ||||||
|  | 	var reqType reflect.Type | ||||||
|  | 	switch typ.NumIn() { | ||||||
|  | 	case 1: | ||||||
|  | 		reqType = typ.In(0) | ||||||
|  | 	case 2: | ||||||
|  | 		reqType = typ.In(1) | ||||||
|  | 	case 3: | ||||||
|  | 		reqType = typ.In(2) | ||||||
|  | 	default: | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return ExtractValue(reqType, 0) | ||||||
|  | } | ||||||
							
								
								
									
										63
									
								
								registry/extractor_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								registry/extractor_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | package registry | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type testHandler struct{} | ||||||
|  |  | ||||||
|  | type testRequest struct{} | ||||||
|  |  | ||||||
|  | type testResponse struct{} | ||||||
|  |  | ||||||
|  | func (t *testHandler) Test(ctx context.Context, req *testRequest, rsp *testResponse) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestExtractEndpoint(t *testing.T) { | ||||||
|  | 	handler := &testHandler{} | ||||||
|  | 	typ := reflect.TypeOf(handler) | ||||||
|  |  | ||||||
|  | 	var endpoints []*Endpoint | ||||||
|  |  | ||||||
|  | 	for m := 0; m < typ.NumMethod(); m++ { | ||||||
|  | 		if e := ExtractEndpoint(typ.Method(m)); e != nil { | ||||||
|  | 			endpoints = append(endpoints, e) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if i := len(endpoints); i != 1 { | ||||||
|  | 		t.Errorf("Expected 1 endpoint, have %d", i) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if endpoints[0].Name != "Test" { | ||||||
|  | 		t.Errorf("Expected handler Test, got %s", endpoints[0].Name) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if endpoints[0].Request == nil { | ||||||
|  | 		t.Error("Expected non nil request") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if endpoints[0].Response == nil { | ||||||
|  | 		t.Error("Expected non nil request") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if endpoints[0].Request.Name != "testRequest" { | ||||||
|  | 		t.Errorf("Expected testRequest got %s", endpoints[0].Request.Name) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if endpoints[0].Response.Name != "testResponse" { | ||||||
|  | 		t.Errorf("Expected testResponse got %s", endpoints[0].Response.Name) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if endpoints[0].Request.Type != "testRequest" { | ||||||
|  | 		t.Errorf("Expected testRequest type got %s", endpoints[0].Request.Type) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if endpoints[0].Response.Type != "testResponse" { | ||||||
|  | 		t.Errorf("Expected testResponse type got %s", endpoints[0].Response.Type) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -1,11 +1,15 @@ | |||||||
| package registry | package registry | ||||||
|  |  | ||||||
| import "fmt" | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | ) | ||||||
|  |  | ||||||
| type noopRegistry struct { | type noopRegistry struct { | ||||||
| 	opts Options | 	opts Options | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Init initialize registry | ||||||
| func (n *noopRegistry) Init(opts ...Option) error { | func (n *noopRegistry) Init(opts ...Option) error { | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&n.opts) | 		o(&n.opts) | ||||||
| @@ -13,41 +17,52 @@ func (n *noopRegistry) Init(opts ...Option) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Options returns options struct | ||||||
| func (n *noopRegistry) Options() Options { | func (n *noopRegistry) Options() Options { | ||||||
| 	return n.opts | 	return n.opts | ||||||
| } | } | ||||||
|  |  | ||||||
| func (n *noopRegistry) Register(*Service, ...RegisterOption) error { | // Connect opens connection to registry | ||||||
|  | func (n *noopRegistry) Connect(ctx context.Context) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (n *noopRegistry) Deregister(*Service, ...DeregisterOption) error { | // Disconnect close connection to registry | ||||||
|  | func (n *noopRegistry) Disconnect(ctx context.Context) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (n *noopRegistry) GetService(string, ...GetOption) ([]*Service, error) { | // Register registers service | ||||||
|  | func (n *noopRegistry) Register(ctx context.Context, svc *Service, opts ...RegisterOption) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Deregister deregisters service | ||||||
|  | func (n *noopRegistry) Deregister(ctx context.Context, svc *Service, opts ...DeregisterOption) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetService returns servive info | ||||||
|  | func (n *noopRegistry) GetService(ctx context.Context, name string, opts ...GetOption) ([]*Service, error) { | ||||||
| 	return []*Service{}, nil | 	return []*Service{}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (n *noopRegistry) ListServices(...ListOption) ([]*Service, error) { | // ListServices listing services | ||||||
|  | func (n *noopRegistry) ListServices(ctx context.Context, opts ...ListOption) ([]*Service, error) { | ||||||
| 	return []*Service{}, nil | 	return []*Service{}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (n *noopRegistry) Watch(...WatchOption) (Watcher, error) { | // Watch is used to watch for service changes | ||||||
|  | func (n *noopRegistry) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) { | ||||||
| 	return nil, fmt.Errorf("not implemented") | 	return nil, fmt.Errorf("not implemented") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // String returns registry string representation | ||||||
| func (n *noopRegistry) String() string { | func (n *noopRegistry) String() string { | ||||||
| 	return "noop" | 	return "noop" | ||||||
| } | } | ||||||
|  |  | ||||||
| // newRegistry returns a new noop registry | // NewRegistry returns a new noop registry | ||||||
| func newRegistry(opts ...Option) Registry { | func NewRegistry(opts ...Option) Registry { | ||||||
| 	options := NewOptions() | 	return &noopRegistry{opts: NewOptions(opts...)} | ||||||
|  |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&options) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &noopRegistry{opts: options} |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,11 +19,15 @@ type Options struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewOptions() Options { | func NewOptions(opts ...Option) Options { | ||||||
| 	return Options{ | 	options := Options{ | ||||||
| 		Logger:  logger.DefaultLogger, | 		Logger:  logger.DefaultLogger, | ||||||
| 		Context: context.Background(), | 		Context: context.Background(), | ||||||
| 	} | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
| } | } | ||||||
|  |  | ||||||
| type RegisterOptions struct { | type RegisterOptions struct { | ||||||
| @@ -33,6 +37,19 @@ type RegisterOptions struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| 	// Domain to register the service in | 	// Domain to register the service in | ||||||
| 	Domain string | 	Domain string | ||||||
|  | 	// Attempts specify attempts for register | ||||||
|  | 	Attempts int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewRegisterOptions(opts ...RegisterOption) RegisterOptions { | ||||||
|  | 	options := RegisterOptions{ | ||||||
|  | 		Domain:  DefaultDomain, | ||||||
|  | 		Context: context.Background(), | ||||||
|  | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
| } | } | ||||||
|  |  | ||||||
| type WatchOptions struct { | type WatchOptions struct { | ||||||
| @@ -46,10 +63,34 @@ type WatchOptions struct { | |||||||
| 	Domain string | 	Domain string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NewWatchOptions(opts ...WatchOption) WatchOptions { | ||||||
|  | 	options := WatchOptions{ | ||||||
|  | 		Domain:  DefaultDomain, | ||||||
|  | 		Context: context.Background(), | ||||||
|  | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  |  | ||||||
| type DeregisterOptions struct { | type DeregisterOptions struct { | ||||||
| 	Context context.Context | 	Context context.Context | ||||||
| 	// Domain the service was registered in | 	// Domain the service was registered in | ||||||
| 	Domain string | 	Domain string | ||||||
|  | 	// Atempts specify max attempts for deregister | ||||||
|  | 	Attempts int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewDeregisterOptions(opts ...DeregisterOption) DeregisterOptions { | ||||||
|  | 	options := DeregisterOptions{ | ||||||
|  | 		Domain:  DefaultDomain, | ||||||
|  | 		Context: context.Background(), | ||||||
|  | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
| } | } | ||||||
|  |  | ||||||
| type GetOptions struct { | type GetOptions struct { | ||||||
| @@ -58,12 +99,34 @@ type GetOptions struct { | |||||||
| 	Domain string | 	Domain string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NewGetOptions(opts ...GetOption) GetOptions { | ||||||
|  | 	options := GetOptions{ | ||||||
|  | 		Domain:  DefaultDomain, | ||||||
|  | 		Context: context.Background(), | ||||||
|  | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  |  | ||||||
| type ListOptions struct { | type ListOptions struct { | ||||||
| 	Context context.Context | 	Context context.Context | ||||||
| 	// Domain to scope the request to | 	// Domain to scope the request to | ||||||
| 	Domain string | 	Domain string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NewListOptions(opts ...ListOption) ListOptions { | ||||||
|  | 	options := ListOptions{ | ||||||
|  | 		Domain:  DefaultDomain, | ||||||
|  | 		Context: context.Background(), | ||||||
|  | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  |  | ||||||
| // Addrs is the registry addresses to use | // Addrs is the registry addresses to use | ||||||
| func Addrs(addrs ...string) Option { | func Addrs(addrs ...string) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| @@ -91,6 +154,13 @@ func Logger(l logger.Logger) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Context sets the context | ||||||
|  | func Context(ctx context.Context) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.Context = ctx | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // Specify TLS Config | // Specify TLS Config | ||||||
| func TLSConfig(t *tls.Config) Option { | func TLSConfig(t *tls.Config) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| @@ -98,6 +168,12 @@ func TLSConfig(t *tls.Config) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func RegisterAttempts(t int) RegisterOption { | ||||||
|  | 	return func(o *RegisterOptions) { | ||||||
|  | 		o.Attempts = t | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func RegisterTTL(t time.Duration) RegisterOption { | func RegisterTTL(t time.Duration) RegisterOption { | ||||||
| 	return func(o *RegisterOptions) { | 	return func(o *RegisterOptions) { | ||||||
| 		o.TTL = t | 		o.TTL = t | ||||||
| @@ -135,6 +211,12 @@ func WatchDomain(d string) WatchOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func DeregisterTimeout(t int) DeregisterOption { | ||||||
|  | 	return func(o *DeregisterOptions) { | ||||||
|  | 		o.Attempts = t | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func DeregisterContext(ctx context.Context) DeregisterOption { | func DeregisterContext(ctx context.Context) DeregisterOption { | ||||||
| 	return func(o *DeregisterOptions) { | 	return func(o *DeregisterOptions) { | ||||||
| 		o.Context = ctx | 		o.Context = ctx | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| package registry | package registry | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"errors" | 	"errors" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -13,7 +14,8 @@ const ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	DefaultRegistry Registry = newRegistry() | 	// DefaultRegistry is the global default registry | ||||||
|  | 	DefaultRegistry Registry = NewRegistry() | ||||||
| 	// ErrNotFound returned when GetService is called and no services found | 	// ErrNotFound returned when GetService is called and no services found | ||||||
| 	ErrNotFound = errors.New("service not found") | 	ErrNotFound = errors.New("service not found") | ||||||
| 	// ErrWatcherStopped returned when when watcher is stopped | 	// ErrWatcherStopped returned when when watcher is stopped | ||||||
| @@ -26,11 +28,13 @@ var ( | |||||||
| type Registry interface { | type Registry interface { | ||||||
| 	Init(...Option) error | 	Init(...Option) error | ||||||
| 	Options() Options | 	Options() Options | ||||||
| 	Register(*Service, ...RegisterOption) error | 	Connect(context.Context) error | ||||||
| 	Deregister(*Service, ...DeregisterOption) error | 	Disconnect(context.Context) error | ||||||
| 	GetService(string, ...GetOption) ([]*Service, error) | 	Register(context.Context, *Service, ...RegisterOption) error | ||||||
| 	ListServices(...ListOption) ([]*Service, error) | 	Deregister(context.Context, *Service, ...DeregisterOption) error | ||||||
| 	Watch(...WatchOption) (Watcher, error) | 	GetService(context.Context, string, ...GetOption) ([]*Service, error) | ||||||
|  | 	ListServices(context.Context, ...ListOption) ([]*Service, error) | ||||||
|  | 	Watch(context.Context, ...WatchOption) (Watcher, error) | ||||||
| 	String() string | 	String() string | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,8 @@ | |||||||
| package registry | package registry | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
| 	"github.com/unistack-org/micro/v3/registry" | 	"github.com/unistack-org/micro/v3/registry" | ||||||
| 	"github.com/unistack-org/micro/v3/resolver" | 	"github.com/unistack-org/micro/v3/resolver" | ||||||
| ) | ) | ||||||
| @@ -14,7 +16,7 @@ type Resolver struct { | |||||||
|  |  | ||||||
| // Resolve assumes ID is a domain name e.g micro.mu | // Resolve assumes ID is a domain name e.g micro.mu | ||||||
| func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) { | func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) { | ||||||
| 	services, err := r.Registry.GetService(name) | 	services, err := r.Registry.GetService(context.TODO(), name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ type Resolver interface { | |||||||
| 	Resolve(name string) ([]*Record, error) | 	Resolve(name string) ([]*Record, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| // A resolved record | // Record that resolved | ||||||
| type Record struct { | type Record struct { | ||||||
| 	Address  string `json:"address"` | 	Address  string `json:"address"` | ||||||
| 	Priority int64  `json:"priority"` | 	Priority int64  `json:"priority"` | ||||||
|   | |||||||
| @@ -77,11 +77,17 @@ func Precache() Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // DefaultOptions returns router default options | // NewOptions returns router default options | ||||||
| func DefaultOptions() Options { | func NewOptions(opts ...Option) Options { | ||||||
| 	return Options{ | 	options := Options{ | ||||||
| 		Id:      uuid.New().String(), | 		Id:       uuid.New().String(), | ||||||
| 		Network: DefaultNetwork, | 		Network:  DefaultNetwork, | ||||||
| 		Context: context.Background(), | 		Registry: registry.DefaultRegistry, | ||||||
|  | 		Logger:   logger.DefaultLogger, | ||||||
|  | 		Context:  context.Background(), | ||||||
| 	} | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	return options | ||||||
| } | } | ||||||
|   | |||||||
| @@ -65,7 +65,7 @@ func QueryLink(link string) QueryOption { | |||||||
| // NewQuery creates new query and returns it | // NewQuery creates new query and returns it | ||||||
| func NewQuery(opts ...QueryOption) QueryOptions { | func NewQuery(opts ...QueryOption) QueryOptions { | ||||||
| 	// default options | 	// default options | ||||||
| 	qopts := QueryOptions{ | 	options := QueryOptions{ | ||||||
| 		Service: "*", | 		Service: "*", | ||||||
| 		Address: "*", | 		Address: "*", | ||||||
| 		Gateway: "*", | 		Gateway: "*", | ||||||
| @@ -75,8 +75,8 @@ func NewQuery(opts ...QueryOption) QueryOptions { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&qopts) | 		o(&options) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return qopts | 	return options | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
|  | 	// DefaultRouter is the global default router | ||||||
| 	DefaultRouter Router | 	DefaultRouter Router | ||||||
| 	// DefaultNetwork is default micro network | 	// DefaultNetwork is default micro network | ||||||
| 	DefaultNetwork = "micro" | 	DefaultNetwork = "micro" | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
|  | 	// ErrAlreadyExists error | ||||||
| 	ErrAlreadyExists = errors.New("already exists") | 	ErrAlreadyExists = errors.New("already exists") | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,11 +6,33 @@ import ( | |||||||
|  |  | ||||||
| type serverKey struct{} | type serverKey struct{} | ||||||
|  |  | ||||||
|  | // FromContext returns Server from context | ||||||
| func FromContext(ctx context.Context) (Server, bool) { | func FromContext(ctx context.Context) (Server, bool) { | ||||||
| 	c, ok := ctx.Value(serverKey{}).(Server) | 	c, ok := ctx.Value(serverKey{}).(Server) | ||||||
| 	return c, ok | 	return c, ok | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // NewContext stores Server to context | ||||||
| func NewContext(ctx context.Context, s Server) context.Context { | func NewContext(ctx context.Context, s Server) context.Context { | ||||||
| 	return context.WithValue(ctx, serverKey{}, s) | 	return context.WithValue(ctx, serverKey{}, s) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Setoption returns a function to setup a context with given value | ||||||
|  | func SetOption(k, v interface{}) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		if o.Context == nil { | ||||||
|  | 			o.Context = context.Background() | ||||||
|  | 		} | ||||||
|  | 		o.Context = context.WithValue(o.Context, k, v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetSubscriberOption returns a function to setup a context with given value | ||||||
|  | func SetSubscriberOption(k, v interface{}) SubscriberOption { | ||||||
|  | 	return func(o *SubscriberOptions) { | ||||||
|  | 		if o.Context == nil { | ||||||
|  | 			o.Context = context.Background() | ||||||
|  | 		} | ||||||
|  | 		o.Context = context.WithValue(o.Context, k, v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,80 +1,59 @@ | |||||||
| package server | package server | ||||||
|  |  | ||||||
| import "context" | import ( | ||||||
|  | 	"reflect" | ||||||
|  |  | ||||||
| type HandlerOption func(*HandlerOptions) | 	"github.com/unistack-org/micro/v3/registry" | ||||||
|  | ) | ||||||
|  |  | ||||||
| type HandlerOptions struct { | type rpcHandler struct { | ||||||
| 	Internal bool | 	name      string | ||||||
| 	Metadata map[string]map[string]string | 	handler   interface{} | ||||||
|  | 	endpoints []*registry.Endpoint | ||||||
|  | 	opts      HandlerOptions | ||||||
| } | } | ||||||
|  |  | ||||||
| type SubscriberOption func(*SubscriberOptions) | func newRpcHandler(handler interface{}, opts ...HandlerOption) Handler { | ||||||
|  | 	options := NewHandlerOptions(opts...) | ||||||
|  |  | ||||||
| type SubscriberOptions struct { | 	typ := reflect.TypeOf(handler) | ||||||
| 	// AutoAck defaults to true. When a handler returns | 	hdlr := reflect.ValueOf(handler) | ||||||
| 	// with a nil error the message is acked. | 	name := reflect.Indirect(hdlr).Type().Name() | ||||||
| 	AutoAck  bool |  | ||||||
| 	Queue    string |  | ||||||
| 	Internal bool |  | ||||||
| 	Context  context.Context |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // EndpointMetadata is a Handler option that allows metadata to be added to | 	var endpoints []*registry.Endpoint | ||||||
| // individual endpoints. |  | ||||||
| func EndpointMetadata(name string, md map[string]string) HandlerOption { | 	for m := 0; m < typ.NumMethod(); m++ { | ||||||
| 	return func(o *HandlerOptions) { | 		if e := registry.ExtractEndpoint(typ.Method(m)); e != nil { | ||||||
| 		o.Metadata[name] = md | 			e.Name = name + "." + e.Name | ||||||
|  |  | ||||||
|  | 			for k, v := range options.Metadata[e.Name] { | ||||||
|  | 				e.Metadata[k] = v | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			endpoints = append(endpoints, e) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &rpcHandler{ | ||||||
|  | 		name:      name, | ||||||
|  | 		handler:   handler, | ||||||
|  | 		endpoints: endpoints, | ||||||
|  | 		opts:      options, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Internal Handler options specifies that a handler is not advertised | func (r *rpcHandler) Name() string { | ||||||
| // to the discovery system. In the future this may also limit request | 	return r.name | ||||||
| // to the internal network or authorised user. |  | ||||||
| func InternalHandler(b bool) HandlerOption { |  | ||||||
| 	return func(o *HandlerOptions) { |  | ||||||
| 		o.Internal = b |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Internal Subscriber options specifies that a subscriber is not advertised | func (r *rpcHandler) Handler() interface{} { | ||||||
| // to the discovery system. | 	return r.handler | ||||||
| func InternalSubscriber(b bool) SubscriberOption { |  | ||||||
| 	return func(o *SubscriberOptions) { |  | ||||||
| 		o.Internal = b |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| func NewSubscriberOptions(opts ...SubscriberOption) SubscriberOptions { |  | ||||||
| 	opt := SubscriberOptions{ |  | ||||||
| 		AutoAck: true, |  | ||||||
| 		Context: context.Background(), |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&opt) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return opt |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // DisableAutoAck will disable auto acking of messages | func (r *rpcHandler) Endpoints() []*registry.Endpoint { | ||||||
| // after they have been handled. | 	return r.endpoints | ||||||
| func DisableAutoAck() SubscriberOption { |  | ||||||
| 	return func(o *SubscriberOptions) { |  | ||||||
| 		o.AutoAck = false |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Shared queue name distributed messages across subscribers | func (r *rpcHandler) Options() HandlerOptions { | ||||||
| func SubscriberQueue(n string) SubscriberOption { | 	return r.opts | ||||||
| 	return func(o *SubscriberOptions) { |  | ||||||
| 		o.Queue = n |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // SubscriberContext set context options to allow broker SubscriberOption passed |  | ||||||
| func SubscriberContext(ctx context.Context) SubscriberOption { |  | ||||||
| 	return func(o *SubscriberOptions) { |  | ||||||
| 		o.Context = ctx |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										456
									
								
								server/noop.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										456
									
								
								server/noop.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,456 @@ | |||||||
|  | package server | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"sort" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	craw "github.com/unistack-org/micro-codec-bytes" | ||||||
|  | 	cjson "github.com/unistack-org/micro-codec-json" | ||||||
|  | 	cjsonrpc "github.com/unistack-org/micro-codec-jsonrpc" | ||||||
|  | 	cproto "github.com/unistack-org/micro-codec-proto" | ||||||
|  | 	cprotorpc "github.com/unistack-org/micro-codec-protorpc" | ||||||
|  | 	"github.com/unistack-org/micro/v3/broker" | ||||||
|  | 	"github.com/unistack-org/micro/v3/codec" | ||||||
|  | 	"github.com/unistack-org/micro/v3/logger" | ||||||
|  | 	"github.com/unistack-org/micro/v3/registry" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	DefaultCodecs = map[string]codec.NewCodec{ | ||||||
|  | 		"application/json":         cjson.NewCodec, | ||||||
|  | 		"application/json-rpc":     cjsonrpc.NewCodec, | ||||||
|  | 		"application/protobuf":     cproto.NewCodec, | ||||||
|  | 		"application/proto-rpc":    cprotorpc.NewCodec, | ||||||
|  | 		"application/octet-stream": craw.NewCodec, | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	defaultContentType = "application/json" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type noopServer struct { | ||||||
|  | 	h           Handler | ||||||
|  | 	opts        Options | ||||||
|  | 	rsvc        *registry.Service | ||||||
|  | 	handlers    map[string]Handler | ||||||
|  | 	subscribers map[*subscriber][]broker.Subscriber | ||||||
|  | 	registered  bool | ||||||
|  | 	started     bool | ||||||
|  | 	exit        chan chan error | ||||||
|  | 	wg          *sync.WaitGroup | ||||||
|  | 	sync.RWMutex | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewServer returns new noop server | ||||||
|  | func NewServer(opts ...Option) Server { | ||||||
|  | 	return &noopServer{opts: NewOptions(opts...)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopServer) newCodec(contentType string) (codec.NewCodec, error) { | ||||||
|  | 	if cf, ok := n.opts.Codecs[contentType]; ok { | ||||||
|  | 		return cf, nil | ||||||
|  | 	} | ||||||
|  | 	if cf, ok := DefaultCodecs[contentType]; ok { | ||||||
|  | 		return cf, nil | ||||||
|  | 	} | ||||||
|  | 	return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopServer) Handle(handler Handler) error { | ||||||
|  | 	n.h = handler | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopServer) Subscribe(sb Subscriber) error { | ||||||
|  | 	sub, ok := sb.(*subscriber) | ||||||
|  | 	if !ok { | ||||||
|  | 		return fmt.Errorf("invalid subscriber: expected *subscriber") | ||||||
|  | 	} | ||||||
|  | 	if len(sub.handlers) == 0 { | ||||||
|  | 		return fmt.Errorf("invalid subscriber: no handler functions") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := ValidateSubscriber(sb); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	n.Lock() | ||||||
|  | 	if _, ok = n.subscribers[sub]; ok { | ||||||
|  | 		n.Unlock() | ||||||
|  | 		return fmt.Errorf("subscriber %v already exists", sub) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	n.subscribers[sub] = nil | ||||||
|  | 	n.Unlock() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopServer) NewHandler(h interface{}, opts ...HandlerOption) Handler { | ||||||
|  | 	return newRpcHandler(h, opts...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopServer) NewSubscriber(topic string, sb interface{}, opts ...SubscriberOption) Subscriber { | ||||||
|  | 	return newSubscriber(topic, sb, opts...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopServer) Init(opts ...Option) error { | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&n.opts) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if n.handlers == nil { | ||||||
|  | 		n.handlers = make(map[string]Handler) | ||||||
|  | 	} | ||||||
|  | 	if n.subscribers == nil { | ||||||
|  | 		n.subscribers = make(map[*subscriber][]broker.Subscriber) | ||||||
|  | 	} | ||||||
|  | 	if n.exit == nil { | ||||||
|  | 		n.exit = make(chan chan error) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopServer) Options() Options { | ||||||
|  | 	return n.opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopServer) String() string { | ||||||
|  | 	return "noop" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopServer) Register() error { | ||||||
|  | 	n.RLock() | ||||||
|  | 	rsvc := n.rsvc | ||||||
|  | 	config := n.opts | ||||||
|  | 	n.RUnlock() | ||||||
|  |  | ||||||
|  | 	// if service already filled, reuse it and return early | ||||||
|  | 	if rsvc != nil { | ||||||
|  | 		if err := DefaultRegisterFunc(rsvc, config); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var err error | ||||||
|  | 	var service *registry.Service | ||||||
|  | 	var cacheService bool | ||||||
|  |  | ||||||
|  | 	service, err = NewRegistryService(n) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	n.RLock() | ||||||
|  | 	// Maps are ordered randomly, sort the keys for consistency | ||||||
|  | 	var handlerList []string | ||||||
|  | 	for n, e := range n.handlers { | ||||||
|  | 		// Only advertise non internal handlers | ||||||
|  | 		if !e.Options().Internal { | ||||||
|  | 			handlerList = append(handlerList, n) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sort.Strings(handlerList) | ||||||
|  |  | ||||||
|  | 	var subscriberList []*subscriber | ||||||
|  | 	for e := range n.subscribers { | ||||||
|  | 		// Only advertise non internal subscribers | ||||||
|  | 		if !e.Options().Internal { | ||||||
|  | 			subscriberList = append(subscriberList, e) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	sort.Slice(subscriberList, func(i, j int) bool { | ||||||
|  | 		return subscriberList[i].topic > subscriberList[j].topic | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	endpoints := make([]*registry.Endpoint, 0, len(handlerList)+len(subscriberList)) | ||||||
|  | 	for _, h := range handlerList { | ||||||
|  | 		endpoints = append(endpoints, n.handlers[h].Endpoints()...) | ||||||
|  | 	} | ||||||
|  | 	for _, e := range subscriberList { | ||||||
|  | 		endpoints = append(endpoints, e.Endpoints()...) | ||||||
|  | 	} | ||||||
|  | 	n.RUnlock() | ||||||
|  |  | ||||||
|  | 	service.Nodes[0].Metadata["protocol"] = "noop" | ||||||
|  | 	service.Nodes[0].Metadata["transport"] = "noop" | ||||||
|  | 	service.Endpoints = endpoints | ||||||
|  |  | ||||||
|  | 	n.RLock() | ||||||
|  | 	registered := n.registered | ||||||
|  | 	n.RUnlock() | ||||||
|  |  | ||||||
|  | 	if !registered { | ||||||
|  | 		if config.Logger.V(logger.InfoLevel) { | ||||||
|  | 			config.Logger.Info("Registry [%s] Registering node: %s", config.Registry.String(), service.Nodes[0].Id) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// register the service | ||||||
|  | 	if err := DefaultRegisterFunc(service, config); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// already registered? don't need to register subscribers | ||||||
|  | 	if registered { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	n.Lock() | ||||||
|  | 	defer n.Unlock() | ||||||
|  |  | ||||||
|  | 	cx := config.Context | ||||||
|  |  | ||||||
|  | 	for sb := range n.subscribers { | ||||||
|  | 		handler := n.createSubHandler(sb, config) | ||||||
|  | 		var opts []broker.SubscribeOption | ||||||
|  | 		if queue := sb.Options().Queue; len(queue) > 0 { | ||||||
|  | 			opts = append(opts, broker.SubscribeGroup(queue)) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if sb.Options().Context != nil { | ||||||
|  | 			cx = sb.Options().Context | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		opts = append(opts, broker.SubscribeContext(cx), broker.SubscribeAutoAck(sb.Options().AutoAck)) | ||||||
|  |  | ||||||
|  | 		if config.Logger.V(logger.InfoLevel) { | ||||||
|  | 			config.Logger.Info("Subscribing to topic: %s", sb.Topic()) | ||||||
|  | 		} | ||||||
|  | 		sub, err := config.Broker.Subscribe(cx, sb.Topic(), handler, opts...) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		n.subscribers[sb] = []broker.Subscriber{sub} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	n.registered = true | ||||||
|  | 	if cacheService { | ||||||
|  | 		n.rsvc = service | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopServer) Deregister() error { | ||||||
|  | 	var err error | ||||||
|  |  | ||||||
|  | 	n.RLock() | ||||||
|  | 	config := n.opts | ||||||
|  | 	n.RUnlock() | ||||||
|  |  | ||||||
|  | 	service, err := NewRegistryService(n) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if config.Logger.V(logger.InfoLevel) { | ||||||
|  | 		config.Logger.Info("deregistering node: %s", service.Nodes[0].Id) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := DefaultDeregisterFunc(service, config); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	n.Lock() | ||||||
|  | 	n.rsvc = nil | ||||||
|  |  | ||||||
|  | 	if !n.registered { | ||||||
|  | 		n.Unlock() | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	n.registered = false | ||||||
|  |  | ||||||
|  | 	cx := config.Context | ||||||
|  |  | ||||||
|  | 	wg := sync.WaitGroup{} | ||||||
|  | 	for sb, subs := range n.subscribers { | ||||||
|  | 		for _, sub := range subs { | ||||||
|  | 			if sb.Options().Context != nil { | ||||||
|  | 				cx = sb.Options().Context | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			wg.Add(1) | ||||||
|  | 			go func(s broker.Subscriber) { | ||||||
|  | 				defer wg.Done() | ||||||
|  | 				if config.Logger.V(logger.InfoLevel) { | ||||||
|  | 					config.Logger.Info("unsubscribing from topic: %s", s.Topic()) | ||||||
|  | 				} | ||||||
|  | 				if err := s.Unsubscribe(cx); err != nil { | ||||||
|  | 					if config.Logger.V(logger.ErrorLevel) { | ||||||
|  | 						config.Logger.Error("unsubscribing from topic: %s err: %v", s.Topic(), err) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			}(sub) | ||||||
|  | 		} | ||||||
|  | 		n.subscribers[sb] = nil | ||||||
|  | 	} | ||||||
|  | 	wg.Wait() | ||||||
|  |  | ||||||
|  | 	n.Unlock() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopServer) Start() error { | ||||||
|  | 	n.RLock() | ||||||
|  | 	if n.started { | ||||||
|  | 		n.RUnlock() | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	config := n.Options() | ||||||
|  | 	n.RUnlock() | ||||||
|  |  | ||||||
|  | 	if config.Logger.V(logger.InfoLevel) { | ||||||
|  | 		config.Logger.Info("Server [noop] Listening on %s", config.Address) | ||||||
|  | 	} | ||||||
|  | 	n.Lock() | ||||||
|  | 	if len(config.Advertise) == 0 { | ||||||
|  | 		config.Advertise = config.Address | ||||||
|  | 	} | ||||||
|  | 	n.Unlock() | ||||||
|  |  | ||||||
|  | 	// only connect if we're subscribed | ||||||
|  | 	if len(n.subscribers) > 0 { | ||||||
|  | 		// connect to the broker | ||||||
|  | 		if err := config.Broker.Connect(config.Context); err != nil { | ||||||
|  | 			if config.Logger.V(logger.ErrorLevel) { | ||||||
|  | 				config.Logger.Error("Broker [%s] connect error: %v", config.Broker.String(), err) | ||||||
|  | 			} | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if config.Logger.V(logger.InfoLevel) { | ||||||
|  | 			config.Logger.Info("Broker [%s] Connected to %s", config.Broker.String(), config.Broker.Address()) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// use RegisterCheck func before register | ||||||
|  | 	if err := config.RegisterCheck(config.Context); err != nil { | ||||||
|  | 		if config.Logger.V(logger.ErrorLevel) { | ||||||
|  | 			config.Logger.Error("Server %s-%s register check error: %s", config.Name, config.Id, err) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		// announce self to the world | ||||||
|  | 		if err := n.Register(); err != nil { | ||||||
|  | 			if config.Logger.V(logger.ErrorLevel) { | ||||||
|  | 				config.Logger.Error("Server register error: %v", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	go func() { | ||||||
|  | 		t := new(time.Ticker) | ||||||
|  |  | ||||||
|  | 		// only process if it exists | ||||||
|  | 		if config.RegisterInterval > time.Duration(0) { | ||||||
|  | 			// new ticker | ||||||
|  | 			t = time.NewTicker(config.RegisterInterval) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// return error chan | ||||||
|  | 		var ch chan error | ||||||
|  |  | ||||||
|  | 	Loop: | ||||||
|  | 		for { | ||||||
|  | 			select { | ||||||
|  | 			// register self on interval | ||||||
|  | 			case <-t.C: | ||||||
|  | 				n.RLock() | ||||||
|  | 				registered := n.registered | ||||||
|  | 				n.RUnlock() | ||||||
|  | 				rerr := config.RegisterCheck(config.Context) | ||||||
|  | 				if rerr != nil && registered { | ||||||
|  | 					if config.Logger.V(logger.ErrorLevel) { | ||||||
|  | 						config.Logger.Error("Server %s-%s register check error: %s, deregister it", config.Name, config.Id, rerr) | ||||||
|  | 					} | ||||||
|  | 					// deregister self in case of error | ||||||
|  | 					if err := n.Deregister(); err != nil { | ||||||
|  | 						if config.Logger.V(logger.ErrorLevel) { | ||||||
|  | 							config.Logger.Error("Server %s-%s deregister error: %s", config.Name, config.Id, err) | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} else if rerr != nil && !registered { | ||||||
|  | 					if config.Logger.V(logger.ErrorLevel) { | ||||||
|  | 						config.Logger.Error("Server %s-%s register check error: %s", config.Name, config.Id, rerr) | ||||||
|  | 					} | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				if err := n.Register(); err != nil { | ||||||
|  | 					if config.Logger.V(logger.ErrorLevel) { | ||||||
|  | 						config.Logger.Error("Server %s-%s register error: %s", config.Name, config.Id, err) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			// wait for exit | ||||||
|  | 			case ch = <-n.exit: | ||||||
|  | 				break Loop | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// deregister self | ||||||
|  | 		if err := n.Deregister(); err != nil { | ||||||
|  | 			if config.Logger.V(logger.ErrorLevel) { | ||||||
|  | 				config.Logger.Error("Server deregister error: ", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// wait for waitgroup | ||||||
|  | 		if n.wg != nil { | ||||||
|  | 			n.wg.Wait() | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// close transport | ||||||
|  | 		ch <- nil | ||||||
|  |  | ||||||
|  | 		if config.Logger.V(logger.InfoLevel) { | ||||||
|  | 			config.Logger.Info("Broker [%s] Disconnected from %s", config.Broker.String(), config.Broker.Address()) | ||||||
|  | 		} | ||||||
|  | 		// disconnect broker | ||||||
|  | 		if err := config.Broker.Disconnect(config.Context); err != nil { | ||||||
|  | 			if config.Logger.V(logger.ErrorLevel) { | ||||||
|  | 				config.Logger.Error("Broker [%s] disconnect error: %v", config.Broker.String(), err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	// mark the server as started | ||||||
|  | 	n.Lock() | ||||||
|  | 	n.started = true | ||||||
|  | 	n.Unlock() | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noopServer) Stop() error { | ||||||
|  | 	n.RLock() | ||||||
|  | 	if !n.started { | ||||||
|  | 		n.RUnlock() | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	n.RUnlock() | ||||||
|  |  | ||||||
|  | 	ch := make(chan error) | ||||||
|  | 	n.exit <- ch | ||||||
|  |  | ||||||
|  | 	err := <-ch | ||||||
|  | 	n.Lock() | ||||||
|  | 	n.rsvc = nil | ||||||
|  | 	n.started = false | ||||||
|  | 	n.Unlock() | ||||||
|  |  | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type noopcodec struct { | ||||||
|  | 	*bytes.Buffer | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c noopcodec) Close() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @@ -9,17 +9,18 @@ import ( | |||||||
| 	"github.com/unistack-org/micro/v3/auth" | 	"github.com/unistack-org/micro/v3/auth" | ||||||
| 	"github.com/unistack-org/micro/v3/broker" | 	"github.com/unistack-org/micro/v3/broker" | ||||||
| 	"github.com/unistack-org/micro/v3/codec" | 	"github.com/unistack-org/micro/v3/codec" | ||||||
| 	"github.com/unistack-org/micro/v3/debug/trace" |  | ||||||
| 	"github.com/unistack-org/micro/v3/logger" | 	"github.com/unistack-org/micro/v3/logger" | ||||||
|  | 	"github.com/unistack-org/micro/v3/network/transport" | ||||||
| 	"github.com/unistack-org/micro/v3/registry" | 	"github.com/unistack-org/micro/v3/registry" | ||||||
| 	"github.com/unistack-org/micro/v3/transport" | 	"github.com/unistack-org/micro/v3/tracer" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // Options server struct | ||||||
| type Options struct { | type Options struct { | ||||||
| 	Codecs       map[string]codec.NewCodec | 	Codecs       map[string]codec.NewCodec | ||||||
| 	Broker       broker.Broker | 	Broker       broker.Broker | ||||||
| 	Registry     registry.Registry | 	Registry     registry.Registry | ||||||
| 	Tracer       trace.Tracer | 	Tracer       tracer.Tracer | ||||||
| 	Auth         auth.Auth | 	Auth         auth.Auth | ||||||
| 	Logger       logger.Logger | 	Logger       logger.Logger | ||||||
| 	Transport    transport.Transport | 	Transport    transport.Transport | ||||||
| @@ -39,6 +40,10 @@ type Options struct { | |||||||
| 	RegisterTTL time.Duration | 	RegisterTTL time.Duration | ||||||
| 	// The interval on which to register | 	// The interval on which to register | ||||||
| 	RegisterInterval time.Duration | 	RegisterInterval time.Duration | ||||||
|  | 	// RegisterAttempts specify how many times try to register | ||||||
|  | 	RegisterAttempts int | ||||||
|  | 	// DeegisterAttempts specify how many times try to deregister | ||||||
|  | 	DeregisterAttempts int | ||||||
|  |  | ||||||
| 	// The router for requests | 	// The router for requests | ||||||
| 	Router Router | 	Router Router | ||||||
| @@ -46,47 +51,42 @@ type Options struct { | |||||||
| 	// TLSConfig specifies tls.Config for secure serving | 	// TLSConfig specifies tls.Config for secure serving | ||||||
| 	TLSConfig *tls.Config | 	TLSConfig *tls.Config | ||||||
|  |  | ||||||
|  | 	Wait *sync.WaitGroup | ||||||
| 	// Other options for implementations of the interface | 	// Other options for implementations of the interface | ||||||
| 	// can be stored in a context | 	// can be stored in a context | ||||||
| 	Context context.Context | 	Context context.Context | ||||||
| } | } | ||||||
|  |  | ||||||
| func newOptions(opt ...Option) Options { | // NewOptions returns new options struct with default or passed values | ||||||
| 	opts := Options{ | func NewOptions(opts ...Option) Options { | ||||||
|  | 	options := Options{ | ||||||
|  | 		Auth:             auth.DefaultAuth, | ||||||
| 		Codecs:           make(map[string]codec.NewCodec), | 		Codecs:           make(map[string]codec.NewCodec), | ||||||
|  | 		Context:          context.Background(), | ||||||
| 		Metadata:         map[string]string{}, | 		Metadata:         map[string]string{}, | ||||||
| 		RegisterInterval: DefaultRegisterInterval, | 		RegisterInterval: DefaultRegisterInterval, | ||||||
| 		RegisterTTL:      DefaultRegisterTTL, | 		RegisterTTL:      DefaultRegisterTTL, | ||||||
|  | 		RegisterCheck:    DefaultRegisterCheck, | ||||||
|  | 		Logger:           logger.DefaultLogger, | ||||||
|  | 		Tracer:           tracer.DefaultTracer, | ||||||
|  | 		Broker:           broker.DefaultBroker, | ||||||
|  | 		Registry:         registry.DefaultRegistry, | ||||||
|  | 		Transport:        transport.DefaultTransport, | ||||||
|  | 		Address:          DefaultAddress, | ||||||
|  | 		Name:             DefaultName, | ||||||
|  | 		Version:          DefaultVersion, | ||||||
|  | 		Id:               DefaultId, | ||||||
|  | 		Namespace:        DefaultNamespace, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, o := range opt { | 	for _, o := range opts { | ||||||
| 		o(&opts) | 		o(&options) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if opts.RegisterCheck == nil { | 	return options | ||||||
| 		opts.RegisterCheck = DefaultRegisterCheck |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(opts.Address) == 0 { |  | ||||||
| 		opts.Address = DefaultAddress |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(opts.Name) == 0 { |  | ||||||
| 		opts.Name = DefaultName |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(opts.Id) == 0 { |  | ||||||
| 		opts.Id = DefaultId |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(opts.Version) == 0 { |  | ||||||
| 		opts.Version = DefaultVersion |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return opts |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Server name | // Name sets the server name option | ||||||
| func Name(n string) Option { | func Name(n string) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Name = n | 		o.Name = n | ||||||
| @@ -100,14 +100,14 @@ func Namespace(n string) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Logger | // Logger sets the logger option | ||||||
| func Logger(l logger.Logger) Option { | func Logger(l logger.Logger) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Logger = l | 		o.Logger = l | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Unique server id | // Id unique server id | ||||||
| func Id(id string) Option { | func Id(id string) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Id = id | 		o.Id = id | ||||||
| @@ -128,7 +128,7 @@ func Address(a string) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // The address to advertise for discovery - host:port | // Advertise the address to advertise for discovery - host:port | ||||||
| func Advertise(a string) Option { | func Advertise(a string) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Advertise = a | 		o.Advertise = a | ||||||
| @@ -166,7 +166,7 @@ func Registry(r registry.Registry) Option { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Tracer mechanism for distributed tracking | // Tracer mechanism for distributed tracking | ||||||
| func Tracer(t trace.Tracer) Option { | func Tracer(t tracer.Tracer) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.Tracer = t | 		o.Tracer = t | ||||||
| 	} | 	} | ||||||
| @@ -200,14 +200,14 @@ func RegisterCheck(fn func(context.Context) error) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Register the service with a TTL | // RegisterTTL registers service with a TTL | ||||||
| func RegisterTTL(t time.Duration) Option { | func RegisterTTL(t time.Duration) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.RegisterTTL = t | 		o.RegisterTTL = t | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Register the service with at interval | // RegisterInterval registers service with at interval | ||||||
| func RegisterInterval(t time.Duration) Option { | func RegisterInterval(t time.Duration) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.RegisterInterval = t | 		o.RegisterInterval = t | ||||||
| @@ -244,26 +244,120 @@ func WithRouter(r Router) Option { | |||||||
| // wait against it on stop. | // wait against it on stop. | ||||||
| func Wait(wg *sync.WaitGroup) Option { | func Wait(wg *sync.WaitGroup) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		if o.Context == nil { |  | ||||||
| 			o.Context = context.Background() |  | ||||||
| 		} |  | ||||||
| 		if wg == nil { | 		if wg == nil { | ||||||
| 			wg = new(sync.WaitGroup) | 			wg = new(sync.WaitGroup) | ||||||
| 		} | 		} | ||||||
| 		o.Context = context.WithValue(o.Context, "wait", wg) | 		o.Wait = wg | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // 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 HandlerWrapper) Option { | func WrapHandler(w HandlerWrapper) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.HdlrWrappers = append(o.HdlrWrappers, w) | 		o.HdlrWrappers = append(o.HdlrWrappers, w) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // 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 SubscriberWrapper) Option { | func WrapSubscriber(w SubscriberWrapper) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.SubWrappers = append(o.SubWrappers, w) | 		o.SubWrappers = append(o.SubWrappers, w) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // HandlerOption func | ||||||
|  | type HandlerOption func(*HandlerOptions) | ||||||
|  |  | ||||||
|  | // HandlerOptions struct | ||||||
|  | type HandlerOptions struct { | ||||||
|  | 	Internal bool | ||||||
|  | 	Metadata map[string]map[string]string | ||||||
|  | 	Context  context.Context | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewHandlerOptions creates new HandlerOptions | ||||||
|  | func NewHandlerOptions(opts ...HandlerOption) HandlerOptions { | ||||||
|  | 	options := HandlerOptions{ | ||||||
|  | 		Context: context.Background(), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SubscriberOption func | ||||||
|  | type SubscriberOption func(*SubscriberOptions) | ||||||
|  |  | ||||||
|  | // SubscriberOptions struct | ||||||
|  | type SubscriberOptions struct { | ||||||
|  | 	// AutoAck defaults to true. When a handler returns | ||||||
|  | 	// with a nil error the message is acked. | ||||||
|  | 	AutoAck  bool | ||||||
|  | 	Queue    string | ||||||
|  | 	Internal bool | ||||||
|  | 	Context  context.Context | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewSubscriberOptions create new SubscriberOptions | ||||||
|  | func NewSubscriberOptions(opts ...SubscriberOption) SubscriberOptions { | ||||||
|  | 	options := SubscriberOptions{ | ||||||
|  | 		AutoAck: true, | ||||||
|  | 		Context: context.Background(), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EndpointMetadata is a Handler option that allows metadata to be added to | ||||||
|  | // individual endpoints. | ||||||
|  | func EndpointMetadata(name string, md map[string]string) HandlerOption { | ||||||
|  | 	return func(o *HandlerOptions) { | ||||||
|  | 		o.Metadata[name] = md | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // InternalHandler options specifies that a handler is not advertised | ||||||
|  | // to the discovery system. In the future this may also limit request | ||||||
|  | // to the internal network or authorised user. | ||||||
|  | func InternalHandler(b bool) HandlerOption { | ||||||
|  | 	return func(o *HandlerOptions) { | ||||||
|  | 		o.Internal = b | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // InternalSubscriber options specifies that a subscriber is not advertised | ||||||
|  | // to the discovery system. | ||||||
|  | func InternalSubscriber(b bool) SubscriberOption { | ||||||
|  | 	return func(o *SubscriberOptions) { | ||||||
|  | 		o.Internal = b | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DisableAutoAck will disable auto acking of messages | ||||||
|  | // after they have been handled. | ||||||
|  | func DisableAutoAck() SubscriberOption { | ||||||
|  | 	return func(o *SubscriberOptions) { | ||||||
|  | 		o.AutoAck = false | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SubscriberQueue sets the shared queue name distributed messages across subscribers | ||||||
|  | func SubscriberQueue(n string) SubscriberOption { | ||||||
|  | 	return func(o *SubscriberOptions) { | ||||||
|  | 		o.Queue = n | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SubscriberContext set context options to allow broker SubscriberOption passed | ||||||
|  | func SubscriberContext(ctx context.Context) SubscriberOption { | ||||||
|  | 	return func(o *SubscriberOptions) { | ||||||
|  | 		o.Context = ctx | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user