Compare commits

..

181 Commits

Author SHA1 Message Date
Asim Aslam
563768b58a v3 refactor (#1868)
* Move to v3

Co-authored-by: Ben Toogood <bentoogood@gmail.com>
2020-07-27 13:22:00 +01:00
Dominic Wong
9dfeb98111 cockroach typo in init (#1872) 2020-07-27 10:16:40 +01:00
Asim Aslam
5f4491bb86 change network resolver http to localhost:8080 2020-07-26 11:21:57 +01:00
Janos Dobronszki
fbdf1f2c1c Fixing micro logs being follow by default against k8s (#1866) 2020-07-23 09:50:38 +02:00
ben-toogood
a3a7434f2c client/grpc: fix error panic (#1860) 2020-07-20 10:04:51 +01:00
Asim Aslam
592179c0a2 Remove deprecations since next release is v3 2020-07-19 19:59:33 +01:00
Asim Aslam
9b74bc52d6 Move defaults to defaults.go 2020-07-19 19:48:11 +01:00
Asim Aslam
05f3e1a125 Add cmd.Run function 2020-07-19 18:43:33 +01:00
Asim Aslam
16c591d741 call cmd.Run 2020-07-19 18:20:17 +01:00
Asim Aslam
755b816086 Use Run instead of Init for cmd 2020-07-19 18:14:18 +01:00
Asim Aslam
7aa92fa8b5 add String method to command 2020-07-19 17:20:05 +01:00
ijayer
5077683b70 refactor(logger): fix the name of defaultLogger receiver (#1859)
Closes #1858

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-19 16:55:50 +01:00
Asim Aslam
7f6cefd9c9 fix grpc test 2020-07-19 15:54:33 +01:00
Asim Aslam
647ce61dec some renaming of types in auth 2020-07-19 14:41:31 +01:00
Asim Aslam
d3326efd4b Move out the token package to util 2020-07-19 13:41:23 +01:00
Asim Aslam
6920677f1e Move rules.Verify to auth.VerifyAccess 2020-07-19 13:12:03 +01:00
Asim Aslam
1838e4a1ee remove auth provider 2020-07-19 11:37:40 +01:00
Asim Aslam
96233b2d9b auth jwt / service package comments 2020-07-19 10:53:38 +01:00
Asim Aslam
e082ac42a0 go fmt 2020-07-19 10:51:16 +01:00
Asim Aslam
d7ef224447 add selector package comment 2020-07-19 10:51:04 +01:00
Asim Aslam
8c6f4062ef Remove error proto in favour of go type 2020-07-19 09:29:48 +01:00
ben-toogood
0d860c53a6 runtime/kubernetes: ignore namespace already exists errors (#1852)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-18 11:12:05 +01:00
bcc890e47c router: pass node metadata to route instead of service metadata (#1855)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-07-17 16:45:22 +01:00
Di Wu
f9bf562393 fix typo in comments (#1840)
* remove global error tracking

* rpc_server: fix invalid register err

* fix typo

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-16 16:33:11 +01:00
dfa50a888d make LookupRoute exported (#1850)
* make LookupRoute exported

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* add missing file

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-07-16 16:32:37 +01:00
Asim Aslam
e63b9015ae Add memcache implementation of cache (#1848) 2020-07-16 14:13:38 +01:00
3627e47f04 client/grpc: dont use codec for raw bytes payload (#1847)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-07-16 13:35:06 +01:00
Asim Aslam
7d41c2224e Don't close the stream (#1844) 2020-07-15 14:44:31 +01:00
ben-toogood
68927e875b store/service: use client passed in Init options (#1843) 2020-07-15 14:01:33 +01:00
ben-toogood
0c19a87c89 cmd/cmd: use service namespace as store database (#1842) 2020-07-15 11:31:42 +01:00
ben-toogood
f73ec65ac3 runtime/kubernetes: increase debugging (#1841) 2020-07-15 09:26:25 +01:00
Asim Aslam
b27e71ae64 rip out cmd.DefaultOptions 2020-07-14 22:23:54 +01:00
Asim Aslam
0299517f0d remove config readme 2020-07-14 21:55:05 +01:00
Asim Aslam
e1404a1100 remove secrets package 2020-07-14 21:53:43 +01:00
Asim Aslam
057d61063f Move back command 2020-07-14 20:57:13 +01:00
ben-toogood
73a3f596e8 util/kubernetes/client: set imagePullPolicy to always (#1838) 2020-07-14 15:17:23 +01:00
ben-toogood
0287ab8751 auth/service/proto: add Delete RPC (#1836)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-14 13:41:35 +01:00
Alex Unger
42c28f2b6d Fix Comments (#1833)
* update store url

* fix leftover copy paste
2020-07-14 13:13:32 +01:00
ben-toogood
a2bb0bea2d auth: add token issuer option (#1835) 2020-07-14 13:44:51 +02:00
ben-toogood
9f9c748f9b auth/service: fix account issuer bug (#1834) 2020-07-14 12:16:52 +01:00
ben-toogood
a5e9dc21ca util/wrapper: allow enforcing a specific namespace when verifying requests (#1832)
* auth/jwt: add debugging

* auth: more debugging

* auth: more debugging

* util/wrapper: don't use request context

* util/wrapper: AuthHandlerNamespace

* remove debugging
2020-07-14 10:27:15 +01:00
Janos Dobronszki
3f4b58b58c Let bolt do locking per each List Get etc op, instead of managing fil… (#1831) 2020-07-14 10:35:46 +02:00
Asim Aslam
0a79db498c do not compare snapshot unless non nil (#1830) 2020-07-14 07:52:45 +01:00
Dominic Wong
7c5e3b0f30 report errors from log streaming (#1828) 2020-07-13 17:35:23 +01:00
ben-toogood
07fbb06ed8 auth/service: fix jwt blank refresh bug (#1827) 2020-07-13 17:12:03 +01:00
ben-toogood
a4252ba69c router/registry: use warn error level (#1826) 2020-07-13 15:27:14 +01:00
ben-toogood
8fe4f1f2c3 config/cmd: don't fatally error if config can't be loaded (#1825)
* config/cmd: don't fatally error if config cannot be loaded

* config/cmd: fix log level typo
2020-07-13 15:08:53 +01:00
ben-toogood
2e04fcd718 config/cmd: don't fatally error if auth account cannot be generated (#1824) 2020-07-13 12:52:54 +01:00
ben-toogood
7355455020 auth/service: generate accounts client side if JWT credentials present (#1823) 2020-07-13 10:20:31 +01:00
Lars Lehtonen
040577fb74 transport/grpc: replace deprecated grpc.WithTimeout() (#1822) 2020-07-13 07:35:53 +01:00
ben-toogood
4e7621da18 config/cmd: standardise error handling (#1816)
* runtime/kubernetes: remove reference to runtime cells

* config/cmd: standardize error handling

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-12 07:22:08 +01:00
Huan Huang
8e30ede8c7 track error (#1815)
* track error

* remove useless code

Co-authored-by: huanghuan.27@bytedance.com <huanghuan.27@bytedance.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-12 07:17:56 +01:00
Asim Aslam
630ceb5dad Update README.md (#1820) 2020-07-11 22:18:53 +01:00
Asim Aslam
85ae232936 Add model to service (#1819) 2020-07-11 21:15:59 +01:00
ben-toogood
13ea0eec02 :registry/mdns: fix nil pointer bug (#1818) 2020-07-10 17:47:07 +01:00
ben-toogood
09ec20fded runtime: provide credentials to services (#1817)
* runtime: inject credentials into service

* util/auth: self generate accounts (needed for jwt)

* runtime/kubernetes: add logging for creds

* runtime/kubernetes: serialize secret name

* runtime/kubernetes: remove unused code

* runtime/kubernetes: base64 encode secret

* runtime/kubernetes: remove metadata from secret

* util/kubernetes/client: omit empty secret metadata

* util/kubernetes/client: fix secret template

* util/kubernetes/client: fix secrets

* web: update auth util

* util/auth: fix missing arg

* extend token expiry

* extend token expiry
2020-07-10 16:25:46 +01:00
ben-toogood
3480e0a64e runtime/kubernetes: remove reference to runtime cells (#1814) 2020-07-09 20:21:34 +01:00
ben-toogood
318a80f824 config/cmd: improve cert loading for infra (#1813)
* config/cmd: improve cert loading for infra

* config/cmd: remove certificate_authorities flag

* config/cmd: fix caps

* config/cmd: fix bug with IsSet

* config/cmd: fix bool flags
2020-07-09 18:02:24 +01:00
ben-toogood
6d9a38a747 kubernetes: fixes for production (#1812)
* util/kubernetes/client: add secrets to deployments

* util/kubernetes/client: remove ServiceAccountName override

* debugging

* runtime/kubernetes: fix error

* runtime/kubernetes: remove test secret

* util/kubernetes/client: update default image

* util/kubernetes/client: remove default command for deployments

* runtime/kubernetes: pass source as arg

* runtime/kubernetes: remove debugging

* util/kubernetes/client: revert default image change
2020-07-09 16:29:01 +01:00
Asim Aslam
58d6726380 The start of mud - the micro data model (#1811)
* The start of mud - the micro data model

* add comments
2020-07-09 12:11:32 +01:00
Huan Huang
e5db6ea8a7 close r/w (#1810)
Co-authored-by: huanghuan.27@bytedance.com <huanghuan.27@bytedance.com>
2020-07-09 10:28:20 +01:00
Huan Huang
3468331506 feat: refactor register func (#1807)
Co-authored-by: huanghuan.27@bytedance.com <huanghuan.27@bytedance.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-08 18:38:01 +01:00
王旭
1bac08cc0e server.Init(Wait(nil)) update server options context (#1804)
Co-authored-by: wangxu <wangxu@oneniceapp.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-08 18:36:07 +01:00
ben-toogood
333320dcb8 config/cmd: secure broker (#1806)
* config/cmd: secure broker

* config/cmd: remove testing
2020-07-08 16:22:48 +01:00
Asim Aslam
ce12c040fa Model updates 2020-07-08 15:09:18 +01:00
Asim Aslam
ee36e26edc Add model interface (#1808) 2020-07-08 14:57:45 +01:00
Asim Aslam
3ffb899951 Fix cache options 2020-07-08 14:53:38 +01:00
Asim Aslam
00bd2bc65f cache interface (#1805) 2020-07-08 11:08:59 +01:00
ben-toogood
86f4235aaf config/cmd: custom certificate authorities & secure registry (#1803)
* config/cmd: add registry_secure option

* config/cmd: tmp load ca

* config/cmd: tmp load ca

* config/cmd: refactor certificate_authorities setup

* config/cmd: improve usage
2020-07-08 08:50:08 +01:00
Di Wu
b37f9c94b8 Fix invalid register check err in log (#1801)
* remove global error tracking

* rpc_server: fix invalid register err

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-07 14:14:23 +01:00
Asim Aslam
0ed1c70d29 Update README.md 2020-07-07 13:34:42 +01:00
Asim Aslam
db8e10834b Update README.md (#1802) 2020-07-07 13:33:54 +01:00
ben-toogood
0a937745cd auth: pass namespace options in auth service requests (#1800)
* auth: pass namespace options in auth service requests

* auth/service/proto: update field index

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-07 08:30:25 +01:00
ben-toogood
f5ed7e5833 config/source/service: change default namespace to micro (#1798)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-07 08:19:52 +01:00
Metauro
859b9e7786 feat(errors): add gateway, service error (#1797) 2020-07-06 20:14:59 +01:00
Lars Lehtonen
2b033b6495 store/cockroach: fix dropped errors (#1796)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-06 20:14:36 +01:00
Di Wu
51caf2a24e remove global error tracking (#1777)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-06 20:14:22 +01:00
Enix Yu
eaa46c2de7 Add recover in grpc service handler (#1727)
* 🐛 bug(grpc): add missing recover in grpc unkonwn service handler

* Add grpc handler recover testcases

* improve test case

Co-authored-by: ben-toogood <bentoogood@gmail.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-06 20:13:51 +01:00
Colin Hoglund
90dca65f55 make environment variable interpolation preprocessor optional (#1715) 2020-07-06 20:13:35 +01:00
97ae2979ad pass request context from request rpc endpoints (#1799)
http middleware can add additional metadata to context,
for example tracing wrappers, pass down it to underlining
services

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-07-06 13:55:17 +01:00
Asim Aslam
6f309dada3 fix typo (#1789) 2020-07-06 13:52:42 +01:00
Huan Huang
f99b436ec2 feat: fix register bug (#1791) 2020-07-03 14:38:25 +01:00
ben-toogood
c817f29d6e router/registry: fix registry not found bug (#1794) 2020-07-03 13:35:59 +01:00
ben-toogood
f744c6248f runtime/service: pass namespace option to service (#1792) 2020-07-03 11:37:14 +01:00
ben-toogood
4ff114e798 router/registry: fix rlock bug when registry errors (#1788)
* client: add select options

* router/registry: fix rlock bug when registry errors

* Revert "client: add select options"

This reverts commit 4d5283452e183f7387b604b51bde1deaf87ee391.

* router/registry: findRoutes under rlock

* add test

Co-authored-by: Dominic Wong <dom@micro.mu>
2020-07-02 18:29:11 +01:00
ben-toogood
c58ac35dfc broker/service: use wrapped micro client to authenticate requests (#1782) 2020-07-02 17:54:53 +01:00
ben-toogood
b5314829fa client/{grpc,rpc}: fix previous breaking change with CallFunc (router.Route vs registry.Node) (#1781) 2020-07-02 17:26:45 +01:00
ben-toogood
41c7688697 options: add router option (#1783)
* broker/service: use wrapped micro client to authenticate requests

* options: add router option

* Revert "broker/service: use wrapped micro client to authenticate requests"

This reverts commit f2383f37c86467b4ce534313a7f59993a904a712.
2020-07-02 17:17:07 +01:00
ben-toogood
b021546c09 client: add select options (#1786) 2020-07-02 17:03:08 +01:00
ben-toogood
6898a65508 selector: add filters to replace depricated client/selector filters (#1785) 2020-07-02 16:09:48 +01:00
ben-toogood
d577dd6abe config: add namespace support (#1780)
* config/cmd: use service namespace for config

* config/service: add namespace option to list
2020-07-02 14:07:58 +01:00
Janos Dobronszki
3c633e3577 Sparse checkout of repos, all repo downloads happen to different folders for concurrency support (#1779) 2020-07-02 14:40:40 +02:00
ben-toogood
174e44b846 Deprecate client/selector (#1767)
* client/{grpc,rpc}: depricate selector (wip)

* {client,cmd}: remove client/selector

* deprecate client/selector

* router/static: fix lookup

* config/cmd: add support for legacy static selector flag

* config/cmd: add support for legacy dns selector flag
2020-07-01 17:06:59 +01:00
Dominic Wong
a63480a81a router/registry: fix fallback fails if service has been seen already (#1776) 2020-07-01 15:20:30 +01:00
ben-toogood
6d9d94b105 api/router/registry: use resolved domain (#1774) 2020-07-01 12:23:55 +01:00
ben-toogood
64e9185386 router/registry: fix nil eventChan bug (#1773)
* router/registry: fix nil eventChan bug
2020-07-01 12:03:13 +01:00
ben-toogood
1b5c83f3cc registry/mdns: fix deregister bug (#1771)
* registry/mdns: fix deregister bug

* Update registry/mdns_registry.go

Co-authored-by: Dominic Wong <domwongemail@googlemail.com>
2020-07-01 11:09:20 +01:00
ben-toogood
979af853b9 proxy/mucp: use Micro-Namespace to filter networks (#1772) 2020-07-01 10:55:46 +01:00
ben-toogood
a64078b5c3 router/service: handle not found error (#1770) 2020-07-01 10:18:19 +01:00
Dominic Wong
58845d7012 only prewarm the route table if requested (#1765) 2020-07-01 10:05:21 +01:00
ben-toogood
dcf01ebbf0 router/registry: fix concurrent map iteration and map write (#1762) 2020-06-30 17:40:38 +01:00
Dominic Wong
355ad2a1af push tags to docker hub (#1766) 2020-06-30 17:26:59 +01:00
ben-toogood
b882ff3df9 selector: update selector.Select to accept a slice of structs (#1764) 2020-06-30 15:51:26 +01:00
ben-toogood
6337c92cd0 selector: new selector interface with random & roundrobin implementation (#1761)
* selector: implement new selector interface plus random & roundrobin implementations

* selector/roundrobin: remove unused consts

* router: add close method to interface

* selector/roundrobin: fix concurrent map iteration and map write

* selector: replace variadic argument on Select
2020-06-30 14:54:38 +01:00
ben-toogood
a95accad56 router: add metadata to routes (#1763)
* router: add metadata to route

* router/registry: set node metadata in route

* router/service: pass metadata to/from router service
2020-06-30 14:10:13 +01:00
Dominic Wong
6532b6208b MDNS registry fix for users on VPNs (#1759)
* filter out unsolicited responses
* send to local ip in case
* allow ip func to be passed in. add option for sending to 0.0.0.0
2020-06-30 11:12:52 +01:00
ben-toogood
0f5c53b6e4 selector: use custom domain (#1760)
* util/wrapper: improve auth errors

* client: add network call option

* client/selector: add domain select option

* client/grpc: pass network option to selector
2020-06-30 10:07:52 +01:00
ben-toogood
deea8fecf4 router/registry: add fallback if routes aren't found in the cache (#1758)
* router/registry: add fallback if routes aren't found in the cache

* router: fix rlock bug

* router/registry: pass fetchRoutes into the table, not the router
2020-06-30 09:53:49 +01:00
ben-toogood
df3e5364ca api/resolver: add resolve options (#1756)
* api/resolver: Resolve options

* router/registry: fix init bug

* router/registry: fix wildcard query bug

* web: fix registation domain bug

* registry/etcd: pass domain in service metadata

* api/resolver/subdomain: expose domain func

* Update api/resolver/subdomain/subdomain.go

Co-authored-by: Dominic Wong <domwongemail@googlemail.com>

Co-authored-by: Dominic Wong <domwongemail@googlemail.com>
2020-06-29 16:37:45 +01:00
Di Wu
132c1e35fe Fix invalid usage for sync.WaitGroup (#1752)
* Custom private blocks

* Fix invalid usage for sync.WaitGroup

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-06-27 20:07:04 +01:00
Dominic Wong
5967a68e78 cached file store (#1739)
* cached file store
2020-06-26 16:13:53 +01:00
ben-toogood
104b7d8f8d api/resolver: update resolver to enable subdomain routing (#1747)
* api/resolver: update domain / service prefix usage

* api/resolver/subdomain: implement subdomain resolver for domain resolution

* api/handler: fix tests
2020-06-26 14:28:18 +01:00
ben-toogood
4f0f4326df router: improve router configuration (#1745)
* router: update default address to :8084

* service: add router to service options

* config/cmd: improve router setup
2020-06-26 10:38:11 +01:00
ben-toogood
ee02511658 proxy/mucp: add support for multi-tenancy (#1746) 2020-06-26 10:31:06 +01:00
ben-toogood
a8fc5590a8 client/selector: query across multiple domains (#1725)
* client/selector: query across multiple domains

* client/selector: check for nil services

* config/cmd: fix merge bug
2020-06-25 15:40:23 +01:00
ben-toogood
bc60f23ff6 config/cmd: setup registry before router (#1743) 2020-06-25 15:24:31 +01:00
ben-toogood
2000da6fd8 router/registry: add support for registry domains (#1744) 2020-06-25 12:35:00 +01:00
ben-toogood
5ab475636a server/{grpc,rpc}: fix deregister domain bug (#1742)
* server/{grpc,rpc}: fix deregister domain bug

* server/grpc: remove unnecessary slice
2020-06-25 11:25:43 +01:00
ben-toogood
51b4ab0abc registry/memory: watcher bug fixes (#1740)
* registry/memory: watcher bugfixes

* registry/memory: fix nil watcher bug

* registry/memory: fix watcher test
2020-06-25 11:02:35 +01:00
ben-toogood
687a5e2e58 util/wrapper: fix 401 error when requesting the default namespace (#1741) 2020-06-25 10:19:03 +01:00
Dominic Wong
fcd307d902 Build and test forked PRs properly (#1738)
* build forks on PRs properly
2020-06-24 23:41:27 +01:00
ben-toogood
00cd07a3a6 util/wrapper: set auth credentials on streams (#1735) 2020-06-24 16:45:34 +01:00
Dominic Wong
a2a1f4dfbd support mono repo deps (#1736)
* support mono repo deps

* add protoc
2020-06-24 16:27:22 +01:00
ben-toogood
2b506b1a2a auth/service: use address option since router may not be configured (#1734) 2020-06-24 13:47:43 +01:00
ben-toogood
a2550820d3 router: add to service options; add dns and static implementations (#1733)
* config/cmd: add router to service options

* router/service: use micro client
2020-06-24 11:46:51 +01:00
ben-toogood
c940961574 router: update interface (#1732)
* router: replace Start and Stop with Close

* router: update default network to micro

* router: update tests
2020-06-24 11:09:16 +01:00
Dominic Wong
695cc9d526 Build against micro and examples on pull requests (#1724)
* Build against micro and examples on pull requests
2020-06-19 17:19:58 +01:00
ben-toogood
87543b2c8a registry/etcd: add support for domain options (#1714) 2020-06-19 14:58:16 +01:00
ben-toogood
5f9c3a6efd registry/cache: add support for the domain option (#1722) 2020-06-19 13:16:44 +01:00
ben-toogood
2b889087bd config/cmd: fix selector setup bug (#1723) 2020-06-19 13:11:48 +01:00
ben-toogood
ece02a6d21 util/wrapper: fix noop auth nil account bug (#1721)
* util/wrapper: fix noop nil account

* util/wrapper: improve comments

* util/wrapper: update tests
2020-06-19 12:16:39 +01:00
ben-toogood
58c6bbbf6b registry/service: pass domain options via rpc (#1719)
* registry/service: regenerate proto

* registry/service: pass domain in proto request options

* registry/service: stop defaulting metadata

* registry: add default domain const; remove from implementations

* registry/memory: fix typo
2020-06-19 10:34:12 +01:00
ben-toogood
c16f4b741c server: register in the services namespace (#1718) 2020-06-19 09:24:32 +01:00
Colin Hoglund
83cecdb294 config: use configured reader by default (#1717) 2020-06-19 08:49:30 +01:00
ben-toogood
8c7c27c573 registry/memory: add support for domain options (#1713)
* registry/memory: add support for the domain options

* registry/memory: swap Fatal test cases with Error

* registry/memory: fix wildcard not found bug

* registry/memory: replace locks with rlocks

* registry/memory: fix deregistration bug
2020-06-18 12:39:19 +01:00
ben-toogood
5fd36d6cc0 config/cmd: remove package duplicate initialization (#1711) 2020-06-18 10:14:04 +01:00
ben-toogood
3b40fde68b registry/mdns: add domain support (#1708)
* registry: add domain options

* registry/mdns: implement domain options

* registry/mdns: return node domain in metadata when querying using wildcard

* Fix nil pointer exception

* registry/mdns: return error from deregister

* registy/mdns: rename tld => domain
2020-06-17 13:23:41 +01:00
ben-toogood
9d3365c4be auth: rename auth.Namespace to auth.Issuer (#1710) 2020-06-17 12:26:27 +01:00
sunfuze
2efb459c66 Fix config watch (#1670)
* add dirty overrite test case

* need version to figure out if config need update or not

* using nanosecond as version for two goroutine can run in same second

* config should check snapshot version when update

* set checksum of ChangeSet

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-06-16 17:10:52 +01:00
Di Wu
6add74b4f6 Custom private blocks (#1705)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-06-16 17:05:42 +01:00
Dominic Wong
c67d78f1ef update PR template, not using gitflow anymore 2020-06-16 17:00:15 +01:00
Dominic Wong
a89610ffea Merge pull request #1706 from micro/develop
Develop -> Master. Abandoning gitflow model
2020-06-16 16:57:40 +01:00
Dominic Wong
da9bb11240 Merge branch 'master' into develop 2020-06-16 16:52:24 +01:00
Dmitry Kozlov
a3a1a84172 Split long discord output message into the chunks by 2000 characters (#1704)
Signed-off-by: Dmitry Kozlov <dmitry.f.kozlov@gmail.com>
2020-06-15 22:22:00 +01:00
ben-toogood
1179d7e89a registry/mdns: fix nil host bug (#1703) 2020-06-15 16:13:45 +01:00
Asim Aslam
a5df913926 Update README.md (#1695) 2020-06-12 15:07:19 +01:00
Asim Aslam
9ce706191b Update FUNDING.yml (#1692) 2020-06-12 15:07:19 +01:00
Dominic Wong
0327f30e3c Fix regex detection. Fixes #1663 (#1696) 2020-06-12 10:42:52 +01:00
Dominic Wong
0ce132eb8f Fix race condition when updating process being waited on (#1694) 2020-06-12 10:42:52 +01:00
Janos Dobronszki
00b76e0a64 Initialize selector before we make an auth.Generate call (#1693) 2020-06-12 10:42:52 +01:00
Dominic Wong
aec27be9b4 Fix race when opening DB for first time (#1691) 2020-06-12 10:42:52 +01:00
Dominic Wong
86dfcb819b Ignore "no such process" error (#1686)
* Cleanup how status is updated for service. Ignore "no such process" error as it could be that the pid died

* add nice error log to record process error exit
2020-06-12 10:42:52 +01:00
Janos Dobronszki
d613804b0a Sigterm instead of Sigkill (#1687)
Co-authored-by: Dominic Wong <domwongemail@googlemail.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-06-12 10:42:52 +01:00
92e9d05432 api/handler/rpc: dont log error on normal websocket error code (#1688)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-06-12 10:42:52 +01:00
ben-toogood
8dfd93e915 util/wrapper: Add Static Client wrapper (#1685)
* util/wrapper: Add Static Client wrapper

* util/wrapper/static: pass address to stream too

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* add static client wrapper tests

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* server: fix error message spaces between words

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* server/{rpc,grpc}: replace log.Error with log.Errorf

* server/grpc: fix log typo

* server/rpc: fix log typo

Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-06-12 10:42:52 +01:00
Dominic Wong
e5136332e3 Add build and test of micro to pre-release testing (#1684)
* fix up example test build

* build and test micro when cutting a new release
2020-06-12 10:42:52 +01:00
Dominic Wong
f10fd4b479 Build all micro/examples for release-X.X.X branches (#1683)
* Build all the examples on push to any release branch
2020-06-12 10:42:52 +01:00
ben-toogood
74368026a5 Fix incorrect namespace variable name (merge conflict) (#1677) 2020-06-12 10:42:52 +01:00
ben-toogood
fde1aa9d6a Move auth account creation to config/cmd (#1676) 2020-06-12 10:42:52 +01:00
ben-toogood
f45cdba9ba Apply wrappers to gRPC streams (#1675)
* Add wrappers to grpc streams

* Fix typo
2020-06-12 10:42:52 +01:00
Dominic Wong
73c2f25935 Fix regex detection. Fixes #1663 (#1696) 2020-06-10 11:18:03 +01:00
Asim Aslam
b270860b79 Update README.md (#1695) 2020-06-10 10:22:53 +01:00
Dominic Wong
8e81cea96f Fix race condition when updating process being waited on (#1694) 2020-06-10 09:36:41 +01:00
Janos Dobronszki
cdd8f9fd82 Initialize selector before we make an auth.Generate call (#1693) 2020-06-09 12:47:31 +02:00
Asim Aslam
e7ba930236 Update FUNDING.yml (#1692) 2020-06-08 18:12:19 +01:00
Dominic Wong
a346064eaf Fix race when opening DB for first time (#1691) 2020-06-08 16:19:22 +01:00
Dominic Wong
47bdd5c993 Ignore "no such process" error (#1686)
* Cleanup how status is updated for service. Ignore "no such process" error as it could be that the pid died

* add nice error log to record process error exit
2020-06-08 10:47:25 +01:00
Janos Dobronszki
9af12ff9df Sigterm instead of Sigkill (#1687)
Co-authored-by: Dominic Wong <domwongemail@googlemail.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-06-06 14:04:14 +01:00
6c7bcf3883 api/handler/rpc: dont log error on normal websocket error code (#1688)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-06-06 00:03:43 +03:00
ben-toogood
bbc3b7040b util/wrapper: Add Static Client wrapper (#1685)
* util/wrapper: Add Static Client wrapper

* util/wrapper/static: pass address to stream too

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* add static client wrapper tests

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* server: fix error message spaces between words

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

* server/{rpc,grpc}: replace log.Error with log.Errorf

* server/grpc: fix log typo

* server/rpc: fix log typo

Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-06-05 10:18:35 +01:00
Dominic Wong
582f2e8b94 Add build and test of micro to pre-release testing (#1684)
* fix up example test build

* build and test micro when cutting a new release
2020-06-04 13:46:22 +01:00
Dominic Wong
bd3ef67328 Build all micro/examples for release-X.X.X branches (#1683)
* Build all the examples on push to any release branch
2020-06-04 09:32:17 +01:00
Dominic Wong
1ccd4cd940 Merge branch 'master' into develop 2020-06-03 10:41:39 +01:00
Dominic Wong
aa679f7a73 Create PULL_REQUEST_TEMPLATE.md 2020-06-03 10:32:28 +01:00
ben-toogood
003731ace9 Fix incorrect namespace variable name (merge conflict) (#1677) 2020-06-03 09:48:19 +01:00
Asim Aslam
7b379bf1f1 WIP: Add metadata to store record (#1604)
* Add metadata to store record

* Add metadata to cockroach store

* add metadata to store service implementation

* fix breaking cache test

* Test/fix cockroach metadata usage

* fix store memory metadata bug
2020-06-03 09:45:08 +01:00
ben-toogood
b6f3e8b715 Move auth account creation to config/cmd (#1676) 2020-06-03 09:43:20 +01:00
ben-toogood
8f6ec21b91 Apply wrappers to gRPC streams (#1675)
* Add wrappers to grpc streams

* Fix typo
2020-06-02 17:56:26 +01:00
Dominic Wong
e4e56b0f3f Merge pull request #1671 from sadwxqezc/fix-jwt
Fix jwt revoke
2020-06-02 09:27:14 +01:00
huanghuan.27@bytedance.com
219d29f664 fix jwt revoke 2020-06-02 10:26:33 +08:00
Asim Aslam
8fb138af06 Update README.md 2020-05-31 11:56:55 +01:00
476 changed files with 10032 additions and 24211 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,3 @@
# These are supported funding model platforms
github: asim
github: micro

9
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,9 @@
## Pull Request template
Please, go through these steps before clicking submit on this PR.
1. Give a descriptive title to your PR.
2. Provide a description of your changes.
3. Make sure you have some relevant tests.
4. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if applicable).
**PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING**

View File

@@ -19,3 +19,4 @@ jobs:
name: micro/go-micro
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
tag_names: true

View File

@@ -0,0 +1,41 @@
#!/bin/bash
# set -x
function build_binary {
echo building $1
pushd $1
go build -o _main
local ret=$?
if [ $ret -gt 0 ]; then
failed=1
failed_arr+=($1)
fi
popd
}
function is_main {
grep "package main" -l -dskip $1/*.go > /dev/null 2>&1
}
function check_dir {
is_main $1
local ret=$?
if [ $ret == 0 ]; then
build_binary $1 $2
fi
for filename in $1/*; do
if [ -d $filename ]; then
check_dir $filename $2
fi
done
}
failed_arr=()
failed=0
go mod edit -replace github.com/micro/go-micro/v2=github.com/$2/v2@$1
check_dir . $1
if [ $failed -gt 0 ]; then
echo Some builds failed
printf '%s\n' "${failed_arr[@]}"
fi
exit $failed

19
.github/workflows/scripts/build-micro.sh vendored Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
# set -x
failed=0
go mod edit -replace github.com/micro/go-micro/v2=github.com/$2/v2@$1
# basic test, build the binary
go install
failed=$?
if [ $failed -gt 0 ]; then
exit $failed
fi
# unit tests
IN_TRAVIS_CI=yes go test -v ./...
./scripts/test-docker.sh
# Generate keys for JWT tests
ssh-keygen -f /tmp/sshkey -m pkcs8 -q -N ""
ssh-keygen -f /tmp/sshkey -e -m pkcs8 > /tmp/sshkey.pub
go clean -testcache && IN_TRAVIS_CI=yes go test --tags=integration -v ./test

View File

@@ -1,6 +1,6 @@
# Go Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Go.Dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/micro/go-micro?tab=doc) [![Travis CI](https://api.travis-ci.org/micro/go-micro.svg?branch=master)](https://travis-ci.org/micro/go-micro) [![Go Report Card](https://goreportcard.com/badge/micro/go-micro)](https://goreportcard.com/report/github.com/micro/go-micro) <a href="https://slack.micro.mu"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
# Go Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Go.Dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/micro/go-micro?tab=doc) [![Travis CI](https://api.travis-ci.org/micro/go-micro.svg?branch=master)](https://travis-ci.org/micro/go-micro) [![Go Report Card](https://goreportcard.com/badge/micro/go-micro)](https://goreportcard.com/report/github.com/micro/go-micro)
Go Micro is a framework for distributed systems development.
Go Micro is a standard library for distributed systems development.
## Overview
@@ -48,24 +48,7 @@ are pluggable and allows Go Micro to be runtime agnostic. You can plugin any und
## Getting Started
To make use of Go Micro
```golang
import "github.com/micro/go-micro/v2"
// create a new service
service := micro.NewService(
micro.Name("helloworld"),
)
// initialise flags
service.Init()
// start the service
service.Run()
```
See the [docs](https://dev.micro.mu) for detailed information on the architecture, installation and use of go-micro.
See [pkg.go.dev](https://pkg.go.dev/github.com/micro/go-micro?tab=doc) for usage.
## License

View File

@@ -2,7 +2,7 @@
package command
var (
// Commmands keyed by golang/regexp patterns
// Commands keyed by golang/regexp patterns
// regexp.Match(key, input) is used to match
Commands = map[string]Command{}
)

View File

@@ -6,8 +6,8 @@ import (
"sync"
"github.com/bwmarrin/discordgo"
"github.com/micro/go-micro/v2/agent/input"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/agent/input"
"github.com/micro/go-micro/v3/logger"
)
type discordConn struct {
@@ -70,12 +70,32 @@ func (dc *discordConn) Recv(event *input.Event) error {
}
}
func ChunkString(s string, chunkSize int) []string {
var chunks []string
runes := []rune(s)
if len(runes) == 0 {
return []string{s}
}
for i := 0; i < len(runes); i += chunkSize {
nn := i + chunkSize
if nn > len(runes) {
nn = len(runes)
}
chunks = append(chunks, string(runes[i:nn]))
}
return chunks
}
func (dc *discordConn) Send(e *input.Event) error {
fields := strings.Split(e.To, ":")
_, err := dc.master.session.ChannelMessageSend(fields[0], string(e.Data))
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error("[bot][loop][send]", err)
for _, chunk := range ChunkString(string(e.Data), 2000) {
_, err := dc.master.session.ChannelMessageSend(fields[0], chunk)
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error("[bot][loop][send]", err)
}
}
}
return nil

View File

@@ -9,7 +9,7 @@ import (
"github.com/bwmarrin/discordgo"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/agent/input"
"github.com/micro/go-micro/v3/agent/input"
)
func init() {

View File

@@ -7,7 +7,7 @@ import (
"sync"
"time"
"github.com/micro/go-micro/v2/agent/input"
"github.com/micro/go-micro/v3/agent/input"
"github.com/nlopes/slack"
)

View File

@@ -5,7 +5,7 @@ import (
"sync"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/agent/input"
"github.com/micro/go-micro/v3/agent/input"
"github.com/nlopes/slack"
)

View File

@@ -6,8 +6,8 @@ import (
"sync"
"github.com/forestgiant/sliceutil"
"github.com/micro/go-micro/v2/agent/input"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/agent/input"
"github.com/micro/go-micro/v3/logger"
tgbotapi "gopkg.in/telegram-bot-api.v4"
)

View File

@@ -6,7 +6,7 @@ import (
"sync"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/agent/input"
"github.com/micro/go-micro/v3/agent/input"
tgbotapi "gopkg.in/telegram-bot-api.v4"
)

View File

@@ -11,9 +11,9 @@ import (
import (
context "context"
api "github.com/micro/go-micro/v2/api"
client "github.com/micro/go-micro/v2/client"
server "github.com/micro/go-micro/v2/server"
api "github.com/micro/go-micro/v3/api"
client "github.com/micro/go-micro/v3/client"
server "github.com/micro/go-micro/v3/server"
)
// Reference imports to suppress errors if they are not otherwise used.

View File

@@ -5,8 +5,8 @@ import (
"regexp"
"strings"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/server"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/server"
)
type Api interface {
@@ -18,7 +18,7 @@ type Api interface {
Register(*Endpoint) error
// Register a route
Deregister(*Endpoint) error
// Implemenation of api
// Implementation of api
String() string
}

View File

@@ -4,13 +4,13 @@ package api
import (
"net/http"
goapi "github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
api "github.com/micro/go-micro/v2/api/proto"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/selector"
"github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/util/ctx"
goapi "github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/handler"
"github.com/micro/go-micro/v3/api/handler/util"
api "github.com/micro/go-micro/v3/api/proto"
"github.com/micro/go-micro/v3/client"
"github.com/micro/go-micro/v3/errors"
"github.com/micro/go-micro/v3/util/ctx"
)
type apiHandler struct {
@@ -71,10 +71,8 @@ func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// create the context from headers
cx := ctx.FromRequest(r)
// create strategy
so := selector.WithStrategy(strategy(service.Services))
if err := c.Call(cx, req, rsp, client.WithSelectOption(so)); err != nil {
if err := c.Call(cx, req, rsp, client.WithRouter(util.Router(service.Services))); err != nil {
w.Header().Set("Content-Type", "application/json")
ce := errors.Parse(err.Error())
switch ce.Code {

View File

@@ -7,9 +7,7 @@ import (
"net/http"
"strings"
api "github.com/micro/go-micro/v2/api/proto"
"github.com/micro/go-micro/v2/client/selector"
"github.com/micro/go-micro/v2/registry"
api "github.com/micro/go-micro/v3/api/proto"
"github.com/oxtoacart/bpool"
)
@@ -109,11 +107,3 @@ func requestToProto(r *http.Request) (*api.Request, error) {
return req, nil
}
// strategy is a hack for selection
func strategy(services []*registry.Service) selector.Strategy {
return func(_ []*registry.Service) selector.Next {
// ignore input to this function, use services above
return selector.Random(services)
}
}

View File

@@ -11,9 +11,9 @@ import (
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/v2/api/handler"
proto "github.com/micro/go-micro/v2/api/proto"
"github.com/micro/go-micro/v2/util/ctx"
"github.com/micro/go-micro/v3/api/handler"
proto "github.com/micro/go-micro/v3/api/proto"
"github.com/micro/go-micro/v3/util/ctx"
"github.com/oxtoacart/bpool"
)

View File

@@ -4,13 +4,14 @@ package http
import (
"errors"
"fmt"
"math/rand"
"net/http"
"net/http/httputil"
"net/url"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/client/selector"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/handler"
"github.com/micro/go-micro/v3/registry"
)
const (
@@ -20,7 +21,7 @@ const (
type httpHandler struct {
options handler.Options
// set with different initialiser
// set with different initializer
s *api.Service
}
@@ -64,16 +65,19 @@ func (h *httpHandler) getService(r *http.Request) (string, error) {
return "", errors.New("no route found")
}
// create a random selector
next := selector.Random(service.Services)
// get the next node
s, err := next()
if err != nil {
return "", nil
// get the nodes for this service
var nodes []*registry.Node
for _, srv := range service.Services {
nodes = append(nodes, srv.Nodes...)
}
return fmt.Sprintf("http://%s", s.Address), nil
// select a random node
if len(nodes) == 0 {
return "", errors.New("no route found")
}
node := nodes[rand.Int()%len(nodes)]
return fmt.Sprintf("http://%s", node.Address), nil
}
func (h *httpHandler) String() string {

View File

@@ -6,13 +6,13 @@ import (
"net/http/httptest"
"testing"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v2/api/resolver/vpath"
"github.com/micro/go-micro/v2/api/router"
regRouter "github.com/micro/go-micro/v2/api/router/registry"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/memory"
"github.com/micro/go-micro/v3/api/handler"
"github.com/micro/go-micro/v3/api/resolver"
"github.com/micro/go-micro/v3/api/resolver/vpath"
"github.com/micro/go-micro/v3/api/router"
regRouter "github.com/micro/go-micro/v3/api/router/registry"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/registry/memory"
)
func testHttp(t *testing.T, path, service, ns string) {
@@ -58,7 +58,7 @@ func testHttp(t *testing.T, path, service, ns string) {
router.WithHandler("http"),
router.WithRegistry(r),
router.WithResolver(vpath.NewResolver(
resolver.WithNamespace(resolver.StaticNamespace(ns)),
resolver.WithServicePrefix(ns),
)),
)

View File

@@ -1,9 +1,9 @@
package handler
import (
"github.com/micro/go-micro/v2/api/router"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/grpc"
"github.com/micro/go-micro/v3/api/router"
"github.com/micro/go-micro/v3/client"
"github.com/micro/go-micro/v3/client/grpc"
)
var (
@@ -62,7 +62,7 @@ func WithClient(c client.Client) Option {
}
}
// WithmaxRecvSize specifies max body size
// WithMaxRecvSize specifies max body size
func WithMaxRecvSize(size int64) Option {
return func(o *Options) {
o.MaxRecvSize = size

View File

@@ -5,25 +5,23 @@ import (
"encoding/json"
"io"
"net/http"
"net/textproto"
"strconv"
"strings"
jsonpatch "github.com/evanphx/json-patch/v5"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/api/internal/proto"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/selector"
"github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v2/codec/jsonrpc"
"github.com/micro/go-micro/v2/codec/protorpc"
"github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/util/ctx"
"github.com/micro/go-micro/v2/util/qson"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/handler"
"github.com/micro/go-micro/v3/api/handler/util"
"github.com/micro/go-micro/v3/api/internal/proto"
"github.com/micro/go-micro/v3/client"
"github.com/micro/go-micro/v3/codec"
"github.com/micro/go-micro/v3/codec/jsonrpc"
"github.com/micro/go-micro/v3/codec/protorpc"
"github.com/micro/go-micro/v3/errors"
"github.com/micro/go-micro/v3/logger"
"github.com/micro/go-micro/v3/metadata"
"github.com/micro/go-micro/v3/util/ctx"
"github.com/micro/go-micro/v3/util/qson"
"github.com/oxtoacart/bpool"
)
@@ -65,14 +63,6 @@ func (b *buffer) Write(_ []byte) (int, error) {
return 0, nil
}
// strategy is a hack for selection
func strategy(services []*registry.Service) selector.Strategy {
return func(_ []*registry.Service) selector.Next {
// ignore input to this function, use services above
return selector.Random(services)
}
}
func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if h.opts.MaxRecvSize > 0 {
@@ -113,36 +103,17 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// create context
cx := ctx.FromRequest(r)
// get context from http handler wrappers
md, ok := metadata.FromContext(r.Context())
if !ok {
md = make(metadata.Metadata)
}
// fill contex with http headers
md["Host"] = r.Host
md["Method"] = r.Method
// get canonical headers
for k, _ := range r.Header {
// may be need to get all values for key like r.Header.Values() provide in go 1.14
md[textproto.CanonicalMIMEHeaderKey(k)] = r.Header.Get(k)
}
// merge context with overwrite
cx = metadata.MergeContext(cx, md, true)
// set merged context to request
*r = *r.Clone(cx)
// if stream we currently only support json
if isStream(r, service) {
// drop older context as it can have timeouts and create new
// md, _ := metadata.FromContext(cx)
//serveWebsocket(context.TODO(), w, r, service, c)
serveWebsocket(cx, w, r, service, c)
return
}
// create strategy
so := selector.WithStrategy(strategy(service.Services))
// create custom router
callOpt := client.WithRouter(util.Router(service.Services))
// walk the standard call path
// get payload
@@ -174,7 +145,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
)
// make the call
if err := c.Call(cx, req, response, client.WithSelectOption(so)); err != nil {
if err := c.Call(cx, req, response, callOpt); err != nil {
writeError(w, r, err)
return
}
@@ -209,7 +180,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
client.WithContentType(ct),
)
// make the call
if err := c.Call(cx, req, &response, client.WithSelectOption(so)); err != nil {
if err := c.Call(cx, req, &response, callOpt); err != nil {
writeError(w, r, err)
return
}
@@ -294,7 +265,7 @@ func requestPayload(r *http.Request) ([]byte, error) {
// otherwise as per usual
ctx := r.Context()
// dont user meadata.FromContext as it mangles names
// dont user metadata.FromContext as it mangles names
md, ok := metadata.FromContext(ctx)
if !ok {
md = make(map[string]string)

View File

@@ -7,7 +7,7 @@ import (
"testing"
"github.com/golang/protobuf/proto"
go_api "github.com/micro/go-micro/v2/api/proto"
go_api "github.com/micro/go-micro/v3/api/proto"
)
func TestRequestPayloadFromRequest(t *testing.T) {

View File

@@ -12,11 +12,11 @@ import (
"github.com/gobwas/httphead"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/selector"
raw "github.com/micro/go-micro/v2/codec/bytes"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/handler/util"
"github.com/micro/go-micro/v3/client"
raw "github.com/micro/go-micro/v3/codec/bytes"
"github.com/micro/go-micro/v3/logger"
)
// serveWebsocket will stream rpc back over websockets assuming json
@@ -110,9 +110,11 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
client.StreamingRequest(),
)
so := selector.WithStrategy(strategy(service.Services))
// create custom router
callOpt := client.WithRouter(util.Router(service.Services))
// create a new stream
stream, err := c.Stream(ctx, req, client.WithSelectOption(so))
stream, err := c.Stream(ctx, req, callOpt)
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
@@ -185,7 +187,11 @@ func writeLoop(rw io.ReadWriter, stream client.Stream) {
if err != nil {
if wserr, ok := err.(wsutil.ClosedError); ok {
switch wserr.Code {
case ws.StatusGoingAway:
// this happens when user leave the page
return
case ws.StatusNormalClosure, ws.StatusNoStatusRcvd:
// this happens when user close ws connection, or we don't get any status
return
}
}

View File

@@ -0,0 +1,32 @@
package util
import (
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/router"
)
// Router is a hack for API routing
func Router(srvs []*registry.Service) router.Router {
var routes []router.Route
for _, srv := range srvs {
for _, n := range srv.Nodes {
routes = append(routes, router.Route{Address: n.Address, Metadata: n.Metadata})
}
}
return &apiRouter{routes: routes}
}
func (r *apiRouter) Lookup(...router.QueryOption) ([]router.Route, error) {
return r.routes, nil
}
type apiRouter struct {
routes []router.Route
router.Router
}
func (r *apiRouter) String() string {
return "api"
}

View File

@@ -5,15 +5,16 @@ import (
"errors"
"fmt"
"io"
"math/rand"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/client/selector"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/handler"
"github.com/micro/go-micro/v3/registry"
)
const (
@@ -70,16 +71,19 @@ func (wh *webHandler) getService(r *http.Request) (string, error) {
return "", errors.New("no route found")
}
// create a random selector
next := selector.Random(service.Services)
// get the next node
s, err := next()
if err != nil {
return "", nil
// get the nodes
var nodes []*registry.Node
for _, srv := range service.Services {
nodes = append(nodes, srv.Nodes...)
}
if len(nodes) == 0 {
return "", errors.New("no route found")
}
return fmt.Sprintf("http://%s", s.Address), nil
// select a random node
node := nodes[rand.Int()%len(nodes)]
return fmt.Sprintf("http://%s", node.Address), nil
}
// serveWebSocket used to serve a web socket proxied connection

View File

@@ -6,12 +6,17 @@ import (
"net/http"
"strings"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v3/api/resolver"
)
type Resolver struct{}
type Resolver struct {
opts resolver.Options
}
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
// parse options
options := resolver.NewResolveOptions(opts...)
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
// /foo.Bar/Service
if req.URL.Path == "/" {
return nil, errors.New("unknown name")
@@ -26,6 +31,7 @@ func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
@@ -34,5 +40,5 @@ func (r *Resolver) String() string {
}
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{}
return &Resolver{opts: resolver.NewOptions(opts...)}
}

View File

@@ -4,19 +4,23 @@ package host
import (
"net/http"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v3/api/resolver"
)
type Resolver struct {
opts resolver.Options
}
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
// parse options
options := resolver.NewResolveOptions(opts...)
return &resolver.Endpoint{
Name: req.Host,
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}

View File

@@ -1,23 +1,16 @@
package resolver
import (
"net/http"
"github.com/micro/go-micro/v3/registry"
)
// NewOptions returns new initialised options
func NewOptions(opts ...Option) Options {
var options Options
for _, o := range opts {
o(&options)
}
if options.Namespace == nil {
options.Namespace = StaticNamespace("go.micro")
}
return options
type Options struct {
Handler string
ServicePrefix string
}
type Option func(o *Options)
// WithHandler sets the handler being used
func WithHandler(h string) Option {
return func(o *Options) {
@@ -25,9 +18,46 @@ func WithHandler(h string) Option {
}
}
// WithNamespace sets the function which determines the namespace for a request
func WithNamespace(n func(*http.Request) string) Option {
// WithServicePrefix sets the ServicePrefix option
func WithServicePrefix(p string) Option {
return func(o *Options) {
o.Namespace = n
o.ServicePrefix = p
}
}
// NewOptions returns new initialised options
func NewOptions(opts ...Option) Options {
var options Options
for _, o := range opts {
o(&options)
}
return options
}
// ResolveOptions are used when resolving a request
type ResolveOptions struct {
Domain string
}
// ResolveOption sets an option
type ResolveOption func(*ResolveOptions)
// Domain sets the resolve Domain option
func Domain(n string) ResolveOption {
return func(o *ResolveOptions) {
o.Domain = n
}
}
// NewResolveOptions returns new initialised resolve options
func NewResolveOptions(opts ...ResolveOption) ResolveOptions {
var options ResolveOptions
for _, o := range opts {
o(&options)
}
if len(options.Domain) == 0 {
options.Domain = registry.DefaultDomain
}
return options
}

View File

@@ -5,26 +5,29 @@ import (
"net/http"
"strings"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v3/api/resolver"
)
type Resolver struct {
opts resolver.Options
}
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
// parse options
options := resolver.NewResolveOptions(opts...)
if req.URL.Path == "/" {
return nil, resolver.ErrNotFound
}
parts := strings.Split(req.URL.Path[1:], "/")
ns := r.opts.Namespace(req)
return &resolver.Endpoint{
Name: ns + "." + parts[0],
Name: r.opts.ServicePrefix + "." + parts[0],
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}

View File

@@ -13,7 +13,7 @@ var (
// Resolver resolves requests to endpoints
type Resolver interface {
Resolve(r *http.Request) (*Endpoint, error)
Resolve(r *http.Request, opts ...ResolveOption) (*Endpoint, error)
String() string
}
@@ -27,18 +27,6 @@ type Endpoint struct {
Method string
// HTTP Path e.g /greeter.
Path string
}
type Options struct {
Handler string
Namespace func(*http.Request) string
}
type Option func(o *Options)
// StaticNamespace returns the same namespace for each request
func StaticNamespace(ns string) func(*http.Request) string {
return func(*http.Request) string {
return ns
}
// Domain endpoint exists within
Domain string
}

View File

@@ -0,0 +1,80 @@
// Package subdomain is a resolver which uses the subdomain to determine the domain to route to. It
// offloads the endpoint resolution to a child resolver which is provided in New.
package subdomain
import (
"net"
"net/http"
"strings"
"github.com/micro/go-micro/v3/api/resolver"
"github.com/micro/go-micro/v3/logger"
"golang.org/x/net/publicsuffix"
)
func NewResolver(parent resolver.Resolver, opts ...resolver.Option) resolver.Resolver {
options := resolver.NewOptions(opts...)
return &Resolver{options, parent}
}
type Resolver struct {
opts resolver.Options
resolver.Resolver
}
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
if dom := r.Domain(req); len(dom) > 0 {
opts = append(opts, resolver.Domain(dom))
}
return r.Resolver.Resolve(req, opts...)
}
func (r *Resolver) Domain(req *http.Request) string {
// determine the host, e.g. foobar.m3o.app
host := req.URL.Hostname()
if len(host) == 0 {
if h, _, err := net.SplitHostPort(req.Host); err == nil {
host = h // host does contain a port
} else if strings.Contains(err.Error(), "missing port in address") {
host = req.Host // host does not contain a port
}
}
// check for an ip address
if net.ParseIP(host) != nil {
return ""
}
// check for dev enviroment
if host == "localhost" || host == "127.0.0.1" {
return ""
}
// extract the top level domain plus one (e.g. 'myapp.com')
domain, err := publicsuffix.EffectiveTLDPlusOne(host)
if err != nil {
logger.Debugf("Unable to extract domain from %v", host)
return ""
}
// there was no subdomain
if host == domain {
return ""
}
// remove the domain from the host, leaving the subdomain, e.g. "staging.foo.myapp.com" => "staging.foo"
subdomain := strings.TrimSuffix(host, "."+domain)
// return the reversed subdomain as the namespace, e.g. "staging.foo" => "foo-staging"
comps := strings.Split(subdomain, ".")
for i := len(comps)/2 - 1; i >= 0; i-- {
opp := len(comps) - 1 - i
comps[i], comps[opp] = comps[opp], comps[i]
}
return strings.Join(comps, "-")
}
func (r *Resolver) String() string {
return "subdomain"
}

View File

@@ -0,0 +1,71 @@
package subdomain
import (
"net/http"
"net/url"
"testing"
"github.com/micro/go-micro/v3/api/resolver/vpath"
"github.com/stretchr/testify/assert"
)
func TestResolve(t *testing.T) {
tt := []struct {
Name string
Host string
Result string
}{
{
Name: "Top level domain",
Host: "micro.mu",
Result: "micro",
},
{
Name: "Effective top level domain",
Host: "micro.com.au",
Result: "micro",
},
{
Name: "Subdomain dev",
Host: "dev.micro.mu",
Result: "dev",
},
{
Name: "Subdomain foo",
Host: "foo.micro.mu",
Result: "foo",
},
{
Name: "Multi-level subdomain",
Host: "staging.myapp.m3o.app",
Result: "myapp-staging",
},
{
Name: "Dev host",
Host: "127.0.0.1",
Result: "micro",
},
{
Name: "Localhost",
Host: "localhost",
Result: "micro",
},
{
Name: "IP host",
Host: "81.151.101.146",
Result: "micro",
},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
r := NewResolver(vpath.NewResolver())
result, err := r.Resolve(&http.Request{URL: &url.URL{Host: tc.Host, Path: "foo/bar"}})
assert.Nil(t, err, "Expecter err to be nil")
if result != nil {
assert.Equal(t, tc.Result, result.Domain, "Expected %v but got %v", tc.Result, result.Domain)
}
})
}
}

View File

@@ -7,7 +7,7 @@ import (
"regexp"
"strings"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v3/api/resolver"
)
func NewResolver(opts ...resolver.Option) resolver.Resolver {
@@ -22,36 +22,41 @@ var (
re = regexp.MustCompile("^v[0-9]+$")
)
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
if req.URL.Path == "/" {
return nil, errors.New("unknown name")
}
options := resolver.NewResolveOptions(opts...)
parts := strings.Split(req.URL.Path[1:], "/")
if len(parts) == 1 {
return &resolver.Endpoint{
Name: r.withNamespace(req, parts...),
Name: r.withPrefix(parts...),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
// /v1/foo
if re.MatchString(parts[0]) {
return &resolver.Endpoint{
Name: r.withNamespace(req, parts[0:2]...),
Name: r.withPrefix(parts[0:2]...),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
return &resolver.Endpoint{
Name: r.withNamespace(req, parts[0]),
Name: r.withPrefix(parts[0]),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
@@ -59,11 +64,12 @@ func (r *Resolver) String() string {
return "path"
}
func (r *Resolver) withNamespace(req *http.Request, parts ...string) string {
ns := r.opts.Namespace(req)
if len(ns) == 0 {
return strings.Join(parts, ".")
// withPrefix transforms "foo" into "go.micro.api.foo"
func (r *Resolver) withPrefix(parts ...string) string {
p := r.opts.ServicePrefix
if len(p) > 0 {
parts = append([]string{p}, parts...)
}
return strings.Join(append([]string{ns}, parts...), ".")
return strings.Join(parts, ".")
}

View File

@@ -1,9 +1,10 @@
package router
import (
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v2/api/resolver/vpath"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v3/api/resolver"
"github.com/micro/go-micro/v3/api/resolver/vpath"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/registry/mdns"
)
type Options struct {
@@ -17,7 +18,7 @@ type Option func(o *Options)
func NewOptions(opts ...Option) Options {
options := Options{
Handler: "meta",
Registry: registry.DefaultRegistry,
Registry: mdns.NewRegistry(),
}
for _, o := range opts {

View File

@@ -10,13 +10,13 @@ import (
"sync"
"time"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/router"
"github.com/micro/go-micro/v2/api/router/util"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/cache"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/router"
"github.com/micro/go-micro/v3/api/router/util"
"github.com/micro/go-micro/v3/logger"
"github.com/micro/go-micro/v3/metadata"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/registry/cache"
)
// endpoint struct, that holds compiled pcre
@@ -99,7 +99,7 @@ func (r *registryRouter) process(res *registry.Result) {
service, err := r.rc.GetService(res.Service.Name)
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("unable to get service: %v", err)
logger.Errorf("unable to get %v service: %v", res.Service.Name, err)
}
return
}
@@ -188,7 +188,7 @@ func (r *registryRouter) store(services []*registry.Service) {
for _, p := range ep.Endpoint.Path {
var pcreok bool
if p[0] == '^' && p[len(p)-1] != '$' {
if p[0] == '^' && p[len(p)-1] == '$' {
pcrereg, err := regexp.CompilePOSIX(p)
if err == nil {
cep.pcreregs = append(cep.pcreregs, pcrereg)
@@ -260,7 +260,7 @@ func (r *registryRouter) watch() {
res, err := w.Next()
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("error getting next endoint: %v", err)
logger.Errorf("error getting next endpoint: %v", err)
}
close(ch)
break
@@ -434,7 +434,7 @@ func (r *registryRouter) Route(req *http.Request) (*api.Service, error) {
name := rp.Name
// get service
services, err := r.rc.GetService(name)
services, err := r.rc.GetService(name, registry.GetDomain(rp.Domain))
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,34 @@
package registry
import (
"testing"
"github.com/micro/go-micro/v3/registry"
"github.com/stretchr/testify/assert"
)
func TestStoreRegex(t *testing.T) {
router := newRouter()
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)
}

View File

@@ -4,7 +4,7 @@ package router
import (
"net/http"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v3/api"
)
// Router is used to determine an endpoint for a request

View File

@@ -9,18 +9,18 @@ import (
"testing"
"time"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/api/handler/rpc"
"github.com/micro/go-micro/v2/api/router"
rregistry "github.com/micro/go-micro/v2/api/router/registry"
rstatic "github.com/micro/go-micro/v2/api/router/static"
"github.com/micro/go-micro/v2/client"
gcli "github.com/micro/go-micro/v2/client/grpc"
rmemory "github.com/micro/go-micro/v2/registry/memory"
"github.com/micro/go-micro/v2/server"
gsrv "github.com/micro/go-micro/v2/server/grpc"
pb "github.com/micro/go-micro/v2/server/grpc/proto"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/handler"
"github.com/micro/go-micro/v3/api/handler/rpc"
"github.com/micro/go-micro/v3/api/router"
rregistry "github.com/micro/go-micro/v3/api/router/registry"
rstatic "github.com/micro/go-micro/v3/api/router/static"
"github.com/micro/go-micro/v3/client"
gcli "github.com/micro/go-micro/v3/client/grpc"
rmemory "github.com/micro/go-micro/v3/registry/memory"
"github.com/micro/go-micro/v3/server"
gsrv "github.com/micro/go-micro/v3/server/grpc"
pb "github.com/micro/go-micro/v3/server/grpc/proto"
)
// server is used to implement helloworld.GreeterServer.

View File

@@ -8,13 +8,13 @@ import (
"strings"
"sync"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/router"
"github.com/micro/go-micro/v2/api/router/util"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v2/registry"
rutil "github.com/micro/go-micro/v2/util/registry"
"github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/router"
"github.com/micro/go-micro/v3/api/router/util"
"github.com/micro/go-micro/v3/logger"
"github.com/micro/go-micro/v3/metadata"
"github.com/micro/go-micro/v3/registry"
rutil "github.com/micro/go-micro/v3/util/registry"
)
type endpoint struct {

View File

@@ -6,7 +6,7 @@ import (
"fmt"
"strings"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/logger"
)
// InvalidTemplateError indicates that the path template is not valid.

View File

@@ -8,7 +8,7 @@ import (
"reflect"
"testing"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/logger"
)
func TestTokenize(t *testing.T) {

View File

@@ -7,7 +7,7 @@ import (
"fmt"
"strings"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/logger"
)
var (

View File

@@ -7,8 +7,8 @@ import (
"net"
"os"
"github.com/micro/go-micro/v2/api/server/acme"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/api/server/acme"
"github.com/micro/go-micro/v3/logger"
"golang.org/x/crypto/acme/autocert"
)

View File

@@ -8,8 +8,8 @@ import (
"time"
"github.com/caddyserver/certmagic"
"github.com/micro/go-micro/v2/api/server/acme"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/api/server/acme"
"github.com/micro/go-micro/v3/logger"
)
type certmagicProvider struct {

View File

@@ -10,8 +10,8 @@ import (
"time"
"github.com/caddyserver/certmagic"
"github.com/micro/go-micro/v2/store"
"github.com/micro/go-micro/v2/sync"
"github.com/micro/go-micro/v3/store"
"github.com/micro/go-micro/v3/sync"
)
// File represents a "File" that will be stored in store.Store - the contents and last modified time

View File

@@ -9,9 +9,9 @@ import (
"sync"
"github.com/gorilla/handlers"
"github.com/micro/go-micro/v2/api/server"
"github.com/micro/go-micro/v2/api/server/cors"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v3/api/server"
"github.com/micro/go-micro/v3/api/server/cors"
"github.com/micro/go-micro/v3/logger"
)
type httpServer struct {

View File

@@ -4,8 +4,8 @@ import (
"crypto/tls"
"net/http"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v2/api/server/acme"
"github.com/micro/go-micro/v3/api/resolver"
"github.com/micro/go-micro/v3/api/server/acme"
)
type Option func(o *Options)

View File

@@ -11,9 +11,9 @@ import (
import (
context "context"
api "github.com/micro/go-micro/v2/api"
client "github.com/micro/go-micro/v2/client"
server "github.com/micro/go-micro/v2/server"
api "github.com/micro/go-micro/v3/api"
client "github.com/micro/go-micro/v3/client"
server "github.com/micro/go-micro/v3/server"
)
// Reference imports to suppress errors if they are not otherwise used.

View File

@@ -1,88 +0,0 @@
package auth
import (
"github.com/google/uuid"
"github.com/micro/go-micro/v2/auth/provider/basic"
)
var (
DefaultAuth = NewAuth()
)
func NewAuth(opts ...Option) Auth {
options := Options{
Provider: basic.NewProvider(),
}
for _, o := range opts {
o(&options)
}
return &noop{
opts: options,
}
}
type noop struct {
opts Options
}
// String returns the name of the implementation
func (n *noop) String() string {
return "noop"
}
// Init the auth
func (n *noop) Init(opts ...Option) {
for _, o := range opts {
o(&n.opts)
}
}
// Options set for auth
func (n *noop) Options() Options {
return n.opts
}
// Generate a new account
func (n *noop) 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().Namespace,
}, nil
}
// Grant access to a resource
func (n *noop) Grant(rule *Rule) error {
return nil
}
// Revoke access to a resource
func (n *noop) Revoke(rule *Rule) error {
return nil
}
// Rules used to verify requests
func (n *noop) Rules(opts ...RulesOption) ([]*Rule, error) {
return []*Rule{}, nil
}
// Verify an account has access to a resource
func (n *noop) Verify(acc *Account, res *Resource, opts ...VerifyOption) error {
return nil
}
// Inspect a token
func (n *noop) Inspect(token string) (*Account, error) {
return &Account{ID: uuid.New().String(), Issuer: n.Options().Namespace}, nil
}
// Token generation using an account id and secret
func (n *noop) Token(opts ...TokenOption) (*Token, error) {
return &Token{}, nil
}

View File

@@ -1,35 +1,35 @@
// Package jwt is a jwt implementation of the auth interface
package jwt
import (
"sync"
"time"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/auth/rules"
"github.com/micro/go-micro/v2/auth/token"
jwtToken "github.com/micro/go-micro/v2/auth/token/jwt"
"github.com/micro/go-micro/v3/auth"
"github.com/micro/go-micro/v3/util/token"
"github.com/micro/go-micro/v3/util/token/jwt"
)
// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
j := new(jwt)
j := new(jwtAuth)
j.Init(opts...)
return j
}
type jwt struct {
type jwtAuth struct {
options auth.Options
jwt token.Provider
token token.Provider
rules []*auth.Rule
sync.Mutex
}
func (j *jwt) String() string {
func (j *jwtAuth) String() string {
return "jwt"
}
func (j *jwt) Init(opts ...auth.Option) {
func (j *jwtAuth) Init(opts ...auth.Option) {
j.Lock()
defer j.Unlock()
@@ -37,31 +37,35 @@ func (j *jwt) Init(opts ...auth.Option) {
o(&j.options)
}
j.jwt = jwtToken.NewTokenProvider(
j.token = jwt.NewTokenProvider(
token.WithPrivateKey(j.options.PrivateKey),
token.WithPublicKey(j.options.PublicKey),
)
}
func (j *jwt) Options() auth.Options {
func (j *jwtAuth) Options() auth.Options {
j.Lock()
defer j.Unlock()
return j.options
}
func (j *jwt) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
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: j.Options().Namespace,
Issuer: options.Issuer,
}
// generate a JWT secret which can be provided to the Token() method
// and exchanged for an access token
secret, err := j.jwt.Generate(account)
secret, err := j.token.Generate(account, token.WithExpiry(time.Hour*24*365))
if err != nil {
return nil, err
}
@@ -71,19 +75,19 @@ func (j *jwt) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, e
return account, nil
}
func (j *jwt) Grant(rule *auth.Rule) error {
func (j *jwtAuth) Grant(rule *auth.Rule) error {
j.Lock()
defer j.Unlock()
j.rules = append(j.rules, rule)
return nil
}
func (j *jwt) Revoke(rule *auth.Rule) error {
func (j *jwtAuth) Revoke(rule *auth.Rule) error {
j.Lock()
defer j.Unlock()
rules := []*auth.Rule{}
for _, r := range rules {
for _, r := range j.rules {
if r.ID != rule.ID {
rules = append(rules, r)
}
@@ -93,7 +97,7 @@ func (j *jwt) Revoke(rule *auth.Rule) error {
return nil
}
func (j *jwt) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error {
func (j *jwtAuth) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error {
j.Lock()
defer j.Unlock()
@@ -102,20 +106,20 @@ func (j *jwt) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyO
o(&options)
}
return rules.Verify(j.rules, acc, res)
return auth.VerifyAccess(j.rules, acc, res)
}
func (j *jwt) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
func (j *jwtAuth) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
j.Lock()
defer j.Unlock()
return j.rules, nil
}
func (j *jwt) Inspect(token string) (*auth.Account, error) {
return j.jwt.Inspect(token)
func (j *jwtAuth) Inspect(token string) (*auth.Account, error) {
return j.token.Inspect(token)
}
func (j *jwt) Token(opts ...auth.TokenOption) (*auth.Token, error) {
func (j *jwtAuth) Token(opts ...auth.TokenOption) (*auth.Token, error) {
options := auth.NewTokenOptions(opts...)
secret := options.RefreshToken
@@ -123,17 +127,17 @@ func (j *jwt) Token(opts ...auth.TokenOption) (*auth.Token, error) {
secret = options.Secret
}
account, err := j.jwt.Inspect(secret)
account, err := j.token.Inspect(secret)
if err != nil {
return nil, err
}
access, err := j.jwt.Generate(account, token.WithExpiry(options.Expiry))
access, err := j.token.Generate(account, token.WithExpiry(options.Expiry))
if err != nil {
return nil, err
}
refresh, err := j.jwt.Generate(account, token.WithExpiry(options.Expiry+time.Hour))
refresh, err := j.token.Generate(account, token.WithExpiry(options.Expiry+time.Hour))
if err != nil {
return nil, err
}

81
auth/noop/noop.go Normal file
View File

@@ -0,0 +1,81 @@
package noop
import (
"github.com/google/uuid"
"github.com/micro/go-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
}

View File

@@ -4,9 +4,7 @@ import (
"context"
"time"
"github.com/micro/go-micro/v2/auth/provider"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/store"
"github.com/micro/go-micro/v3/store"
)
func NewOptions(opts ...Option) Options {
@@ -14,16 +12,12 @@ func NewOptions(opts ...Option) Options {
for _, o := range opts {
o(&options)
}
if options.Client == nil {
options.Client = client.DefaultClient
}
return options
}
type Options struct {
// Namespace the service belongs to
Namespace string
// Issuer of the service's account
Issuer string
// ID is the services auth ID
ID string
// Secret is used to authenticate the service
@@ -34,16 +28,14 @@ type Options struct {
PublicKey string
// PrivateKey for encoding JWTs
PrivateKey string
// Provider is an auth provider
Provider provider.Provider
// LoginURL is the relative url path where a user can login
LoginURL string
// Store to back auth
Store store.Store
// Client to use for RPC
Client client.Client
// Addrs sets the addresses of auth
Addrs []string
// Context to store other options
Context context.Context
}
type Option func(o *Options)
@@ -55,10 +47,10 @@ func Addrs(addrs ...string) Option {
}
}
// Namespace the service belongs to
func Namespace(n string) Option {
// Issuer of the services account
func Issuer(i string) Option {
return func(o *Options) {
o.Namespace = n
o.Issuer = i
}
}
@@ -98,13 +90,6 @@ func ClientToken(token *Token) Option {
}
}
// Provider set the auth provider
func Provider(p provider.Provider) Option {
return func(o *Options) {
o.Provider = p
}
}
// LoginURL sets the auth LoginURL
func LoginURL(url string) Option {
return func(o *Options) {
@@ -112,13 +97,6 @@ func LoginURL(url string) Option {
}
}
// WithClient sets the client to use when making requests
func WithClient(c client.Client) Option {
return func(o *Options) {
o.Client = c
}
}
type GenerateOptions struct {
// Metadata associated with the account
Metadata map[string]string
@@ -130,6 +108,8 @@ type GenerateOptions struct {
Type string
// Secret used to authenticate the account
Secret string
// Issuer of the account, e.g. micro
Issuer string
}
type GenerateOption func(o *GenerateOptions)
@@ -169,6 +149,13 @@ func WithScopes(s ...string) GenerateOption {
}
}
// WithIssuer for the generated account
func WithIssuer(i string) GenerateOption {
return func(o *GenerateOptions) {
o.Issuer = i
}
}
// NewGenerateOptions from a slice of options
func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
var options GenerateOptions
@@ -187,6 +174,8 @@ type TokenOptions struct {
RefreshToken string
// Expiry is the time the token should live for
Expiry time.Duration
// Issuer of the account
Issuer string
}
type TokenOption func(o *TokenOptions)
@@ -211,6 +200,12 @@ func WithToken(rt string) TokenOption {
}
}
func WithTokenIssuer(iss string) TokenOption {
return func(o *TokenOptions) {
o.Issuer = iss
}
}
// NewTokenOptions from a slice of options
func NewTokenOptions(opts ...TokenOption) TokenOptions {
var options TokenOptions
@@ -227,7 +222,8 @@ func NewTokenOptions(opts ...TokenOption) TokenOptions {
}
type VerifyOptions struct {
Context context.Context
Context context.Context
Namespace string
}
type VerifyOption func(o *VerifyOptions)
@@ -237,9 +233,15 @@ func VerifyContext(ctx context.Context) VerifyOption {
o.Context = ctx
}
}
func VerifyNamespace(ns string) VerifyOption {
return func(o *VerifyOptions) {
o.Namespace = ns
}
}
type RulesOptions struct {
Context context.Context
Context context.Context
Namespace string
}
type RulesOption func(o *RulesOptions)
@@ -249,3 +251,9 @@ func RulesContext(ctx context.Context) RulesOption {
o.Context = ctx
}
}
func RulesNamespace(ns string) RulesOption {
return func(o *RulesOptions) {
o.Namespace = ns
}
}

View File

@@ -1,34 +0,0 @@
package basic
import (
"github.com/micro/go-micro/v2/auth/provider"
)
// NewProvider returns an initialised basic provider
func NewProvider(opts ...provider.Option) provider.Provider {
var options provider.Options
for _, o := range opts {
o(&options)
}
return &basic{options}
}
type basic struct {
opts provider.Options
}
func (b *basic) String() string {
return "basic"
}
func (b *basic) Options() provider.Options {
return b.opts
}
func (b *basic) Endpoint(...provider.EndpointOption) string {
return ""
}
func (b *basic) Redirect() string {
return ""
}

View File

@@ -1,65 +0,0 @@
package oauth
import (
"fmt"
"net/url"
"github.com/micro/go-micro/v2/auth/provider"
)
// NewProvider returns an initialised oauth provider
func NewProvider(opts ...provider.Option) provider.Provider {
var options provider.Options
for _, o := range opts {
o(&options)
}
return &oauth{options}
}
type oauth struct {
opts provider.Options
}
func (o *oauth) String() string {
return "oauth"
}
func (o *oauth) Options() provider.Options {
return o.opts
}
func (o *oauth) Endpoint(opts ...provider.EndpointOption) string {
var options provider.EndpointOptions
for _, o := range opts {
o(&options)
}
params := make(url.Values)
params.Add("response_type", "code")
if len(options.State) > 0 {
params.Add("state", options.State)
}
if len(options.LoginHint) > 0 {
params.Add("login_hint", options.LoginHint)
}
if clientID := o.opts.ClientID; len(clientID) > 0 {
params.Add("client_id", clientID)
}
if scope := o.opts.Scope; len(scope) > 0 {
params.Add("scope", scope)
}
if redir := o.Redirect(); len(redir) > 0 {
params.Add("redirect_uri", redir)
}
return fmt.Sprintf("%v?%v", o.opts.Endpoint, params.Encode())
}
func (o *oauth) Redirect() string {
return o.opts.Redirect
}

View File

@@ -1,47 +0,0 @@
package provider
// Option returns a function which sets an option
type Option func(*Options)
// Options a provider can have
type Options struct {
// ClientID is the application's ID.
ClientID string
// ClientSecret is the application's secret.
ClientSecret string
// Endpoint for the provider
Endpoint string
// Redirect url incase of UI
Redirect string
// Scope of the oauth request
Scope string
}
// Credentials is an option which sets the client id and secret
func Credentials(id, secret string) Option {
return func(o *Options) {
o.ClientID = id
o.ClientSecret = secret
}
}
// Endpoint sets the endpoint option
func Endpoint(e string) Option {
return func(o *Options) {
o.Endpoint = e
}
}
// Redirect sets the Redirect option
func Redirect(r string) Option {
return func(o *Options) {
o.Redirect = r
}
}
// Scope sets the oauth scope
func Scope(s string) Option {
return func(o *Options) {
o.Scope = s
}
}

View File

@@ -1,49 +0,0 @@
// Package provider is an external auth provider e.g oauth
package provider
import (
"time"
)
// Provider is an auth provider
type Provider interface {
// String returns the name of the provider
String() string
// Options returns the options of a provider
Options() Options
// Endpoint for the provider
Endpoint(...EndpointOption) string
// Redirect url incase of UI
Redirect() string
}
// Grant is a granted authorisation
type Grant struct {
// token for reuse
Token string
// Expiry of the token
Expiry time.Time
// Scopes associated with grant
Scopes []string
}
type EndpointOptions struct {
// State is a code to verify the req
State string
// LoginHint prefils the user id on oauth clients
LoginHint string
}
type EndpointOption func(*EndpointOptions)
func WithState(c string) EndpointOption {
return func(o *EndpointOptions) {
o.State = c
}
}
func WithLoginHint(hint string) EndpointOption {
return func(o *EndpointOptions) {
o.LoginHint = hint
}
}

View File

@@ -1,17 +1,15 @@
package rules
package auth
import (
"fmt"
"sort"
"strings"
"github.com/micro/go-micro/v2/auth"
)
// Verify an account has access to a resource using the rules provided. If the account does not have
// VerifyAccess an account has access to a resource using the rules provided. If the account does not have
// access an error will be returned. If there are no rules provided which match the resource, an error
// will be returned
func Verify(rules []*auth.Rule, acc *auth.Account, res *auth.Resource) error {
func VerifyAccess(rules []*Rule, acc *Account, res *Resource) error {
// the rule is only to be applied if the type matches the resource or is catch-all (*)
validTypes := []string{"*", res.Type}
@@ -29,7 +27,7 @@ func Verify(rules []*auth.Rule, acc *auth.Account, res *auth.Resource) error {
}
// filter the rules to the ones which match the criteria above
filteredRules := make([]*auth.Rule, 0)
filteredRules := make([]*Rule, 0)
for _, rule := range rules {
if !include(validTypes, rule.Resource.Type) {
continue
@@ -51,9 +49,9 @@ func Verify(rules []*auth.Rule, acc *auth.Account, res *auth.Resource) error {
// loop through the rules and check for a rule which applies to this account
for _, rule := range filteredRules {
// a blank scope indicates the rule applies to everyone, even nil accounts
if rule.Scope == auth.ScopePublic && rule.Access == auth.AccessDenied {
return auth.ErrForbidden
} else if rule.Scope == auth.ScopePublic && rule.Access == auth.AccessGranted {
if rule.Scope == ScopePublic && rule.Access == AccessDenied {
return ErrForbidden
} else if rule.Scope == ScopePublic && rule.Access == AccessGranted {
return nil
}
@@ -63,22 +61,22 @@ func Verify(rules []*auth.Rule, acc *auth.Account, res *auth.Resource) error {
}
// this rule applies to any account
if rule.Scope == auth.ScopeAccount && rule.Access == auth.AccessDenied {
return auth.ErrForbidden
} else if rule.Scope == auth.ScopeAccount && rule.Access == auth.AccessGranted {
if rule.Scope == ScopeAccount && rule.Access == AccessDenied {
return ErrForbidden
} else if rule.Scope == ScopeAccount && rule.Access == AccessGranted {
return nil
}
// if the account has the necessary scope
if include(acc.Scopes, rule.Scope) && rule.Access == auth.AccessDenied {
return auth.ErrForbidden
} else if include(acc.Scopes, rule.Scope) && rule.Access == auth.AccessGranted {
if include(acc.Scopes, rule.Scope) && rule.Access == AccessDenied {
return ErrForbidden
} else if include(acc.Scopes, rule.Scope) && rule.Access == AccessGranted {
return nil
}
}
// if no rules matched then return forbidden
return auth.ErrForbidden
return ErrForbidden
}
// include is a helper function which checks to see if the slice contains the value. includes is

View File

@@ -1,25 +1,23 @@
package rules
package auth
import (
"testing"
"github.com/micro/go-micro/v2/auth"
)
func TestVerify(t *testing.T) {
srvResource := &auth.Resource{
srvResource := &Resource{
Type: "service",
Name: "go.micro.service.foo",
Endpoint: "Foo.Bar",
}
webResource := &auth.Resource{
webResource := &Resource{
Type: "service",
Name: "go.micro.web.foo",
Endpoint: "/foo/bar",
}
catchallResource := &auth.Resource{
catchallResource := &Resource{
Type: "*",
Name: "*",
Endpoint: "*",
@@ -27,24 +25,24 @@ func TestVerify(t *testing.T) {
tt := []struct {
Name string
Rules []*auth.Rule
Account *auth.Account
Resource *auth.Resource
Rules []*Rule
Account *Account
Resource *Resource
Error error
}{
{
Name: "NoRules",
Rules: []*auth.Rule{},
Rules: []*Rule{},
Account: nil,
Resource: srvResource,
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "CatchallPublicAccount",
Account: &auth.Account{},
Account: &Account{},
Resource: srvResource,
Rules: []*auth.Rule{
&auth.Rule{
Rules: []*Rule{
&Rule{
Scope: "",
Resource: catchallResource,
},
@@ -53,8 +51,8 @@ func TestVerify(t *testing.T) {
{
Name: "CatchallPublicNoAccount",
Resource: srvResource,
Rules: []*auth.Rule{
&auth.Rule{
Rules: []*Rule{
&Rule{
Scope: "",
Resource: catchallResource,
},
@@ -62,10 +60,10 @@ func TestVerify(t *testing.T) {
},
{
Name: "CatchallPrivateAccount",
Account: &auth.Account{},
Account: &Account{},
Resource: srvResource,
Rules: []*auth.Rule{
&auth.Rule{
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
},
@@ -74,22 +72,22 @@ func TestVerify(t *testing.T) {
{
Name: "CatchallPrivateNoAccount",
Resource: srvResource,
Rules: []*auth.Rule{
&auth.Rule{
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "CatchallServiceRuleMatch",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &auth.Resource{
Resource: &Resource{
Type: srvResource.Type,
Name: srvResource.Name,
Endpoint: "*",
@@ -100,27 +98,27 @@ func TestVerify(t *testing.T) {
{
Name: "CatchallServiceRuleNoMatch",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &auth.Resource{
Resource: &Resource{
Type: srvResource.Type,
Name: "wrongname",
Endpoint: "*",
},
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "ExactRuleValidScope",
Resource: srvResource,
Account: &auth.Account{
Account: &Account{
Scopes: []string{"neededscope"},
},
Rules: []*auth.Rule{
&auth.Rule{
Rules: []*Rule{
&Rule{
Scope: "neededscope",
Resource: srvResource,
},
@@ -129,58 +127,58 @@ func TestVerify(t *testing.T) {
{
Name: "ExactRuleInvalidScope",
Resource: srvResource,
Account: &auth.Account{
Account: &Account{
Scopes: []string{"neededscope"},
},
Rules: []*auth.Rule{
&auth.Rule{
Rules: []*Rule{
&Rule{
Scope: "invalidscope",
Resource: srvResource,
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "CatchallDenyWithAccount",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessDenied,
Access: AccessDenied,
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "CatchallDenyWithNoAccount",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessDenied,
Access: AccessDenied,
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "RulePriorityGrantFirst",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessGranted,
Access: AccessGranted,
Priority: 1,
},
&auth.Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessDenied,
Access: AccessDenied,
Priority: 0,
},
},
@@ -188,29 +186,29 @@ func TestVerify(t *testing.T) {
{
Name: "RulePriorityDenyFirst",
Resource: srvResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessGranted,
Access: AccessGranted,
Priority: 0,
},
&auth.Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: auth.AccessDenied,
Access: AccessDenied,
Priority: 1,
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "WebExactEndpointValid",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: webResource,
},
@@ -219,27 +217,27 @@ func TestVerify(t *testing.T) {
{
Name: "WebExactEndpointInalid",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &auth.Resource{
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "invalidendpoint",
},
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
{
Name: "WebWildcardEndpoint",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &auth.Resource{
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "*",
@@ -250,11 +248,11 @@ func TestVerify(t *testing.T) {
{
Name: "WebWildcardPathEndpointValid",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &auth.Resource{
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "/foo/*",
@@ -265,24 +263,24 @@ func TestVerify(t *testing.T) {
{
Name: "WebWildcardPathEndpointInvalid",
Resource: webResource,
Account: &auth.Account{},
Rules: []*auth.Rule{
&auth.Rule{
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &auth.Resource{
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "/bar/*",
},
},
},
Error: auth.ErrForbidden,
Error: ErrForbidden,
},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
if err := Verify(tc.Rules, tc.Account, tc.Resource); err != tc.Error {
if err := VerifyAccess(tc.Rules, tc.Account, tc.Resource); err != tc.Error {
t.Errorf("Expected %v but got %v", tc.Error, err)
}
})

File diff suppressed because it is too large Load Diff

View File

@@ -1,279 +0,0 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: auth/service/proto/auth.proto
package go_micro_auth
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
api "github.com/micro/go-micro/v2/api"
client "github.com/micro/go-micro/v2/client"
server "github.com/micro/go-micro/v2/server"
)
// 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
// Reference imports to suppress errors if they are not otherwise used.
var _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for Auth service
func NewAuthEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for Auth service
type AuthService interface {
Generate(ctx context.Context, in *GenerateRequest, opts ...client.CallOption) (*GenerateResponse, error)
Inspect(ctx context.Context, in *InspectRequest, opts ...client.CallOption) (*InspectResponse, error)
Token(ctx context.Context, in *TokenRequest, opts ...client.CallOption) (*TokenResponse, error)
}
type authService struct {
c client.Client
name string
}
func NewAuthService(name string, c client.Client) AuthService {
return &authService{
c: c,
name: name,
}
}
func (c *authService) Generate(ctx context.Context, in *GenerateRequest, opts ...client.CallOption) (*GenerateResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Generate", in)
out := new(GenerateResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authService) Inspect(ctx context.Context, in *InspectRequest, opts ...client.CallOption) (*InspectResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Inspect", in)
out := new(InspectResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authService) Token(ctx context.Context, in *TokenRequest, opts ...client.CallOption) (*TokenResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Token", in)
out := new(TokenResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Auth service
type AuthHandler interface {
Generate(context.Context, *GenerateRequest, *GenerateResponse) error
Inspect(context.Context, *InspectRequest, *InspectResponse) error
Token(context.Context, *TokenRequest, *TokenResponse) error
}
func RegisterAuthHandler(s server.Server, hdlr AuthHandler, opts ...server.HandlerOption) error {
type auth interface {
Generate(ctx context.Context, in *GenerateRequest, out *GenerateResponse) error
Inspect(ctx context.Context, in *InspectRequest, out *InspectResponse) error
Token(ctx context.Context, in *TokenRequest, out *TokenResponse) error
}
type Auth struct {
auth
}
h := &authHandler{hdlr}
return s.Handle(s.NewHandler(&Auth{h}, opts...))
}
type authHandler struct {
AuthHandler
}
func (h *authHandler) Generate(ctx context.Context, in *GenerateRequest, out *GenerateResponse) error {
return h.AuthHandler.Generate(ctx, in, out)
}
func (h *authHandler) Inspect(ctx context.Context, in *InspectRequest, out *InspectResponse) error {
return h.AuthHandler.Inspect(ctx, in, out)
}
func (h *authHandler) Token(ctx context.Context, in *TokenRequest, out *TokenResponse) error {
return h.AuthHandler.Token(ctx, in, out)
}
// Api Endpoints for Accounts service
func NewAccountsEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for Accounts service
type AccountsService interface {
List(ctx context.Context, in *ListAccountsRequest, opts ...client.CallOption) (*ListAccountsResponse, error)
}
type accountsService struct {
c client.Client
name string
}
func NewAccountsService(name string, c client.Client) AccountsService {
return &accountsService{
c: c,
name: name,
}
}
func (c *accountsService) List(ctx context.Context, in *ListAccountsRequest, opts ...client.CallOption) (*ListAccountsResponse, error) {
req := c.c.NewRequest(c.name, "Accounts.List", in)
out := new(ListAccountsResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Accounts service
type AccountsHandler interface {
List(context.Context, *ListAccountsRequest, *ListAccountsResponse) error
}
func RegisterAccountsHandler(s server.Server, hdlr AccountsHandler, opts ...server.HandlerOption) error {
type accounts interface {
List(ctx context.Context, in *ListAccountsRequest, out *ListAccountsResponse) error
}
type Accounts struct {
accounts
}
h := &accountsHandler{hdlr}
return s.Handle(s.NewHandler(&Accounts{h}, opts...))
}
type accountsHandler struct {
AccountsHandler
}
func (h *accountsHandler) List(ctx context.Context, in *ListAccountsRequest, out *ListAccountsResponse) error {
return h.AccountsHandler.List(ctx, in, out)
}
// Api Endpoints for Rules service
func NewRulesEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for Rules service
type RulesService interface {
Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error)
Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error)
List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error)
}
type rulesService struct {
c client.Client
name string
}
func NewRulesService(name string, c client.Client) RulesService {
return &rulesService{
c: c,
name: name,
}
}
func (c *rulesService) Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error) {
req := c.c.NewRequest(c.name, "Rules.Create", in)
out := new(CreateResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *rulesService) Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error) {
req := c.c.NewRequest(c.name, "Rules.Delete", in)
out := new(DeleteResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *rulesService) List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) {
req := c.c.NewRequest(c.name, "Rules.List", in)
out := new(ListResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Rules service
type RulesHandler interface {
Create(context.Context, *CreateRequest, *CreateResponse) error
Delete(context.Context, *DeleteRequest, *DeleteResponse) error
List(context.Context, *ListRequest, *ListResponse) error
}
func RegisterRulesHandler(s server.Server, hdlr RulesHandler, opts ...server.HandlerOption) error {
type rules interface {
Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error
Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error
List(ctx context.Context, in *ListRequest, out *ListResponse) error
}
type Rules struct {
rules
}
h := &rulesHandler{hdlr}
return s.Handle(s.NewHandler(&Rules{h}, opts...))
}
type rulesHandler struct {
RulesHandler
}
func (h *rulesHandler) Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error {
return h.RulesHandler.Create(ctx, in, out)
}
func (h *rulesHandler) Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error {
return h.RulesHandler.Delete(ctx, in, out)
}
func (h *rulesHandler) List(ctx context.Context, in *ListRequest, out *ListResponse) error {
return h.RulesHandler.List(ctx, in, out)
}

View File

@@ -1,127 +0,0 @@
syntax = "proto3";
package go.micro.auth;
service Auth {
rpc Generate(GenerateRequest) returns (GenerateResponse) {};
rpc Inspect(InspectRequest) returns (InspectResponse) {};
rpc Token(TokenRequest) returns (TokenResponse) {};
}
service Accounts {
rpc List(ListAccountsRequest) returns (ListAccountsResponse) {};
}
service Rules {
rpc Create(CreateRequest) returns (CreateResponse) {};
rpc Delete(DeleteRequest) returns (DeleteResponse) {};
rpc List(ListRequest) returns (ListResponse) {};
}
message ListAccountsRequest {
}
message ListAccountsResponse {
repeated Account accounts = 1;
}
message Token {
string access_token = 1;
string refresh_token = 2;
int64 created = 3;
int64 expiry = 4;
}
message Account {
string id = 1;
string type = 2;
map<string, string> metadata = 4;
repeated string scopes = 5;
string issuer = 6;
string secret = 7;
}
message Resource{
string name = 1;
string type = 2;
string endpoint = 3;
}
message GenerateRequest {
string id = 1;
map<string, string> metadata = 3;
repeated string scopes = 4;
string secret = 5;
string type = 6;
string provider = 7;
}
message GenerateResponse {
Account account = 1;
}
message GrantRequest {
string scope = 1;
Resource resource = 2;
}
message GrantResponse {}
message RevokeRequest {
string scope = 1;
Resource resource = 2;
}
message RevokeResponse {}
message InspectRequest {
string token = 1;
}
message InspectResponse {
Account account = 1;
}
message TokenRequest {
string id = 1;
string secret = 2;
string refresh_token = 3;
int64 token_expiry = 4;
}
message TokenResponse {
Token token = 1;
}
enum Access {
UNKNOWN = 0;
GRANTED = 1;
DENIED = 2;
}
message Rule {
string id = 1;
string scope = 2;
Resource resource = 3;
Access access = 4;
int32 priority = 5;
}
message CreateRequest {
Rule rule = 1;
}
message CreateResponse {}
message DeleteRequest {
string id = 1;
}
message DeleteResponse {}
message ListRequest {
}
message ListResponse {
repeated Rule rules = 1;
}

View File

@@ -1,228 +0,0 @@
package service
import (
"context"
"strings"
"time"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/auth/rules"
pb "github.com/micro/go-micro/v2/auth/service/proto"
"github.com/micro/go-micro/v2/auth/token"
"github.com/micro/go-micro/v2/auth/token/jwt"
"github.com/micro/go-micro/v2/client"
)
// svc is the service implementation of the Auth interface
type svc struct {
options auth.Options
auth pb.AuthService
rules pb.RulesService
jwt token.Provider
}
func (s *svc) String() string {
return "service"
}
func (s *svc) Init(opts ...auth.Option) {
for _, o := range opts {
o(&s.options)
}
if s.options.Client == nil {
s.options.Client = client.DefaultClient
}
s.auth = pb.NewAuthService("go.micro.auth", s.options.Client)
s.rules = pb.NewRulesService("go.micro.auth", s.options.Client)
// if we have a JWT public key passed as an option,
// we can decode tokens with the type "JWT" locally
// and not have to make an RPC call
if key := s.options.PublicKey; len(key) > 0 {
s.jwt = jwt.NewTokenProvider(token.WithPublicKey(key))
}
}
func (s *svc) Options() auth.Options {
return s.options
}
// Generate a new account
func (s *svc) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
options := auth.NewGenerateOptions(opts...)
rsp, err := s.auth.Generate(context.TODO(), &pb.GenerateRequest{
Id: id,
Type: options.Type,
Secret: options.Secret,
Scopes: options.Scopes,
Metadata: options.Metadata,
Provider: options.Provider,
})
if err != nil {
return nil, err
}
return serializeAccount(rsp.Account), nil
}
// Grant access to a resource
func (s *svc) Grant(rule *auth.Rule) error {
access := pb.Access_UNKNOWN
if rule.Access == auth.AccessGranted {
access = pb.Access_GRANTED
} else if rule.Access == auth.AccessDenied {
access = pb.Access_DENIED
}
_, err := s.rules.Create(context.TODO(), &pb.CreateRequest{
Rule: &pb.Rule{
Id: rule.ID,
Scope: rule.Scope,
Priority: rule.Priority,
Access: access,
Resource: &pb.Resource{
Type: rule.Resource.Type,
Name: rule.Resource.Name,
Endpoint: rule.Resource.Endpoint,
},
},
})
return err
}
// Revoke access to a resource
func (s *svc) Revoke(rule *auth.Rule) error {
_, err := s.rules.Delete(context.TODO(), &pb.DeleteRequest{
Id: rule.ID,
})
return err
}
func (s *svc) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) {
var options auth.RulesOptions
for _, o := range opts {
o(&options)
}
if options.Context == nil {
options.Context = context.TODO()
}
rsp, err := s.rules.List(options.Context, &pb.ListRequest{}, client.WithCache(time.Second*30))
if err != nil {
return nil, err
}
rules := make([]*auth.Rule, len(rsp.Rules))
for i, r := range rsp.Rules {
rules[i] = serializeRule(r)
}
return rules, nil
}
// Verify an account has access to a resource
func (s *svc) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error {
var options auth.VerifyOptions
for _, o := range opts {
o(&options)
}
rs, err := s.Rules(auth.RulesContext(options.Context))
if err != nil {
return err
}
return rules.Verify(rs, acc, res)
}
// Inspect a token
func (s *svc) Inspect(token string) (*auth.Account, error) {
// try to decode JWT locally and fall back to srv if an error occurs
if len(strings.Split(token, ".")) == 3 && s.jwt != nil {
return s.jwt.Inspect(token)
}
// the token is not a JWT or we do not have the keys to decode it,
// fall back to the auth service
rsp, err := s.auth.Inspect(context.TODO(), &pb.InspectRequest{Token: token})
if err != nil {
return nil, err
}
return serializeAccount(rsp.Account), nil
}
// Token generation using an account ID and secret
func (s *svc) Token(opts ...auth.TokenOption) (*auth.Token, error) {
options := auth.NewTokenOptions(opts...)
rsp, err := s.auth.Token(context.Background(), &pb.TokenRequest{
Id: options.ID,
Secret: options.Secret,
RefreshToken: options.RefreshToken,
TokenExpiry: int64(options.Expiry.Seconds()),
})
if err != nil {
return nil, err
}
return serializeToken(rsp.Token), nil
}
func serializeToken(t *pb.Token) *auth.Token {
return &auth.Token{
AccessToken: t.AccessToken,
RefreshToken: t.RefreshToken,
Created: time.Unix(t.Created, 0),
Expiry: time.Unix(t.Expiry, 0),
}
}
func serializeAccount(a *pb.Account) *auth.Account {
return &auth.Account{
ID: a.Id,
Secret: a.Secret,
Issuer: a.Issuer,
Metadata: a.Metadata,
Scopes: a.Scopes,
}
}
func serializeRule(r *pb.Rule) *auth.Rule {
var access auth.Access
if r.Access == pb.Access_GRANTED {
access = auth.AccessGranted
} else {
access = auth.AccessDenied
}
return &auth.Rule{
ID: r.Id,
Scope: r.Scope,
Access: access,
Priority: r.Priority,
Resource: &auth.Resource{
Type: r.Resource.Type,
Name: r.Resource.Name,
Endpoint: r.Resource.Endpoint,
},
}
}
// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
options := auth.NewOptions(opts...)
if options.Client == nil {
options.Client = client.DefaultClient
}
return &svc{
auth: pb.NewAuthService("go.micro.auth", options.Client),
rules: pb.NewRulesService("go.micro.auth", options.Client),
options: options,
}
}

View File

@@ -37,31 +37,3 @@ type Subscriber interface {
Topic() string
Unsubscribe() error
}
var (
DefaultBroker Broker = NewBroker()
)
func Init(opts ...Option) error {
return DefaultBroker.Init(opts...)
}
func Connect() error {
return DefaultBroker.Connect()
}
func Disconnect() error {
return DefaultBroker.Disconnect()
}
func Publish(topic string, msg *Message, opts ...PublishOption) error {
return DefaultBroker.Publish(topic, msg, opts...)
}
func Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
return DefaultBroker.Subscribe(topic, handler, opts...)
}
func String() string {
return DefaultBroker.String()
}

View File

@@ -1,711 +0,0 @@
// Package http provides a http based message broker
package broker
import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net"
"net/http"
"net/url"
"runtime"
"sync"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/v2/codec/json"
merr "github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/cache"
maddr "github.com/micro/go-micro/v2/util/addr"
mnet "github.com/micro/go-micro/v2/util/net"
mls "github.com/micro/go-micro/v2/util/tls"
"golang.org/x/net/http2"
)
// HTTP Broker is a point to point async broker
type httpBroker struct {
id string
address string
opts Options
mux *http.ServeMux
c *http.Client
r registry.Registry
sync.RWMutex
subscribers map[string][]*httpSubscriber
running bool
exit chan chan error
// offline message inbox
mtx sync.RWMutex
inbox map[string][][]byte
}
type httpSubscriber struct {
opts SubscribeOptions
id string
topic string
fn Handler
svc *registry.Service
hb *httpBroker
}
type httpEvent struct {
m *Message
t string
err error
}
var (
DefaultPath = "/"
DefaultAddress = "127.0.0.1:0"
serviceName = "micro.http.broker"
broadcastVersion = "ff.http.broadcast"
registerTTL = time.Minute
registerInterval = time.Second * 30
)
func init() {
rand.Seed(time.Now().Unix())
}
func newTransport(config *tls.Config) *http.Transport {
if config == nil {
config = &tls.Config{
InsecureSkipVerify: true,
}
}
dialTLS := func(network string, addr string) (net.Conn, error) {
return tls.Dial(network, addr, config)
}
t := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
DialTLS: dialTLS,
}
runtime.SetFinalizer(&t, func(tr **http.Transport) {
(*tr).CloseIdleConnections()
})
// setup http2
http2.ConfigureTransport(t)
return t
}
func newHttpBroker(opts ...Option) Broker {
options := Options{
Codec: json.Marshaler{},
Context: context.TODO(),
Registry: registry.DefaultRegistry,
}
for _, o := range opts {
o(&options)
}
// set address
addr := DefaultAddress
if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 {
addr = options.Addrs[0]
}
h := &httpBroker{
id: uuid.New().String(),
address: addr,
opts: options,
r: options.Registry,
c: &http.Client{Transport: newTransport(options.TLSConfig)},
subscribers: make(map[string][]*httpSubscriber),
exit: make(chan chan error),
mux: http.NewServeMux(),
inbox: make(map[string][][]byte),
}
// specify the message handler
h.mux.Handle(DefaultPath, h)
// get optional handlers
if h.opts.Context != nil {
handlers, ok := h.opts.Context.Value("http_handlers").(map[string]http.Handler)
if ok {
for pattern, handler := range handlers {
h.mux.Handle(pattern, handler)
}
}
}
return h
}
func (h *httpEvent) Ack() error {
return nil
}
func (h *httpEvent) Error() error {
return h.err
}
func (h *httpEvent) Message() *Message {
return h.m
}
func (h *httpEvent) Topic() string {
return h.t
}
func (h *httpSubscriber) Options() SubscribeOptions {
return h.opts
}
func (h *httpSubscriber) Topic() string {
return h.topic
}
func (h *httpSubscriber) Unsubscribe() error {
return h.hb.unsubscribe(h)
}
func (h *httpBroker) saveMessage(topic string, msg []byte) {
h.mtx.Lock()
defer h.mtx.Unlock()
// get messages
c := h.inbox[topic]
// save message
c = append(c, msg)
// max length 64
if len(c) > 64 {
c = c[:64]
}
// save inbox
h.inbox[topic] = c
}
func (h *httpBroker) getMessage(topic string, num int) [][]byte {
h.mtx.Lock()
defer h.mtx.Unlock()
// get messages
c, ok := h.inbox[topic]
if !ok {
return nil
}
// more message than requests
if len(c) >= num {
msg := c[:num]
h.inbox[topic] = c[num:]
return msg
}
// reset inbox
h.inbox[topic] = nil
// return all messages
return c
}
func (h *httpBroker) subscribe(s *httpSubscriber) error {
h.Lock()
defer h.Unlock()
if err := h.r.Register(s.svc, registry.RegisterTTL(registerTTL)); err != nil {
return err
}
h.subscribers[s.topic] = append(h.subscribers[s.topic], s)
return nil
}
func (h *httpBroker) unsubscribe(s *httpSubscriber) error {
h.Lock()
defer h.Unlock()
//nolint:prealloc
var subscribers []*httpSubscriber
// look for subscriber
for _, sub := range h.subscribers[s.topic] {
// deregister and skip forward
if sub == s {
_ = h.r.Deregister(sub.svc)
continue
}
// keep subscriber
subscribers = append(subscribers, sub)
}
// set subscribers
h.subscribers[s.topic] = subscribers
return nil
}
func (h *httpBroker) run(l net.Listener) {
t := time.NewTicker(registerInterval)
defer t.Stop()
for {
select {
// heartbeat for each subscriber
case <-t.C:
h.RLock()
for _, subs := range h.subscribers {
for _, sub := range subs {
_ = h.r.Register(sub.svc, registry.RegisterTTL(registerTTL))
}
}
h.RUnlock()
// received exit signal
case ch := <-h.exit:
ch <- l.Close()
h.RLock()
for _, subs := range h.subscribers {
for _, sub := range subs {
_ = h.r.Deregister(sub.svc)
}
}
h.RUnlock()
return
}
}
}
func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
err := merr.BadRequest("go.micro.broker", "Method not allowed")
http.Error(w, err.Error(), http.StatusMethodNotAllowed)
return
}
defer req.Body.Close()
req.ParseForm()
b, err := ioutil.ReadAll(req.Body)
if err != nil {
errr := merr.InternalServerError("go.micro.broker", "Error reading request body: %v", err)
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
var m *Message
if err = h.opts.Codec.Unmarshal(b, &m); err != nil {
errr := merr.InternalServerError("go.micro.broker", "Error parsing request body: %v", err)
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
topic := m.Header["Micro-Topic"]
//delete(m.Header, ":topic")
if len(topic) == 0 {
errr := merr.InternalServerError("go.micro.broker", "Topic not found")
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
p := &httpEvent{m: m, t: topic}
id := req.Form.Get("id")
//nolint:prealloc
var subs []Handler
h.RLock()
for _, subscriber := range h.subscribers[topic] {
if id != subscriber.id {
continue
}
subs = append(subs, subscriber.fn)
}
h.RUnlock()
// execute the handler
for _, fn := range subs {
p.err = fn(p)
}
}
func (h *httpBroker) Address() string {
h.RLock()
defer h.RUnlock()
return h.address
}
func (h *httpBroker) Connect() error {
h.RLock()
if h.running {
h.RUnlock()
return nil
}
h.RUnlock()
h.Lock()
defer h.Unlock()
var l net.Listener
var err error
if h.opts.Secure || h.opts.TLSConfig != nil {
config := h.opts.TLSConfig
fn := func(addr string) (net.Listener, error) {
if config == nil {
hosts := []string{addr}
// check if its a valid host:port
if host, _, err := net.SplitHostPort(addr); err == nil {
if len(host) == 0 {
hosts = maddr.IPs()
} else {
hosts = []string{host}
}
}
// generate a certificate
cert, err := mls.Certificate(hosts...)
if err != nil {
return nil, err
}
config = &tls.Config{Certificates: []tls.Certificate{cert}}
}
return tls.Listen("tcp", addr, config)
}
l, err = mnet.Listen(h.address, fn)
} else {
fn := func(addr string) (net.Listener, error) {
return net.Listen("tcp", addr)
}
l, err = mnet.Listen(h.address, fn)
}
if err != nil {
return err
}
addr := h.address
h.address = l.Addr().String()
go http.Serve(l, h.mux)
go func() {
h.run(l)
h.Lock()
h.opts.Addrs = []string{addr}
h.address = addr
h.Unlock()
}()
// get registry
reg := h.opts.Registry
if reg == nil {
reg = registry.DefaultRegistry
}
// set cache
h.r = cache.New(reg)
// set running
h.running = true
return nil
}
func (h *httpBroker) Disconnect() error {
h.RLock()
if !h.running {
h.RUnlock()
return nil
}
h.RUnlock()
h.Lock()
defer h.Unlock()
// stop cache
rc, ok := h.r.(cache.Cache)
if ok {
rc.Stop()
}
// exit and return err
ch := make(chan error)
h.exit <- ch
err := <-ch
// set not running
h.running = false
return err
}
func (h *httpBroker) Init(opts ...Option) error {
h.RLock()
if h.running {
h.RUnlock()
return errors.New("cannot init while connected")
}
h.RUnlock()
h.Lock()
defer h.Unlock()
for _, o := range opts {
o(&h.opts)
}
if len(h.opts.Addrs) > 0 && len(h.opts.Addrs[0]) > 0 {
h.address = h.opts.Addrs[0]
}
if len(h.id) == 0 {
h.id = "go.micro.http.broker-" + uuid.New().String()
}
// get registry
reg := h.opts.Registry
if reg == nil {
reg = registry.DefaultRegistry
}
// get cache
if rc, ok := h.r.(cache.Cache); ok {
rc.Stop()
}
// set registry
h.r = cache.New(reg)
// reconfigure tls config
if c := h.opts.TLSConfig; c != nil {
h.c = &http.Client{
Transport: newTransport(c),
}
}
return nil
}
func (h *httpBroker) Options() Options {
return h.opts
}
func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
// create the message first
m := &Message{
Header: make(map[string]string),
Body: msg.Body,
}
for k, v := range msg.Header {
m.Header[k] = v
}
m.Header["Micro-Topic"] = topic
// encode the message
b, err := h.opts.Codec.Marshal(m)
if err != nil {
return err
}
// save the message
h.saveMessage(topic, b)
// now attempt to get the service
h.RLock()
s, err := h.r.GetService(serviceName)
if err != nil {
h.RUnlock()
return err
}
h.RUnlock()
pub := func(node *registry.Node, t string, b []byte) error {
scheme := "http"
// check if secure is added in metadata
if node.Metadata["secure"] == "true" {
scheme = "https"
}
vals := url.Values{}
vals.Add("id", node.Id)
uri := fmt.Sprintf("%s://%s%s?%s", scheme, node.Address, DefaultPath, vals.Encode())
r, err := h.c.Post(uri, "application/json", bytes.NewReader(b))
if err != nil {
return err
}
// discard response body
io.Copy(ioutil.Discard, r.Body)
r.Body.Close()
return nil
}
srv := func(s []*registry.Service, b []byte) {
for _, service := range s {
var nodes []*registry.Node
for _, node := range service.Nodes {
// only use nodes tagged with broker http
if node.Metadata["broker"] != "http" {
continue
}
// look for nodes for the topic
if node.Metadata["topic"] != topic {
continue
}
nodes = append(nodes, node)
}
// only process if we have nodes
if len(nodes) == 0 {
continue
}
switch service.Version {
// broadcast version means broadcast to all nodes
case broadcastVersion:
var success bool
// publish to all nodes
for _, node := range nodes {
// publish async
if err := pub(node, topic, b); err == nil {
success = true
}
}
// save if it failed to publish at least once
if !success {
h.saveMessage(topic, b)
}
default:
// select node to publish to
node := nodes[rand.Int()%len(nodes)]
// publish async to one node
if err := pub(node, topic, b); err != nil {
// if failed save it
h.saveMessage(topic, b)
}
}
}
}
// do the rest async
go func() {
// get a third of the backlog
messages := h.getMessage(topic, 8)
delay := (len(messages) > 1)
// publish all the messages
for _, msg := range messages {
// serialize here
srv(s, msg)
// sending a backlog of messages
if delay {
time.Sleep(time.Millisecond * 100)
}
}
}()
return nil
}
func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
var err error
var host, port string
options := NewSubscribeOptions(opts...)
// parse address for host, port
host, port, err = net.SplitHostPort(h.Address())
if err != nil {
return nil, err
}
addr, err := maddr.Extract(host)
if err != nil {
return nil, err
}
var secure bool
if h.opts.Secure || h.opts.TLSConfig != nil {
secure = true
}
// register service
node := &registry.Node{
Id: topic + "-" + h.id,
Address: mnet.HostPort(addr, port),
Metadata: map[string]string{
"secure": fmt.Sprintf("%t", secure),
"broker": "http",
"topic": topic,
},
}
// check for queue group or broadcast queue
version := options.Queue
if len(version) == 0 {
version = broadcastVersion
}
service := &registry.Service{
Name: serviceName,
Version: version,
Nodes: []*registry.Node{node},
}
// generate subscriber
subscriber := &httpSubscriber{
opts: options,
hb: h,
id: node.Id,
topic: topic,
fn: handler,
svc: service,
}
// subscribe now
if err := h.subscribe(subscriber); err != nil {
return nil, err
}
// return the subscriber
return subscriber, nil
}
func (h *httpBroker) String() string {
return "http"
}
// NewBroker returns a new http broker
func NewBroker(opts ...Option) Broker {
return newHttpBroker(opts...)
}

View File

@@ -2,10 +2,712 @@
package http
import (
"github.com/micro/go-micro/v2/broker"
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net"
"net/http"
"net/url"
"runtime"
"sync"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/v3/broker"
"github.com/micro/go-micro/v3/codec/json"
merr "github.com/micro/go-micro/v3/errors"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/registry/cache"
"github.com/micro/go-micro/v3/registry/mdns"
maddr "github.com/micro/go-micro/v3/util/addr"
mnet "github.com/micro/go-micro/v3/util/net"
mls "github.com/micro/go-micro/v3/util/tls"
"golang.org/x/net/http2"
)
// HTTP Broker is a point to point async broker
type httpBroker struct {
id string
address string
opts broker.Options
mux *http.ServeMux
c *http.Client
r registry.Registry
sync.RWMutex
subscribers map[string][]*httpSubscriber
running bool
exit chan chan error
// offline message inbox
mtx sync.RWMutex
inbox map[string][][]byte
}
type httpSubscriber struct {
opts broker.SubscribeOptions
id string
topic string
fn broker.Handler
svc *registry.Service
hb *httpBroker
}
type httpEvent struct {
m *broker.Message
t string
err error
}
var (
DefaultPath = "/"
DefaultAddress = "127.0.0.1:0"
serviceName = "micro.http.broker"
broadcastVersion = "ff.http.broadcast"
registerTTL = time.Minute
registerInterval = time.Second * 30
)
func init() {
rand.Seed(time.Now().Unix())
}
func newTransport(config *tls.Config) *http.Transport {
if config == nil {
config = &tls.Config{
InsecureSkipVerify: true,
}
}
dialTLS := func(network string, addr string) (net.Conn, error) {
return tls.Dial(network, addr, config)
}
t := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
DialTLS: dialTLS,
}
runtime.SetFinalizer(&t, func(tr **http.Transport) {
(*tr).CloseIdleConnections()
})
// setup http2
http2.ConfigureTransport(t)
return t
}
func newHttpBroker(opts ...broker.Option) broker.Broker {
options := broker.Options{
Codec: json.Marshaler{},
Context: context.TODO(),
Registry: mdns.NewRegistry(),
}
for _, o := range opts {
o(&options)
}
// set address
addr := DefaultAddress
if len(options.Addrs) > 0 && len(options.Addrs[0]) > 0 {
addr = options.Addrs[0]
}
h := &httpBroker{
id: uuid.New().String(),
address: addr,
opts: options,
r: options.Registry,
c: &http.Client{Transport: newTransport(options.TLSConfig)},
subscribers: make(map[string][]*httpSubscriber),
exit: make(chan chan error),
mux: http.NewServeMux(),
inbox: make(map[string][][]byte),
}
// specify the message handler
h.mux.Handle(DefaultPath, h)
// get optional handlers
if h.opts.Context != nil {
handlers, ok := h.opts.Context.Value("http_handlers").(map[string]http.Handler)
if ok {
for pattern, handler := range handlers {
h.mux.Handle(pattern, handler)
}
}
}
return h
}
func (h *httpEvent) Ack() error {
return nil
}
func (h *httpEvent) Error() error {
return h.err
}
func (h *httpEvent) Message() *broker.Message {
return h.m
}
func (h *httpEvent) Topic() string {
return h.t
}
func (h *httpSubscriber) Options() broker.SubscribeOptions {
return h.opts
}
func (h *httpSubscriber) Topic() string {
return h.topic
}
func (h *httpSubscriber) Unsubscribe() error {
return h.hb.unsubscribe(h)
}
func (h *httpBroker) saveMessage(topic string, msg []byte) {
h.mtx.Lock()
defer h.mtx.Unlock()
// get messages
c := h.inbox[topic]
// save message
c = append(c, msg)
// max length 64
if len(c) > 64 {
c = c[:64]
}
// save inbox
h.inbox[topic] = c
}
func (h *httpBroker) getMessage(topic string, num int) [][]byte {
h.mtx.Lock()
defer h.mtx.Unlock()
// get messages
c, ok := h.inbox[topic]
if !ok {
return nil
}
// more message than requests
if len(c) >= num {
msg := c[:num]
h.inbox[topic] = c[num:]
return msg
}
// reset inbox
h.inbox[topic] = nil
// return all messages
return c
}
func (h *httpBroker) subscribe(s *httpSubscriber) error {
h.Lock()
defer h.Unlock()
if err := h.r.Register(s.svc, registry.RegisterTTL(registerTTL)); err != nil {
return err
}
h.subscribers[s.topic] = append(h.subscribers[s.topic], s)
return nil
}
func (h *httpBroker) unsubscribe(s *httpSubscriber) error {
h.Lock()
defer h.Unlock()
//nolint:prealloc
var subscribers []*httpSubscriber
// look for subscriber
for _, sub := range h.subscribers[s.topic] {
// deregister and skip forward
if sub == s {
_ = h.r.Deregister(sub.svc)
continue
}
// keep subscriber
subscribers = append(subscribers, sub)
}
// set subscribers
h.subscribers[s.topic] = subscribers
return nil
}
func (h *httpBroker) run(l net.Listener) {
t := time.NewTicker(registerInterval)
defer t.Stop()
for {
select {
// heartbeat for each subscriber
case <-t.C:
h.RLock()
for _, subs := range h.subscribers {
for _, sub := range subs {
_ = h.r.Register(sub.svc, registry.RegisterTTL(registerTTL))
}
}
h.RUnlock()
// received exit signal
case ch := <-h.exit:
ch <- l.Close()
h.RLock()
for _, subs := range h.subscribers {
for _, sub := range subs {
_ = h.r.Deregister(sub.svc)
}
}
h.RUnlock()
return
}
}
}
func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
err := merr.BadRequest("go.micro.broker", "Method not allowed")
http.Error(w, err.Error(), http.StatusMethodNotAllowed)
return
}
defer req.Body.Close()
req.ParseForm()
b, err := ioutil.ReadAll(req.Body)
if err != nil {
errr := merr.InternalServerError("go.micro.broker", "Error reading request body: %v", err)
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
var m *broker.Message
if err = h.opts.Codec.Unmarshal(b, &m); err != nil {
errr := merr.InternalServerError("go.micro.broker", "Error parsing request body: %v", err)
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
topic := m.Header["Micro-Topic"]
//delete(m.Header, ":topic")
if len(topic) == 0 {
errr := merr.InternalServerError("go.micro.broker", "Topic not found")
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
p := &httpEvent{m: m, t: topic}
id := req.Form.Get("id")
//nolint:prealloc
var subs []broker.Handler
h.RLock()
for _, subscriber := range h.subscribers[topic] {
if id != subscriber.id {
continue
}
subs = append(subs, subscriber.fn)
}
h.RUnlock()
// execute the handler
for _, fn := range subs {
p.err = fn(p)
}
}
func (h *httpBroker) Address() string {
h.RLock()
defer h.RUnlock()
return h.address
}
func (h *httpBroker) Connect() error {
h.RLock()
if h.running {
h.RUnlock()
return nil
}
h.RUnlock()
h.Lock()
defer h.Unlock()
var l net.Listener
var err error
if h.opts.Secure || h.opts.TLSConfig != nil {
config := h.opts.TLSConfig
fn := func(addr string) (net.Listener, error) {
if config == nil {
hosts := []string{addr}
// check if its a valid host:port
if host, _, err := net.SplitHostPort(addr); err == nil {
if len(host) == 0 {
hosts = maddr.IPs()
} else {
hosts = []string{host}
}
}
// generate a certificate
cert, err := mls.Certificate(hosts...)
if err != nil {
return nil, err
}
config = &tls.Config{Certificates: []tls.Certificate{cert}}
}
return tls.Listen("tcp", addr, config)
}
l, err = mnet.Listen(h.address, fn)
} else {
fn := func(addr string) (net.Listener, error) {
return net.Listen("tcp", addr)
}
l, err = mnet.Listen(h.address, fn)
}
if err != nil {
return err
}
addr := h.address
h.address = l.Addr().String()
go http.Serve(l, h.mux)
go func() {
h.run(l)
h.Lock()
h.opts.Addrs = []string{addr}
h.address = addr
h.Unlock()
}()
// get registry
reg := h.opts.Registry
if reg == nil {
reg = mdns.NewRegistry()
}
// set cache
h.r = cache.New(reg)
// set running
h.running = true
return nil
}
func (h *httpBroker) Disconnect() error {
h.RLock()
if !h.running {
h.RUnlock()
return nil
}
h.RUnlock()
h.Lock()
defer h.Unlock()
// stop cache
rc, ok := h.r.(cache.Cache)
if ok {
rc.Stop()
}
// exit and return err
ch := make(chan error)
h.exit <- ch
err := <-ch
// set not running
h.running = false
return err
}
func (h *httpBroker) Init(opts ...broker.Option) error {
h.RLock()
if h.running {
h.RUnlock()
return errors.New("cannot init while connected")
}
h.RUnlock()
h.Lock()
defer h.Unlock()
for _, o := range opts {
o(&h.opts)
}
if len(h.opts.Addrs) > 0 && len(h.opts.Addrs[0]) > 0 {
h.address = h.opts.Addrs[0]
}
if len(h.id) == 0 {
h.id = "go.micro.http.broker-" + uuid.New().String()
}
// get registry
reg := h.opts.Registry
if reg == nil {
reg = mdns.NewRegistry()
}
// get cache
if rc, ok := h.r.(cache.Cache); ok {
rc.Stop()
}
// set registry
h.r = cache.New(reg)
// reconfigure tls config
if c := h.opts.TLSConfig; c != nil {
h.c = &http.Client{
Transport: newTransport(c),
}
}
return nil
}
func (h *httpBroker) Options() broker.Options {
return h.opts
}
func (h *httpBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error {
// create the message first
m := &broker.Message{
Header: make(map[string]string),
Body: msg.Body,
}
for k, v := range msg.Header {
m.Header[k] = v
}
m.Header["Micro-Topic"] = topic
// encode the message
b, err := h.opts.Codec.Marshal(m)
if err != nil {
return err
}
// save the message
h.saveMessage(topic, b)
// now attempt to get the service
h.RLock()
s, err := h.r.GetService(serviceName)
if err != nil {
h.RUnlock()
return err
}
h.RUnlock()
pub := func(node *registry.Node, t string, b []byte) error {
scheme := "http"
// check if secure is added in metadata
if node.Metadata["secure"] == "true" {
scheme = "https"
}
vals := url.Values{}
vals.Add("id", node.Id)
uri := fmt.Sprintf("%s://%s%s?%s", scheme, node.Address, DefaultPath, vals.Encode())
r, err := h.c.Post(uri, "application/json", bytes.NewReader(b))
if err != nil {
return err
}
// discard response body
io.Copy(ioutil.Discard, r.Body)
r.Body.Close()
return nil
}
srv := func(s []*registry.Service, b []byte) {
for _, service := range s {
var nodes []*registry.Node
for _, node := range service.Nodes {
// only use nodes tagged with broker http
if node.Metadata["broker"] != "http" {
continue
}
// look for nodes for the topic
if node.Metadata["topic"] != topic {
continue
}
nodes = append(nodes, node)
}
// only process if we have nodes
if len(nodes) == 0 {
continue
}
switch service.Version {
// broadcast version means broadcast to all nodes
case broadcastVersion:
var success bool
// publish to all nodes
for _, node := range nodes {
// publish async
if err := pub(node, topic, b); err == nil {
success = true
}
}
// save if it failed to publish at least once
if !success {
h.saveMessage(topic, b)
}
default:
// select node to publish to
node := nodes[rand.Int()%len(nodes)]
// publish async to one node
if err := pub(node, topic, b); err != nil {
// if failed save it
h.saveMessage(topic, b)
}
}
}
}
// do the rest async
go func() {
// get a third of the backlog
messages := h.getMessage(topic, 8)
delay := (len(messages) > 1)
// publish all the messages
for _, msg := range messages {
// serialize here
srv(s, msg)
// sending a backlog of messages
if delay {
time.Sleep(time.Millisecond * 100)
}
}
}()
return nil
}
func (h *httpBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
var err error
var host, port string
options := broker.NewSubscribeOptions(opts...)
// parse address for host, port
host, port, err = net.SplitHostPort(h.Address())
if err != nil {
return nil, err
}
addr, err := maddr.Extract(host)
if err != nil {
return nil, err
}
var secure bool
if h.opts.Secure || h.opts.TLSConfig != nil {
secure = true
}
// register service
node := &registry.Node{
Id: topic + "-" + h.id,
Address: mnet.HostPort(addr, port),
Metadata: map[string]string{
"secure": fmt.Sprintf("%t", secure),
"broker": "http",
"topic": topic,
},
}
// check for queue group or broadcast queue
version := options.Queue
if len(version) == 0 {
version = broadcastVersion
}
service := &registry.Service{
Name: serviceName,
Version: version,
Nodes: []*registry.Node{node},
}
// generate subscriber
subscriber := &httpSubscriber{
opts: options,
hb: h,
id: node.Id,
topic: topic,
fn: handler,
svc: service,
}
// subscribe now
if err := h.subscribe(subscriber); err != nil {
return nil, err
}
// return the subscriber
return subscriber, nil
}
func (h *httpBroker) String() string {
return "http"
}
// NewBroker returns a new http broker
func NewBroker(opts ...broker.Option) broker.Broker {
return broker.NewBroker(opts...)
return newHttpBroker(opts...)
}

View File

@@ -1,4 +1,4 @@
package broker_test
package http
import (
"sync"
@@ -6,9 +6,9 @@ import (
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/memory"
"github.com/micro/go-micro/v3/broker"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/registry/memory"
)
var (
@@ -61,7 +61,7 @@ func sub(be *testing.B, c int) {
be.StopTimer()
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
b := NewBroker(broker.Registry(m))
topic := uuid.New().String()
if err := b.Init(); err != nil {
@@ -120,7 +120,7 @@ func sub(be *testing.B, c int) {
func pub(be *testing.B, c int) {
be.StopTimer()
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
b := NewBroker(broker.Registry(m))
topic := uuid.New().String()
if err := b.Init(); err != nil {
@@ -189,7 +189,7 @@ func pub(be *testing.B, c int) {
func TestBroker(t *testing.T) {
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
b := NewBroker(broker.Registry(m))
if err := b.Init(); err != nil {
t.Fatalf("Unexpected init error: %v", err)
@@ -236,7 +236,7 @@ func TestBroker(t *testing.T) {
func TestConcurrentSubBroker(t *testing.T) {
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
b := NewBroker(broker.Registry(m))
if err := b.Init(); err != nil {
t.Fatalf("Unexpected init error: %v", err)
@@ -293,7 +293,7 @@ func TestConcurrentSubBroker(t *testing.T) {
func TestConcurrentPubBroker(t *testing.T) {
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
b := NewBroker(broker.Registry(m))
if err := b.Init(); err != nil {
t.Fatalf("Unexpected init error: %v", err)

View File

@@ -4,7 +4,7 @@ import (
"context"
"net/http"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v3/broker"
)
// Handle registers the handler for the given pattern.

View File

@@ -9,10 +9,10 @@ import (
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/logger"
maddr "github.com/micro/go-micro/v2/util/addr"
mnet "github.com/micro/go-micro/v2/util/net"
"github.com/micro/go-micro/v3/broker"
"github.com/micro/go-micro/v3/logger"
maddr "github.com/micro/go-micro/v3/util/addr"
mnet "github.com/micro/go-micro/v3/util/net"
)
type memoryBroker struct {

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"testing"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v3/broker"
)
func TestMemoryBroker(t *testing.T) {

View File

@@ -3,7 +3,7 @@ package nats
import (
"context"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v3/broker"
)
// setBrokerOption returns a function to setup a context with given value

View File

@@ -7,10 +7,10 @@ import (
"strings"
"sync"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/codec/json"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v3/broker"
"github.com/micro/go-micro/v3/codec/json"
"github.com/micro/go-micro/v3/logger"
"github.com/micro/go-micro/v3/registry/mdns"
nats "github.com/nats-io/nats.go"
)
@@ -131,6 +131,10 @@ func (n *natsBroker) Connect() error {
c, err := opts.Connect()
if err != nil {
if logger.V(logger.WarnLevel, logger.DefaultLogger) {
logger.Warnf("Error connecting to broker: %v", err)
}
return err
}
n.conn = c
@@ -306,7 +310,7 @@ func NewBroker(opts ...broker.Option) broker.Broker {
// Default codec
Codec: json.Marshaler{},
Context: context.Background(),
Registry: registry.DefaultRegistry,
Registry: mdns.NewRegistry(),
}
n := &natsBroker{

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"testing"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v3/broker"
nats "github.com/nats-io/nats.go"
)

View File

@@ -1,7 +1,7 @@
package nats
import (
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v3/broker"
nats "github.com/nats-io/nats.go"
)

View File

@@ -4,8 +4,8 @@ import (
"context"
"crypto/tls"
"github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v3/codec"
"github.com/micro/go-micro/v3/registry"
)
type Options struct {
@@ -13,7 +13,7 @@ type Options struct {
Secure bool
Codec codec.Marshaler
// Handler executed when error happens in broker mesage
// Handler executed when error happens in broker message
// processing
ErrorHandler Handler

View File

@@ -1,374 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: broker/service/proto/broker.proto
package go_micro_broker
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
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
type Empty struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
func (*Empty) Descriptor() ([]byte, []int) {
return fileDescriptor_df4d8f04292cf3fe, []int{0}
}
func (m *Empty) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Empty.Unmarshal(m, b)
}
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
}
func (m *Empty) XXX_Merge(src proto.Message) {
xxx_messageInfo_Empty.Merge(m, src)
}
func (m *Empty) XXX_Size() int {
return xxx_messageInfo_Empty.Size(m)
}
func (m *Empty) XXX_DiscardUnknown() {
xxx_messageInfo_Empty.DiscardUnknown(m)
}
var xxx_messageInfo_Empty proto.InternalMessageInfo
type PublishRequest struct {
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
Message *Message `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PublishRequest) Reset() { *m = PublishRequest{} }
func (m *PublishRequest) String() string { return proto.CompactTextString(m) }
func (*PublishRequest) ProtoMessage() {}
func (*PublishRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_df4d8f04292cf3fe, []int{1}
}
func (m *PublishRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PublishRequest.Unmarshal(m, b)
}
func (m *PublishRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PublishRequest.Marshal(b, m, deterministic)
}
func (m *PublishRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_PublishRequest.Merge(m, src)
}
func (m *PublishRequest) XXX_Size() int {
return xxx_messageInfo_PublishRequest.Size(m)
}
func (m *PublishRequest) XXX_DiscardUnknown() {
xxx_messageInfo_PublishRequest.DiscardUnknown(m)
}
var xxx_messageInfo_PublishRequest proto.InternalMessageInfo
func (m *PublishRequest) GetTopic() string {
if m != nil {
return m.Topic
}
return ""
}
func (m *PublishRequest) GetMessage() *Message {
if m != nil {
return m.Message
}
return nil
}
type SubscribeRequest struct {
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
Queue string `protobuf:"bytes,2,opt,name=queue,proto3" json:"queue,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SubscribeRequest) Reset() { *m = SubscribeRequest{} }
func (m *SubscribeRequest) String() string { return proto.CompactTextString(m) }
func (*SubscribeRequest) ProtoMessage() {}
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_df4d8f04292cf3fe, []int{2}
}
func (m *SubscribeRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SubscribeRequest.Unmarshal(m, b)
}
func (m *SubscribeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SubscribeRequest.Marshal(b, m, deterministic)
}
func (m *SubscribeRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SubscribeRequest.Merge(m, src)
}
func (m *SubscribeRequest) XXX_Size() int {
return xxx_messageInfo_SubscribeRequest.Size(m)
}
func (m *SubscribeRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SubscribeRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SubscribeRequest proto.InternalMessageInfo
func (m *SubscribeRequest) GetTopic() string {
if m != nil {
return m.Topic
}
return ""
}
func (m *SubscribeRequest) GetQueue() string {
if m != nil {
return m.Queue
}
return ""
}
type Message struct {
Header map[string]string `protobuf:"bytes,1,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Body []byte `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Message) Reset() { *m = Message{} }
func (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {}
func (*Message) Descriptor() ([]byte, []int) {
return fileDescriptor_df4d8f04292cf3fe, []int{3}
}
func (m *Message) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Message.Unmarshal(m, b)
}
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
}
func (m *Message) XXX_Merge(src proto.Message) {
xxx_messageInfo_Message.Merge(m, src)
}
func (m *Message) XXX_Size() int {
return xxx_messageInfo_Message.Size(m)
}
func (m *Message) XXX_DiscardUnknown() {
xxx_messageInfo_Message.DiscardUnknown(m)
}
var xxx_messageInfo_Message proto.InternalMessageInfo
func (m *Message) GetHeader() map[string]string {
if m != nil {
return m.Header
}
return nil
}
func (m *Message) GetBody() []byte {
if m != nil {
return m.Body
}
return nil
}
func init() {
proto.RegisterType((*Empty)(nil), "go.micro.broker.Empty")
proto.RegisterType((*PublishRequest)(nil), "go.micro.broker.PublishRequest")
proto.RegisterType((*SubscribeRequest)(nil), "go.micro.broker.SubscribeRequest")
proto.RegisterType((*Message)(nil), "go.micro.broker.Message")
proto.RegisterMapType((map[string]string)(nil), "go.micro.broker.Message.HeaderEntry")
}
func init() { proto.RegisterFile("broker/service/proto/broker.proto", fileDescriptor_df4d8f04292cf3fe) }
var fileDescriptor_df4d8f04292cf3fe = []byte{
// 299 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x51, 0x4d, 0x4b, 0xc3, 0x40,
0x14, 0xec, 0xb6, 0xb6, 0xa1, 0xaf, 0xa2, 0x65, 0x29, 0x12, 0x7a, 0x31, 0x0d, 0x1e, 0x72, 0xda,
0x48, 0xbc, 0xa8, 0x88, 0x07, 0xb1, 0xe0, 0x41, 0x41, 0xd6, 0x9b, 0xb7, 0x6c, 0xfa, 0x68, 0x43,
0x1b, 0x37, 0xdd, 0x4d, 0x0a, 0xf9, 0x23, 0x9e, 0xfc, 0xb1, 0xd2, 0xdd, 0xf8, 0xd5, 0x50, 0x6f,
0x6f, 0xde, 0xce, 0xce, 0x1b, 0x66, 0x60, 0x22, 0x94, 0x5c, 0xa2, 0x0a, 0x35, 0xaa, 0x4d, 0x9a,
0x60, 0x98, 0x2b, 0x59, 0xc8, 0xd0, 0x2e, 0x99, 0x01, 0xf4, 0x78, 0x2e, 0x59, 0x96, 0x26, 0x4a,
0x32, 0xbb, 0xf6, 0x1d, 0xe8, 0x4e, 0xb3, 0xbc, 0xa8, 0xfc, 0x57, 0x38, 0x7a, 0x2e, 0xc5, 0x2a,
0xd5, 0x0b, 0x8e, 0xeb, 0x12, 0x75, 0x41, 0x47, 0xd0, 0x2d, 0x64, 0x9e, 0x26, 0x2e, 0xf1, 0x48,
0xd0, 0xe7, 0x16, 0xd0, 0x08, 0x9c, 0x0c, 0xb5, 0x8e, 0xe7, 0xe8, 0xb6, 0x3d, 0x12, 0x0c, 0x22,
0x97, 0xed, 0x68, 0xb2, 0x27, 0xfb, 0xce, 0xbf, 0x88, 0xfe, 0x2d, 0x0c, 0x5f, 0x4a, 0xa1, 0x13,
0x95, 0x0a, 0xfc, 0x5f, 0x7d, 0x04, 0xdd, 0x75, 0x89, 0xa5, 0xd5, 0xee, 0x73, 0x0b, 0xfc, 0x77,
0x02, 0x4e, 0x2d, 0x4a, 0x6f, 0xa0, 0xb7, 0xc0, 0x78, 0x86, 0xca, 0x25, 0x5e, 0x27, 0x18, 0x44,
0x67, 0xfb, 0xce, 0xb3, 0x07, 0x43, 0x9b, 0xbe, 0x15, 0xaa, 0xe2, 0xf5, 0x1f, 0x4a, 0xe1, 0x40,
0xc8, 0x59, 0x65, 0xe4, 0x0f, 0xb9, 0x99, 0xc7, 0x57, 0x30, 0xf8, 0x45, 0xa5, 0x43, 0xe8, 0x2c,
0xb1, 0xaa, 0x6d, 0x6d, 0xc7, 0xad, 0xa9, 0x4d, 0xbc, 0xfa, 0x31, 0x65, 0xc0, 0x75, 0xfb, 0x92,
0x44, 0x1f, 0x04, 0x7a, 0x77, 0xe6, 0x2a, 0xbd, 0x07, 0xa7, 0xce, 0x8f, 0x9e, 0x36, 0x2c, 0xfd,
0x4d, 0x76, 0x7c, 0xd2, 0x20, 0xd8, 0x0e, 0x5a, 0xf4, 0x11, 0xfa, 0xdf, 0x49, 0xd1, 0x49, 0x83,
0xb6, 0x9b, 0xe2, 0x78, 0x6f, 0xf8, 0x7e, 0xeb, 0x9c, 0x88, 0x9e, 0x29, 0xfd, 0xe2, 0x33, 0x00,
0x00, 0xff, 0xff, 0x19, 0x9f, 0x10, 0x75, 0x19, 0x02, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// BrokerClient is the client API for Broker service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type BrokerClient interface {
Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*Empty, error)
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Broker_SubscribeClient, error)
}
type brokerClient struct {
cc *grpc.ClientConn
}
func NewBrokerClient(cc *grpc.ClientConn) BrokerClient {
return &brokerClient{cc}
}
func (c *brokerClient) Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := c.cc.Invoke(ctx, "/go.micro.broker.Broker/Publish", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *brokerClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Broker_SubscribeClient, error) {
stream, err := c.cc.NewStream(ctx, &_Broker_serviceDesc.Streams[0], "/go.micro.broker.Broker/Subscribe", opts...)
if err != nil {
return nil, err
}
x := &brokerSubscribeClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type Broker_SubscribeClient interface {
Recv() (*Message, error)
grpc.ClientStream
}
type brokerSubscribeClient struct {
grpc.ClientStream
}
func (x *brokerSubscribeClient) Recv() (*Message, error) {
m := new(Message)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// BrokerServer is the server API for Broker service.
type BrokerServer interface {
Publish(context.Context, *PublishRequest) (*Empty, error)
Subscribe(*SubscribeRequest, Broker_SubscribeServer) error
}
// UnimplementedBrokerServer can be embedded to have forward compatible implementations.
type UnimplementedBrokerServer struct {
}
func (*UnimplementedBrokerServer) Publish(ctx context.Context, req *PublishRequest) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method Publish not implemented")
}
func (*UnimplementedBrokerServer) Subscribe(req *SubscribeRequest, srv Broker_SubscribeServer) error {
return status.Errorf(codes.Unimplemented, "method Subscribe not implemented")
}
func RegisterBrokerServer(s *grpc.Server, srv BrokerServer) {
s.RegisterService(&_Broker_serviceDesc, srv)
}
func _Broker_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PublishRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(BrokerServer).Publish(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.broker.Broker/Publish",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BrokerServer).Publish(ctx, req.(*PublishRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Broker_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(SubscribeRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(BrokerServer).Subscribe(m, &brokerSubscribeServer{stream})
}
type Broker_SubscribeServer interface {
Send(*Message) error
grpc.ServerStream
}
type brokerSubscribeServer struct {
grpc.ServerStream
}
func (x *brokerSubscribeServer) Send(m *Message) error {
return x.ServerStream.SendMsg(m)
}
var _Broker_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.broker.Broker",
HandlerType: (*BrokerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Publish",
Handler: _Broker_Publish_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Subscribe",
Handler: _Broker_Subscribe_Handler,
ServerStreams: true,
},
},
Metadata: "broker/service/proto/broker.proto",
}

View File

@@ -1,185 +0,0 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: broker/service/proto/broker.proto
package go_micro_broker
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
api "github.com/micro/go-micro/v2/api"
client "github.com/micro/go-micro/v2/client"
server "github.com/micro/go-micro/v2/server"
)
// 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
// Reference imports to suppress errors if they are not otherwise used.
var _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for Broker service
func NewBrokerEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for Broker service
type BrokerService interface {
Publish(ctx context.Context, in *PublishRequest, opts ...client.CallOption) (*Empty, error)
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (Broker_SubscribeService, error)
}
type brokerService struct {
c client.Client
name string
}
func NewBrokerService(name string, c client.Client) BrokerService {
return &brokerService{
c: c,
name: name,
}
}
func (c *brokerService) Publish(ctx context.Context, in *PublishRequest, opts ...client.CallOption) (*Empty, error) {
req := c.c.NewRequest(c.name, "Broker.Publish", in)
out := new(Empty)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *brokerService) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (Broker_SubscribeService, error) {
req := c.c.NewRequest(c.name, "Broker.Subscribe", &SubscribeRequest{})
stream, err := c.c.Stream(ctx, req, opts...)
if err != nil {
return nil, err
}
if err := stream.Send(in); err != nil {
return nil, err
}
return &brokerServiceSubscribe{stream}, nil
}
type Broker_SubscribeService interface {
Context() context.Context
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Recv() (*Message, error)
}
type brokerServiceSubscribe struct {
stream client.Stream
}
func (x *brokerServiceSubscribe) Close() error {
return x.stream.Close()
}
func (x *brokerServiceSubscribe) Context() context.Context {
return x.stream.Context()
}
func (x *brokerServiceSubscribe) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *brokerServiceSubscribe) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *brokerServiceSubscribe) Recv() (*Message, error) {
m := new(Message)
err := x.stream.Recv(m)
if err != nil {
return nil, err
}
return m, nil
}
// Server API for Broker service
type BrokerHandler interface {
Publish(context.Context, *PublishRequest, *Empty) error
Subscribe(context.Context, *SubscribeRequest, Broker_SubscribeStream) error
}
func RegisterBrokerHandler(s server.Server, hdlr BrokerHandler, opts ...server.HandlerOption) error {
type broker interface {
Publish(ctx context.Context, in *PublishRequest, out *Empty) error
Subscribe(ctx context.Context, stream server.Stream) error
}
type Broker struct {
broker
}
h := &brokerHandler{hdlr}
return s.Handle(s.NewHandler(&Broker{h}, opts...))
}
type brokerHandler struct {
BrokerHandler
}
func (h *brokerHandler) Publish(ctx context.Context, in *PublishRequest, out *Empty) error {
return h.BrokerHandler.Publish(ctx, in, out)
}
func (h *brokerHandler) Subscribe(ctx context.Context, stream server.Stream) error {
m := new(SubscribeRequest)
if err := stream.Recv(m); err != nil {
return err
}
return h.BrokerHandler.Subscribe(ctx, m, &brokerSubscribeStream{stream})
}
type Broker_SubscribeStream interface {
Context() context.Context
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Send(*Message) error
}
type brokerSubscribeStream struct {
stream server.Stream
}
func (x *brokerSubscribeStream) Close() error {
return x.stream.Close()
}
func (x *brokerSubscribeStream) Context() context.Context {
return x.stream.Context()
}
func (x *brokerSubscribeStream) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *brokerSubscribeStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *brokerSubscribeStream) Send(m *Message) error {
return x.stream.Send(m)
}

View File

@@ -1,25 +0,0 @@
syntax = "proto3";
package go.micro.broker;
service Broker {
rpc Publish(PublishRequest) returns (Empty) {};
rpc Subscribe(SubscribeRequest) returns (stream Message) {};
}
message Empty {}
message PublishRequest {
string topic = 1;
Message message = 2;
}
message SubscribeRequest {
string topic = 1;
string queue = 2;
}
message Message {
map<string,string> header = 1;
bytes body = 2;
}

View File

@@ -1,146 +0,0 @@
// Package service provides the broker service client
package service
import (
"context"
"time"
"github.com/micro/go-micro/v2/broker"
pb "github.com/micro/go-micro/v2/broker/service/proto"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/logger"
)
type serviceBroker struct {
Addrs []string
Client pb.BrokerService
options broker.Options
}
var (
DefaultName = "go.micro.broker"
)
func (b *serviceBroker) Address() string {
return b.Addrs[0]
}
func (b *serviceBroker) Connect() error {
return nil
}
func (b *serviceBroker) Disconnect() error {
return nil
}
func (b *serviceBroker) Init(opts ...broker.Option) error {
for _, o := range opts {
o(&b.options)
}
return nil
}
func (b *serviceBroker) Options() broker.Options {
return b.options
}
func (b *serviceBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Publishing to topic %s broker %v", topic, b.Addrs)
}
_, err := b.Client.Publish(context.TODO(), &pb.PublishRequest{
Topic: topic,
Message: &pb.Message{
Header: msg.Header,
Body: msg.Body,
},
}, client.WithAddress(b.Addrs...))
return err
}
func (b *serviceBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
var options broker.SubscribeOptions
for _, o := range opts {
o(&options)
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Subscribing to topic %s queue %s broker %v", topic, options.Queue, b.Addrs)
}
stream, err := b.Client.Subscribe(context.TODO(), &pb.SubscribeRequest{
Topic: topic,
Queue: options.Queue,
}, client.WithAddress(b.Addrs...), client.WithRequestTimeout(time.Hour))
if err != nil {
return nil, err
}
sub := &serviceSub{
topic: topic,
queue: options.Queue,
handler: handler,
stream: stream,
closed: make(chan bool),
options: options,
}
go func() {
for {
select {
case <-sub.closed:
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Unsubscribed from topic %s", topic)
}
return
default:
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
// run the subscriber
logger.Debugf("Streaming from broker %v to topic [%s] queue [%s]", b.Addrs, topic, options.Queue)
}
if err := sub.run(); err != nil {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Resubscribing to topic %s broker %v", topic, b.Addrs)
}
stream, err := b.Client.Subscribe(context.TODO(), &pb.SubscribeRequest{
Topic: topic,
Queue: options.Queue,
}, client.WithAddress(b.Addrs...), client.WithRequestTimeout(time.Hour))
if err != nil {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Failed to resubscribe to topic %s: %v", topic, err)
}
time.Sleep(time.Second)
continue
}
// new stream
sub.stream = stream
}
}
}
}()
return sub, nil
}
func (b *serviceBroker) String() string {
return "service"
}
func NewBroker(opts ...broker.Option) broker.Broker {
var options broker.Options
for _, o := range opts {
o(&options)
}
addrs := options.Addrs
if len(addrs) == 0 {
addrs = []string{"127.0.0.1:8001"}
}
cli := client.DefaultClient
return &serviceBroker{
Addrs: addrs,
Client: pb.NewBrokerService(DefaultName, cli),
options: options,
}
}

View File

@@ -1,108 +0,0 @@
package service
import (
"github.com/micro/go-micro/v2/broker"
pb "github.com/micro/go-micro/v2/broker/service/proto"
"github.com/micro/go-micro/v2/logger"
)
type serviceSub struct {
topic string
queue string
handler broker.Handler
stream pb.Broker_SubscribeService
closed chan bool
options broker.SubscribeOptions
}
type serviceEvent struct {
topic string
err error
message *broker.Message
}
func (s *serviceEvent) Topic() string {
return s.topic
}
func (s *serviceEvent) Message() *broker.Message {
return s.message
}
func (s *serviceEvent) Ack() error {
return nil
}
func (s *serviceEvent) Error() error {
return s.err
}
func (s *serviceSub) isClosed() bool {
select {
case <-s.closed:
return true
default:
return false
}
}
func (s *serviceSub) run() error {
exit := make(chan bool)
go func() {
select {
case <-exit:
case <-s.closed:
}
// close the stream
s.stream.Close()
}()
for {
// TODO: do not fail silently
msg, err := s.stream.Recv()
if err != nil {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Streaming error for subcription to topic %s: %v", s.Topic(), err)
}
// close the exit channel
close(exit)
// don't return an error if we unsubscribed
if s.isClosed() {
return nil
}
// return stream error
return err
}
p := &serviceEvent{
topic: s.topic,
message: &broker.Message{
Header: msg.Header,
Body: msg.Body,
},
}
p.err = s.handler(p)
}
}
func (s *serviceSub) Options() broker.SubscribeOptions {
return s.options
}
func (s *serviceSub) Topic() string {
return s.topic
}
func (s *serviceSub) Unsubscribe() error {
select {
case <-s.closed:
return nil
default:
close(s.closed)
}
return nil
}

29
cache/cache.go vendored Normal file
View File

@@ -0,0 +1,29 @@
// 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
}
}

80
cache/memcache/memcache.go vendored Normal file
View File

@@ -0,0 +1,80 @@
// Package memcache is a memcache implementation of the Cache
package memcache
import (
"encoding/json"
"github.com/bradfitz/gomemcache/memcache"
"github.com/micro/go-micro/v3/cache"
)
type memcacheCache struct {
options cache.Options
client *memcache.Client
}
type memcacheItem struct {
Key string
Value interface{}
}
func (m *memcacheCache) Init(opts ...cache.Option) error {
for _, o := range opts {
o(&m.options)
}
return nil
}
func (m *memcacheCache) Get(key string) (interface{}, error) {
item, err := m.client.Get(key)
if err != nil {
return nil, err
}
var mc *memcacheItem
if err := json.Unmarshal(item.Value, &mc); err != nil {
return nil, err
}
return mc.Value, nil
}
func (m *memcacheCache) Set(key string, val interface{}) error {
b, err := json.Marshal(val)
if err != nil {
return err
}
return m.client.Set(&memcache.Item{
Key: key,
Value: b,
})
}
func (m *memcacheCache) Delete(key string) error {
return m.client.Delete(key)
}
func (m *memcacheCache) String() string {
return "memcache"
}
// NewCache returns a new memcache Cache
func NewCache(opts ...cache.Option) cache.Cache {
var options cache.Options
for _, o := range opts {
o(&options)
}
// get and set the nodes
nodes := options.Nodes
if len(nodes) == 0 {
nodes = []string{"localhost:11211"}
}
return &memcacheCache{
options: options,
client: memcache.New(nodes...),
}
}

56
cache/memory/memory.go vendored Normal file
View File

@@ -0,0 +1,56 @@
// Package memory is an in memory cache
package memory
import (
"sync"
"github.com/micro/go-micro/v3/cache"
"github.com/micro/go-micro/v3/errors"
)
type memoryCache struct {
// TODO: use a decent caching library
sync.RWMutex
values map[string]interface{}
}
func (m *memoryCache) Init(opts ...cache.Option) error {
// TODO: implement
return nil
}
func (m *memoryCache) Get(key string) (interface{}, error) {
m.RLock()
defer m.RUnlock()
v, ok := m.values[key]
if !ok {
return nil, errors.NotFound("go.micro.cache", key+" not found")
}
return v, nil
}
func (m *memoryCache) Set(key string, val interface{}) error {
m.Lock()
m.values[key] = val
m.Unlock()
return nil
}
func (m *memoryCache) Delete(key string) error {
m.Lock()
delete(m.values, key)
m.Unlock()
return nil
}
func (m *memoryCache) String() string {
return "memory"
}
func NewCache(opts ...cache.Option) cache.Cache {
return &memoryCache{
values: make(map[string]interface{}),
}
}

View File

@@ -4,7 +4,7 @@ import (
"context"
"time"
"github.com/micro/go-micro/v2/util/backoff"
"github.com/micro/go-micro/v3/util/backoff"
)
type BackoffFunc func(ctx context.Context, req Request, attempts int) (time.Duration, error)

View File

@@ -16,10 +16,13 @@ func TestBackoff(t *testing.T) {
7900 * time.Millisecond,
}
c := NewClient()
r := &testRequest{
service: "test",
method: "test",
}
for i := 0; i < 5; i++ {
d, err := exponentialBackoff(context.TODO(), c.NewRequest("test", "test", nil), i)
d, err := exponentialBackoff(context.TODO(), r, i)
if err != nil {
t.Fatal(err)
}

View File

@@ -7,7 +7,7 @@ import (
"hash/fnv"
"time"
"github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v3/metadata"
cache "github.com/patrickmn/go-cache"
)
@@ -24,12 +24,12 @@ type Cache struct {
}
// Get a response from the cache
func (c *Cache) Get(ctx context.Context, req *Request) (interface{}, bool) {
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) {
func (c *Cache) Set(ctx context.Context, req Request, rsp interface{}, expiry time.Duration) {
c.cache.Set(key(ctx, req), rsp, expiry)
}
@@ -47,16 +47,16 @@ func (c *Cache) List() map[string]string {
}
// key returns a hash for the context and request
func key(ctx context.Context, req *Request) string {
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(),
"service": req.Service(),
"endpoint": req.Endpoint(),
"method": req.Method(),
"body": req.Body(),
},
})

View File

@@ -5,15 +5,15 @@ import (
"testing"
"time"
"github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v3/metadata"
)
func TestCache(t *testing.T) {
ctx := context.TODO()
req := NewRequest("go.micro.service.foo", "Foo.Bar", nil)
req := &testRequest{service: "go.micro.service.foo", method: "Foo.Bar"}
t.Run("CacheMiss", func(t *testing.T) {
if _, ok := NewCache().Get(ctx, &req); ok {
if _, ok := NewCache().Get(ctx, req); ok {
t.Errorf("Expected to get no result from Get")
}
})
@@ -22,9 +22,9 @@ func TestCache(t *testing.T) {
c := NewCache()
rsp := "theresponse"
c.Set(ctx, &req, rsp, time.Minute)
c.Set(ctx, req, rsp, time.Minute)
if res, ok := c.Get(ctx, &req); !ok {
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)
@@ -34,21 +34,22 @@ func TestCache(t *testing.T) {
func TestCacheKey(t *testing.T) {
ctx := context.TODO()
req1 := NewRequest("go.micro.service.foo", "Foo.Bar", nil)
req2 := NewRequest("go.micro.service.foo", "Foo.Baz", nil)
req3 := NewRequest("go.micro.service.foo", "Foo.Baz", "customquery")
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)
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)
key1 := key(ctx, req1)
key2 := key(ctx, req2)
if key1 == key2 {
t.Errorf("Expected the keys to differ for different request endpoints")
@@ -56,8 +57,8 @@ func TestCacheKey(t *testing.T) {
})
t.Run("DifferentRequestBody", func(t *testing.T) {
key1 := key(ctx, &req2)
key2 := key(ctx, &req3)
key1 := key(ctx, req2)
key2 := key(ctx, req3)
if key1 == key2 {
t.Errorf("Expected the keys to differ for different request bodies")
@@ -66,8 +67,8 @@ func TestCacheKey(t *testing.T) {
t.Run("DifferentMetadata", func(t *testing.T) {
mdCtx := metadata.Set(context.TODO(), "Micro-Namespace", "bar")
key1 := key(mdCtx, &req1)
key2 := key(ctx, &req1)
key1 := key(mdCtx, req1)
key2 := key(ctx, req1)
if key1 == key2 {
t.Errorf("Expected the keys to differ for different metadata")

View File

@@ -5,7 +5,7 @@ import (
"context"
"time"
"github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v3/codec"
)
// Client is the interface used to make requests to services.
@@ -22,11 +22,6 @@ type Client interface {
String() string
}
// Router manages request routing
type Router interface {
SendRequest(context.Context, Request) (Response, error)
}
// Message is the interface for publishing asynchronously
type Message interface {
Topic() string
@@ -62,7 +57,7 @@ type Response interface {
Read() ([]byte, error)
}
// Stream is the inteface for a bidirectional synchronous stream
// Stream is the interface for a bidirectional synchronous stream
type Stream interface {
// Context for the stream
Context() context.Context
@@ -96,8 +91,6 @@ type MessageOption func(*MessageOptions)
type RequestOption func(*RequestOptions)
var (
// DefaultClient is a default client to use out of the box
DefaultClient Client = newRpcClient()
// DefaultBackoff is the default backoff function for retries
DefaultBackoff = exponentialBackoff
// DefaultRetry is the default check-for-retry function for retries
@@ -110,39 +103,4 @@ var (
DefaultPoolSize = 100
// DefaultPoolTTL sets the connection pool ttl
DefaultPoolTTL = time.Minute
// NewClient returns a new client
NewClient func(...Option) Client = newRpcClient
)
// Makes a synchronous call to a service using the default client
func Call(ctx context.Context, request Request, response interface{}, opts ...CallOption) error {
return DefaultClient.Call(ctx, request, response, opts...)
}
// Publishes a publication using the default client. Using the underlying broker
// set within the options.
func Publish(ctx context.Context, msg Message, opts ...PublishOption) error {
return DefaultClient.Publish(ctx, msg, opts...)
}
// Creates a new message using the default client
func NewMessage(topic string, payload interface{}, opts ...MessageOption) Message {
return DefaultClient.NewMessage(topic, payload, opts...)
}
// Creates a new request using the default client. Content Type will
// be set to the default within options and use the appropriate codec
func NewRequest(service, endpoint string, request interface{}, reqOpts ...RequestOption) Request {
return DefaultClient.NewRequest(service, endpoint, request, reqOpts...)
}
// Creates a streaming connection with a service and returns responses on the
// channel passed in. It's up to the user to close the streamer.
func NewStream(ctx context.Context, request Request, opts ...CallOption) (Stream, error) {
return DefaultClient.Stream(ctx, request, opts...)
}
func String() string {
return DefaultClient.String()
}

View File

@@ -9,8 +9,8 @@ import (
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
"github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v2/codec/bytes"
"github.com/micro/go-micro/v3/codec"
"github.com/micro/go-micro/v3/codec/bytes"
"github.com/oxtoacart/bpool"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding"

View File

@@ -1,7 +1,7 @@
package grpc
import (
"github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v3/errors"
"google.golang.org/grpc/status"
)
@@ -24,7 +24,9 @@ func microError(err error) error {
// return first error from details
if details := s.Details(); len(details) > 0 {
return microError(details[0].(error))
if verr, ok := details[0].(error); ok {
return microError(verr)
}
}
// try to decode micro *errors.Error

View File

@@ -6,18 +6,17 @@ import (
"crypto/tls"
"fmt"
"net"
"reflect"
"strings"
"sync/atomic"
"time"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/selector"
raw "github.com/micro/go-micro/v2/codec/bytes"
"github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v2/registry"
pnet "github.com/micro/go-micro/v2/util/net"
"github.com/micro/go-micro/v3/broker"
"github.com/micro/go-micro/v3/client"
raw "github.com/micro/go-micro/v3/codec/bytes"
"github.com/micro/go-micro/v3/errors"
"github.com/micro/go-micro/v3/metadata"
"github.com/micro/go-micro/v3/registry"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
@@ -71,35 +70,9 @@ func (g *grpcClient) secure(addr string) grpc.DialOption {
return grpc.WithInsecure()
}
func (g *grpcClient) next(request client.Request, opts client.CallOptions) (selector.Next, error) {
service, address, _ := pnet.Proxy(request.Service(), opts.Address)
// return remote address
if len(address) > 0 {
return func() (*registry.Node, error) {
return &registry.Node{
Address: address[0],
}, nil
}, nil
}
// get next nodes from the selector
next, err := g.opts.Selector.Select(service, opts.SelectOptions...)
if err != nil {
if err == selector.ErrNotFound {
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
}
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
}
return next, nil
}
func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error {
var header map[string]string
address := node.Address
header = make(map[string]string)
if md, ok := metadata.FromContext(ctx); ok {
header = make(map[string]string, len(md))
@@ -130,7 +103,7 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
grpcDialOptions := []grpc.DialOption{
grpc.WithTimeout(opts.DialTimeout),
g.secure(address),
g.secure(node.Address),
grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(maxRecvMsgSize),
grpc.MaxCallSendMsgSize(maxSendMsgSize),
@@ -141,13 +114,13 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
grpcDialOptions = append(grpcDialOptions, opts...)
}
cc, err := g.pool.getConn(address, grpcDialOptions...)
cc, err := g.pool.getConn(node.Address, grpcDialOptions...)
if err != nil {
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
}
defer func() {
// defer execution of release
g.pool.release(address, cc, grr)
g.pool.release(node.Address, cc, grr)
}()
ch := make(chan error, 1)
@@ -173,11 +146,9 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
return grr
}
func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client.Request, opts client.CallOptions) (client.Stream, error) {
func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error {
var header map[string]string
address := node.Address
if md, ok := metadata.FromContext(ctx); ok {
header = make(map[string]string, len(md))
for k, v := range md {
@@ -199,7 +170,7 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
cf, err := g.newGRPCCodec(req.ContentType())
if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error())
return errors.InternalServerError("go.micro.client", err.Error())
}
var dialCtx context.Context
@@ -215,16 +186,16 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
grpcDialOptions := []grpc.DialOption{
grpc.WithTimeout(opts.DialTimeout),
g.secure(address),
g.secure(node.Address),
}
if opts := g.getGrpcDialOptions(); opts != nil {
grpcDialOptions = append(grpcDialOptions, opts...)
}
cc, err := grpc.DialContext(dialCtx, address, grpcDialOptions...)
cc, err := grpc.DialContext(dialCtx, node.Address, grpcDialOptions...)
if err != nil {
return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
}
desc := &grpc.StreamDesc{
@@ -252,7 +223,7 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
// close the connection
cc.Close()
// now return the error
return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err))
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err))
}
codec := &grpcCodec{
@@ -265,21 +236,25 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
r.codec = codec
}
rsp := &response{
conn: cc,
// setup the stream response
stream := &grpcStream{
context: ctx,
request: req,
response: &response{
conn: cc,
stream: st,
codec: cf,
gcodec: codec,
},
stream: st,
codec: cf,
gcodec: codec,
conn: cc,
cancel: cancel,
}
return &grpcStream{
context: ctx,
request: req,
response: rsp,
stream: st,
conn: cc,
cancel: cancel,
}, nil
// set the stream as the response
val := reflect.ValueOf(rsp).Elem()
val.Set(reflect.ValueOf(stream).Elem())
return nil
}
func (g *grpcClient) poolMaxStreams() int {
@@ -385,11 +360,6 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
opt(&callOpts)
}
next, err := g.next(req, callOpts)
if err != nil {
return err
}
// check if we already have a deadline
d, ok := ctx.Deadline()
if !ok {
@@ -432,19 +402,33 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
time.Sleep(t)
}
// select next node
node, err := next()
service := req.Service()
if err != nil {
if err == selector.ErrNotFound {
return errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
}
return errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
// use the router passed as a call option, or fallback to the rpc clients router
if callOpts.Router == nil {
callOpts.Router = g.opts.Router
}
// use the selector passed as a call option, or fallback to the rpc clients selector
if callOpts.Selector == nil {
callOpts.Selector = g.opts.Selector
}
// lookup the route to send the reques to
route, err := client.LookupRoute(req, callOpts)
if err != nil {
return err
}
// pass a node to enable backwards compatability as changing the
// call func would be a breaking change.
// todo v3: change the call func to accept a route
node := &registry.Node{Address: route.Address}
// make the call
err = gcall(ctx, node, req, rsp, callOpts)
g.opts.Selector.Mark(service, node, err)
// record the result of the call to inform future routing decisions
g.opts.Selector.Record(*route, err)
// try and transform the error to a go-micro error
if verr, ok := err.(*errors.Error); ok {
return verr
}
@@ -492,11 +476,6 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
opt(&callOpts)
}
next, err := g.next(req, callOpts)
if err != nil {
return nil, err
}
// #200 - streams shouldn't have a request timeout set on the context
// should we noop right here?
@@ -506,6 +485,14 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
default:
}
// make a copy of stream
gstream := g.stream
// wrap the call in reverse
for i := len(callOpts.CallWrappers); i > 0; i-- {
gstream = callOpts.CallWrappers[i-1](gstream)
}
call := func(i int) (client.Stream, error) {
// call backoff first. Someone may want an initial start delay
t, err := callOpts.Backoff(ctx, req, i)
@@ -518,17 +505,39 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
time.Sleep(t)
}
node, err := next()
service := req.Service()
if err != nil {
if err == selector.ErrNotFound {
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
}
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
// use the router passed as a call option, or fallback to the rpc clients router
if callOpts.Router == nil {
callOpts.Router = g.opts.Router
}
// use the selector passed as a call option, or fallback to the rpc clients selector
if callOpts.Selector == nil {
callOpts.Selector = g.opts.Selector
}
stream, err := g.stream(ctx, node, req, callOpts)
g.opts.Selector.Mark(service, node, err)
// lookup the route to send the reques to
route, err := client.LookupRoute(req, callOpts)
if err != nil {
return nil, err
}
// pass a node to enable backwards compatability as changing the
// call func would be a breaking change.
// todo v3: change the call func to accept a route
node := &registry.Node{Address: route.Address}
// make the call
stream := &grpcStream{}
err = g.stream(ctx, node, req, stream, callOpts)
// record the result of the call to inform future routing decisions
g.opts.Selector.Record(*route, err)
// try and transform the error to a go-micro error
if verr, ok := err.(*errors.Error); ok {
return nil, verr
}
g.opts.Selector.Record(*route, err)
return stream, err
}
@@ -555,7 +564,7 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
return rsp.stream, nil
}
retry, rerr := callOpts.Retry(ctx, req, i, err)
retry, rerr := callOpts.Retry(ctx, req, i, grr)
if rerr != nil {
return nil, rerr
}
@@ -573,6 +582,16 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
var options client.PublishOptions
var body []byte
// fail early on connect error
if !g.once.Load().(bool) {
if err := g.opts.Broker.Connect(); err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
g.once.Store(true)
}
for _, o := range opts {
o(&options)
}
@@ -584,17 +603,15 @@ func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...clie
md["Content-Type"] = p.ContentType()
md["Micro-Topic"] = p.Topic()
cf, err := g.newGRPCCodec(p.ContentType())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
var body []byte
// passed in raw data
if d, ok := p.Payload().(*raw.Frame); ok {
body = d.Data
} else {
// use codec for payload
cf, err := g.newGRPCCodec(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 {
@@ -603,13 +620,6 @@ func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...clie
body = b
}
if !g.once.Load().(bool) {
if err = g.opts.Broker.Connect(); err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
g.once.Store(true)
}
topic := p.Topic()
// get the exchange

View File

@@ -5,11 +5,12 @@ import (
"net"
"testing"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/client/selector"
"github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/memory"
"github.com/micro/go-micro/v3/client"
"github.com/micro/go-micro/v3/errors"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/registry/memory"
"github.com/micro/go-micro/v3/router"
regRouter "github.com/micro/go-micro/v3/router/registry"
pgrpc "google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
@@ -56,16 +57,11 @@ func TestGRPCClient(t *testing.T) {
},
})
// create selector
se := selector.NewSelector(
selector.Registry(r),
)
// create router
rtr := regRouter.NewRouter(router.Registry(r))
// create client
c := NewClient(
client.Registry(r),
client.Selector(se),
)
c := NewClient(client.Router(rtr))
testMethods := []string{
"/helloworld.Greeter/SayHello",

View File

@@ -1,7 +1,7 @@
package grpc
import (
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v3/client"
)
type grpcEvent struct {

View File

@@ -5,7 +5,7 @@ import (
"context"
"crypto/tls"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v3/client"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding"
)

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"strings"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v3/client"
"github.com/micro/go-micro/v3/codec"
)
type grpcRequest struct {

View File

@@ -3,8 +3,8 @@ package grpc
import (
"strings"
"github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v2/codec/bytes"
"github.com/micro/go-micro/v3/codec"
"github.com/micro/go-micro/v3/codec/bytes"
"google.golang.org/grpc"
"google.golang.org/grpc/encoding"
)

View File

@@ -5,7 +5,7 @@ import (
"io"
"sync"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v3/client"
"google.golang.org/grpc"
)

View File

@@ -1,7 +1,7 @@
package client
package mucp
import (
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v3/registry"
)
var (

View File

@@ -2,10 +2,623 @@
package mucp
import (
"github.com/micro/go-micro/v2/client"
"context"
"fmt"
"sync/atomic"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/v3/broker"
"github.com/micro/go-micro/v3/client"
"github.com/micro/go-micro/v3/codec"
raw "github.com/micro/go-micro/v3/codec/bytes"
"github.com/micro/go-micro/v3/errors"
"github.com/micro/go-micro/v3/metadata"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/transport"
"github.com/micro/go-micro/v3/util/buf"
"github.com/micro/go-micro/v3/util/net"
"github.com/micro/go-micro/v3/util/pool"
)
// NewClient returns a new micro client interface
func NewClient(opts ...client.Option) client.Client {
return client.NewClient(opts...)
return newClient(opts...)
}
type rpcClient struct {
once atomic.Value
opts client.Options
pool pool.Pool
seq uint64
}
func newClient(opt ...client.Option) client.Client {
opts := client.NewOptions(opt...)
p := pool.NewPool(
pool.Size(opts.PoolSize),
pool.TTL(opts.PoolTTL),
pool.Transport(opts.Transport),
)
rc := &rpcClient{
opts: opts,
pool: p,
seq: 0,
}
rc.once.Store(false)
c := client.Client(rc)
// wrap in reverse
for i := len(opts.Wrappers); i > 0; i-- {
c = opts.Wrappers[i-1](c)
}
return c
}
func (r *rpcClient) newCodec(contentType string) (codec.NewCodec, error) {
if c, ok := r.opts.Codecs[contentType]; ok {
return c, nil
}
if cf, ok := DefaultCodecs[contentType]; ok {
return cf, nil
}
return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType)
}
func (r *rpcClient) call(ctx context.Context, node *registry.Node, req client.Request, resp interface{}, opts client.CallOptions) error {
msg := &transport.Message{
Header: make(map[string]string),
}
md, ok := metadata.FromContext(ctx)
if ok {
for k, v := range md {
// don't copy Micro-Topic header, that used for pub/sub
// this fix case then client uses the same context that received in subscriber
if k == "Micro-Topic" {
continue
}
msg.Header[k] = v
}
}
// set timeout in nanoseconds
msg.Header["Timeout"] = fmt.Sprintf("%d", opts.RequestTimeout)
// set the content type for the request
msg.Header["Content-Type"] = req.ContentType()
// set the accept header
msg.Header["Accept"] = req.ContentType()
// setup old protocol
cf := setupProtocol(msg, node)
// no codec specified
if cf == nil {
var err error
cf, err = r.newCodec(req.ContentType())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
}
dOpts := []transport.DialOption{
transport.WithStream(),
}
if opts.DialTimeout >= 0 {
dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
}
c, err := r.pool.Get(node.Address, dOpts...)
if err != nil {
return errors.InternalServerError("go.micro.client", "connection error: %v", err)
}
seq := atomic.AddUint64(&r.seq, 1) - 1
codec := newRpcCodec(msg, c, cf, "")
rsp := &rpcResponse{
socket: c,
codec: codec,
}
stream := &rpcStream{
id: fmt.Sprintf("%v", seq),
context: ctx,
request: req,
response: rsp,
codec: codec,
closed: make(chan bool),
release: func(err error) { r.pool.Release(c, err) },
sendEOS: false,
}
// close the stream on exiting this function
defer stream.Close()
// wait for error response
ch := make(chan error, 1)
go func() {
defer func() {
if r := recover(); r != nil {
ch <- errors.InternalServerError("go.micro.client", "panic recovered: %v", r)
}
}()
// send request
if err := stream.Send(req.Body()); err != nil {
ch <- err
return
}
// recv request
if err := stream.Recv(resp); err != nil {
ch <- err
return
}
// success
ch <- nil
}()
var grr error
select {
case err := <-ch:
return err
case <-ctx.Done():
grr = errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
}
// set the stream error
if grr != nil {
stream.Lock()
stream.err = grr
stream.Unlock()
return grr
}
return nil
}
func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req client.Request, opts client.CallOptions) (client.Stream, error) {
msg := &transport.Message{
Header: make(map[string]string),
}
md, ok := metadata.FromContext(ctx)
if ok {
for k, v := range md {
msg.Header[k] = v
}
}
// set timeout in nanoseconds
if opts.StreamTimeout > time.Duration(0) {
msg.Header["Timeout"] = fmt.Sprintf("%d", opts.StreamTimeout)
}
// set the content type for the request
msg.Header["Content-Type"] = req.ContentType()
// set the accept header
msg.Header["Accept"] = req.ContentType()
// set old codecs
cf := setupProtocol(msg, node)
// no codec specified
if cf == nil {
var err error
cf, err = r.newCodec(req.ContentType())
if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error())
}
}
dOpts := []transport.DialOption{
transport.WithStream(),
}
if opts.DialTimeout >= 0 {
dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
}
c, err := r.opts.Transport.Dial(node.Address, dOpts...)
if err != nil {
return nil, errors.InternalServerError("go.micro.client", "connection error: %v", err)
}
// increment the sequence number
seq := atomic.AddUint64(&r.seq, 1) - 1
id := fmt.Sprintf("%v", seq)
// create codec with stream id
codec := newRpcCodec(msg, c, cf, id)
rsp := &rpcResponse{
socket: c,
codec: codec,
}
// set request codec
if r, ok := req.(*rpcRequest); ok {
r.codec = codec
}
stream := &rpcStream{
id: id,
context: ctx,
request: req,
response: rsp,
codec: codec,
// used to close the stream
closed: make(chan bool),
// signal the end of stream,
sendEOS: true,
// release func
release: func(err error) { c.Close() },
}
// wait for error response
ch := make(chan error, 1)
go func() {
// send the first message
ch <- stream.Send(req.Body())
}()
var grr error
select {
case err := <-ch:
grr = err
case <-ctx.Done():
grr = errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
}
if grr != nil {
// set the error
stream.Lock()
stream.err = grr
stream.Unlock()
// close the stream
stream.Close()
return nil, grr
}
return stream, nil
}
func (r *rpcClient) Init(opts ...client.Option) error {
size := r.opts.PoolSize
ttl := r.opts.PoolTTL
tr := r.opts.Transport
for _, o := range opts {
o(&r.opts)
}
// update pool configuration if the options changed
if size != r.opts.PoolSize || ttl != r.opts.PoolTTL || tr != r.opts.Transport {
// close existing pool
r.pool.Close()
// create new pool
r.pool = pool.NewPool(
pool.Size(r.opts.PoolSize),
pool.TTL(r.opts.PoolTTL),
pool.Transport(r.opts.Transport),
)
}
return nil
}
func (r *rpcClient) Options() client.Options {
return r.opts
}
func (r *rpcClient) Call(ctx context.Context, request client.Request, response interface{}, opts ...client.CallOption) error {
// make a copy of call opts
callOpts := r.opts.CallOptions
for _, opt := range opts {
opt(&callOpts)
}
// check if we already have a deadline
if d, ok := ctx.Deadline(); !ok {
// no deadline so we create a new one
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, callOpts.RequestTimeout)
defer cancel()
} else {
// got a deadline so no need to setup context
// but we need to set the timeout we pass along
remaining := d.Sub(time.Now())
client.WithRequestTimeout(remaining)(&callOpts)
}
// should we noop right here?
select {
case <-ctx.Done():
return errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
default:
}
// make copy of call method
rcall := r.call
// wrap the call in reverse
for i := len(callOpts.CallWrappers); i > 0; i-- {
rcall = callOpts.CallWrappers[i-1](rcall)
}
// return errors.New("go.micro.client", "request timeout", 408)
call := func(i int) error {
// call backoff first. Someone may want an initial start delay
t, err := callOpts.Backoff(ctx, request, i)
if err != nil {
return errors.InternalServerError("go.micro.client", "backoff error: %v", err.Error())
}
// only sleep if greater than 0
if t.Seconds() > 0 {
time.Sleep(t)
}
// use the router passed as a call option, or fallback to the rpc clients router
if callOpts.Router == nil {
callOpts.Router = r.opts.Router
}
// use the selector passed as a call option, or fallback to the rpc clients selector
if callOpts.Selector == nil {
callOpts.Selector = r.opts.Selector
}
// lookup the route to send the request via
route, err := client.LookupRoute(request, callOpts)
if err != nil {
return err
}
// pass a node to enable backwards comparability as changing the
// call func would be a breaking change.
// todo v3: change the call func to accept a route
node := &registry.Node{Address: route.Address, Metadata: route.Metadata}
// make the call
err = rcall(ctx, node, request, response, callOpts)
// record the result of the call to inform future routing decisions
r.opts.Selector.Record(*route, err)
return err
}
// get the retries
retries := callOpts.Retries
// disable retries when using a proxy
if _, _, ok := net.Proxy(request.Service(), callOpts.Address); ok {
retries = 0
}
ch := make(chan error, retries+1)
var gerr error
for i := 0; i <= retries; i++ {
go func(i int) {
ch <- call(i)
}(i)
select {
case <-ctx.Done():
return errors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err()))
case err := <-ch:
// if the call succeeded lets bail early
if err == nil {
return nil
}
retry, rerr := callOpts.Retry(ctx, request, i, err)
if rerr != nil {
return rerr
}
if !retry {
return err
}
gerr = err
}
}
return gerr
}
func (r *rpcClient) Stream(ctx context.Context, request client.Request, opts ...client.CallOption) (client.Stream, error) {
// make a copy of call opts
callOpts := r.opts.CallOptions
for _, opt := range opts {
opt(&callOpts)
}
// should we noop right here?
select {
case <-ctx.Done():
return nil, errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
default:
}
call := func(i int) (client.Stream, error) {
// call backoff first. Someone may want an initial start delay
t, err := callOpts.Backoff(ctx, request, i)
if err != nil {
return nil, errors.InternalServerError("go.micro.client", "backoff error: %v", err.Error())
}
// only sleep if greater than 0
if t.Seconds() > 0 {
time.Sleep(t)
}
// use the router passed as a call option, or fallback to the rpc clients router
if callOpts.Router == nil {
callOpts.Router = r.opts.Router
}
// use the selector passed as a call option, or fallback to the rpc clients selector
if callOpts.Selector == nil {
callOpts.Selector = r.opts.Selector
}
// lookup the route to send the request via
route, err := client.LookupRoute(request, callOpts)
if err != nil {
return nil, err
}
// pass a node to enable backwards compatability as changing the
// call func would be a breaking change.
// todo v3: change the call func to accept a route
node := &registry.Node{Address: route.Address, Metadata: route.Metadata}
// perform the call
stream, err := r.stream(ctx, node, request, callOpts)
// record the result of the call to inform future routing decisions
r.opts.Selector.Record(*route, err)
return stream, err
}
type response struct {
stream client.Stream
err error
}
// get the retries
retries := callOpts.Retries
// disable retries when using a proxy
if _, _, ok := net.Proxy(request.Service(), callOpts.Address); ok {
retries = 0
}
ch := make(chan response, retries+1)
var grr error
for i := 0; i <= retries; i++ {
go func(i int) {
s, err := call(i)
ch <- response{s, err}
}(i)
select {
case <-ctx.Done():
return nil, errors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err()))
case rsp := <-ch:
// if the call succeeded lets bail early
if rsp.err == nil {
return rsp.stream, nil
}
retry, rerr := callOpts.Retry(ctx, request, i, rsp.err)
if rerr != nil {
return nil, rerr
}
if !retry {
return nil, rsp.err
}
grr = rsp.err
}
}
return nil, grr
}
func (r *rpcClient) Publish(ctx context.Context, msg client.Message, opts ...client.PublishOption) error {
options := client.PublishOptions{
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
md, ok := metadata.FromContext(ctx)
if !ok {
md = make(map[string]string)
}
id := uuid.New().String()
md["Content-Type"] = msg.ContentType()
md["Micro-Topic"] = msg.Topic()
md["Micro-Id"] = id
// set the topic
topic := msg.Topic()
// get the exchange
if len(options.Exchange) > 0 {
topic = options.Exchange
}
// encode message body
cf, err := r.newCodec(msg.ContentType())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
var body []byte
// passed in raw data
if d, ok := msg.Payload().(*raw.Frame); ok {
body = d.Data
} else {
// new buffer
b := buf.New(nil)
if err := cf(b).Write(&codec.Message{
Target: topic,
Type: codec.Event,
Header: map[string]string{
"Micro-Id": id,
"Micro-Topic": msg.Topic(),
},
}, msg.Payload()); err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
// set the body
body = b.Bytes()
}
if !r.once.Load().(bool) {
if err = r.opts.Broker.Connect(); err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
r.once.Store(true)
}
return r.opts.Broker.Publish(topic, &broker.Message{
Header: md,
Body: body,
}, broker.PublishContext(options.Context))
}
func (r *rpcClient) NewMessage(topic string, message interface{}, opts ...client.MessageOption) client.Message {
return newMessage(topic, message, r.opts.ContentType, opts...)
}
func (r *rpcClient) NewRequest(service, method string, request interface{}, reqOpts ...client.RequestOption) client.Request {
return newRequest(service, method, request, r.opts.ContentType, reqOpts...)
}
func (r *rpcClient) String() string {
return "mucp"
}

View File

@@ -1,19 +1,19 @@
package client
package mucp
import (
"bytes"
errs "errors"
"github.com/micro/go-micro/v2/codec"
raw "github.com/micro/go-micro/v2/codec/bytes"
"github.com/micro/go-micro/v2/codec/grpc"
"github.com/micro/go-micro/v2/codec/json"
"github.com/micro/go-micro/v2/codec/jsonrpc"
"github.com/micro/go-micro/v2/codec/proto"
"github.com/micro/go-micro/v2/codec/protorpc"
"github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/transport"
"github.com/micro/go-micro/v3/codec"
raw "github.com/micro/go-micro/v3/codec/bytes"
"github.com/micro/go-micro/v3/codec/grpc"
"github.com/micro/go-micro/v3/codec/json"
"github.com/micro/go-micro/v3/codec/jsonrpc"
"github.com/micro/go-micro/v3/codec/proto"
"github.com/micro/go-micro/v3/codec/protorpc"
"github.com/micro/go-micro/v3/errors"
"github.com/micro/go-micro/v3/registry"
"github.com/micro/go-micro/v3/transport"
)
const (
@@ -129,10 +129,8 @@ func setHeaders(m *codec.Message, stream string) {
// setupProtocol sets up the old protocol
func setupProtocol(msg *transport.Message, node *registry.Node) codec.NewCodec {
protocol := node.Metadata["protocol"]
// got protocol
if len(protocol) > 0 {
// get the protocol from node metadata
if protocol := node.Metadata["protocol"]; len(protocol) > 0 {
return nil
}

Some files were not shown because too many files have changed in this diff Show More