Compare commits

..

976 Commits

Author SHA1 Message Date
jxlwqq d9a6faeb7a Add latest version (#2303)
Run tests / Test repo (push) Waiting to run
2021-10-11 15:15:42 +01:00
Asim Aslam 4cb6168780 Update README.md 2021-10-11 09:21:06 +01:00
Asim Aslam b8d7f87d17 remove file 2021-10-11 09:19:20 +01:00
Asim Aslam b2e17a89e5 rename file 2021-10-11 09:19:01 +01:00
Asim Aslam 4ae2528cbe add m3o services (#2301)
* add m3o services

* update readme
2021-10-11 09:18:28 +01:00
Asim Aslam 043a82bce2 Update README.md 2021-10-11 08:38:31 +01:00
Asim Aslam 99e0b182b7 Update README.md 2021-10-11 08:38:07 +01:00
Asim Aslam ab1b10f13d Update README.md 2021-10-11 08:37:38 +01:00
jxlwqq 86eabf4a4c Use go install (#2300)
go get: installing executables with 'go get' in module mode is deprecated.
        To adjust and download dependencies of the current module, use 'go get -d'.
        To install using requirements of the current module, use 'go install'.
        To install ignoring the current module, use 'go install' with a version,
        like 'go install example.com/cmd@latest'.
        For more information, see https://golang.org/doc/go-get-install-deprecation
        or run 'go help get' or 'go help install'.
2021-10-11 06:32:13 +01:00
Qalifah a99a1e9356 add MultiError type (#2297) 2021-10-06 17:55:14 +01:00
Asim Aslam 2ef523a7eb Delete index.html 2021-10-02 13:13:22 +01:00
Asim Aslam a1da40d9aa Delete CNAME 2021-10-01 20:23:38 +01:00
Defoo Li a315fc2dda Fix missing content type (#2289) 2021-09-30 12:15:09 +01:00
Johnson C 6dc25053ea Errors (#2290)
* add errors.As

convert target err to *Error, return false if err don't match *Error

* update errors.As to (*Error, bool)

* fixing FromError panic issue when err is nil
2021-09-30 07:45:10 +01:00
Qiu Yu 4612baa7f8 zap plugin: allow injecting zap logger options (#2287)
Though some of the Zap logger option can be customized through
plugins/logger/zap.Options, this change allows go.uber.org/zap.Option be
be injected directly for deeper customization.
2021-09-29 07:12:16 +01:00
Johnson C 44ecd6a457 Hystrix filter (#2286)
* support hystrix filter

* filter function should return true of false
2021-09-28 11:23:04 +01:00
Asim Aslam 8c39b1e120 Update index.html 2021-09-24 09:10:04 +01:00
Asim Aslam 12eff1cc60 go fmt 2021-09-24 09:08:39 +01:00
Asim Aslam 9deb715ebb Update go.mod 2021-09-24 09:08:16 +01:00
Arsen 3b60db0dcd logger helper: add "inject" method, to make a pair with "extract" (#2283) 2021-09-23 16:48:08 +01:00
Asim Aslam af22cbb108 Update README.md 2021-09-22 14:48:20 +01:00
Asim Aslam 9d7131a512 Update README.md 2021-09-22 14:43:37 +01:00
Asim Aslam bca9ab165d Update README.md 2021-09-22 14:42:17 +01:00
Asim Aslam d111f96993 Update README.md 2021-09-22 14:41:29 +01:00
Asim Aslam 2f223c276c Update README.md 2021-09-22 14:32:06 +01:00
Asim Aslam 33acb4a956 update readme 2021-09-22 14:31:21 +01:00
Asim Aslam b515785637 add services dir 2021-09-22 14:30:40 +01:00
Asim Aslam 3dcdcdad32 Update README.md 2021-09-22 13:37:07 +01:00
Arsen 916ed6b8ee logger: caller's skip correction (#2280) 2021-09-22 09:01:03 +01:00
gregkv b8fbe87e1f Use context to log "panic recovered" errors in grpc-server plugin (#2278) 2021-09-20 10:50:13 +01:00
Asim Aslam cb3db7dd83 Update options.go 2021-09-19 17:22:28 +01:00
simon 066ce5045b Command Option add NewConfig,NewProfile func (#2276)
* Add grpc,memory,quic transport automatically discover

* Add grpc,memory,quic transport automatically discover

* Add jwt auth automatically discover

* Add jwt auth automatically discover

* Add config command option automatically discover

* Add AuthCall wrapper func

* Add NewConfig func

* Add NewProfile func
2021-09-19 17:21:55 +01:00
Asim Aslam a65932ff82 Create CNAME 2021-09-19 15:10:47 +01:00
Asim Aslam 5496086916 Create index.html 2021-09-19 14:44:03 +01:00
Kurisu_Amatist efd4ef0e62 fix(cache): only watch calling service in registry (#2273) 2021-09-19 10:40:21 +01:00
gregkv 00d819a199 Remove fields map from Helper, add Extract method and fix for defaultLogger.Fields (#2274) 2021-09-19 10:40:09 +01:00
helloword ad532522ea Config CORS (#2270)
* Added cors.config for CORS

* Added cors.config for CORS

* Added cors.config for CORS

Co-authored-by: 于海洋 <yhy@iothinking.com>
2021-09-17 10:17:12 +01:00
Christoffer Åström 4c7d2e28eb test(api): fix randomly failing rpc test (#2268) 2021-09-13 21:56:36 +01:00
Branden Horiuchi d78d35078c Consul sync plugin (#2267)
* adding consul sync provider

* adding logging options

Co-authored-by: Branden Horiuchi <Branden.Horiuchi@blackline.com>
2021-09-13 19:08:05 +01:00
Johnson C 22409c8ff3 support hystrix filter (#2265) 2021-09-13 09:23:26 +01:00
binbin.zhang 6c3a5c161f upgrade nacos sdk version (#2264)
Co-authored-by: binbin <binbin@didiglobal.com>
2021-09-12 22:16:46 +01:00
Niek den Breeje fa27250605 Add generate kubernetes command (#2261) 2021-09-10 18:56:51 +01:00
Niek den Breeje ac21bb5b19 Document Gomu's generator package (#2262) 2021-09-10 18:48:09 +01:00
Niek den Breeje 5b8d22a463 Add Kubernetes flag to new command (#2263)
To remain consistent with the Gomu's generate command, we add a
Kubernetes flag to Gomu's new command as well.
2021-09-10 18:47:55 +01:00
Niek den Breeje 56d5143557 Document Gomu's generate command (#2260) 2021-09-10 14:30:46 +01:00
Niek den Breeje 5772697752 Make generator package a first class citizen (#2259)
There's really no point in having the `generator` be embedded in a
`file` package so we remove the `file` package and make the `generator`
package a first class citizen instead.
2021-09-10 13:31:52 +01:00
Niek den Breeje e23006b1a5 Generate Gomu files after the fact (#2258)
* Move file generation to new package

* Use text/template instead of html/template

* Make config variables more consistent

* Combine generate files and print comments there

* Add gomu generate command

* Refactor project templating to file library

* Determine client earlier
2021-09-10 13:20:57 +01:00
Niek den Breeje 01b7b4409b Fix generating Dockerfile with Gomu (#2254)
Somehow I didn't test this and managed to forget to properly close a
template control structure. This change fixes that.
2021-09-08 14:43:52 +01:00
Niek den Breeje 0dd6afe128 Optimize Dockerfile generated by Gomu (#2253)
This change prevents having to rebuild the entire Docker image when your
source code changes.
2021-09-08 12:50:30 +01:00
Niek den Breeje a36f52c6d2 Fix Gomu's call response (#2251)
Gomu expects a `map[string]string` type response back, but this isn't
always the case. When Gomu calls a service endpoint that responds with,
let's say, a key where its value is a map or a list, Gomu would be
unable to decode that response. By expecting a `map[string]interface{}`
type response, Gomu is able to decode those responses as well.
2021-09-08 08:30:53 +01:00
simon 440aa4a1ce Add AuthCall wrapper func (#2250)
* Add grpc,memory,quic transport automatically discover

* Add grpc,memory,quic transport automatically discover

* Add jwt auth automatically discover

* Add jwt auth automatically discover

* Add config command option automatically discover

* Add AuthCall wrapper func
2021-09-07 07:13:56 +01:00
Niek den Breeje f77c91b7ae Simplify gomu cmd registering (#2249)
* Use internal runtime package for gomu run

This change refactors the `gomu run` command to use Go Micro's internal
runtime package in order to run services. Not only does this clean up
duplicate functionality between Go Micro and Gomu, but also adds the
feature to Gomu to run remote projects. For example, the following
command pulls in a remote project and runs it locally.

```bash
gomu run github.com/auditemarlow/helloworld
```

The `gomu run` command remains backwards compatible. By invoking `gomu
run` in a Go Micro project directory, Gomu will simply run that project.

* Simplify Gomu's command registering

By leveraging Go's `init()` function, we can simplify registering
commands just a tad.
2021-09-06 14:12:27 +01:00
Niek den Breeje a58b8883f8 Use internal runtime package for gomu run (#2248)
This change refactors the `gomu run` command to use Go Micro's internal
runtime package in order to run services. Not only does this clean up
duplicate functionality between Go Micro and Gomu, but also adds the
feature to Gomu to run remote projects. For example, the following
command pulls in a remote project and runs it locally.

```bash
gomu run github.com/auditemarlow/helloworld
```

The `gomu run` command remains backwards compatible. By invoking `gomu
run` in a Go Micro project directory, Gomu will simply run that project.
2021-09-06 14:01:29 +01:00
simon 270d910b73 Add config command option automatically discover (#2246)
* Add grpc,memory,quic transport automatically discover

* Add grpc,memory,quic transport automatically discover

* Add jwt auth automatically discover

* Add jwt auth automatically discover

* Add config command option automatically discover
2021-09-04 07:17:21 +01:00
Niek den Breeje 77bf39f2cd Fix client gRPC plugin (#2245)
The helloworld examples found in the `google.golang.org/grpc/examples`
package were imported multiple times as different versions, resulting in
package conflicts. By running `go mod tidy`, these conflicts are
resolved and the gRPC client plugin can now be imported again.
2021-09-03 14:16:56 +01:00
Niek den Breeje c45073a308 Fix Skaffold pipelines for client projects (#2244)
* Update greeter references to helloworld

* Add new client command

With this change, Gomu users will be able to generate template projects
for clients to services. Additionally vendor support has been built in
so Gomu users can now generate projects using fully qualified package
names, for example:

```bash
gomu new service github.com/auditemarlow/helloworld
```

This will create a new service project `helloworld` with its module name
already set to `github.com/auditemarlow/helloworld`. Likewise, Gomu
users can then generate client projects in the same manner:

```bash
gomu new client github.com/auditemarlow/helloworld
```

This will create a `helloworld-client` project that uses the protobufs
found in the `github.com/auditemarlow/helloworld` service. This removes
at least some strain in configuring these module dependencies yourself;
you can just scaffold them outright from the start.

Although the default client project is highly opinionated, it works
straight out of the box and has Skaffold in mind. Gomu users should be
able to get going in a matter of seconds.

* Update README

* Fix Skaffold pipeline for generated client projects
2021-09-03 13:26:34 +01:00
Niek den Breeje 86de031adc [WIP] Add new client cmd (#2243)
* Update greeter references to helloworld

* Add new client command

With this change, Gomu users will be able to generate template projects
for clients to services. Additionally vendor support has been built in
so Gomu users can now generate projects using fully qualified package
names, for example:

```bash
gomu new service github.com/auditemarlow/helloworld
```

This will create a new service project `helloworld` with its module name
already set to `github.com/auditemarlow/helloworld`. Likewise, Gomu
users can then generate client projects in the same manner:

```bash
gomu new client github.com/auditemarlow/helloworld
```

This will create a `helloworld-client` project that uses the protobufs
found in the `github.com/auditemarlow/helloworld` service. This removes
at least some strain in configuring these module dependencies yourself;
you can just scaffold them outright from the start.

Although the default client project is highly opinionated, it works
straight out of the box and has Skaffold in mind. Gomu users should be
able to get going in a matter of seconds.

* Update README
2021-09-03 12:33:10 +01:00
simon 80dbe51077 Add jwt auth automatically discover (#2242)
* Add grpc,memory,quic transport automatically discover

* Add grpc,memory,quic transport automatically discover

* Add jwt auth automatically discover

* Add jwt auth automatically discover
2021-09-03 07:49:49 +01:00
Johnson C f444dadd50 fixing #2235 win10 install failed issue (#2239) 2021-09-03 07:49:39 +01:00
Christoffer Åström e080791787 chore(gomu): tweak makefile template (#2238) 2021-09-02 18:24:28 +01:00
simon a159598f36 Add grpc,memory,quic transport automatically discover (#2237)
* Add grpc,memory,quic transport automatically discover

* Add grpc,memory,quic transport automatically discover
2021-09-02 18:10:24 +01:00
Niek den Breeje 993e2f55bd Add Kubernetes resources (#2236)
* Refact new command implementation

Everytime I want to add or modify files in the new command, I will have
to do so in 2 places. This change will consolidate functionality so
there's only a single place to modify.

* Move Kubernetes resources to its own file

* Add Kubernetes resources
2021-09-02 13:35:00 +01:00
Niek den Breeje ed9053ed94 Add cache example (#2232) 2021-09-01 10:08:43 +01:00
Niek den Breeje 05a299b76c Add simple in-memory cache (#2231)
* Add simple in-memory cache

* Support configuring cache expiration duration

* Support preinitializing cache with items

* Register cache
2021-08-31 15:31:16 +01:00
Asim Aslam dd0a7746ff update the gomu gomod 2021-08-31 15:19:45 +01:00
Asim Aslam 088ccb5001 Update README.md 2021-08-31 09:27:36 +01:00
Niek den Breeje 4363678e48 Exit when help flag is provided on service run (#2225) 2021-08-31 09:13:35 +01:00
Niek den Breeje 9a77c06b44 Move from micro/cli/v2 to urfave/cli/v2 (#2224)
* Use urfave/cli/v2 instead of micro/cli/v2

Wherever possible we may want to eliminate the use of github.com/micro
imports.

* Fix broken cli test
2021-08-31 09:05:08 +01:00
Niek den Breeje 78bea87690 Add gomu CLI tool (#2223) 2021-08-30 15:48:57 +01:00
Johnson C 49eccbc85a fixing proto3 optional unsupported error (#2213)
add supported features
2021-08-24 08:14:33 +01:00
Asim Aslam 7f1ebaa572 Update README.md 2021-08-23 16:45:04 +01:00
Qiu Yu c7195aae98 Grpc server injection (#2208)
Run tests / Test repo (push) Waiting to run
* plugin: update grpc server readme

* plugin: refactor gprc server test

This is a no-op change to enable test logic reuse for different
combinations.

* plugin: grpc server test Init after New

* plugin: allow grpc.Server to be injected
2021-08-12 18:26:26 +01:00
Arsen ffb0a2f896 grpc client: fix grpc code to micro http status conversion for the fallback case with empty Details (#2206) 2021-08-12 12:15:49 +01:00
Qiu Yu b977a51253 Examples: fix grpc config (#2207)
This change fixes grpc config example code which is currently broken.

Several pieces in the change:
- use yaml encoder to read config files
- rename *.yml to *.yaml to fix format (file suffix) for encoder lookup
- replace util/log package with logger as the former one is deprecated
2021-08-12 06:27:04 +01:00
dependabot[bot] 046d0785c9 Bump github.com/gin-gonic/gin from 1.6.3 to 1.7.0 in /examples (#2205)
Bumps [github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) from 1.6.3 to 1.7.0.
- [Release notes](https://github.com/gin-gonic/gin/releases)
- [Changelog](https://github.com/gin-gonic/gin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gin-gonic/gin/compare/v1.6.3...v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/gin-gonic/gin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-11 13:57:15 +01:00
Qiu Yu 828d3f2b74 loader: avoid overwriting values if error happens (#2204) 2021-08-11 13:33:58 +01:00
Qiu Yu a46dc0b856 Add yaml config example (#2203)
* examples: add yaml config

* examples: update go mod for yaml plugin
2021-08-11 13:31:29 +01:00
Johnson C 3e0411a3f6 fixing mem.pprof issue: (#2198)
parsing profile: concatenated profiles detected
fixing web service not start profile issue
fixing no default profile options issue
2021-08-04 09:39:01 +01:00
bacndcmc c3107e6843 fix code proto return invalid message (#2196)
Co-authored-by: ben <norton0395@gmail.com>
2021-07-26 06:25:21 +01:00
Asim Aslam e1bc7e3028 Update README.md 2021-07-21 09:06:34 +01:00
Asim Aslam 992418236e Update README.md 2021-07-21 09:05:38 +01:00
Asim Aslam 27a792790b Update README.md 2021-07-21 09:05:20 +01:00
Asim Aslam bd9821111e remove tidy file 2021-07-19 17:57:33 +01:00
Asim Aslam dbba07e7ed Delete tidy.md 2021-07-19 17:55:08 +01:00
xshytikx 753022d2ae fix: imported and not used (log, os) (#2195)
github.com/asim/go-micro/plugins/registry/consul/v3@v3.0.0-20210716165540-546225f1d8db/watcher.go:5:2: imported and not used: "log"
github.com/asim/go-micro/plugins/registry/consul/v3@v3.0.0-20210716165540-546225f1d8db/watcher.go:5:2: imported and not used: "log"
2021-07-18 07:18:11 +01:00
JeffreyBool 546225f1d8 Not Recommended Function Correction (#2194)
* Update http.go

Exit before deregister is executed

* Create http.go

Exit before deregister is executed

* Solve the problem that the resources have not been fully released due to early exit

* Optimize some code

* Optimize some code

* Optimize some code

* fix service default logger

* Not Recommended Function Correction
2021-07-16 17:55:40 +01:00
JeffreyBool 0532fd9de8 fix logger v3 (#2193)
* Update http.go

Exit before deregister is executed

* Create http.go

Exit before deregister is executed

* Solve the problem that the resources have not been fully released due to early exit

* Optimize some code

* Optimize some code

* Optimize some code

* fix service default logger

* Repair mq asynchronous send, mq write failure without error output

* Repair mq asynchronous send, mq write failure without error output

* fix logger v3
2021-07-12 07:18:37 +01:00
lanrion 3fbf2c304f Optimize prometheus wrapper,removed mutex,initialize MetricVec at init() (#2192)
Co-authored-by: dylan.deng <dylan.deng@yijinin.com>
2021-07-09 12:52:08 +01:00
JeffreyBool 6cdf28270f Repair mq asynchronous send, mq write failure without error output (#2191)
* Update http.go

Exit before deregister is executed

* Create http.go

Exit before deregister is executed

* Solve the problem that the resources have not been fully released due to early exit

* Optimize some code

* Optimize some code

* Optimize some code

* fix service default logger

* Repair mq asynchronous send, mq write failure without error output

* Repair mq asynchronous send, mq write failure without error output
2021-07-09 06:19:09 +01:00
Johnson C 7f1de77e8c try fixing staticcheck warning (#2190)
Run tests / Test repo (push) Waiting to run
https://github.com/golang/protobuf/issues/1077
package github.com/golang/protobuf/proto is deprecated: Use the "google.golang.org/protobuf/proto" package instead.  (SA1019)
2021-07-06 12:51:28 +01:00
Asim Aslam 1c482e8922 add web package 2021-07-06 11:12:19 +01:00
wangYue 70ed9bf154 fix missing error (#2189)
Co-authored-by: wangyue <wangyue@actiontech.com>
2021-06-30 10:13:05 +01:00
Jerry 93ba8cd0df continue fix pre version go get bug that unknown v3.5.1 (#2188)
* 1.fix plugins go get bug.
2.update all mode.
3.add tidy tools

* continue fix pre version go get bug that unknown v3.5.1
2021-06-30 09:24:00 +01:00
Jerry c13bb07171 1.fix plugins go get bug. (#2187)
2.update all mode.
3.add tidy tools
2021-06-30 07:21:03 +01:00
Jerry 4929a7c16e update etcd version (#2186)
Remove missing gRPC example from README.md (#2112)

Delete docker.yml

Delete Dockerfile

update plugins version & remove replace (#2118)

* update memory registry plugins version & remove replace

* update plugins version & remove replace

Co-authored-by: 申法宽 <shenfakuan@163.com>

update client/grpc plugins version & remove replace (#2119)

* update memory registry plugins version & remove replace

* update plugins version & remove replace

* update plugins/client/grpc/v3 version

Co-authored-by: 申法宽 <shenfakuan@163.com>

update etcd version (#2120)

update mod version

update

update pulgin registry mod version (#2121)

* update etcd version

* update mod version

* update

fix store delete

support for tls on http plugin (#2126)

improve code quality (#2128)

* Fix inefficient string comparison

* Fix unnecessary calls to Printf

* Canonicalize header key

* Replace `t.Sub(time.Now())` with `time.Until`

* Remove unnecessary blank (_) identifier

* Remove unnecessary use of slice

* Remove unnecessary comparison with bool

Update README.md

Update README.md

remove network package

update quic go mod

remove indirects

update etcd mod version

Update registry plugins mod version (#2130)

* update etcd version

* update mod version

* update

* update etcd mod version

Update README.md

Update README.md

Update README.md

fixing etcd stack in getToken (#2145)

when provide username and password, etcd will try to get auth token from server
if server is unavailble, etcd client will stack in
when dial timeout is set, it will return err instead of stack in

Update README.md

add http demo; http client can call http server; http client can call rpc server (#2149)

Add etcd to default registries when plugin is loaded (#2150)

Co-authored-by: Andrew Jones <andrew@gotoblink.com>

Update README.md

make rpcClient compatible with 32bit arm systems (#2156)

On ARM, 386, and 32-bit MIPS, it is the caller's responsibility to
arrange for 64-bit alignment of 64-bit words accessed
atomically. Only the first word in an allocated struct can
be relied upon to be 64-bit aligned.

optimize the process of switching grpc error to micro error (#2158)

Fix util/log/log.Infof format didn't work (#2160)

Co-authored-by: Cui Gang <cuigang@yunpbx.com>

fixing string field contains invalid UTF-8 issue (#2164)

fix k8s api memory leak (#2166)

fix http No release Broker (#2167)

* Update http.go

Exit before deregister is executed

* Create http.go

Exit before deregister is executed

fix: "Solve the problem that the resources have not been fully released due to early exit" (#2168)

* Update http.go

Exit before deregister is executed

* Create http.go

Exit before deregister is executed

* Solve the problem that the resources have not been fully released due to early exit

* Optimize some code

* Optimize some code

fix service default logger (#2171)

* Update http.go

Exit before deregister is executed

* Create http.go

Exit before deregister is executed

* Solve the problem that the resources have not been fully released due to early exit

* Optimize some code

* Optimize some code

* Optimize some code

* fix service default logger

Update README.md

get k8s pod (#2173)

Update README.md

fix:field (#2176)

* get k8s pod

* fix: filed

* field

Update README.md

add rmq message properties (#2177)

Co-authored-by: dtitov <dtitov@might24.ru>

Update README.md

grpc server add RegisterCheck (#2178)

fix 404 bug (#2179)

fix undefined: err (#2181)

Add registry and config/source plugins based on nacos/v2 (#2182)

* Add registry plugins implement by nacos/v2

* Add config/source plugins implement by nacos/v2

support hystrix fallback (#2183)

Windows event log plugin (#2180)

* add rmq message properties

* eventlog start

* start eventlog

* windows event logger

* readme

* readme

Co-authored-by: dtitov <dtitov@might24.ru>

support etcd auth with env args (#2184)

* support etcd auth with env args
set default registry address with env arg instead of 127.0.0.1

* fixing MICRO_REGISTRY_ADDRESS may empty issue

update mod version
2021-06-29 13:40:54 +01:00
Johnson C 212df8e6c3 support etcd auth with env args (#2184)
* support etcd auth with env args
set default registry address with env arg instead of 127.0.0.1

* fixing MICRO_REGISTRY_ADDRESS may empty issue
2021-06-23 07:45:01 +01:00
Dmitry Titov 8dc9bf49a1 Windows event log plugin (#2180)
* add rmq message properties

* eventlog start

* start eventlog

* windows event logger

* readme

* readme

Co-authored-by: dtitov <dtitov@might24.ru>
2021-06-20 09:28:30 +01:00
qm012 4daa499912 support hystrix fallback (#2183) 2021-06-20 09:28:15 +01:00
Yusan Kurban 939f346d83 Add registry and config/source plugins based on nacos/v2 (#2182)
* Add registry plugins implement by nacos/v2

* Add config/source plugins implement by nacos/v2
2021-06-18 14:23:03 +01:00
qm012 08216ccf31 fix undefined: err (#2181) 2021-06-17 11:11:38 +01:00
qm012 4deeaff8ad fix 404 bug (#2179) 2021-06-16 07:56:41 +01:00
dudu b892efa25f grpc server add RegisterCheck (#2178) 2021-06-11 09:57:44 +01:00
Asim Aslam 4af9e245fb Update README.md 2021-06-09 10:31:10 +01:00
Dmitry Titov 52bb3845f6 add rmq message properties (#2177)
Co-authored-by: dtitov <dtitov@might24.ru>
2021-06-08 10:34:47 +01:00
Asim Aslam a1e9b88495 Update README.md 2021-06-04 10:17:30 +01:00
biubiubiu-ljd ed44e9acc3 fix:field (#2176)
* get k8s pod

* fix: filed

* field
2021-06-03 07:01:26 +01:00
Asim Aslam 86d8d8f07e Update README.md 2021-06-02 14:02:58 +01:00
biubiubiu-ljd 62112b015f get k8s pod (#2173) 2021-06-02 13:54:02 +01:00
Asim Aslam ca2014bf8e Update README.md 2021-06-01 06:23:33 +01:00
JeffreyBool acc3f5479f fix service default logger (#2171)
* Update http.go

Exit before deregister is executed

* Create http.go

Exit before deregister is executed

* Solve the problem that the resources have not been fully released due to early exit

* Optimize some code

* Optimize some code

* Optimize some code

* fix service default logger
2021-05-23 08:38:20 +01:00
JeffreyBool f48911d2c3 fix: "Solve the problem that the resources have not been fully released due to early exit" (#2168)
* Update http.go

Exit before deregister is executed

* Create http.go

Exit before deregister is executed

* Solve the problem that the resources have not been fully released due to early exit

* Optimize some code

* Optimize some code
2021-05-17 08:16:52 +01:00
JeffreyBool 4c1f81dadb fix http No release Broker (#2167)
* Update http.go

Exit before deregister is executed

* Create http.go

Exit before deregister is executed
2021-05-13 13:07:25 +01:00
Tt yo 32cb1b435b fix k8s api memory leak (#2166) 2021-05-11 08:58:19 +01:00
Johnson C 8c9c7a5927 fixing string field contains invalid UTF-8 issue (#2164) 2021-05-10 11:25:31 +01:00
Cui Gang 9e9157d878 Fix util/log/log.Infof format didn't work (#2160)
Co-authored-by: Cui Gang <cuigang@yunpbx.com>
2021-05-06 06:43:05 +01:00
Scout Wang 1b5d372b5b optimize the process of switching grpc error to micro error (#2158) 2021-04-29 13:03:40 +01:00
Tobias Wellnitz b11a2f17e9 make rpcClient compatible with 32bit arm systems (#2156)
On ARM, 386, and 32-bit MIPS, it is the caller's responsibility to
arrange for 64-bit alignment of 64-bit words accessed
atomically. Only the first word in an allocated struct can
be relied upon to be 64-bit aligned.
2021-04-25 06:54:00 +01:00
Asim Aslam a91d1f7a3d Update README.md
Run tests / Test repo (push) Waiting to run
2021-04-16 17:34:42 +01:00
Andrew 0d57213d3f Add etcd to default registries when plugin is loaded (#2150)
Co-authored-by: Andrew Jones <andrew@gotoblink.com>
2021-04-08 18:31:39 +01:00
orange-jacky 6ae252b892 add http demo; http client can call http server; http client can call rpc server (#2149) 2021-04-08 18:31:15 +01:00
Asim Aslam e7a7e3a050 Update README.md 2021-04-03 08:39:40 +01:00
Johnson C bed53b605f fixing etcd stack in getToken (#2145)
when provide username and password, etcd will try to get auth token from server
if server is unavailble, etcd client will stack in
when dial timeout is set, it will return err instead of stack in
2021-04-01 09:55:21 +01:00
Asim Aslam 9b41d1bf08 Update README.md 2021-03-29 11:33:59 +01:00
Asim Aslam 0a41e6d80f Update README.md 2021-03-17 09:37:20 +00:00
Asim Aslam b636a4bfe3 Update README.md 2021-03-17 07:52:35 +00:00
Jerry df90f2ca63 Update registry plugins mod version (#2130)
* update etcd version

* update mod version

* update

* update etcd mod version
2021-02-27 06:48:44 +00:00
Asim Aslam 0bf3719a9a remove indirects 2021-02-26 08:34:03 +00:00
Asim Aslam bfa9e7c88c update quic go mod 2021-02-26 08:14:11 +00:00
Asim Aslam 57003414be remove network package 2021-02-26 08:13:12 +00:00
Asim Aslam a54f40baa7 Update README.md 2021-02-25 10:32:47 +00:00
Asim Aslam 56c779f9df Update README.md 2021-02-25 10:31:55 +00:00
Shubhendra Singh Chauhan 26b859c4f9 improve code quality (#2128)
* Fix inefficient string comparison

* Fix unnecessary calls to Printf

* Canonicalize header key

* Replace `t.Sub(time.Now())` with `time.Until`

* Remove unnecessary blank (_) identifier

* Remove unnecessary use of slice

* Remove unnecessary comparison with bool
2021-02-25 08:30:35 +00:00
Alex Unger 0f0ace1a44 support for tls on http plugin (#2126) 2021-02-17 18:20:06 +00:00
Asim Aslam f9f5e7422d fix store delete 2021-02-10 07:14:49 +00:00
Jerry 2653e7a977 update pulgin registry mod version (#2121)
* update etcd version

* update mod version

* update
2021-02-08 08:56:39 +00:00
Jerry 55b477ba07 update etcd version (#2120) 2021-02-07 12:15:06 +00:00
isfk 6f666d63c8 update client/grpc plugins version & remove replace (#2119)
* update memory registry plugins version & remove replace

* update plugins version & remove replace

* update plugins/client/grpc/v3 version

Co-authored-by: 申法宽 <shenfakuan@163.com>
2021-02-05 09:50:42 +00:00
isfk e8167a8b79 update plugins version & remove replace (#2118)
* update memory registry plugins version & remove replace

* update plugins version & remove replace

Co-authored-by: 申法宽 <shenfakuan@163.com>
2021-02-05 09:09:25 +00:00
Asim Aslam 0702501552 Delete Dockerfile 2021-02-02 14:58:31 +00:00
Asim Aslam 89fe31310e Delete docker.yml 2021-02-02 14:58:19 +00:00
Josemy Duarte ca3dfc4580 Remove missing gRPC example from README.md (#2112) 2021-01-30 18:13:56 +00:00
francescocarzaniga bba3107ae1 Remove hpcloud/tail in favour of nxadm/tail (#2109)
Docker / build (push) Waiting to run
Run tests / Test repo (push) Waiting to run
2021-01-25 07:51:17 +00:00
Asim Aslam 60010e82e2 update protoc-gen-micro
Docker / build (push) Waiting to run
Run tests / Test repo (push) Waiting to run
2021-01-20 21:36:17 +00:00
Asim Aslam ad94eeb635 Update README.md
Docker / build (push) Waiting to run
Run tests / Test repo (push) Waiting to run
2021-01-20 21:30:26 +00:00
Asim Aslam 88be70a6af Update README.md 2021-01-20 21:30:10 +00:00
Asim Aslam 226833c4ab Update README.md 2021-01-20 21:29:36 +00:00
Asim Aslam 8e3b7a57d9 examples at v3 2021-01-20 21:28:48 +00:00
Asim Aslam dc8236ec05 update v3 plugins (#2105) 2021-01-20 21:01:10 +00:00
Asim Aslam d94936f6c9 v3 (#2104)
* v3

* revert plugins

* fixup some issues
2021-01-20 13:54:31 +00:00
Goober bf4ab679e1 Fix zk watchDir (#2102)
Signed-off-by: Goober <chenhao86899@gmail.com>
2021-01-05 17:32:17 +00:00
Asim Aslam 20b5755788 move encoders out to plugins 2020-12-30 08:46:31 +00:00
Asim Aslam f64ffdbab1 remove indirects in go mod 2020-12-30 08:26:26 +00:00
Asim Aslam eb1e22bd10 strip grpc 2020-12-30 08:21:30 +00:00
Asim Aslam 18fb7a5d62 move certmagic 2020-12-29 20:12:10 +00:00
Asim Aslam ee8b369a19 add initialisers 2020-12-29 16:50:18 +00:00
Asim Aslam f4f6feafb3 remove etcd store 2020-12-29 16:32:06 +00:00
Asim Aslam 9ddfe696a4 go fmt 2020-12-29 16:29:58 +00:00
Asim Aslam 762fff8a51 Merge branch 'master' of ssh://github.com/asim/go-micro 2020-12-29 16:28:21 +00:00
Asim Aslam 36bcd8317b remove micro deps 2020-12-29 16:28:12 +00:00
Asim Aslam 10005db702 Rename default.go to noop.go 2020-12-29 16:24:31 +00:00
Asim Aslam 1b610403ba go mod tidy 2020-12-29 15:50:14 +00:00
Asim Aslam 2fde8a76eb Merge branch 'master' of ssh://github.com/asim/go-micro 2020-12-29 15:49:33 +00:00
Asim Aslam a7c31a0d2b refactor all the things 2020-12-29 15:49:26 +00:00
Asim Aslam 35d72660c8 Update README.md 2020-12-26 15:42:10 +00:00
Asim Aslam d197438e16 Merge branch 'master' of ssh://github.com/asim/go-micro 2020-12-26 15:32:54 +00:00
Asim Aslam 7fc0b7ef72 add all the plugins 2020-12-26 15:32:45 +00:00
Asim Aslam f44b832bac Update README.md 2020-12-26 15:23:06 +00:00
Asim Aslam 5fe144bda8 Update README.md 2020-12-26 15:22:41 +00:00
Asim Aslam df2dab0169 Update README.md 2020-12-26 15:22:14 +00:00
Asim Aslam 7ab46b0850 rename imports 2020-12-26 15:21:29 +00:00
Asim Aslam c3fe6eb3ac update readme 2020-12-26 15:18:11 +00:00
Asim Aslam a34c70de0e Add examples 2020-12-26 15:17:20 +00:00
Asim Aslam 273bab5dd7 update readme 2020-12-26 15:15:39 +00:00
Asim Aslam f8fc9d0304 Add protoc-gen-micro 2020-12-26 15:13:09 +00:00
Hao b5431de2e8 Use singleflight to prevent registry cache breakdown (#2098) 2020-12-21 14:34:59 +00:00
Asim Aslam dc0cac9ba4 Update README.md 2020-12-18 18:45:14 +00:00
Asim Aslam 8628dc0fc4 Update FUNDING.yml 2020-12-13 08:18:02 +00:00
Asim Aslam b57f56fb0b Merge branch 'master' of ssh://github.com/asim/go-micro 2020-12-12 20:51:40 +00:00
Asim Aslam 690169f6e5 remove k8s logs 2020-12-12 20:51:32 +00:00
Asim Aslam a543157f50 Delete debug.go 2020-12-12 20:50:53 +00:00
Asim Aslam 15a62ae0b9 move debug handler 2020-12-12 20:50:36 +00:00
Asim Aslam 28afbf164f Merge branch 'master' of ssh://github.com/asim/go-micro 2020-12-12 20:44:45 +00:00
Asim Aslam 4ce77373c0 remove auth cruft 2020-12-12 20:44:32 +00:00
Asim Aslam 8de1ede0f0 Update network.go 2020-12-12 20:35:43 +00:00
Asim Aslam 8054478cc3 remove util/scope 2020-12-12 20:28:09 +00:00
Asim Aslam 167fcd0d78 fix wrapper test 2020-12-12 20:25:29 +00:00
Asim Aslam 206bf8cd0a remove web 2020-12-12 20:15:59 +00:00
Asim Aslam df687fe5d4 move selector 2020-12-12 20:14:50 +00:00
Asim Aslam de4f3ee4a2 separate rules and auth 2020-12-12 20:08:39 +00:00
Asim Aslam 202338bd2d update all the things to go 1.15 2020-12-12 19:51:18 +00:00
Asim Aslam e2034b2438 Merge branch 'master' of ssh://github.com/asim/go-micro 2020-12-12 19:39:14 +00:00
Asim Aslam 0ec2399a2c fix command 2020-12-12 19:39:04 +00:00
Asim Aslam df5f158cef Delete .golangci.yml 2020-12-12 19:09:48 +00:00
Asim Aslam 19ac4fbcaf move rules 2020-12-12 19:08:36 +00:00
Asim Aslam d07de3751e remove auth provider 2020-12-12 19:06:43 +00:00
Asim Aslam 4977aca09c move router 2020-12-12 19:04:19 +00:00
Asim Aslam 43ff2a540d move proxy 2020-12-12 19:02:04 +00:00
Asim Aslam 35c59042bf refactor network 2020-12-12 18:59:40 +00:00
Asim Aslam f8f84b42ac Merge branch 'master' of ssh://github.com/asim/go-micro 2020-12-11 21:42:43 +00:00
Asim Aslam 1dc9b40b90 move network/tunnel 2020-12-11 21:42:31 +00:00
Asim Aslam 345845ec16 Delete CNAME 2020-12-11 11:22:37 +00:00
Asim Aslam dbe8c93e20 remove service implementations (#2094) 2020-12-11 11:12:44 +00:00
Asim Aslam 4d481b0363 Update README.md 2020-12-09 21:52:18 +00:00
Asim Aslam 88d954cf97 Update README.md 2020-12-09 18:35:07 +00:00
Asim Aslam 71883f1b04 Update tests.yml 2020-12-09 18:19:37 +00:00
Asim Aslam bfc212f7ed remove service package 2020-12-09 18:11:10 +00:00
Asim Aslam 43f80b1b0d Delete build-micro.sh 2020-12-09 18:09:02 +00:00
Asim Aslam f83d64d092 Delete build-all-examples.sh 2020-12-09 18:08:55 +00:00
Asim Aslam e67b9f0cc1 Delete micro-main.yml 2020-12-09 18:08:39 +00:00
Asim Aslam 134c5f0e41 Delete micro-examples.yml 2020-12-09 18:08:26 +00:00
Asim Aslam e761aa1940 move cmd 2020-12-09 18:07:01 +00:00
Asim Aslam 8c8380dcd4 delete agent package 2020-12-09 18:03:00 +00:00
Asim Aslam dd5eca2561 Update README.md 2020-12-09 17:05:05 +00:00
Dominic Wong 94bd1025a6 push tags to docker hub (#1766)
Docker / build (push) Waiting to run
Run tests / Test repo (push) Waiting to run
2020-07-03 11:30:59 +01:00
Dominic Wong 7be4a67673 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-07-03 11:30:59 +01:00
Di Wu 3e6ac73cfe 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-07-03 11:30:59 +01:00
Colin Hoglund aef6878ee0 config: use configured reader by default (#1717) 2020-07-03 11:30:59 +01:00
sunfuze 81aa8e0231 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-07-03 11:30:59 +01:00
Di Wu c28f625cd4 Custom private blocks (#1705)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-07-03 11:30:59 +01:00
Dmitry Kozlov 5b161b88f7 Split long discord output message into the chunks by 2000 characters (#1704)
Signed-off-by: Dmitry Kozlov <dmitry.f.kozlov@gmail.com>
2020-07-03 11:30:59 +01:00
ben-toogood cca8826a1f registry/mdns: fix nil host bug (#1703) 2020-07-03 11:30:59 +01:00
Dominic Wong 0327f30e3c Fix regex detection. Fixes #1663 (#1696)
Docker / build (push) Waiting to run
Run tests / Test repo (push) Waiting to run
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
Vasiliy Tolstov 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
Asim Aslam b270860b79 Update README.md (#1695) 2020-06-10 10:22:53 +01:00
Asim Aslam e7ba930236 Update FUNDING.yml (#1692) 2020-06-08 18:12:19 +01:00
Dominic Wong aa679f7a73 Create PULL_REQUEST_TEMPLATE.md 2020-06-03 10:32:28 +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
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
Asim Aslam a39e6515da Update README.md
Docker / build (push) Waiting to run
Run tests / Test repo (push) Waiting to run
2020-05-31 11:35:09 +01:00
Asim Aslam 2c7fd286de Update README.md 2020-05-31 11:34:49 +01:00
Asim Aslam 8aa2712b4d Delete README.zh-cn.md 2020-05-31 11:33:31 +01:00
Asim Aslam b5c2121cef Update README.md 2020-05-31 11:31:41 +01:00
Asim Aslam ca9b877646 Update README.md 2020-05-31 11:28:32 +01:00
Asim Aslam ff49b4fc71 Update README.md 2020-05-31 11:27:54 +01:00
Asim Aslam 222431b57a Update README.md 2020-05-31 11:26:46 +01:00
Asim Aslam ddb51529a7 Update README.md 2020-05-31 11:26:18 +01:00
Asim Aslam 7c048f331a Update README.md 2020-05-31 11:21:55 +01:00
Asim Aslam 8475183bbb Update README.md 2020-05-31 11:19:26 +01:00
Asim Aslam 10f35db3ed Update README.md 2020-05-31 11:16:20 +01:00
Asim Aslam b68af8ab63 run go fmt 2020-05-30 11:00:43 +01:00
Asim Aslam 266602a3d6 Update README.md 2020-05-30 10:59:59 +01:00
mlboy 15d5142d9b fix: misspell (#1667) 2020-05-29 17:49:22 +01:00
Máximo Cuadros 0d88650511 go modules cleanup and remove wrong self import to v1 (#1658)
* Runtime local git, simply go-git code
* go modules cleanup and remove wrong self import to v1
* pin mergo v0.3.8 to avoid panics

Signed-off-by: Máximo Cuadros <mcuadros@gmail.com>
Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-05-29 14:32:11 +03:00
Dominic Wong 8660370dc9 Merge pull request #1657 from xpunch/master
logger caller not trim in windows
2020-05-29 10:35:03 +01:00
Dominic Wong 73339dde85 Merge branch 'master' into master 2020-05-29 10:27:20 +01:00
Dominic Wong 3f354f3c30 Merge pull request #1661 from micro/bugfix/sock_pool_threads
fix locking of socket pool
2020-05-28 08:31:47 +01:00
potato c08eb5f892 Merge branch 'master' into master 2020-05-28 10:19:53 +08:00
Dominic Wong 27e41c4ad5 fix locking of socket pool 2020-05-27 20:18:26 +01:00
Dominic Wong 1da8a640da Merge pull request #1660 from micro/bugfix/mdns_nil_host
Check ipv4 or ipv6 address is valid before assigning
2020-05-27 15:53:27 +01:00
Dominic Wong e7ad031eb8 Check ipv4 or ipv6 address is valid before assigning 2020-05-27 15:47:12 +01:00
ben-toogood 192f548c83 Merge pull request #1659 from micro/config-srv-not-found
Handle config service not found errors
2020-05-27 12:24:33 +01:00
Ben Toogood d85b4197b4 Return nil changeset and not blank 2020-05-27 12:20:31 +01:00
Ben Toogood bb5f2e5525 Handle config service not found errors 2020-05-27 12:12:34 +01:00
ben-toogood f00b696282 Merge pull request #1654 from micro/auth-scopes
Auth Improvements
2020-05-27 10:52:07 +01:00
Ben Toogood e2d662608c Fix tests 2020-05-27 09:14:16 +01:00
Ben Toogood 9e9773c9c7 Only use namespace for cache key 2020-05-27 09:07:59 +01:00
potato 2f8e2487f7 Merge branch 'master' into master 2020-05-27 09:32:27 +08:00
Ben Toogood d6c1fbf841 Fix web service auth name 2020-05-26 17:43:45 +01:00
Ben Toogood c3b404bab0 Fix server calling across namespace 2020-05-26 17:35:06 +01:00
Ben Toogood cd283654eb Cache Rules 2020-05-26 15:53:28 +01:00
Ben Toogood 5712cc9c62 Merge master 2020-05-26 15:52:21 +01:00
ben-toogood be5a10a4d4 Merge pull request #1656 from micro/client-cache
Client Cache
2020-05-26 15:38:30 +01:00
Ben Toogood b53a2c67f1 Merge branch 'master' of https://github.com/micro/go-micro into auth-scopes 2020-05-26 15:37:31 +01:00
johnson cc79692d68 make caller filepath package/file style
this code is from zap
https://github.com/uber-go/zap/blob/9a9fa7d4b5f07a9b634983678a65b5525f81e58b/zapcore/entry.go#L101
2020-05-26 14:33:56 +08:00
potato 796a598b37 Merge pull request #7 from micro/master
go micro v2
2020-05-26 14:18:25 +08:00
Ben Toogood 73b4423682 Merge branch 'master' of https://github.com/micro/go-micro into client-cache 2020-05-24 20:36:22 +01:00
Ben Toogood 198e942889 Remove redundant test 2020-05-24 20:32:22 +01:00
Ben Toogood 95703e4565 Fixes and improved test coverage 2020-05-24 20:26:37 +01:00
Ben Toogood 2729569f66 Add Debug.Cache method 2020-05-24 18:45:57 +01:00
Ben Toogood 67146ecdc2 Client Cache tests 2020-05-24 18:05:23 +01:00
Asim Aslam bd049a51e6 Update README.md 2020-05-23 16:47:23 +01:00
Asim Aslam ffd89599a0 Update README.md 2020-05-23 16:46:50 +01:00
Ben Toogood 496293afa1 Use hash/fnv, add tests, fix request bug 2020-05-23 11:34:44 +01:00
Ben Toogood 7d7f4046e8 Client Cache 2020-05-22 16:52:24 +01:00
Ben Toogood c800070477 Check for error before loading rules 2020-05-22 14:03:12 +01:00
Ben Toogood 877fe5fb0a Update web wildcard to enable /foo/bar/baz/* to verify /foo/bar/baz 2020-05-22 14:02:02 +01:00
Ben Toogood dad011cab4 Fix noop issuer bug 2020-05-22 12:40:34 +01:00
Ben Toogood f939200b34 Improve service auth log 2020-05-22 12:24:37 +01:00
Ben Toogood 9c072a372c Add auth scope constants 2020-05-22 11:37:12 +01:00
Ben Toogood fbb91c6cb7 Auth wrapper tests 2020-05-22 10:44:18 +01:00
Ben Toogood b2cf501952 Auth Rules tests & bug fixes 2020-05-22 09:31:15 +01:00
Ben Toogood 1fce0f02b6 Verify Namespace 2020-05-21 18:11:35 +01:00
Ben Toogood 12061bd006 Add account issuers 2020-05-21 16:41:55 +01:00
Ben Toogood 856c73b341 Remove roles (replaced with scope) 2020-05-21 14:56:17 +01:00
Ben Toogood 4de19805ba Remove redundant test 2020-05-21 12:33:58 +01:00
Ben Toogood c09b871a6b Merge branch 'master' of https://github.com/micro/go-micro into auth-scopes 2020-05-21 12:32:52 +01:00
Ben Toogood e876cb917d auth/service support for micro clients (rules from mutltiple namespaces 2020-05-21 12:25:47 +01:00
Ben Toogood 8f5ef012ff Update Rules.Delete proto 2020-05-21 12:07:22 +01:00
Ben Toogood 287992cef3 Fix service => service namespace bug 2020-05-21 11:35:07 +01:00
Ben Toogood 344ce061ce Verify Options 2020-05-20 16:49:52 +01:00
Ben Toogood 5d14970a55 Fix nil account bug 2020-05-20 16:11:34 +01:00
Janos Dobronszki 0615fe825f Auth invalid token fix (#1650) 2020-05-20 16:18:05 +02:00
Asim Aslam 6a661fd08c check if the db conn is nil before doing anything (#1652) 2020-05-20 14:03:38 +01:00
Ben Toogood f6d9416a9e Add Rule to Auth interface 2020-05-20 11:59:01 +01:00
Asim Aslam a29676b86a Registration Retry / Interval (#1651)
* Change the default ttl to 90 seconds

* add retries to registration

* Add retry to web register
2020-05-20 11:49:09 +01:00
Ben Toogood dc10f88c12 Replace auth account.Namespace with account.Scopes 2020-05-19 18:17:17 +01:00
ben-toogood e61edf6280 Merge pull request #1645 from micro/runtime-multitenancy
Runtime multi-tenancy
2020-05-19 17:06:11 +01:00
ben-toogood 3410a0949b Merge branch 'master' into runtime-multitenancy 2020-05-19 17:00:51 +01:00
Jake Sanders 9216a47724 fix client race (#1647) 2020-05-19 14:44:46 +01:00
ben-toogood cf37d64819 Merge branch 'master' into runtime-multitenancy 2020-05-19 13:24:35 +01:00
Patrik Lindahl f0c0f3d4c4 Fixes for #1560 (#1644)
close #1560

This fixes one of the reported data races and also allows for
having a different name on the micro.Service and web.Service.
This makes it possible to discover the two service variants separately.

Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-05-19 14:11:26 +03:00
Ben Toogood c4e3f8c336 Merge branch 'master' of https://github.com/micro/go-micro into runtime-multitenancy 2020-05-19 11:02:40 +01:00
Ben Toogood 8875719619 Default Runtime multi-tenancy 2020-05-19 11:01:06 +01:00
Ben Toogood c19b349e96 Update runtime.Event struct 2020-05-19 10:14:07 +01:00
Ben Toogood 14155c7e02 Add runtime ErrNotFound 2020-05-19 09:28:00 +01:00
Maarten Bezemer 3d36398818 Fix client RPC stream close mutex (#1643)
Docker / build (push) Waiting to run
Run tests / Test repo (push) Waiting to run
2020-05-18 17:22:33 +01:00
Asim Aslam 56a7897c91 update readme 2020-05-17 12:39:20 +01:00
ben-toogood 5efb386224 Merge pull request #1640 from micro/auth/public-rule
Auth: setup a public rule
2020-05-15 10:28:52 +01:00
Ben Toogood 4d2de923cd Auth: setup a public rule 2020-05-15 10:24:30 +01:00
ben-toogood f64b1468a5 Merge pull request #1639 from micro/registy-not-found
Registry service: return not found error
2020-05-14 19:43:19 +01:00
ben-toogood 56f281002b Merge branch 'master' into registy-not-found 2020-05-14 19:39:43 +01:00
Ben Toogood 0d7250352f Registry service: return not found error 2020-05-14 19:38:56 +01:00
ben-toogood ef43f01da4 Merge pull request #1638 from micro/registry-addrs-fix
Fix registry address option unused
2020-05-14 18:07:14 +01:00
ben-toogood c9e5ae6a2b Merge branch 'master' into registry-addrs-fix 2020-05-14 18:03:46 +01:00
Ben Toogood 8a802d8f7a Fix registry address option unused 2020-05-14 18:00:13 +01:00
ben-toogood 331ab3715c Merge pull request #1636 from micro/auth-util
Refactor auth setup to util/auth
2020-05-14 16:15:47 +01:00
Ben Toogood 6b451a2197 Refactor auth setup to util/auth 2020-05-14 16:10:14 +01:00
ben-toogood b4c0224746 Merge pull request #1635 from micro/auth-fixes
Auth: Move token generation logic out the client wrappers
2020-05-14 14:00:55 +01:00
Ben Toogood 500d793fc4 Merge branch 'auth-fixes' of https://github.com/micro/go-micro into auth-fixes 2020-05-14 13:57:00 +01:00
Ben Toogood 16af265e8b Seperate JWT refresh / access tokens 2020-05-14 13:56:51 +01:00
ben-toogood b222cf8e13 Merge branch 'master' into auth-fixes 2020-05-14 13:47:26 +01:00
Ben Toogood f549e20fa2 tidy go mdo 2020-05-14 13:33:11 +01:00
Ben Toogood 83e9c1fad2 Remove unnecessary change 2020-05-14 13:32:42 +01:00
Ben Toogood c220686c29 Fix token bug 2020-05-14 13:30:21 +01:00
Ben Toogood 1b18730d54 Custom micro client 2020-05-14 11:25:19 +01:00
Ben Toogood 5764519f5b Refactor auth to load token outside wrappers 2020-05-14 11:06:22 +01:00
ben-toogood 957001f8ad Merge pull request #1634 from micro/disable-clients
Disable Clients
2020-05-13 18:54:34 +01:00
Ben Toogood 0955671e45 Merge branch 'disable-clients' of https://github.com/micro/go-micro into disable-clients 2020-05-13 18:49:47 +01:00
Ben Toogood 57b060bac5 Disable Addresses 2020-05-13 18:49:36 +01:00
ben-toogood 3136e1409e Merge branch 'master' into disable-clients 2020-05-13 18:48:24 +01:00
Ben Toogood ca791d7e8d Disable Clients 2020-05-13 18:47:53 +01:00
Dominic Wong 05858b746c kill all processes correctly for micro kill command (#1633) 2020-05-13 18:36:45 +01:00
ben-toogood 09d1450d7d Merge pull request #1632 from micro/fix-auth-bug
Auth: Fix recursive bug
2020-05-13 18:18:39 +01:00
Ben Toogood 1ca1fd411a Auth: Fix recursive bug 2020-05-13 18:17:04 +01:00
ben-toogood a2d4d62f1c Merge pull request #1631 from micro/auth-address
Auth: Set address
2020-05-13 18:02:10 +01:00
Ben Toogood 8ab20f501c Fix merge conflicts 2020-05-13 17:58:03 +01:00
Ben Toogood 366fb228e5 Auth: Set address 2020-05-13 17:54:47 +01:00
Asim Aslam bba8c254d7 fix auth initialisation (#1630) 2020-05-13 17:35:57 +01:00
ben-toogood ebd53794af Merge pull request #1629 from micro/auth/rules-fix
Auth: Load rules if not present
2020-05-13 17:27:53 +01:00
Ben Toogood 2299244332 Auth: Load rules if not present 2020-05-13 17:07:46 +01:00
ben-toogood cf61d98635 Merge pull request #1628 from micro/registry
Misc Muti-Tenancy / Auth Fixes
2020-05-13 16:53:39 +01:00
ben-toogood 15d1967aaf Merge branch 'master' into registry 2020-05-13 16:50:12 +01:00
Ben Toogood 410fec8ee4 Fix auth bug 2020-05-13 16:49:17 +01:00
Ben Toogood c831b6c03a Fix 2020-05-13 16:35:57 +01:00
Asim Aslam 290595f88e Strip down router code (#1627) 2020-05-13 16:13:36 +01:00
ben-toogood ba64518ebd Merge pull request #1626 from PieterVoorwinden/master
Check if auth is nil to prevent nilpointer
2020-05-13 15:18:58 +01:00
Pieter Voorwinden b14d63b4a1 Check if auth is nil to prevent nilpointer 2020-05-13 16:13:23 +02:00
x1nchen af2db0a0d9 fix: update dependency certmagic (#1625)
module github.com/mholt/certmagic has been renamed github.com/caddyserver/certmagic,
so upgrade on this module will fail.

fix: micro/micro#835

caddyserver/certmagic@v0.10.6 is Maximum upgradeable version with go version 1.13

Higher version use *tls.ClientHelloInfo.SupportsCertificate which only supported in go 1.14
2020-05-13 15:00:13 +01:00
ben-toogood fb255a7e5a Merge pull request #1622 from micro/registry-multi-tenancy
Registry mutli-tenancy
2020-05-13 13:54:39 +01:00
Ben Toogood 47c1cb433e Store account credentials 2020-05-13 13:48:25 +01:00
Ben Toogood 3fac7d79ab Remove service type role 2020-05-13 13:42:56 +01:00
Ben Toogood 25c937fd0e Naming changes 2020-05-13 13:38:13 +01:00
Ben Toogood e5c1fbc591 Merge branch 'master' of https://github.com/micro/go-micro into registry-multi-tenancy 2020-05-13 13:35:47 +01:00
Ben Toogood d781c9ae2d Remove namespace specific logic 2020-05-13 13:35:34 +01:00
Ben Toogood 54951740bf Authenticate on service start 2020-05-13 13:13:11 +01:00
Janos Dobronszki 0fb4734e67 Upload local source code to micro server (#1613) 2020-05-13 12:07:53 +02:00
Ben Toogood 346e034d0a Add mutli-tenancy support to the registry 2020-05-13 10:40:08 +01:00
Asim Aslam 116cc1e9ee Stop parsing proxy address (#1619) 2020-05-12 17:38:22 +01:00
ben-toogood 762a5bc9e8 Merge pull request #1618 from micro/auth-namespace-flag
Auth Namespace Flag
2020-05-12 16:45:42 +01:00
Ben Toogood d39b723511 Auth Namespace Flag 2020-05-12 16:41:29 +01:00
ben-toogood 5494e935f4 Merge pull request #1617 from micro/k8s/secret-type
K8s: Add Secret Type to yaml template
2020-05-12 14:21:30 +01:00
Ben Toogood e0863bb7eb K8s: Add Secret Type to yaml template 2020-05-12 14:10:39 +01:00
ben-toogood 89f86167ad Merge pull request #1616 from micro/k8s/secret-template-fix
Fix k8s secret template (yaml)
2020-05-12 11:45:12 +01:00
ben-toogood dfec1ad6b1 Merge branch 'master' into k8s/secret-template-fix 2020-05-12 11:41:41 +01:00
Ben Toogood 66d3e4a595 Fix k8s secret template (yaml) 2020-05-12 11:40:54 +01:00
Asim Aslam 19a03babc4 Update server.go 2020-05-12 11:32:01 +01:00
ben-toogood ee24b4f083 Merge pull request #1615 from micro/disable-auth-client
Disable auth service client
2020-05-11 20:38:49 +01:00
Ben Toogood 937ecc8d2f Disable auth service client 2020-05-11 20:38:05 +01:00
ben-toogood 6078adb8bc Merge pull request #1614 from micro/runtime-clients
Runtime Options: Replace client.DefaultClient
2020-05-11 20:05:00 +01:00
ben-toogood 39f18b0b70 Merge branch 'master' into runtime-clients 2020-05-11 18:03:11 +01:00
Ben Toogood efb64b7dbb Pass client to more of the runtime 2020-05-11 17:57:39 +01:00
Ben Toogood f892b41299 Add runtime to service.Options() 2020-05-11 17:09:28 +01:00
Janos Dobronszki 1eb63635b5 Adding file upload and download capabilities (#1610) 2020-05-11 14:08:27 +02:00
ben-toogood 688228377b Merge pull request #1612 from micro/auth-options
Auth: pass options in service RPC
2020-05-11 11:53:38 +01:00
Ben Toogood 506006f0fa Auth Options 2020-05-11 11:47:59 +01:00
ben-toogood 22de001a80 Merge pull request #1611 from micro/auth-has-role
Auth account.HasRole
2020-05-11 11:40:20 +01:00
ben-toogood d90cc8bf2f Merge branch 'master' into auth-has-role 2020-05-11 11:36:06 +01:00
Ben Toogood 5a8f19589b Auth account.HasRole 2020-05-11 11:34:22 +01:00
gggwvg d61df6363b web: fix advertise address (#1608)
* web: fix advertise address
* web: fix test

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
Co-authored-by: Asim Aslam <asim@aslam.me>
Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-05-08 12:31:03 +03:00
ben-toogood f062013a7b Merge pull request #1607 from micro/k8s-debug
Log k8s Requests
2020-05-07 11:41:43 +01:00
Ben Toogood fea93a5b7a Log k8s Requests 2020-05-07 11:35:56 +01:00
fztcjjl 30dc29e17f fix ring buffer (#1606) 2020-05-07 10:45:48 +01:00
ben-toogood 5387f73b5d Handle cockroach createDB error (#1603) 2020-05-06 10:58:14 +01:00
Vasiliy Tolstov 90dd1f63c8 api/handler/rpc: fix encoding of inner message (#1601)
* api/handler/rpc: fix encoding of inner message

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-05-04 15:50:53 +03:00
Asim Aslam 38cdb9cc2f Set table name in store
Docker / build (push) Waiting to run
Run tests / Test repo (push) Waiting to run
2020-05-01 18:24:35 +01:00
Asim Aslam b3915b6020 Add store to options (#1600) 2020-05-01 18:05:09 +01:00
Asim Aslam 08a2de1ef5 Account for missing options database/table in cockroach store 2020-05-01 15:31:55 +01:00
Asim Aslam 7a2dea6cc2 Set database/table from init first 2020-05-01 15:22:44 +01:00
Asim Aslam 2a14feed93 force codec on call not on dial (#1599) 2020-05-01 14:59:50 +01:00
Asim Aslam e8105d22ad cruft 2020-05-01 00:25:17 +01:00
Asim Aslam c76a5e608d sql fixes 2020-04-30 23:53:54 +01:00
Asim Aslam 359b8bc503 Add opts to service proto (#1517)
* Add opts to service proto

* Support database/table opts
2020-04-30 22:51:25 +01:00
Janos Dobronszki fccab8ad27 Runtime name should be base folder outside repos (#1598) 2020-04-30 18:20:51 +02:00
Socket 46d09ec2bc unsubscribe can async (#1596)
Co-authored-by: huangshaojie <huangshaojie@corp.netease.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-04-30 10:42:13 +01:00
Asim Aslam 7792dbc34d Update FUNDING.yml 2020-04-29 18:45:55 +01:00
ben-toogood 1d29f126f9 Merge pull request #1595 from micro/auth-client-wrapper
Auth Client Wrapper
2020-04-29 15:43:30 +01:00
Ben Toogood bcddb98867 Fix Tests 2020-04-29 15:37:02 +01:00
Ben Toogood f48dec1fb0 Use Server ID in account name 2020-04-29 15:27:18 +01:00
Ben Toogood ef9f65c78b Improve Comments 2020-04-29 15:15:38 +01:00
Ben Toogood 99f8be5b3d Auth Client Wrapper 2020-04-29 15:11:06 +01:00
ben-toogood 9d2fdb84be Merge pull request #1592 from micro/jwt-auth
JWT auth implementation
2020-04-29 14:10:05 +01:00
ben-toogood 8b004feb9a Merge branch 'master' into jwt-auth 2020-04-29 13:33:47 +01:00
Ben Toogood 70736e24c0 Set RefreshToken 2020-04-29 13:33:22 +01:00
Vasiliy Tolstov d44adafca5 api/router: avoid unneeded loops and fix path match (#1594)
* api/router: avoid unneeded loops and fix path match

* if match found in google api path syntax, not try pcre loop
* if path is not ending via $ sign, append it to pcre to avoid matching other strings like
  /api/account/register can be matched to /api/account
* api: add tests and validations

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-29 15:23:10 +03:00
ben-toogood e57b20c1f8 Merge branch 'master' into jwt-auth 2020-04-29 13:22:09 +01:00
Ben Toogood 94971aee77 Complete JWT implementation 2020-04-29 13:21:51 +01:00
Ben Toogood 0ed66d0664 Fix Typo 2020-04-29 09:38:39 +01:00
Ben Toogood 7e27c97c6c Remove Comment 2020-04-29 09:22:15 +01:00
Ben Toogood 669364985e JWT auth implementation 2020-04-29 09:21:17 +01:00
Asim Aslam c7440274dd touch 2020-04-28 19:35:13 +01:00
Asim Aslam 8ccbf53dfc secret cookie unused 2020-04-28 18:12:07 +01:00
Asim Aslam f908110fb6 swap out context access for account (#1589) 2020-04-28 17:35:18 +01:00
Vasiliy Tolstov 9bb1904a38 broker: add publish context (#1590)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-28 19:29:00 +03:00
Vasiliy Tolstov 06220ab8c8 client: add context publish option (#1588)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-28 19:03:37 +03:00
Janos Dobronszki da66561d1e Fixing too large offsets for default runtime logs (#1587) 2020-04-28 13:42:15 +01:00
Dmitry Kozlov 52861310b0 fix HTTP 401 Unauthorized, {"message": "401: Unauthorized", "code": 0} (#1586)
fix file=bot.go:426 level=error service=bot error starting bot HTTP 401 Unauthorized, {"message": "401: Unauthorized", "code": 0}
see https://github.com/bwmarrin/discordgo#usage
2020-04-28 13:06:01 +01:00
Vasiliy Tolstov 414b2ec5f8 web: fix deadlock (#1585)
* web: fix deadlock

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

* add web tests

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-28 12:23:52 +01:00
Janos Dobronszki b875868a39 Don't ignore errors from checkout source code (#1584)
Don't check out code for builtin services.
2020-04-28 10:51:39 +02:00
Janos Dobronszki 8148e0a0f8 Micro log fixes (#1570) 2020-04-28 09:49:39 +02:00
ben-toogood 25c82245b1 Merge pull request #1582 from micro/k8s-srv-accounts
Runtime: Add Kubernetes ServiceAccounts & Remove imagePullSecrets
2020-04-27 15:24:16 +01:00
ben-toogood 95a7e21f5f Merge branch 'master' into k8s-srv-accounts 2020-04-27 15:08:24 +01:00
Asim Aslam 83ab47333f rename Codec to Secrets (#1581) 2020-04-27 14:57:57 +01:00
Ben Toogood 8d7d6ef358 Add k8s secrets 2020-04-27 14:37:28 +01:00
Ben Toogood 494e0b5060 Runtime: Add Kubernetes ServiceAccounts & Remove imagePullSecrets 2020-04-27 14:13:51 +01:00
Janos Dobronszki 434997e676 Display only logging file name as opposed to path in logs (#1580) 2020-04-27 09:55:50 +01:00
Janos Dobronszki ec44b67e9f Fixing log file path in logs (#1578) 2020-04-27 09:36:09 +01:00
Vasiliy Tolstov e0c9234c0e web: use default logger (#1577)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-27 00:03:05 +03:00
Vasiliy Tolstov 980b772801 fix races in web and logger (#1576)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-26 17:41:36 +03:00
Vasiliy Tolstov a22da39e1c logger: add caller info to default implementation (#1575)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-26 17:11:53 +03:00
徐旭 7253635cd3 delete invalid copy (#1573)
* prealloc

* delete invalid copy
2020-04-26 12:44:59 +01:00
Asim Aslam 0a030f3d8a strip unused list endpoint 2020-04-24 18:05:38 +01:00
Asim Aslam edee3b6765 Add proxy env test (#1569) 2020-04-24 11:26:46 +01:00
Asim Aslam d62ae23a9c Strip label 2020-04-23 20:20:48 +01:00
Asim Aslam c68226e9b0 only do namespace check if not default 2020-04-23 19:19:13 +01:00
ben-toogood 041d68b1ce Merge pull request #1566 from micro/image-pull-secret-fix
Fix Runtime Namespace List
2020-04-23 18:16:38 +01:00
ben-toogood 85a8f36565 Merge branch 'master' into image-pull-secret-fix 2020-04-23 18:15:04 +01:00
Ben Toogood f34d58cfbd Remove Debug 2020-04-23 18:14:06 +01:00
Asim Aslam e0a651bfc3 set namespace on create 2020-04-23 18:10:13 +01:00
Ben Toogood cd35f503a0 Remove hardcoded labels 2020-04-23 18:08:02 +01:00
Ben Toogood 8b3d223fc0 Remove hardcoded labels: 2020-04-23 18:05:58 +01:00
Ben Toogood bb25bd94c8 Log k8s requests 2020-04-23 17:56:00 +01:00
ben-toogood 986e3d3c35 Merge pull request #1565 from micro/image-pull-secret-fix
Runtime: Fix ImagePullSecret
2020-04-23 17:53:09 +01:00
Ben Toogood 616db3442a Debugging 2020-04-23 17:44:40 +01:00
Ben Toogood 5fe3c0bfe5 Merge branch 'image-pull-secret-fix' of https://github.com/micro/go-micro into image-pull-secret-fix 2020-04-23 17:37:33 +01:00
Ben Toogood 8849b85a7f Merge branch 'master' of https://github.com/micro/go-micro into image-pull-secret-fix 2020-04-23 17:37:15 +01:00
ben-toogood 893bbafa03 Merge branch 'master' into image-pull-secret-fix 2020-04-23 17:28:06 +01:00
Ben Toogood 4c05623a3c Image pull secret fix 2020-04-23 17:26:59 +01:00
Asim Aslam ec929b3d2f log error and ensure we pass through namespace 2020-04-23 17:14:30 +01:00
Asim Aslam 2299559397 Check for namespace (#1564) 2020-04-23 16:22:41 +01:00
ben-toogood 6be53536d3 Merge pull request #1562 from micro/git-secrets
Runtime - Image Pull Secrets
2020-04-23 15:45:32 +01:00
ben-toogood 99d4b2b31a Merge branch 'master' into git-secrets 2020-04-23 15:39:37 +01:00
Janos Dobronszki ff8ad7d4ca Default runtime now checks out code on demand (#1563)
* Default runtime now checks out code on demand

* Go mod tidy
2020-04-23 16:30:43 +02:00
ben-toogood b692c045b5 Merge branch 'master' into git-secrets 2020-04-23 15:01:47 +01:00
Ben Toogood b5f53595ca Pass image_pull_secrets in runtime service 2020-04-23 14:22:23 +01:00
Ben Toogood 88176dca53 Remove debugging 2020-04-23 14:13:07 +01:00
Ben Toogood 020476614c Tweak CreateImagePullSecret 2020-04-23 14:06:33 +01:00
Ben Toogood 0f42346976 Additonal Debugging 2020-04-23 14:03:04 +01:00
ben-toogood 692b27578c Runtime Namespace (#1547)
* Add context option to runtime; Add dynamic namespace to kubectl client

* Add namespace runtime arg

* Fixes & Debugging

* Pass options in k8s runtime

* Set namespace on k8s resources

* Additional Logging

* More debugging

* Remove Debugging

* Ensure namespace exists

* Add debugging

* Refactor namespaceExists check

* Fix

* Fix

* Fix

* Fix

* Change the way we check for namespace

* Fix

* Tidying Up

* Fix Test

* Fix merge bugs

* Serialize k8s namespaces

* Add namespace to watch

* Serialize namespace when creating k8s namespace

Co-authored-by: Ben Toogood <ben@micro.mu>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-04-23 13:53:42 +01:00
Micro 316b81f790 Debugging 2020-04-23 13:11:00 +01:00
Micro 5e3262a62c Passs img pull secrets using name key 2020-04-23 12:52:59 +01:00
Micro 053fa0e457 Fix template syntax 2020-04-23 12:38:00 +01:00
Micro 501a6bf3ea Add imagePullSecrets to PodSpec 2020-04-23 12:27:36 +01:00
Asim Aslam 7345ce9192 change logging for service startup 2020-04-23 11:24:39 +01:00
Vasiliy Tolstov 6fa27373ed bundle qson lib in util (#1561)
* copy qson from https://github.com/joncalhoun/qson
  as author not want to maintain repo
* latest code contains our fix to proper decode strings
  with escaped & symbol
* replace package in api/handler/rpc

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-23 11:08:09 +03:00
徐旭 e55c23164a fix prealloc in trace (#1558) 2020-04-22 16:10:59 +03:00
Asim Aslam e25ab9f4ca Fix typo for proxy 2020-04-22 10:44:34 +01:00
Vasiliy Tolstov bea092f082 server: set registered only after configuring subscribers (#1557)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-21 23:01:59 +03:00
Asim Aslam d7ecb58f6c Add network proxying (#1556)
* Add network proxying

* go fmt
2020-04-21 15:54:40 +01:00
Jake Sanders 05d2b34e10 Add util/pki for creating and signing certificates (#1555) 2020-04-21 15:03:33 +01:00
ben-toogood 211fd9b9a3 Merge pull request #1554 from micro/oauth-login-hint
Add oauth login hint param
2020-04-21 13:40:47 +01:00
Ben Toogood 19f0836e70 Add oauth login hint param 2020-04-21 13:37:26 +01:00
Janos Dobronszki 075d7d4fef Renaming ShutdownSignals -> Shutdown (#1553) 2020-04-21 14:14:20 +02:00
Janos Dobronszki e5c215556e Add SIGKILL to shutdown signals (#1552)
* Add SIGKILL to shutdown signals

* go mod tidy

* Add missing file
2020-04-21 14:00:12 +02:00
Janos Dobronszki 7c31edd5f8 Enabling default runtime to run multiple versions (#1545)
* Enabling default runtime to run multiple versions

* Trigger build

* Fix

* Sprintf
2020-04-20 15:54:29 +02:00
Asim Aslam c4acf3c2cb Static serving disabled 2020-04-19 20:30:38 +01:00
Asim Aslam 53db26a614 Use go.micro.mu 2020-04-19 17:03:25 +01:00
Asim Aslam dde8f18b52 Update readme 2020-04-19 00:46:33 +01:00
Asim Aslam 6071b74fb5 Update readme 2020-04-19 00:45:29 +01:00
Asim Aslam ab041012b2 Update readme 2020-04-19 00:44:52 +01:00
Asim Aslam 226d6ad22b log whats happening in http handler 2020-04-19 00:41:03 +01:00
Asim Aslam a08ff90976 fix this bs logging issue 2020-04-18 23:36:00 +01:00
Asim Aslam ae8404d760 Log listening port 2020-04-18 23:32:20 +01:00
Vasiliy Tolstov f00fd7a49e api/router: support pcre and google.api pattern matching (#1549)
* api/router: support pcre and google.api pattern matching

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-19 00:31:34 +03:00
Asim Aslam ecbc42755c set network nodes in http resolver 2020-04-18 21:00:00 +01:00
Asim Aslam 16db76bee2 remove list endpoint from runtime and stop checking type in update 2020-04-17 17:54:34 +01:00
Asim Aslam dca5305e8a replaced build with updated timestamp in runtime 2020-04-17 16:29:05 +01:00
Asim Aslam c0b0f63757 Update docker workflow to push releases 2020-04-17 10:50:44 +01:00
Janos Dobronszki ac5822f1ee Fix local runtime updates (#1543) 2020-04-16 17:50:24 +02:00
ben-toogood ae56becbbd Merge pull request #1542 from micro/stream-auth
Set authorization header on grpc stream
2020-04-16 15:06:19 +01:00
ben-toogood 5bb18e685e Merge branch 'master' into stream-auth 2020-04-16 15:03:12 +01:00
Ben Toogood 2dfaab439c Set authorization header on grpc stream 2020-04-16 15:01:16 +01:00
Vasiliy Tolstov 62cedf64da api/router/registry: extract path based parameters from url to req (#1530)
Run tests / Test repo (push) Waiting to run
* api/router/registry: extract path based parameters from url to req
* api/handler/rpc: fix empty body request parsing
* bundle grpc-gateway util funcs

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-15 17:50:51 +03:00
ben-toogood 9961ebb46e Merge pull request #1538 from micro/rule-priority
Add priority to auth.CreateRequest and auth.DeleteRequest
2020-04-15 11:54:01 +01:00
Ben Toogood fe31a71557 Fix formatting 2020-04-15 11:50:52 +01:00
Ben Toogood c9a6b07c52 Add priority to auth.CreateRequest and auth.DeleteRequest 2020-04-15 11:49:24 +01:00
ben-toogood f1e6eff303 Merge pull request #1537 from micro/rule-priority
Add Priority to auth rules
2020-04-15 11:42:53 +01:00
Ben Toogood 2de03e5fd7 Tidy go mod 2020-04-15 11:39:53 +01:00
Ben Toogood 234c192faf Update protoc-gen-micro 2020-04-15 11:39:12 +01:00
Ben Toogood ea29920afb Add Priority to auth rules 2020-04-15 11:31:19 +01:00
Vasiliy Tolstov 4d177a782e vendor proto files from google (#1536)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-15 13:22:32 +03:00
Vasiliy Tolstov b700d425a4 api/handler/rpc: improvements and fixes (#1535)
* api/handler/rpc: fix empty body case
* api/handler/rpc: copy all request headers to metadata

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-15 01:37:15 +03:00
Asim Aslam 9a5b8ff50d use api 2020-04-14 22:14:55 +01:00
Asim Aslam c787fd0483 fix missing pointer 2020-04-14 17:13:38 +01:00
Asim Aslam 1134ea5ff3 make proto.Message compatible with raw json 2020-04-14 16:59:24 +01:00
ben-toogood fd16cd298f Merge pull request #1532 from micro/registry-namespace
Registry Namespace
2020-04-14 16:14:18 +01:00
ben-toogood 67e7aa223a Merge branch 'master' into registry-namespace 2020-04-14 16:03:29 +01:00
Asim Aslam 9d0381306d add a proto message without serialisation 2020-04-14 15:54:25 +01:00
ben-toogood f8837bfcbd Merge branch 'master' into registry-namespace 2020-04-14 15:37:44 +01:00
Vasiliy Tolstov 268651df18 regenerate all proto based files (#1531)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-14 16:25:09 +03:00
Ben Toogood e17825474f Add context options to the runtime 2020-04-14 12:32:59 +01:00
Ben Toogood 0c75a0306b Merge master into registry-namespace 2020-04-14 09:15:13 +01:00
Ben Toogood d61d30ef66 Inject Namespace into Context 2020-04-14 09:14:07 +01:00
Asim Aslam 71d4253927 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-04-13 23:05:47 +01:00
Asim Aslam e515005083 Remove only allowing certain methods 2020-04-13 23:05:39 +01:00
Asim Aslam 4bdc18d64a Update README.md 2020-04-13 22:15:21 +01:00
Asim Aslam f840a5003e Remove runtime List 2020-04-12 23:46:06 +01:00
Asim Aslam 5ef1698632 remove readme 2020-04-12 23:43:55 +01:00
Asim Aslam 1bb6967a38 reorder 2020-04-12 23:41:21 +01:00
Asim Aslam a056bdce7c fix metadata parsing 2020-04-12 14:40:37 +01:00
Asim Aslam b08c636b44 fixup handler tests 2020-04-12 14:29:38 +01:00
Asim Aslam d03a02f2e4 fix import 2020-04-12 11:25:12 +01:00
Asim Aslam 08ca61c121 add metadata set 2020-04-12 11:17:23 +01:00
Asim Aslam 962588b649 Strip MetadataKey global var 2020-04-12 11:16:08 +01:00
Asim Aslam cf67d460b7 strip down mdns watcher 2020-04-12 11:01:09 +01:00
Asim Aslam 4e539361fa strip file 2020-04-12 10:58:12 +01:00
Vasiliy Tolstov 3ce2ab88f5 broker/nats: remove embed nats server reference (#1527)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-11 22:37:29 +03:00
Vasiliy Tolstov 0a2363b49b api minor improvements (#1526)
* api/handler/rpc: unblock all http methods and set Host meta
* api/router/static: add debug log

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-11 22:21:55 +03:00
Asim Aslam ec80ceb8c2 Update readme 2020-04-11 18:23:37 +01:00
Asim Aslam ea2bb0275c Strip external use of mdns 2020-04-11 13:02:53 +01:00
Asim Aslam 51d4f737b8 fixup store cache# 2020-04-11 12:10:19 +01:00
Asim Aslam 3f81f685df Move sync 2020-04-11 12:00:34 +01:00
Asim Aslam bb1ccf09e8 prefix store dir 2020-04-11 11:23:41 +01:00
Asim Aslam c878237567 fix log file creation 2020-04-11 11:22:02 +01:00
Asim Aslam ac8b6f944e Prefix logs dir micro/logs for runtime 2020-04-11 11:15:01 +01:00
Asim Aslam 0f2006ac50 fix compilation issues 2020-04-11 11:02:06 +01:00
Asim Aslam c697eed1be Update comments 2020-04-11 10:48:32 +01:00
Asim Aslam b887d91f94 remove readme 2020-04-11 10:38:13 +01:00
Asim Aslam 39470c1b11 Completely replace sync implementation 2020-04-11 10:37:54 +01:00
Asim Aslam 6d553cb6fe add whitespace 2020-04-11 09:34:04 +01:00
Asim Aslam c612d86480 Move sync store 2020-04-11 09:33:10 +01:00
Asim Aslam 3f3d2f5027 fixup broker http address 2020-04-11 01:51:26 +01:00
Vasiliy Tolstov bc71640fd9 broker: swap default broker from eats to http (#1524)
* broker: swap default broker from eats to http

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-11 03:46:54 +03:00
Asim Aslam b979db6d9d remove sync event 2020-04-10 23:29:15 +01:00
Asim Aslam 57b758db7e push 2020-04-10 22:09:06 +01:00
Asim Aslam b5f546b137 go mod tidy 2020-04-10 19:55:45 +01:00
Asim Aslam d4b2c948dd Remove cloudflare store 2020-04-10 19:50:57 +01:00
Asim Aslam b9a5e9d610 fixup sync map 2020-04-10 17:47:13 +01:00
Asim Aslam 57853b2849 remove etcd store 2020-04-10 17:43:02 +01:00
Asim Aslam e5268dd0a6 move reg util to own package (#1523)
* move reg util to own package

* fix test

* fix broken static router
2020-04-10 17:41:10 +01:00
Asim Aslam 4fd12430d0 cleanup mdns files 2020-04-10 17:19:26 +01:00
Asim Aslam d134b469be rename file 2020-04-10 17:17:24 +01:00
Asim Aslam 9a685b2df5 delete k8s registry (#1522) 2020-04-10 17:15:20 +01:00
Jake Sanders 6a666c9c7d Add json tags to store.Record (#1518) 2020-04-09 19:38:43 +01:00
Asim Aslam 53549b6b30 Add options for Database/Table (#1516)
* Add options for Database/Table

* fix opts
2020-04-09 17:56:13 +01:00
Jake Sanders 0a27a08184 Add Databases and Tables endpoints to store RPC proto (#1515)
* Add Databases and Tables to store RPC

* add Database to TablesRequest
2020-04-09 16:37:32 +01:00
Janos Dobronszki 77f0abb0ba Enabling micro run for subfolders (#1510)
* Enabling micro run for subfolders

* Use source instead of os.Args[2]

* Works now

* PR comments

* WorkDir -> Dir
2020-04-09 15:44:39 +01:00
Asim Aslam 29cccd0b4a minor tweak add log line to proxy and basic auth provider by default (#1513) 2020-04-09 14:10:17 +01:00
ben-toogood bf65dc71c7 Merge pull request #1505 from micro/resover-refactor
Extract Micro Resolver (Namespace)
2020-04-09 13:14:49 +01:00
Asim Aslam 5bc8ee39f7 Merge branch 'master' into resover-refactor 2020-04-09 13:07:05 +01:00
Vasiliy Tolstov 8c1b477279 store/cockroach: fixup test (#1512)
* store/cockroach: fixup test

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-09 14:58:50 +03:00
Ben Toogood f9cfbe96c0 Merge master into resover-refactor 2020-04-09 12:42:34 +01:00
Jake Sanders 2e379ca7d0 Don't break the build! 2020-04-09 12:18:02 +01:00
Jake Sanders 2659215d5e cockroachDB doesn't support this syntax (#1509) 2020-04-09 12:11:24 +01:00
Vasiliy Tolstov 1063b954de dont display t.Log/t.Logf as errors in github actions (#1508)
* fix tests and github action annotations

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-09 14:05:46 +03:00
Ben Toogood 4ff959ef50 Dynamic Namespace 2020-04-09 11:03:33 +01:00
Janos Dobronszki bc1c8223e6 Remove ugly unneeded log in runtime local (#1507) 2020-04-09 11:50:12 +02:00
Ben Toogood 27eb7db1c2 Add default resolver to api router 2020-04-09 10:34:21 +01:00
Ben Toogood 3ede494945 Change import name 2020-04-09 10:32:08 +01:00
Ben Toogood f102aba4c1 Fix HTTP tests 2020-04-09 10:28:38 +01:00
Asim Aslam f2dd091ec0 strip log 2020-04-09 10:28:16 +01:00
Asim Aslam c1ad6d6c7c set service name in web 2020-04-09 09:41:50 +01:00
Jake Sanders 1e7cd8c484 Make the constraint explicit rather than inferred (#1506) 2020-04-08 23:52:35 +01:00
Asim Aslam bf8ebf8ad2 add namespace 2020-04-08 23:27:32 +01:00
Asim Aslam 1768958af7 fix typo 2020-04-08 22:50:56 +01:00
Asim Aslam bf41d8d28e fix store table env var 2020-04-08 19:44:49 +01:00
Asim Aslam 45700eaabe set database/table in header 2020-04-08 19:25:57 +01:00
Asim Aslam 48dd30c4c2 fix http test 2020-04-08 19:20:43 +01:00
Ben Toogood 8ff86ae08b Extract micro resolver 2020-04-08 16:21:53 +01:00
Asim Aslam b2079669f7 Strip namespace from router 2020-04-08 15:39:01 +01:00
Asim Aslam 2c1d1afd71 Strip namespace from registry router 2020-04-08 15:38:02 +01:00
Asim Aslam 9a73828782 Remove unused handlers 2020-04-08 15:34:11 +01:00
ben-toogood c5d085cff8 Merge pull request #1496 from micro/namespace
Configurable Namespace & Public Suffix Domain Resolution
2020-04-08 13:48:50 +01:00
ben-toogood 9f4286fc4e Merge branch 'master' into namespace 2020-04-08 13:44:46 +01:00
Jake Sanders 77f5cc5023 Fix nil dereference in cloudflare store (#1504) 2020-04-08 13:00:30 +01:00
Vasiliy Tolstov 8400aba81c broker/memory: small memory improvements (#1501)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-08 14:56:54 +03:00
Jake Sanders cc027d900e Close statements, add default table if the store was not initialised through service.Init() (#1502) 2020-04-08 12:08:08 +01:00
Edward bc0dc2e509 fix :no syscall.Kill on windows #1474 (#1474) 2020-04-08 10:50:44 +01:00
Vasiliy Tolstov 1fbc056dd4 minimize allocations (#1472)
* server: minimize allocations on re-register

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

* server: stop old instance before Init()

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

* client/grpc: fix allocations in protobuf marshal

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

* codec/json: fix allocations in protobuf marshal

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

* remove stop from init

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

* codec/grpc: expose MaxMessageSize

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

* codec: use buffer pool

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

* metadata: minimize reallocations

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

* util/wrapper: use metadata helper

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

* registry/cache: move logs to debug level

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

* server: move logs to debug level

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

* server: cache service only when Advertise is ip addr

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

* server: use metadata.Copy

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-08 10:50:19 +01:00
Asim Aslam 98fc3dfbad use single data bucket 2020-04-08 09:57:51 +01:00
Asim Aslam 4b0e27413e add Store Close method (#1500)
* add Store Close method

* Update sync store build failure
2020-04-08 09:51:10 +01:00
ben-toogood 6b524e2c55 Merge branch 'master' into namespace 2020-04-08 09:12:28 +01:00
Asim Aslam 4cac7dcc48 fix file tests 2020-04-07 19:45:27 +01:00
Ben Toogood e907d24e3b API Wrappers 2020-04-07 19:29:26 +01:00
Asim Aslam 39c352f210 Remove the test that takes 30 seconds sleeping 2020-04-07 18:22:40 +01:00
Ben Toogood 67cd59d7bc Rename namespace from Resolver.Endpoint 2020-04-07 16:27:59 +01:00
Ben Toogood 3735b0e529 Remove global namespace option 2020-04-07 16:27:01 +01:00
Ben Toogood 4362a885eb Refactor Namespace Resolver 2020-04-07 16:24:51 +01:00
Janos Dobronszki 038b936ce9 Setting up file store in constructor and not in init which is o… (#1499) 2020-04-07 16:43:43 +02:00
Vasiliy Tolstov 6aaad7d63f api/router/static: allow to specify body dst (#1486)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-07 15:38:27 +01:00
Janos Dobronszki aaee01b1a7 Use file store by default (as opposed to memory store) (#1498)
* Use file store by default (as opposed to memory store)

* Default table for file store
2020-04-07 15:19:45 +02:00
Jake Sanders 71538adfdc Explicitly set the table name during service init (#1497) 2020-04-07 13:00:05 +01:00
Janos Dobronszki 2ea5b33955 Disk backed local store (#1491) 2020-04-07 13:53:22 +02:00
Ben Toogood 3df87510a1 Add namespace 2020-04-07 12:46:44 +01:00
Ben Toogood 9d598836c3 Fix Tests 2020-04-07 11:37:04 +01:00
Ben Toogood 05ac3ff274 Tweak 2020-04-07 11:24:13 +01:00
Ben Toogood 76f6f80318 Default to Hostname 2020-04-07 11:23:21 +01:00
Ben Toogood cb96949551 Merge branch 'master' of https://github.com/micro/go-micro into namespace 2020-04-07 10:58:54 +01:00
ben-toogood 87cc4f273b Merge pull request #1495 from micro/log-level
Change cross namespace request err level
2020-04-07 10:58:22 +01:00
Ben Toogood f0980e9b30 Change cross namespace request err level 2020-04-07 10:54:27 +01:00
Ben Toogood 977934f8fd ServiceNamespace => ServicePrefix in api server 2020-04-07 10:39:27 +01:00
Ben Toogood 9e116731b1 ServiceNamespace => ServicePrefix in api server 2020-04-07 10:38:27 +01:00
Ben Toogood 316424f0f7 Fix comments typo 2020-04-07 10:35:57 +01:00
Ben Toogood bd23dc1f18 Improve micro.mu check 2020-04-07 10:34:26 +01:00
Ben Toogood 501fc5c059 Refactor to use publicsuffix 2020-04-07 10:28:39 +01:00
Ben Toogood 11e1e9120a Remove debugging 2020-04-07 10:10:37 +01:00
Ben Toogood a81d86ed08 Merge Asim's Fixes 2020-04-07 10:08:06 +01:00
Ben Toogood 7206d5f964 Add Namespace to CombinedAuthHandler 2020-04-07 09:40:40 +01:00
Asim Aslam b5f5027549 Move store scope to util 2020-04-07 02:23:16 +01:00
Asim Aslam e8a86585da contains missing host port 2020-04-07 00:54:27 +01:00
Asim Aslam 5374896ed0 clone request 2020-04-07 00:29:35 +01:00
Asim Aslam b6348ba59a Fix cruft 2020-04-07 00:25:11 +01:00
Asim Aslam ca11c4a672 Few nitpicks 2020-04-07 00:19:49 +01:00
Lars Lehtonen 900b2d24f9 config/secrets/box: fix dropped test error (#1494) 2020-04-06 23:09:42 +01:00
Jake Sanders 3324d140c0 Rename store Namespace / Prefix options to Database and Table (#1492)
* Rename Namespace to DB, Rename Prefix to table, Remove Suffix Option

* Rename options

* Rename options

* Add store_table option

* Table per service, not Database per service
2020-04-06 16:45:55 +01:00
ben-toogood 3a378eb7d6 Merge pull request #1493 from micro/auth-encode-endpoint
Encode Endpoint in API auth wrapper
2020-04-06 16:21:14 +01:00
Ben Toogood 574bf5ac69 Set value in context, not metadata 2020-04-06 16:10:08 +01:00
Ben Toogood 774c0d30a7 Encode Endpoint in API auth wrapper 2020-04-06 16:01:42 +01:00
ben-toogood 0f570d98e1 Merge pull request #1475 from micro/auth-resolver
Auth integrate resolver to support micro web & api
2020-04-06 14:57:41 +01:00
ben-toogood 7f07e1a642 Merge branch 'master' into auth-resolver 2020-04-06 14:43:22 +01:00
ben-toogood 9b546a7242 Change auth namespace log level (#1490)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-04-06 13:51:28 +01:00
Asim Aslam c4442a7533 Don't set the registry in new options for web services (#1489) 2020-04-06 13:40:40 +01:00
ben-toogood bea7c3f7e7 Merge pull request #1488 from micro/disable-warn-log
Change namespace error log level
2020-04-06 12:55:47 +01:00
ben-toogood cca9773269 Merge branch 'master' into disable-warn-log 2020-04-06 12:51:47 +01:00
Ben Toogood 600b20fb81 Change namespace error log level 2020-04-06 12:50:04 +01:00
Edward 31a1ea6fae fix: use registry from opts not use default directly:(#1436) (#1468)
web: use passed user registry, or default
2020-04-05 13:15:38 +03:00
Vasiliy Tolstov bc7579f1d8 api/handler/rpc: fix panic on invalid error conversation (#1483)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-04 00:55:15 +03:00
Vasiliy Tolstov 38aed6f0f6 api/handler/rpc: not log error on client disconnect (#1482)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-04 00:37:18 +03:00
ben-toogood 7f8b35e295 Merge pull request #1480 from micro/host-fix
Add Debugging
2020-04-03 15:07:22 +01:00
ben-toogood b09dd9a689 Merge branch 'master' into host-fix 2020-04-03 15:03:49 +01:00
Ben Toogood a82ce4d1ae Add Debug 2020-04-03 15:03:18 +01:00
ben-toogood 34234fc486 Merge pull request #1479 from micro/host-fix
Auth host fix
2020-04-03 14:43:35 +01:00
Ben Toogood 4a850ff8a0 Auth host fix 2020-04-03 14:40:24 +01:00
ben-toogood 350dd41732 Merge branch 'master' into auth-resolver 2020-04-03 14:19:03 +01:00
ben-toogood d8cca31738 Merge pull request #1478 from micro/auth-hosts-fix
Fix auth hosts bug
2020-04-03 14:13:51 +01:00
Ben Toogood b864b3e350 Fix auth hosts bug 2020-04-03 14:09:25 +01:00
ben-toogood 41b746e435 Merge pull request #1477 from micro/fix
Hotfix
2020-04-03 13:37:50 +01:00
Ben Toogood 906263291b Hotfix 2020-04-03 13:37:02 +01:00
ben-toogood 46f0bda31e Merge pull request #1476 from micro/namespace-fix
Namespace Fix
2020-04-03 13:30:30 +01:00
Ben Toogood d0e47206cc Fix 2020-04-03 13:29:48 +01:00
ben-toogood ed6fe67880 Merge pull request #1471 from micro/namespace
Detect & Propagate Namespace
2020-04-03 13:07:26 +01:00
Ben Toogood 1374a9e528 Fix namespace bug in auth wrapper 2020-04-03 13:03:27 +01:00
Ben Toogood a9c0e043d2 Fix nil grpc server auth bug 2020-04-03 12:50:50 +01:00
Ben Toogood 49a568e9c0 Set default server auth 2020-04-03 12:33:19 +01:00
Ben Toogood dea2d7ab9f Fix go-micro auth wrapper init 2020-04-03 12:27:01 +01:00
Ben Toogood ebb1a42d48 Merge branch 'namespace' of https://github.com/micro/go-micro into namespace 2020-04-03 12:14:26 +01:00
Ben Toogood 1096c8fb39 Fix failing test 2020-04-03 10:16:19 +01:00
Ben Toogood 91b9c3f92e Add defaults 2020-04-03 10:08:39 +01:00
Ben Toogood 183c8bfb81 Apply fix for apis 2020-04-03 09:45:39 +01:00
Ben Toogood 49a1130281 Merge branch 'auth-resolver' of https://github.com/micro/go-micro into auth-resolver 2020-04-03 09:34:57 +01:00
Ben Toogood 760233b858 Reverse Change 2020-04-03 09:34:52 +01:00
ben-toogood ede076e899 Merge branch 'master' into auth-resolver 2020-04-03 09:33:13 +01:00
Ben Toogood fdcb013f24 Fix web registry compatability bugs 2020-04-03 09:18:30 +01:00
Ben Toogood ce23ab36cb Improve Err Handling 2020-04-02 18:41:06 +01:00
ben-toogood 61f0619e97 Merge branch 'master' into namespace 2020-04-02 18:05:21 +01:00
Ben Toogood cfde3ec3d9 Remove resolver logic 2020-04-02 18:03:57 +01:00
Ben Toogood 4a4c666528 Remove resolver logic 2020-04-02 18:03:21 +01:00
Ben Toogood 8b35c264eb Pass resolver to api auth handler 2020-04-02 17:44:48 +01:00
Ben Toogood 4999f6dfd4 Namespace requests coming via api & web 2020-04-02 17:01:06 +01:00
Asim Aslam 31c4452fc7 delete monitor (#1470) 2020-04-02 14:05:17 +01:00
Janos Dobronszki 2cafa289b6 Stop LogStream if there is an error in k8s pod log streaming (#1469)
* Stop LogStream if there is an error in k8s pod log streaming

* Locking stream Stops

* PR comment
2020-04-02 12:16:35 +01:00
Vasiliy Tolstov 0241197c6a api/handler/rpc: binary streaming support (#1466)
* api/handler/rpc: binary streaming support

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

* fixup

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

* fix

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

* fix sec webscoekt protol

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-02 10:13:04 +01:00
Asim Aslam 0a15ae9b9d Move String method (#1467) 2020-04-01 23:27:15 +01:00
Janos Dobronszki d2b6d35220 log.Errorf when pod streaming fails (#1463)
* log.Errorf when pod streaming fails

* Error method added for loggers

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-04-01 23:03:26 +01:00
Asim Aslam e1bc0f6288 replace strings for store prefix (#1465)
Co-authored-by: ben-toogood <bentoogood@gmail.com>
2020-04-01 20:19:21 +01:00
ben-toogood cd3d704aa5 Merge pull request #1459 from micro/auth-interface-update
Auth Interface Iteration
2020-04-01 17:56:38 +01:00
Ben Toogood 9de69529ce Fix token tests 2020-04-01 17:29:17 +01:00
ben-toogood 623f0c0c90 Merge branch 'master' into auth-interface-update 2020-04-01 17:24:01 +01:00
Ben Toogood c766679687 Fix typo 2020-04-01 17:22:01 +01:00
Ben Toogood df8c0bb5e1 Auth Generate, make secret optional 2020-04-01 17:20:02 +01:00
Ben Toogood d577c32563 Add back auth.PrivateKey 2020-04-01 17:17:40 +01:00
Ben Toogood 365dfe9df5 Code => State 2020-04-01 17:11:46 +01:00
Ben Toogood ae15793fc3 Support oauth codes 2020-04-01 15:36:22 +01:00
Janos Dobronszki 15fcd5ecef Remove Go micro 1.18 dependency (#1462) 2020-04-01 16:14:08 +02:00
Ben Toogood 1750fd8d10 Merge branch 'auth-interface-update' of https://github.com/micro/go-micro into auth-interface-update 2020-04-01 14:42:37 +01:00
Ben Toogood 525ab094c8 Remove LoginOptions 2020-04-01 14:42:11 +01:00
Janos Dobronszki bb51b8203e Runtime logs (#1447)
* Runtime logs

* Slightly broken

* Pushing for diff

* Log trailing works locally

* LogsOptions

* Comments and streamcount support for local logs

* Adding kubernetes logs

* Fixing k8s logs

* K8s fixes

* StreamCount is now nuked

* PR comments

* PR comments again

* Fix typo
2020-04-01 15:40:15 +02:00
ben-toogood 75a75c56ad Merge branch 'master' into auth-interface-update 2020-04-01 14:37:06 +01:00
Ben Toogood 26cb6bf5b9 Remove Legacy JWT fields 2020-04-01 14:27:56 +01:00
Ben Toogood 9cbbd71855 Remove default login 2020-04-01 14:26:24 +01:00
Ben Toogood f7655b71ea Merge branch 'auth-interface-update' of https://github.com/micro/go-micro into auth-interface-update 2020-04-01 14:25:07 +01:00
Ben Toogood 8e4d9e1702 Further Refactoring 2020-04-01 14:25:00 +01:00
Asim Aslam 20c95d94cd api completeness (#1460) 2020-04-01 12:07:50 +01:00
ben-toogood 0a7d8afe67 Merge branch 'master' into auth-interface-update 2020-04-01 09:42:45 +01:00
Vasiliy Tolstov 7b7a859a03 api: use http request Clone (#1458)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-01 01:50:37 +03:00
Vasiliy Tolstov 8a8742f867 api/handler/rpc: dont change types of url fields (#1457)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-01 01:26:58 +03:00
Asim Aslam 68b0238a5d add stream timeout option which defaults to 0 (#1456)
* add stream timeout option which defaults to 0

* fix option
2020-03-31 23:22:11 +01:00
Vasiliy Tolstov 1490aff38e api/handler/rpc: correctly parse nested url vars (#1455)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-04-01 00:23:17 +03:00
Vasiliy Tolstov 3a22efbd7d metadata: change method name (#1454)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-31 23:39:18 +03:00
Vasiliy Tolstov 5e65a46be3 metadata: allow to remove key from metadata (#1453)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-31 22:55:33 +03:00
Vasiliy Tolstov 18061723bb fix api metadata extract from context (#1452)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-31 22:36:51 +03:00
Vasiliy Tolstov d6bef84de0 api/handler/rpc: fix metadata cleanup (#1451)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-31 21:59:35 +03:00
Ben Toogood 82bc3cbf8d Update interface to add provider and make secret optional 2020-03-31 19:01:43 +01:00
Ben Toogood cffb0a1eae Remove ContextWithToken 2020-03-31 18:34:31 +01:00
Ben Toogood 134bc1c68a Implement new interface 2020-03-31 18:17:01 +01:00
Asim Aslam 6c6c5359b1 Add options to config (#1450) 2020-03-31 17:13:21 +01:00
Ben Toogood 8dbb5153f4 Tweak Auth Interface 2020-03-31 17:01:51 +01:00
ben-toogood 2674790694 Service => Service Auth (#1448)
* Service => Service Auth

* WithServicePrivileges => ServicePrivileges

* Fixes for CLI login

* ServicePrivileges => ServiceToken

* Fallback to service token

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-31 16:18:04 +01:00
ben-toogood 9fb1d476a2 Merge branch 'master' into auth-srv-srv 2020-03-31 16:15:17 +01:00
Ben Toogood 36386354d7 Fallback to service token 2020-03-31 13:51:32 +01:00
Ben Toogood bd70820b6b ServicePrivileges => ServiceToken 2020-03-31 13:48:28 +01:00
Ben Toogood 956029ae3d Fixes for CLI login 2020-03-31 13:30:14 +01:00
Ben Toogood e0c7f48d20 WithServicePrivileges => ServicePrivileges 2020-03-31 12:57:38 +01:00
Ben Toogood d659e435c6 Service => Service Auth 2020-03-31 12:44:34 +01:00
Jake Sanders 3d274ab6a2 Add namespace support to Kubernetes client (#1446)
* Add namespace support to Kubernetes client

* Fix LastUpdateTime Condition
2020-03-31 12:03:32 +01:00
Asim Aslam 1222d076f2 There can be only one! (#1445)
Run tests / Test repo (push) Waiting to run
* There can be only one

* fix proto?
2020-03-31 10:18:50 +01:00
ben-toogood 76ade7efd9 Auth - Swap Refresh to Token and change secrets to be strings, not tokens (#1444)
* Refresh => Token

* Secret is no longer a token

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-31 10:06:13 +01:00
Vasiliy Tolstov c706ebe3fb auth proto: provide help to protoc-gen-go (#1442)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-31 00:35:11 +03:00
Vasiliy Tolstov 9e6db79860 regenerate all proto (#1440)
* regenerate all proto

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

* regenerate from proto

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

* regenerate from proto

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-30 21:58:32 +01:00
Vasiliy Tolstov 756b346672 auth/service: move all proto files to single dir (#1439)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-30 18:23:00 +03:00
ben-toogood 4db2f5e79d Add Namespace to Auth (#1438)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-30 09:51:37 +01:00
Vasiliy Tolstov 3d7d5ce6b4 api: add static router and improve path parser in rpc handler (#1437)
* api: add static router and improve path parser in rpc handler

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

* expose metadata context key to be able to get unmodified map keys

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

* server/grpc: fix jsonpb codec for protobuf msg

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

* api/handler/rpc: write 204 status code when rsp is nil

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

* api/handler/rpc: add check for nil response for non javascript

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-30 09:04:59 +01:00
Socket 8282e781e4 grpc pool should check state (#1435)
Co-authored-by: huangshaojie <huangshaojie@corp.netease.com>
2020-03-28 08:48:25 +00:00
Vasiliy Tolstov e4acc63d5f add mdns registry debug (#1434)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-27 22:33:49 +03:00
Asim Aslam 45ee5e9ad1 Move error for api validation to trace level (#1432)
* remove error on endpoint validation

* trace level
2020-03-27 14:01:47 +00:00
Asim Aslam b60fde0e64 Pass through source and metadata in Update and Delete calls to runtime (#1431) 2020-03-27 11:37:12 +00:00
Lars Lehtonen 011a783a9e store/cockroach: fix dropped test errors (#1419)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-27 10:15:37 +00:00
ben-toogood 6723d17b22 Default auth, return account secret on Inspect (#1430)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-27 09:54:29 +00:00
ben-toogood 47c7181d41 Default Auth: Add blank secret to account to prevent nil errors (#1429)
* Remove debug auth logs

* Default auth, return account secret on Inspect

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-27 09:46:17 +00:00
Vasiliy Tolstov b38da6ced0 api/handler/rpc: process all methods and merge url params to json body (#1427)
* api/handler/rpc: process all methods and merge url params to json body

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

* add merge json test

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-27 07:59:31 +00:00
ben-toogood 1a53307a78 Remove debug auth logs (#1426)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-26 19:00:24 +00:00
Asim Aslam e204f3e2e8 Add metadata Get method (#1425) 2020-03-26 18:50:00 +00:00
ben-toogood 329bd09f93 Fix Auth Init bug (#1424)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-26 18:09:51 +00:00
ben-toogood 4648fd0d09 Auth debugging (#1423)
* More auth debugging

* More auth debugging

* Increase auth debugging

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-26 17:55:35 +00:00
ben-toogood c905df3be6 Log auth verify requests (#1422)
* More auth debugging

* More auth debugging

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-26 17:35:28 +00:00
Asim Aslam 62f9a054a4 100mb (#1421) 2020-03-26 16:57:31 +00:00
ben-toogood 00e7804f96 Auth - Add debugging to loading rules (#1420)
* Fix auth multi-rule edgecase

* Add logging to auth rules

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-26 16:30:31 +00:00
ben-toogood 42b6bf5bbf Fix auth multi-rule edgecase (#1418)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-26 15:52:48 +00:00
ben-toogood 844c456839 Refactor Auth Service Protos, Add Access Rules (#1411)
* Refactor auth/service into two protos

* Accounts Proto

* Store Prefixes

* Misc

* Tweak Protos

Co-authored-by: Ben Toogood <ben@micro.mu>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-26 13:12:43 +00:00
Asim Aslam 7182ca1fd0 fix server logging (#1417) 2020-03-26 13:08:06 +00:00
Vasiliy Tolstov 02839cfba5 api/handler: use http.MaxBytesReader and buffer pool (#1415)
* api/handler: use http.MaxBytesReader

protect api handlers from OOM cases

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-26 14:29:28 +03:00
Asim Aslam 776a7d6cd6 Update filter comment for proxy (#1416) 2020-03-26 08:05:00 +00:00
Vasiliy Tolstov beaa434610 logger: fix reading env var (#1414)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-25 22:00:43 +00:00
Asim Aslam 6efc5556e5 use requested service (#1413) 2020-03-25 20:59:37 +00:00
Asim Aslam 8d0826a031 Add check for k8s condition (#1412) 2020-03-25 19:32:41 +00:00
ben-toogood 378d03eb66 Tidying up auth (#1410)
* Don't clear auth rules if request fails

* Add jitter to auth service loading rules

* Remove unused error from ContextWithToken result

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-25 18:34:13 +00:00
ben-toogood 56af826230 Update auth to pass seconds and not nanoseconds (#1409)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-25 17:03:45 +00:00
ben-toogood 511ebd8ec2 Fix Token Expiry Bug (#1408)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-25 14:40:37 +00:00
ben-toogood 1057ef6acb Add ContextWithToken (#1407)
* Add ContextWithToken

* Tidying up BearerScheme

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-25 11:20:53 +00:00
ben-toogood 35e2a68a98 Fix auth bug restricting access to unauthorised endpoints (#1405)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-25 10:31:33 +00:00
ben-toogood 0e56382107 Fix service level auth, add improved error descriptions to aid with debugging (#1403)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-25 09:35:29 +00:00
Jake Sanders dff98355be Missing ; in SQL query 2020-03-24 23:49:09 +00:00
Vasiliy Tolstov 8100d26430 api/router/registry: use logger (#1402)
* api/router/registry: use logger

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

* api/server/acme: use logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-24 20:45:11 +00:00
Jake Sanders 397a8638f4 Cockroach Store bugfix (#1401) 2020-03-24 17:16:38 +00:00
Jake Sanders eb4d2ae6aa Remove useless variable from cockroach store (#1400) 2020-03-24 15:37:30 +00:00
Jake Sanders 914340585c Trim space from env variables (#1399) 2020-03-24 14:51:43 +00:00
ben-toogood 84b4eb5404 Fix missing loop (#1398)
* WithRoles variadic args

* Load Rules

* Timer => Ticker

* Add missing for loop in auth service

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-24 14:16:57 +00:00
ben-toogood fd664f4392 Auth load rules (#1397)
* WithRoles variadic args

* Load Rules

* Timer => Ticker

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-24 13:48:37 +00:00
ben-toogood 86272a3064 WithRoles variadic args (#1395)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-24 10:18:34 +00:00
ben-toogood c1978265ab Auth Wildcard Endpoints (#1394)
* Auth Wildcard Endpoints

* Fix joinkey bug, improve tests

* Change joinKey

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-24 09:39:33 +00:00
ben-toogood e0e77f3983 Updated auth interface (#1384)
* Updated  auth interface

* Add Rule

* Remove Rule

* Return token from Renew

* Renew => Refresh

* Implement Tokens & Default Auth Implementation

* Change default auth to noop

* Change default auth to noop

* Move token.Token to auth.Token

* Remove Token from Account

* Auth service implementation

* Decode JWT locally

* Cookie for secret

* Move string to bottom of interface definition

* Depricate auth_exclude

* Update auth wrappers

* Update go.sum

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-23 16:19:30 +00:00
Vasiliy Tolstov 9826ddbd64 api/handler/rpc: log errors (#1390)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-23 10:31:35 +03:00
0987363 87617be227 Add client header to rpcRequest header; issue #957 (#1378)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-21 23:25:23 +00:00
Vasiliy Tolstov d559587807 client/grpc: remove json-iterator usage (#1387)
* minimize external deps and binary size
* if user wants json-iterator codec it must be used in server and
  client code. so best to use it via go-plugins

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-21 09:40:58 +00:00
Janos Dobronszki 9501512219 Auth util func RequestToContext (#1386) 2020-03-20 15:23:12 +00:00
Asim Aslam d2f153d795 Add type of service (#1385) 2020-03-20 12:48:12 +00:00
Asim Aslam e49be1da42 fix local runtime (#1383) 2020-03-19 22:38:37 +00:00
Jake Sanders 4c6f68d537 Implement store read cache (#1366)
* Implement store read cache

* Added cache tests and fixed a bug in memory store
2020-03-19 18:19:07 +00:00
Vasiliy Tolstov cbb958def5 config: fix panic on multiple Close() (#1374)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-19 12:54:59 +03:00
Asim Aslam 40ff6ddfcf sigh, further status changes (#1371) 2020-03-18 22:47:03 +00:00
Asim Aslam 5ad7c36bd4 Fix labels for k8s (#1370) 2020-03-18 22:13:21 +00:00
Asim Aslam 99c3fe2bb8 fix status parsing (#1368) 2020-03-18 21:50:52 +00:00
Asim Aslam 1bd340701b add k8s service ip to metadata (#1367)
* add k8s service ip to metadata

* go fmt

* use same port as container
2020-03-18 18:27:29 +00:00
Jake Sanders c91bf7e9e7 [WIP] Store Sync (#1365)
* Initial cache implementation

* Write queue implementation

* Accidentally started writing the storage sync service
2020-03-18 16:39:36 +00:00
Vasiliy Tolstov 41f8a8cb00 errors: add FromError func (#1362)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-18 03:10:38 +03:00
ben-toogood cd04111e3d Pass redirect_to param on auth (#1361)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-17 20:04:16 +00:00
ben-toogood 00cd2448a4 Fix bug where auth token is not set from cookie when excluded endpoint (#1360)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-17 19:24:10 +00:00
ben-toogood 8a41d369f2 Auth JWT ID Fix (#1359)
* Auth JWT ID Fix

* Remove unused ID in jwt claims

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-17 16:59:57 +00:00
Jake Sanders 638c219736 Cockroach store feature completion (#1358)
* Start fixing cockroach store

* Add prefix, suffix, limit, offset for cockroachdb store
2020-03-17 16:15:23 +00:00
ben-toogood b3c631dd38 Support Wildcard Auth Excludes (#1357)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-17 16:03:49 +00:00
Vasiliy Tolstov ab73127063 grpc client/server fixes (#1355)
Run tests / Test repo (push) Waiting to run
* grpc client/server fixes

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-17 14:27:20 +03:00
Asim Aslam 03031a694d use pod phase/status (#1356) 2020-03-16 23:47:34 +00:00
li.peng 5712aafba9 fix: context cancel (#1350)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-16 10:45:33 +00:00
Vasiliy Tolstov ac333d9d47 client/grpc: unwrap error after call (#1352)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-16 13:33:38 +03:00
ben-toogood 247707f583 Return store.ErrNotFound if not found when calling over rpc (#1353)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-16 10:30:56 +00:00
ben-toogood d91c14eb30 grpc client error fix (#1351)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-16 12:53:15 +03:00
Asim Aslam ca8684a886 fix k8s issues (#1349) 2020-03-15 15:09:18 +00:00
Asim Aslam 0449138f61 fix panic (#1348) 2020-03-14 21:18:41 +00:00
Vasiliy Tolstov 609f4826b3 server: remove duplicate code (#1346)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-13 22:15:09 +00:00
Vasiliy Tolstov 60993e6275 config/source/service: base64 fix (#1345)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-13 21:44:55 +00:00
Asim Aslam e803fb0855 Runtime hacks (#1344)
* Add Args/Image to runtime

* remove the hacks
2020-03-13 18:39:59 +00:00
Vasiliy Tolstov 3543b275e0 fix log level helper (#1342)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-13 17:36:42 +03:00
Vasiliy Tolstov fbde872e7f Revert "server/grpc: fix ordering of register and check for registered (#1338)" (#1341)
This reverts commit 62a644ddd8.
2020-03-13 09:30:44 +00:00
Asim Aslam 078dd4eb9b fix etcd (#1340)
* fix etcd

* update go mod
2020-03-13 08:55:23 +00:00
Vasiliy Tolstov 62a644ddd8 server/grpc: fix ordering of register and check for registered (#1338)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-12 22:04:11 +00:00
Asim Aslam d8cfa7a295 add config to cmd (#1337)
* add config to cmd

* fix build
2020-03-12 18:47:40 +00:00
ben-toogood 47f1203e97 Add Config to service options (#1336)
Co-authored-by: Ben Toogood <ben@micro.mu>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-12 18:13:03 +00:00
Jake Sanders 1b4e881d74 Rewrite the store interface (#1335)
* WIP store rewrite

* Fix memory store tests

* Store hard expiry times rather than duration!

* Clarify memory test

* Add limit to store interface

* Implement suffix option

* Don't return nils from noop store

* Fix syncmap

* Start fixing store service

* wip service and cache

* Use _ for special characters in cockroachdb namespace

* Improve cockroach namespace comment

* Use service name as default store namespace

* Fixes

* Implement Store Scope

* Start fixing etcd

* implement read and write with expiry and prefix

* Fix etcd tests

* Fix cockroach store

* Fix cloudflare interface

* Fix certmagic / cloudflare store

* comment lint

* cache isn't implemented yet

* Only prepare DB staements once

Co-authored-by: Ben Toogood <ben@micro.mu>
Co-authored-by: ben-toogood <bentoogood@gmail.com>
2020-03-12 13:41:30 +00:00
ben-toogood 20ce61da5a Oauth google fixes (#1330)
* Fix Auth Headers

* Tweak Oauth to work for Google

Co-authored-by: Ben Toogood <ben@micro.mu>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-12 13:11:35 +00:00
chengguoqiang eef4825be4 Update etcd.go (#1334)
add leaseId to the trace log
2020-03-12 10:09:38 +00:00
Asim Aslam be9c6141f5 delete options (#1333) 2020-03-12 09:05:09 +00:00
Asim Aslam 1ca4619506 return store.ErrNotFound (#1332) 2020-03-11 23:09:42 +00:00
Asim Aslam f55493993c set namespace rather than key (#1331) 2020-03-11 22:31:24 +00:00
Vasiliy Tolstov 7b385bf163 minimize allocations in logger and tunnel code (#1323)
* logs alloc

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

* fix allocs

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

* fix allocs

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

* tunnel allocs

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

* try to fix tunnel

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

* cache cipher for send

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

* more logger

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

* more logger

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

* more logger

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

* more logger

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

* more logger

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

* more logger

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

* more logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-11 17:55:39 +00:00
Jake Sanders 4125ae8d53 Add secrets interface to config/secrets (#1325)
* Interface for secrets

* Add secretbox secrets implementation

* Start working on box

* typo

* Add asymmetric encryption implementation

* go mod tidy

* Fix review comments

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-10 22:52:06 +00:00
ben-toogood 48b2a5c37c Fix Auth Headers (#1324)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-10 16:47:01 +00:00
Asim Aslam ed83c27f0e add websocket streaming to api rpc handler (#1326) 2020-03-10 15:21:43 +00:00
Vasiliy Tolstov 241614ff68 add helper function to determine logger level (#1321)
* add helper function to determine logger level

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-09 23:43:05 +03:00
mlboy 1a4f608ed1 add: auth add generate options Expiry for set token expires (#1319)
Co-authored-by: mlboy <ml3@meitu.com>
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-09 17:16:31 +00:00
Vasiliy Tolstov 43b0dbb123 tunnel: reduce allocation and improve performance (#1320)
* tunnel: reduce allocation and improve performance

BenchmarkSha256Old-16 100000 156748 ns/op 11835 B/op 168 allocs/op
BenchmarkSha256Old-16 100000 156229 ns/op 11819 B/op 168 allocs/op

BenchmarkSha256New-16 100000 154751 ns/op 11107 B/op 161 allocs/op
BenchmarkSha256New-16 100000 154263 ns/op 11110 B/op 161 allocs/op

simple change lowers allocations and brings performance

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

* fix

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

* tunnel: reuse buf in Decrypt

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

* fix unneeded conversations

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

* base32 string is smaller than hex string

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-09 17:10:08 +00:00
ben-toogood b344171c80 URL Encode Provider.Endpoint() (#1317)
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-09 10:21:49 +00:00
ben-toogood e3ce45495a os.Exit on log.Fatal (#1316)
* os.Exit on log.Fatal

* Fix TestOptions

Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-09 09:23:42 +00:00
Vasiliy Tolstov f01664a551 Merge pull request #1313 from micro/upstream
fix ipv6 address usage in mdns registry and util/addr
2020-03-07 23:50:03 +03:00
Vasiliy Tolstov 8ecbdc1cd6 registry/mdns: add logging for invalid endpoint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-07 23:19:48 +03:00
Vasiliy Tolstov 55c19afb0b registry/mdns: fix ipv6 addr in mdns registry
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-07 23:17:00 +03:00
Vasiliy Tolstov 077063c212 util/addr: check ip addrs before return
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-07 23:13:56 +03:00
ben-toogood 9a7a65f05e Auth Provider (#1309)
* auth provider mock interface

* Auth Provider Options

* Implement API Server Auth Package

* Add weh utils

* Add Login URL

* Auth Provider Options

* Add auth provider scope and setting token in cookie

* Remove auth_login_url flag

Co-authored-by: Asim Aslam <asim@aslam.me>
Co-authored-by: Ben Toogood <ben@micro.mu>
2020-03-07 11:06:57 +00:00
Vasiliy Tolstov 8ee5607254 [WIP]: broker ErrorHandler option (#1296)
* broker ErrorHandler option

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

* rewrite Event interface, add error

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

* implement new interface

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

* change ErrorHandler func to broker.Handler

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

* fix

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-06 21:25:16 +00:00
Vasiliy Tolstov 11be2c68b9 util/stream: fix imports (#1310)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-06 21:17:57 +00:00
Vasiliy Tolstov a864f812f1 web: fix ipv6 address issue (#1308)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-06 18:44:56 +03:00
Asim Aslam ae60bea8d8 add stream fix (#1305) 2020-03-06 14:40:47 +00:00
Jake Sanders a851b9db7a Comment typo in gRPC subscriber (#1304) 2020-03-05 14:55:46 +00:00
Vasiliy Tolstov d807dac2a7 server/grpc: avoid panic in case of nil Header (#1303)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-05 17:45:31 +03:00
Vasiliy Tolstov ce2ba71002 server: subscribe to topic with own name if router not nil (#1295)
* server: subscribe to topic with own name if router not nil

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-05 10:29:50 +03:00
Asim Aslam 67c26c71b6 add jitter (#1298) 2020-03-04 15:37:17 +00:00
ben-toogood 9386f36a13 Exit on log.Fatal (#1297) 2020-03-04 13:46:01 +00:00
ben-toogood 6d803d9e45 Implement api/server/cors (#1294) 2020-03-04 11:40:53 +00:00
ben-toogood 6a9001bdb1 Set auth account in context (#1293) 2020-03-04 09:54:52 +00:00
Jake Sanders 3f0c28a815 Expiration is actually a unix timestamp (#1290)
* Expiration is actually a unix timestamp

* int -> int64
2020-03-03 18:15:50 +00:00
Asim Aslam 49ffc60afb Use Foo.Call on /foo (#1286)
Co-authored-by: Jake Sanders <i@am.so-aweso.me>
2020-03-03 16:47:15 +00:00
Jake Sanders beb5e80e87 Fix nil pointer dereference (#1289) 2020-03-03 13:54:56 +00:00
Jake Sanders eebd69c995 Change from renekroon/ttlcache to patrickmn/go-cache (#1288) 2020-03-03 13:35:49 +00:00
Jake Sanders bc71989e2c int64 -> time.Duration (#1287) 2020-03-03 13:15:26 +00:00
Vasiliy Tolstov 89ba602e17 logger fixes and improvements (#1285)
* fix helper fields
* add metadata output for default logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-03 11:07:37 +03:00
Jake Sanders f6102bde70 Add a cache to workers KV storage implementation (#1284)
* cloudflare-cache

* go mod tidy
2020-03-02 18:14:25 +00:00
Pieter Voorwinden 7cad77bfc0 Initialize header to prevent assignment to entry in nil map error (#1282)
Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-02 19:17:26 +03:00
ben-toogood 1f2e067f71 k8s runtime - get status from pods (#1283) 2020-03-02 15:49:10 +00:00
Vasiliy Tolstov b555269b1b copy fields in helper (#1281)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-03-02 14:18:36 +00:00
ben-toogood 9200c70202 Replace validation error with regex for cockroach namespace (#1270)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-03-01 22:09:06 +00:00
Sumanth Chinthagunta d8377e09c9 feat(dockerfile): adding dumb-init to base image (#1278) 2020-02-29 21:55:15 +00:00
Vasiliy Tolstov 0754229878 broker/memory: add codec support (#1276)
allow easy testing of other services with memory broker
and also allows to more deeply simulate real brokers

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-29 23:00:29 +03:00
Vasiliy Tolstov 6b8930a960 add new helper method to logger (#1273)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-29 00:39:41 +00:00
Vasiliy Tolstov d0a978bd50 redesign logger (#1272)
* redesign logger

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-29 03:31:59 +03:00
ben-toogood afe6861e2f Update the k8s deployment to use metadata labels & custom source (#1271)
Run tests / Test repo (push) Waiting to run
2020-02-28 15:07:55 +00:00
ben-toogood 962567ef42 Implement config singleton (#1268)
* Implement config singleton

* Pass token in grpc request headers

* Refactor BearerScheme

* Fix typo
2020-02-28 12:58:27 +00:00
Asim Aslam e21ed3a183 gen account on base32 decode failure (#1269) 2020-02-27 16:11:05 +00:00
Vasiliy Tolstov 64a5ce9607 various fixes (#1267)
* logger: remove Panic log level

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

* server/grpc: add missing Unlock in Subscribe error

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

* server: minor code change

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

* server/grpc: extend test suite with pub/sub testing

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

* server/grpc: fix invalid check and allow subscriber error to be returned

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

* server/grpc: add pubsub tests

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

* client/grpc: check for nil req/rsp

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-26 18:34:40 +00:00
Asim Aslam d651b16acd generate pseudo accounts (#1264)
* generate pseudo accounts

* when you think you're being clever

* return garbage pseudo account when no token
2020-02-26 13:42:32 +00:00
Eric 1034837f69 Adjusting the BeforeStart () position (#1263)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-02-26 11:44:10 +03:00
Lars Lehtonen 80f2bfd5d0 config: remove unused sep variable (#1262) 2020-02-26 08:25:33 +00:00
Asim Aslam 6aaaf54275 add MICRO_AUTH_TOKEN, parse token in wrapper, preload config and othe… (#1261)
* add MICRO_AUTH_TOKEN, parse token in wrapper, preload config and other things

* fix wrapper panic
2020-02-25 22:15:44 +00:00
Di Wu 603d37b135 Set option and cli args to the service profile (#1259) 2020-02-25 16:42:42 +00:00
Eric 53c3bff819 add Panic & Panicf to logger (#1258)
* add Panic & Panicf to logger
2020-02-25 17:44:29 +03:00
ben-toogood dcf859098b Fix k8s commands for github (#1257) 2020-02-25 11:39:03 +00:00
Vasiliy Tolstov b4a743898e fix router panic (#1254)
Run tests / Test repo (push) Waiting to run
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-24 23:15:59 +00:00
ben-toogood f1e7ea3020 Handle non IsNotExist errors in config (#1251)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-02-24 18:07:11 +00:00
ben-toogood 5e8d5834eb Dynamic Runtime source for k8s with github packages (#1252)
* Dynamic Runtime source for k8s

* Still check for source

* Replace / with - for k8s service names

* Simplify sourceForName function
2020-02-24 17:47:47 +00:00
ben-toogood ffdf986aac Refactor auth: add token and store implementations (#1230)
* Refactor auth: add token and memory implementations

* Fix typo

* Remove memory auth (implemented already by the store implementation), revert default to noop

* Add grpc header

* Global Config

* config/global => util/config

* Rename package to remove confict

* Tweak

* Improve Error Handling
2020-02-24 15:07:27 +00:00
Jake Sanders 56f8115ea8 Rename PR job (#1250) 2020-02-24 14:16:51 +00:00
Vasiliy Tolstov 5b0175c2e5 allocations improvements and tunnel fixes (#1248)
* reduce allocations in tunnel code

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

* another allocation fix

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

* allocate maps with len if it known

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

* allocate key for send once

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-24 14:15:20 +00:00
Jake Sanders 01d88601c0 Split PR and merge tests (#1249) 2020-02-24 14:11:17 +00:00
Lars Lehtonen d467236f8f broker/nats: remove unused setPublishOption() (#1234)
broker/nats: remove unused setSubscribeOption()
2020-02-24 13:49:27 +00:00
Vasiliy Tolstov 24d574ae71 server/grpc: add MaxConn option to limit max inflight requests (#1247)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-24 13:48:56 +00:00
Vasiliy Tolstov cf0b39eaac logger fixes (#1244)
* logger: fix race conditions

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

* restore util/log for compatibility

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-24 13:07:40 +00:00
Vasiliy Tolstov 1f767ba18c update go modules (#1240)
Run tests / Test repo (push) Waiting to run
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-23 20:47:44 +00:00
Asim Aslam 915c424213 Add docker build (#1239) 2020-02-23 15:57:21 +00:00
Vasiliy Tolstov 117f56ebf7 prune util/log and user logger (#1237)
* prune util/log and user logger

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

* plaintext logger

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

* add newline

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-23 13:45:20 +00:00
Gao.QiLin ceed8942fc Update README.md change godoc => go.dev (#1236)
* Update README.md add go dev

* Update README.zh-cn.md add go dev
2020-02-22 08:56:42 +00:00
Vasiliy Tolstov d1e25e7ead add metadata set method (#1232)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-21 23:04:47 +03:00
Jake Sanders 7e24c0c1cf Also run tests on PR (#1233) 2020-02-21 17:57:07 +00:00
Jake Sanders ca251ba111 Switch from Travis CI to GitHub Actions (#1231)
* Create Github Action to run tests on push

* -v

* Update tests.yml

* TODO: Fix tests

* Fix colours

* Delete .travis.yml

* Update tests.yml

* builds -> build
2020-02-21 17:43:50 +00:00
Asim Aslam 116855572b Add log level helper funtions (#1229) 2020-02-21 08:43:23 +00:00
Asim Aslam ee977acfef strip SetGlobalLevel (#1228) 2020-02-21 08:28:21 +00:00
Sumanth Chinthagunta 3fa7c26946 logger with helper methods (#1216)
* support unix daemon socket

* refactor(logger): logger fields changed to map[string]interface{}

* improvement(logger): adding string to Level Parser

* improvement(logger): rename ParseLevel to GetLevel

* refactor(logger): adding basic logger

adding micro default logger, and refactor logger interface

* refactor(logger): moved basic logger to top level package

* refactor(logger): adding default logger
2020-02-21 07:57:59 +00:00
Lars Lehtonen 88457b812e tunnel: Prune Unused Functions (#1224)
* tunnel: remove unused link.setLoopback()

* tunnel: remove unused link.accept()

* tunnel: remove unused link.connect()
2020-02-20 17:05:49 +00:00
Asim Aslam 78df154a4d move log level setting to option (#1222) 2020-02-20 08:26:12 +00:00
Lars Lehtonen c7eed618c2 server/grpc: Prune Unused Code (#1220)
* server/grpc: remove unused grpcServer.newCodec()

* server/grpc: remove unused defaultRPCCodecs
2020-02-19 20:58:22 +00:00
ben-toogood 36bcd3bd82 Improve JWT Package Errors (#1206)
Co-authored-by: Asim Aslam <asim@aslam.me>
2020-02-19 09:51:43 +01:00
Asim Aslam f4118dc357 secure the grpc client (#1215)
* secure the grpc client

* make compile

* Add system cert pool

* Revert manually adding ca certs

* Tweak comment

Co-authored-by: ben-toogood <bentoogood@gmail.com>
2020-02-19 08:44:35 +00:00
Vasiliy Tolstov 58598d0fe0 fixes for safe conversation and avoid panics (#1213)
* fixes for safe convertation

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

* fix client publish panic

If broker connect returns error we dont check it status and use
it later to publish message, mostly this is unexpected because
broker connection failed and we cant use it.
Also proposed solution have benefit - we flag connection status
only when we have succeseful broker connection

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

* api/handler/broker: fix possible broker publish panic

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-18 23:05:38 +00:00
Vasiliy Tolstov 6248f05f74 add missing option to client.NewMessage (#1212)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-18 14:18:59 +03:00
ben-toogood aa9a0a8d23 Fix Micro Proxy nil Transport Bug (#1208) 2020-02-17 12:28:48 +03:00
ben-toogood 1e40c86dfe Ignore gRPC Proxy (#1205) 2020-02-17 08:14:45 +00:00
Asim Aslam 9696efde02 reorder auth interface (#1204) 2020-02-16 19:36:45 +00:00
Asim Aslam b3fc8be24e normalise proxy name (#1203) 2020-02-15 21:57:30 +00:00
Sumanth Chinthagunta fc5339a368 [W.I.P] refactor(logger): logger fields changed to map[string]interface{} (#1198)
* support unix daemon socket

* refactor(logger): logger fields changed to map[string]interface{}

* improvement(logger): adding string to Level Parser

* improvement(logger): rename ParseLevel to GetLevel
2020-02-15 18:19:28 +00:00
Asim Aslam 964b7dee3f add tls config to server (#1202)
* add tls config

* add TLSConfig to acme provider
2020-02-15 15:10:26 +00:00
Asim Aslam 158949d0d0 accept Listen option in grpc server (#1201) 2020-02-15 14:09:24 +00:00
Asim Aslam eed8a0bf50 delete proxy cached route before updating (#1200) 2020-02-15 12:05:22 +00:00
Asim Aslam c691d116ab when the stream errors cleanup the connection (#1199) 2020-02-15 11:35:08 +00:00
Eric cbe8b7dd09 Removed redundant spaces (#1196) 2020-02-14 10:32:02 +03:00
Asim Aslam 203486fd31 check for etcd watcher canceled value 2020-02-13 22:34:56 +00:00
Asim Aslam d9b3b17582 set dial timeout in stream 2020-02-13 18:51:32 +00:00
ben-toogood e080ecb43a Auth Improvements (#1195)
* Exclude Stats & Trace from Auth

* Update Excluded Endpoints Format

* Tweak Implementation
2020-02-13 14:07:14 +00:00
ben-toogood ea70711dd3 Exclude Stats & Trace from Auth (#1192) 2020-02-13 12:02:29 +00:00
Vasiliy Tolstov 6dc942bc19 client/grpc: fix panic on invalid message (#1191)
* client/grpc: fix panic on invalid message
* travis: disable lint and race for now

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-13 14:57:21 +03:00
Janos Dobronszki d76baf59de Trace type is now being recorded (#1188) 2020-02-12 10:57:17 +00:00
Vasiliy Tolstov 79ad1e6fe3 various fixes for broker and messaging in server (#1187)
* provide broker disconnect messages in server

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

* broker/eats: another fix

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-11 18:41:23 +00:00
Vasiliy Tolstov 2764de9a1a broker/eats: broker disconnect fix (#1186)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-11 15:46:50 +00:00
Asim Aslam d1d6eada98 parse url encoded form in rpc handler (#1183)
* parse url encoded form in rpc handler

* Remove comment
2020-02-11 11:27:16 +00:00
ben-toogood 4a03183481 Return a 401 error on invalid auth tokens (#1184) 2020-02-11 11:22:22 +00:00
Asim Aslam 8ea84ac3eb Fix router panic for nil watcher 2020-02-10 15:38:41 +00:00
ben-toogood 4401c12e6c Auth Wrapper (#1174)
* Auth Wrapper

* Tweak cmd flag

* auth_excludes => auth_exclude

* Make Auth.Excludes variadic

* Use metadata.Get (passes through http and http2 it will go through various case formats)

* fix auth wrapper auth.Auth interface initialisation

Co-authored-by: Asim Aslam <asim@aslam.me>
2020-02-10 08:26:28 +00:00
Vasiliy Tolstov c706afcf04 logger helper to pass down it via context (#1180)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-09 21:26:46 +00:00
Lars Lehtonen ca1d0b94c3 config/cmd: remove 8 unused variables (#1175) 2020-02-08 11:19:10 +00:00
Vasiliy Tolstov 67acd9288b config/source/cli: fix tests (#1179)
* config/source/cli: fix tests
* skip mdns test in travis

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-08 02:45:32 +03:00
Vasiliy Tolstov 0bf6c9fc08 config/source/cli: fix default flag value loading (#1178)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-08 02:14:34 +03:00
Vasiliy Tolstov 99807a680c strip Micro-Topic header from incoming context in client.Call (#1177)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-07 22:09:52 +00:00
Vasiliy Tolstov f0f7f860d6 add some docs (#1176)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-07 21:48:24 +00:00
Asim Aslam ef537270ad Don't store traces for Debug endpoints 2020-02-07 20:58:03 +00:00
Asim Aslam c7f075d157 rename file 2020-02-07 14:00:27 +00:00
Asim Aslam 812ea78604 Fix runtime deadlock 2020-02-07 14:00:09 +00:00
Asim Aslam 0755084a59 fix deadlock 2020-02-07 13:55:55 +00:00
ben-toogood fe7f5a4134 Runtime Retries Limit (#1163)
* Implement Runtime Retries

* Remove Debug

* Action Feedback

* Refactor Retries

* Fix WithRetires Typo
2020-02-07 12:02:41 +00:00
ben-toogood 19c454ec4b Fix Local Runtime Default Command (#1173)
* Auth API Proto

* Fix local runtime bug

* Add Platform Proto

* Restructuring
2020-02-07 11:39:26 +00:00
Asim Aslam 0e9b4c26a4 import with braces 2020-02-06 21:39:08 +00:00
Asim Aslam d40b13a045 mux to mtx 2020-02-06 21:37:17 +00:00
Asim Aslam 4079b22c1e reorder logger methods 2020-02-06 21:36:33 +00:00
Shu xian fdfb2bc4c1 [WIP] logger first (#1161)
* logger first

* log->logger

* update comment

* add context in Options

* add Fields

* remove logfi

* add field encode

* add common Field Types

* update logger field
2020-02-06 21:35:46 +00:00
Asim Aslam dbeb7cfe9c remove errors import 2020-02-06 18:45:12 +00:00
Asim Aslam 512df2628f trim source url if its set to github.com/ 2020-02-06 18:34:16 +00:00
Janos Dobronszki 92571db693 Tracing: traces now correctly form a tree (#1170)
* First cut of trace

* Dial it back yo

* Defensive programming
2020-02-06 17:22:16 +00:00
ben-toogood 16552620e0 Runtime Custom Source (Part 2) (#1169) 2020-02-06 16:16:01 +00:00
Ben Toogood 5414195dc3 Add Args 2020-02-06 12:31:54 +00:00
Ben Toogood 0591760932 Arg => Args 2020-02-06 12:17:16 +00:00
Ben Toogood 48b9f3f5e9 Fix 2020-02-06 11:28:34 +00:00
Ben Toogood e46278a766 Test 2020-02-06 11:24:56 +00:00
Ben Toogood 2925b1615c Fix 2020-02-06 11:15:30 +00:00
Ben Toogood fc4191c647 Fix 2020-02-06 11:12:40 +00:00
Ben Toogood 111126c780 Debugging 2020-02-06 11:00:14 +00:00
Ben Toogood 54371bba6a Debugging 2020-02-06 10:55:41 +00:00
Ben Toogood c28737e88e Debugging 2020-02-06 10:54:11 +00:00
Ben Toogood c7d922fac2 Debugging 2020-02-06 10:49:01 +00:00
Ben Toogood f8e696bd30 Debugging 2020-02-06 10:44:12 +00:00
Ben Toogood 6aef28dad2 Runtime set service source 2020-02-06 10:33:23 +00:00
Vasiliy Tolstov 7105e4099c pass micro errors from grpc server to grpc client (#1167)
* pass micro errors from grpc server to grpc client

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

* wrap micro errors.Error to grpc status

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-06 10:18:33 +00:00
Ben Toogood f0762bbb6b Improve Logging 2020-02-06 10:16:32 +00:00
Ben Toogood 243c6a4246 Debug 2020-02-06 10:08:56 +00:00
Ben Toogood 9983aea928 Tidying Up 2020-02-06 09:29:27 +00:00
Ben Toogood aa58a9749b Action Asim's Feedback 2020-02-06 09:17:10 +00:00
ben-toogood d8110b70a3 Runtime custom docker img (#1168)
* Add DeploymentOptions to K8s Client

* WithBaseImage for  Runtime

* Revert Change

* Fix sequencing
2020-02-06 08:52:25 +00:00
Vasiliy Tolstov a44dc90d45 fix ctx.Done issue #720 (#1166)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-05 21:13:14 +00:00
Shu xian 12181bd441 fix LevelInfo > LevelWarn (#1162) 2020-02-05 18:16:57 +00:00
Ben Toogood 8d44f7226f Merge branch 'master' of https://github.com/micro/go-micro 2020-02-05 13:59:51 +00:00
ben-toogood bf747a86f4 Runtime (#1160)
* Add String to Runtime interface

* Setup Dynamic Runtime Configuration
2020-02-05 13:59:35 +00:00
Lars Lehtonen 4333f00a43 runtime/kubernetes: remove unused constants (#1159) 2020-02-04 21:02:05 +00:00
Vasiliy Tolstov 7ab3a31ac4 update micro/cli, tidy mod (#1156)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-03 23:29:44 +00:00
Ben Toogood a1d5d6831f Add String to Runtime interface 2020-02-03 15:56:16 +00:00
ben-toogood 1b9cabd654 Update Micro Auth Protocol Buffer to use V2 (#1155) 2020-02-03 08:26:57 +00:00
ben-toogood d621548120 Auth (#1147)
Implement the Auth interface, with JWT and service implementations.

* Update Auth Interface

* Define Auth Service Implementation

* Support Service Auth

* Add Auth Service Proto

* Remove erronious files

* Implement Auth Service Package

* Update Auth Interface

* Update Auth Interface. Add Validate, remove Add/Remove roles

* Make Revoke interface more explicit

* Refactor serializing and deserializing service accounts

* Fix srv name & update interface to be more explicit

* Require jwt public key for auth

* Rename Variables (Resource.ID => Resource.Name & ServiceAccount => Account)

* Implement JWT Auth Package

* Remove parent, add ID

* Update auth imports to v2. Add String() to auth interface
2020-02-03 08:16:02 +00:00
tpam28 449bcb46fe New backoff (#1153)
* new backoff function

* use backoff from util/backoff

* remove reset atemts

* change comment

* fmt
2020-02-02 20:32:55 +00:00
Asim Aslam 079102ea59 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-02-02 19:50:09 +00:00
Asim Aslam a9d371e727 fatal on command error 2020-02-02 19:49:59 +00:00
Asim Aslam 27efbc8779 Merge pull request #1150 from unistack-org/grpc_race
fix map race condition in grpc server
2020-02-01 00:10:46 +00:00
Vasiliy Tolstov efb59d9709 fix map race condition in grpc server
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2020-02-01 02:52:53 +03:00
Asim Aslam 003f00b483 Merge branch 'master' of ssh://github.com/micro/go-micro 2020-01-30 23:24:54 +00:00
Asim Aslam 0be22c98c6 add tracing 2020-01-30 23:24:46 +00:00
Asim Aslam bafedf1aad Merge pull request #1149 from alrs/remove-unused-kubernetes-variable
runtime/kubernetes: remove unused name variable
2020-01-30 17:30:43 +00:00
Lars Lehtonen 98d55545fd runtime/kubernetes: remove unused name variable 2020-01-30 09:25:50 -08:00
Asim Aslam 5f6271b044 Merge pull request #1148 from tpam28/master
fix test and description
2020-01-30 17:09:41 +00:00
Evgeniy 87753ad289 format results in TestBacloff 2020-01-30 20:08:03 +03:00
Evgeniy ffb9da0230 fix test and description 2020-01-30 19:43:03 +03:00
Asim Aslam 50ac642666 Merge pull request #1146 from tpam28/master
exponentialBackoff was changed from power function to exponential
2020-01-30 14:35:17 +00:00
Evgeniy f6fcfcb8fc exponentialBackoff was changed from power function to exponential function 2020-01-30 17:25:07 +03:00
1360 changed files with 167895 additions and 31777 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
# These are supported funding model platforms
issuehunt: micro/development
github: asim
+10
View File
@@ -0,0 +1,10 @@
## Pull Request template
Please, go through these steps before clicking submit on this PR.
1. Make sure this PR targets the `develop` branch. We follow the git-flow branching model.
2. Give a descriptive title to your PR.
3. Provide a description of your changes.
4. Make sure you have some relevant tests.
5. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if applicable).
**PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING**
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash -e
find . -type f -name '*.pb.*.go' -o -name '*.pb.go' -a ! -name 'message.pb.go' -delete
PROTOS=$(find . -type f -name '*.proto' | grep -v proto/google/api)
mkdir -p proto/google/api
curl -s -o proto/google/api/annotations.proto -L https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto
curl -s -o proto/google/api/http.proto -L https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto
for PROTO in $PROTOS; do
echo $PROTO
protoc -I./proto -I. -I$(dirname $PROTO) --go_out=plugins=grpc,paths=source_relative:. --micro_out=paths=source_relative:. $PROTO
done
rm -r proto
+28
View File
@@ -0,0 +1,28 @@
name: PR Sanity Check
on: pull_request
jobs:
prtest:
name: PR sanity check
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.15
uses: actions/setup-go@v1
with:
go-version: 1.15
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
go get -v -t -d ./...
- name: Run tests
id: tests
env:
IN_TRAVIS_CI: yes
run: go test -v ./...
+29
View File
@@ -0,0 +1,29 @@
name: Run tests
on: [push]
jobs:
test:
name: Test repo
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.15
uses: actions/setup-go@v1
with:
go-version: 1.15
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
go get -v -t -d ./...
- name: Run tests
id: tests
env:
IN_TRAVIS_CI: yes
run: go test -v ./...
-17
View File
@@ -1,17 +0,0 @@
language: go
go:
- 1.13.x
env:
- GO111MODULE=on IN_TRAVIS_CI=yes
before_script:
- go install github.com/golangci/golangci-lint/cmd/golangci-lint
script:
- golangci-lint run || true
- go test -v -race ./... || true
- go test -v ./...
notifications:
slack:
secure: aEvhLbhujaGaKSrOokiG3//PaVHTIrc3fBpoRbCRqfZpyq6WREoapJJhF+tIpWWOwaC9GmChbD6aHo/jMUgwKXVyPSaNjiEL87YzUUpL8B2zslNp1rgfTg/LrzthOx3Q1TYwpaAl3to0fuHUVFX4yMeC2vuThq7WSXgMMxFCtbc=
cache:
directories:
- $GOPATH/pkg/mod
-1
View File
@@ -1 +0,0 @@
go-micro.dev
-13
View File
@@ -1,13 +0,0 @@
FROM golang:1.13-alpine
RUN mkdir /user && \
echo 'nobody:x:65534:65534:nobody:/:' > /user/passwd && \
echo 'nobody:x:65534:' > /user/group
ENV GO111MODULE=on
RUN apk --no-cache add make git gcc libtool musl-dev ca-certificates && \
rm -rf /var/cache/apk/* /tmp/*
WORKDIR /
COPY ./go.mod ./go.sum ./
RUN go mod download && rm go.mod go.sum
+61 -17
View File
@@ -1,23 +1,27 @@
# Go Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![GoDoc](https://godoc.org/github.com/micro/go-micro?status.svg)](https://godoc.org/github.com/micro/go-micro) [![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 [![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/asim/go-micro/v3?tab=doc)
Go Micro is a framework for microservice development.
Go Micro is a framework for distributed systems development.
## Overview
Go Micro provides the core requirements for distributed systems development including RPC and Event driven communication.
The **micro** philosophy is sane defaults with a pluggable architecture. We provide defaults to get you started quickly
The **Micro** philosophy is sane defaults with a pluggable architecture. We provide defaults to get you started quickly
but everything can be easily swapped out.
<img src="https://micro.mu/docs/images/go-micro.svg" />
Plugins are available at [github.com/micro/go-plugins](https://github.com/micro/go-plugins).
Follow us on [Twitter](https://twitter.com/microhq) or join the [Community](https://micro.mu/slack).
## Features
Go Micro abstracts away the details of distributed systems. Here are the main features.
- **Authentication** - Auth is built in as a first class citizen. Authentication and authorization enable secure
zero trust networking by providing every service an identity and certificates. This additionally includes rule
based access control.
- **Dynamic Config** - Load and hot reload dynamic config from anywhere. The config interface provides a way to load application
level config from any source such as env vars, file, etcd. You can merge the sources and even define fallbacks.
- **Data Storage** - A simple data store interface to read, write and delete records. It includes support for memory, file and
CockroachDB by default. State and persistence becomes a core requirement beyond prototyping and Micro looks to build that into the framework.
- **Service Discovery** - Automatic service registration and name resolution. Service discovery is at the core of micro service
development. When service A needs to speak to service B it needs the location of that service. The default discovery mechanism is
multicast DNS (mdns), a zeroconf system.
@@ -30,19 +34,59 @@ across the services and retry a different node if there's a problem.
to seamlessly encode and decode Go types for you. Any variety of messages could be encoded and sent from different clients. The client
and server handle this by default. This includes protobuf and json by default.
- **Request/Response** - RPC based request/response with support for bidirectional streaming. We provide an abstraction for synchronous
communication. A request made to a service will be automatically resolved, load balanced, dialled and streamed. The default
transport is [gRPC](https://grpc.io/).
- **RPC Client/Server** - RPC based request/response with support for bidirectional streaming. We provide an abstraction for synchronous
communication. A request made to a service will be automatically resolved, load balanced, dialled and streamed.
- **Async Messaging** - PubSub is built in as a first class citizen for asynchronous communication and event driven architectures.
Event notifications are a core pattern in micro service development. The default messaging system is an embedded [NATS](https://nats.io/)
server.
Event notifications are a core pattern in micro service development. The default messaging system is a HTTP event message broker.
- **Synchronization** - Distributed systems are often built in an eventually consistent manner. Support for distributed locking and
leadership are built in as a Sync interface. When using an eventually consistent database or scheduling use the Sync interface.
- **Pluggable Interfaces** - Go Micro makes use of Go interfaces for each distributed system abstraction. Because of this these interfaces
are pluggable and allows Go Micro to be runtime agnostic. You can plugin any underlying technology. Find plugins in
[github.com/micro/go-plugins](https://github.com/micro/go-plugins).
are pluggable and allows Go Micro to be runtime agnostic. You can plugin any underlying technology.
## Getting Started
See the [docs](https://micro.mu/docs/framework.html) for detailed information on the architecture, installation and use of go-micro.
To make use of Go Micro
```golang
import "github.com/asim/go-micro/v3"
// create a new service
service := micro.NewService(
micro.Name("helloworld"),
)
// initialise flags
service.Init()
// start the service
service.Run()
```
See the [examples](https://github.com/micro/go-micro/tree/master/examples) for detailed information on usage.
## Command Line Interface
See [cmd/gomu](https://github.com/asim/go-micro/tree/master/cmd/gomu) for the command line interface.
## Code Generation
See [cmd/protoc-gen-micro](https://github.com/micro/go-micro/tree/master/cmd/protoc-gen-micro) for protobuf code generation.
## Example Usage
See [examples](https://github.com/micro/go-micro/tree/master/examples) directory for usage examples.
## Plugins
See [plugins](https://github.com/micro/go-micro/tree/master/plugins) directory for all the plugins.
## Services
See [services](https://github.com/micro/go-micro/tree/master/services) directory for third party services.
## License
Go Micro is Apache 2.0 licensed.
-36
View File
@@ -1,36 +0,0 @@
# Go Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![GoDoc](https://godoc.org/github.com/micro/go-micro?status.svg)](https://godoc.org/github.com/micro/go-micro) [![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是基于Golang的微服务开发框架。
## 概览
Go Micro提供分布式系统开发的核心库,包含RPC与事件驱动的通信机制。
**micro**的设计哲学是可插拔的架构理念,她提供可快速构建系统的组件,并且可以根据自身的需求剥离默认实现并自行定制。
<img src="https://micro.mu/docs/images/go-micro.svg" />
所有插件可在仓库[github.com/micro/go-plugins](https://github.com/micro/go-plugins)中找到。
可以订阅我们的[Twitter](https://twitter.com/microhq)或者加入[Slack](http://slack.micro.mu/)论坛。
## 特性
Go Micro把分布式系统的各种细节抽象出来。下面是它的主要特性。
- **服务发现(Service Discovery** - 自动服务注册与名称解析。服务发现是微服务开发中的核心。当服务A要与服务B协作时,它得知道B在哪里。默认的服务发现系统是Consul,而multicast DNS (mdns,组播)机制作为本地解决方案,或者零依赖的P2P网络中的SWIM协议(gossip)。
- **负载均衡(Load Balancing)** - 在服务发现之上构建了负载均衡机制。当我们得到一个服务的任意多个的实例节点时,我们要一个机制去决定要路由到哪一个节点。我们使用随机处理过的哈希负载均衡机制来保证对服务请求颁发的均匀分布,并且在发生问题时进行重试。
- **消息编码(Message Encoding** - 支持基于内容类型(content-type)动态编码消息。客户端和服务端会一起使用content-type的格式来对Go进行无缝编/解码。各种各样的消息被编码会发送到不同的客户端,客户端服服务端默认会处理这些消息。content-type默认包含proto-rpc和json-rpc。
- **Request/Response** - RPC通信基于支持双向流的请求/响应方式,我们提供有抽象的同步通信机制。请求发送到服务时,会自动解析、负载均衡、拨号、转成字节流。默认的传输协议是http/1.1,而tls下使用http2协议。
- **异步消息(Async Messaging** - 发布订阅(PubSub)头等功能内置在异步通信与事件驱动架构中。事件通知在微服务开发中处于核心位置。默认的消息传送使用点到点http/1.1,激活tls时则使用http2。
- **可插拔接口(Pluggable Interfaces** - Go Micro为每个分布式系统抽象出接口。因此,Go Micro的接口都是可插拔的,允许其在运行时不可知的情况下仍可支持。所以只要实现接口,可以在内部使用任何的技术。更多插件请参考:[github.com/micro/go-plugins](https://github.com/micro/go-plugins)。
## 快速上手
更多关于架构、安装的资料可以查看[文档](https://micro.mu/docs/cn/)。
-2
View File
@@ -1,2 +0,0 @@
// Package agent provides an interface for building robots
package agent
-54
View File
@@ -1,54 +0,0 @@
// Package command is an interface for defining bot commands
package command
var (
// Commmands keyed by golang/regexp patterns
// regexp.Match(key, input) is used to match
Commands = map[string]Command{}
)
// Command is the interface for specific named
// commands executed via plugins or the bot.
type Command interface {
// Executes the command with args passed in
Exec(args ...string) ([]byte, error)
// Usage of the command
Usage() string
// Description of the command
Description() string
// Name of the command
String() string
}
type cmd struct {
name string
usage string
description string
exec func(args ...string) ([]byte, error)
}
func (c *cmd) Description() string {
return c.description
}
func (c *cmd) Exec(args ...string) ([]byte, error) {
return c.exec(args...)
}
func (c *cmd) Usage() string {
return c.usage
}
func (c *cmd) String() string {
return c.name
}
// NewCommand helps quickly create a new command
func NewCommand(name, usage, description string, exec func(args ...string) ([]byte, error)) Command {
return &cmd{
name: name,
usage: usage,
description: description,
exec: exec,
}
}
-65
View File
@@ -1,65 +0,0 @@
package command
import (
"testing"
)
func TestCommand(t *testing.T) {
c := &cmd{
name: "test",
usage: "test usage",
description: "test description",
exec: func(args ...string) ([]byte, error) {
return []byte("test"), nil
},
}
if c.String() != c.name {
t.Fatalf("expected name %s got %s", c.name, c.String())
}
if c.Usage() != c.usage {
t.Fatalf("expected usage %s got %s", c.usage, c.Usage())
}
if c.Description() != c.description {
t.Fatalf("expected description %s got %s", c.description, c.Description())
}
if r, err := c.Exec(); err != nil {
t.Fatal(err)
} else if string(r) != "test" {
t.Fatalf("expected exec result test got %s", string(r))
}
}
func TestNewCommand(t *testing.T) {
c := &cmd{
name: "test",
usage: "test usage",
description: "test description",
exec: func(args ...string) ([]byte, error) {
return []byte("test"), nil
},
}
nc := NewCommand(c.name, c.usage, c.description, c.exec)
if nc.String() != c.name {
t.Fatalf("expected name %s got %s", c.name, nc.String())
}
if nc.Usage() != c.usage {
t.Fatalf("expected usage %s got %s", c.usage, nc.Usage())
}
if nc.Description() != c.description {
t.Fatalf("expected description %s got %s", c.description, nc.Description())
}
if r, err := nc.Exec(); err != nil {
t.Fatal(err)
} else if string(r) != "test" {
t.Fatalf("expected exec result test got %s", string(r))
}
}
-22
View File
@@ -1,22 +0,0 @@
# Discord input for micro-bot
[Discord](https://discordapp.com) support for micro bot based on [discordgo](github.com/bwmarrin/discordgo).
This was originally written by Aleksandr Tihomirov (@zet4) and can be found at https://github.com/zet4/micro-misc/.
## Options
### discord_token
You have to supply an application token via `--discord_token`.
Head over to Discord's [developer introduction](https://discordapp.com/developers/docs/intro)
to learn how to create applications and how the API works.
### discord_prefix
Set a command prefix with `--discord_prefix`. The default prefix is `Micro `.
You can mention the bot or use the prefix to run a command.
### discord_whitelist
Pass a list of comma-separated user IDs with `--discord_whitelist`. Only allow
these users to use the bot.
-94
View File
@@ -1,94 +0,0 @@
package discord
import (
"errors"
"strings"
"sync"
"github.com/bwmarrin/discordgo"
"github.com/micro/go-micro/v2/agent/input"
"github.com/micro/go-micro/v2/util/log"
)
type discordConn struct {
master *discordInput
exit chan struct{}
recv chan *discordgo.Message
sync.Mutex
}
func newConn(master *discordInput) *discordConn {
conn := &discordConn{
master: master,
exit: make(chan struct{}),
recv: make(chan *discordgo.Message),
}
conn.master.session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.ID == master.botID {
return
}
whitelisted := false
for _, ID := range conn.master.whitelist {
if m.Author.ID == ID {
whitelisted = true
break
}
}
if len(master.whitelist) > 0 && !whitelisted {
return
}
var valid bool
m.Message.Content, valid = conn.master.prefixfn(m.Message.Content)
if !valid {
return
}
conn.recv <- m.Message
})
return conn
}
func (dc *discordConn) Recv(event *input.Event) error {
for {
select {
case <-dc.exit:
return errors.New("connection closed")
case msg := <-dc.recv:
event.From = msg.ChannelID + ":" + msg.Author.ID
event.To = dc.master.botID
event.Type = input.TextEvent
event.Data = []byte(msg.Content)
return nil
}
}
}
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 {
log.Log("[bot][loop][send]", err)
}
return nil
}
func (dc *discordConn) Close() error {
if err := dc.master.session.Close(); err != nil {
return err
}
select {
case <-dc.exit:
return nil
default:
close(dc.exit)
}
return nil
}
-153
View File
@@ -1,153 +0,0 @@
package discord
import (
"fmt"
"sync"
"errors"
"strings"
"github.com/bwmarrin/discordgo"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/agent/input"
)
func init() {
input.Inputs["discord"] = newInput()
}
func newInput() *discordInput {
return &discordInput{}
}
type discordInput struct {
token string
whitelist []string
prefix string
prefixfn func(string) (string, bool)
botID string
session *discordgo.Session
sync.Mutex
running bool
exit chan struct{}
}
func (d *discordInput) Flags() []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "discord_token",
EnvVars: []string{"MICRO_DISCORD_TOKEN"},
Usage: "Discord token (prefix with Bot if it's for bot account)",
},
&cli.StringFlag{
Name: "discord_whitelist",
EnvVars: []string{"MICRO_DISCORD_WHITELIST"},
Usage: "Discord Whitelist (seperated by ,)",
},
&cli.StringFlag{
Name: "discord_prefix",
Usage: "Discord Prefix",
EnvVars: []string{"MICRO_DISCORD_PREFIX"},
Value: "Micro ",
},
}
}
func (d *discordInput) Init(ctx *cli.Context) error {
token := ctx.String("discord_token")
whitelist := ctx.String("discord_whitelist")
prefix := ctx.String("discord_prefix")
if len(token) == 0 {
return errors.New("require token")
}
d.token = token
d.prefix = prefix
if len(whitelist) > 0 {
d.whitelist = strings.Split(whitelist, ",")
}
return nil
}
func (d *discordInput) Start() error {
if len(d.token) == 0 {
return errors.New("missing discord configuration")
}
d.Lock()
defer d.Unlock()
if d.running {
return nil
}
var err error
d.session, err = discordgo.New(d.token)
if err != nil {
return err
}
u, err := d.session.User("@me")
if err != nil {
return err
}
d.botID = u.ID
d.prefixfn = CheckPrefixFactory(fmt.Sprintf("<@%s> ", d.botID), fmt.Sprintf("<@!%s> ", d.botID), d.prefix)
d.exit = make(chan struct{})
d.running = true
return nil
}
func (d *discordInput) Stream() (input.Conn, error) {
d.Lock()
defer d.Unlock()
if !d.running {
return nil, errors.New("not running")
}
//Fire-n-forget close just in case...
d.session.Close()
conn := newConn(d)
if err := d.session.Open(); err != nil {
return nil, err
}
return conn, nil
}
func (d *discordInput) Stop() error {
d.Lock()
defer d.Unlock()
if !d.running {
return nil
}
close(d.exit)
d.running = false
return nil
}
func (d *discordInput) String() string {
return "discord"
}
// CheckPrefixFactory Creates a prefix checking function and stuff.
func CheckPrefixFactory(prefixes ...string) func(string) (string, bool) {
return func(content string) (string, bool) {
for _, prefix := range prefixes {
if strings.HasPrefix(content, prefix) {
return strings.TrimPrefix(content, prefix), true
}
}
return "", false
}
}
-55
View File
@@ -1,55 +0,0 @@
// Package input is an interface for bot inputs
package input
import (
"github.com/micro/cli/v2"
)
type EventType string
const (
TextEvent EventType = "text"
)
var (
// Inputs keyed by name
// Example slack or hipchat
Inputs = map[string]Input{}
)
// Event is the unit sent and received
type Event struct {
Type EventType
From string
To string
Data []byte
Meta map[string]interface{}
}
// Input is an interface for sources which
// provide a way to communicate with the bot.
// Slack, HipChat, XMPP, etc.
type Input interface {
// Provide cli flags
Flags() []cli.Flag
// Initialise input using cli context
Init(*cli.Context) error
// Stream events from the input
Stream() (Conn, error)
// Start the input
Start() error
// Stop the input
Stop() error
// name of the input
String() string
}
// Conn interface provides a way to
// send and receive events. Send and
// Recv both block until succeeding
// or failing.
type Conn interface {
Close() error
Recv(*Event) error
Send(*Event) error
}
-160
View File
@@ -1,160 +0,0 @@
package slack
import (
"errors"
"fmt"
"strings"
"sync"
"time"
"github.com/micro/go-micro/v2/agent/input"
"github.com/nlopes/slack"
)
// Satisfies the input.Conn interface
type slackConn struct {
auth *slack.AuthTestResponse
rtm *slack.RTM
exit chan bool
sync.Mutex
names map[string]string
}
func (s *slackConn) run() {
// func retrieves user names and maps to IDs
setNames := func() {
names := make(map[string]string)
users, err := s.rtm.Client.GetUsers()
if err != nil {
return
}
for _, user := range users {
names[user.ID] = user.Name
}
s.Lock()
s.names = names
s.Unlock()
}
setNames()
t := time.NewTicker(time.Minute)
defer t.Stop()
for {
select {
case <-s.exit:
return
case <-t.C:
setNames()
}
}
}
func (s *slackConn) getName(id string) string {
s.Lock()
name := s.names[id]
s.Unlock()
return name
}
func (s *slackConn) Close() error {
select {
case <-s.exit:
return nil
default:
close(s.exit)
}
return nil
}
func (s *slackConn) Recv(event *input.Event) error {
if event == nil {
return errors.New("event cannot be nil")
}
for {
select {
case <-s.exit:
return errors.New("connection closed")
case e := <-s.rtm.IncomingEvents:
switch ev := e.Data.(type) {
case *slack.MessageEvent:
// only accept type message
if ev.Type != "message" {
continue
}
// only accept DMs or messages to me
switch {
case strings.HasPrefix(ev.Channel, "D"):
case strings.HasPrefix(ev.Text, s.auth.User):
case strings.HasPrefix(ev.Text, fmt.Sprintf("<@%s>", s.auth.UserID)):
default:
continue
}
// Strip username from text
switch {
case strings.HasPrefix(ev.Text, s.auth.User):
args := strings.Split(ev.Text, " ")[1:]
ev.Text = strings.Join(args, " ")
event.To = s.auth.User
case strings.HasPrefix(ev.Text, fmt.Sprintf("<@%s>", s.auth.UserID)):
args := strings.Split(ev.Text, " ")[1:]
ev.Text = strings.Join(args, " ")
event.To = s.auth.UserID
}
if event.Meta == nil {
event.Meta = make(map[string]interface{})
}
// fill in the blanks
event.From = ev.Channel + ":" + ev.User
event.Type = input.TextEvent
event.Data = []byte(ev.Text)
event.Meta["reply"] = ev
return nil
case *slack.InvalidAuthEvent:
return errors.New("invalid credentials")
}
}
}
}
func (s *slackConn) Send(event *input.Event) error {
var channel, message, name string
if len(event.To) == 0 {
return errors.New("require Event.To")
}
parts := strings.Split(event.To, ":")
if len(parts) == 2 {
channel = parts[0]
name = s.getName(parts[1])
// try using reply meta
} else if ev, ok := event.Meta["reply"]; ok {
channel = ev.(*slack.MessageEvent).Channel
name = s.getName(ev.(*slack.MessageEvent).User)
}
// don't know where to send the message
if len(channel) == 0 {
return errors.New("could not determine who message is to")
}
if len(name) == 0 || strings.HasPrefix(channel, "D") {
message = string(event.Data)
} else {
message = fmt.Sprintf("@%s: %s", name, string(event.Data))
}
s.rtm.SendMessage(s.rtm.NewOutgoingMessage(message, channel))
return nil
}
-147
View File
@@ -1,147 +0,0 @@
package slack
import (
"errors"
"sync"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/agent/input"
"github.com/nlopes/slack"
)
type slackInput struct {
debug bool
token string
sync.Mutex
running bool
exit chan bool
api *slack.Client
}
func init() {
input.Inputs["slack"] = NewInput()
}
func (p *slackInput) Flags() []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
Name: "slack_debug",
Usage: "Slack debug output",
EnvVars: []string{"MICRO_SLACK_DEBUG"},
},
&cli.StringFlag{
Name: "slack_token",
Usage: "Slack token",
EnvVars: []string{"MICRO_SLACK_TOKEN"},
},
}
}
func (p *slackInput) Init(ctx *cli.Context) error {
debug := ctx.Bool("slack_debug")
token := ctx.String("slack_token")
if len(token) == 0 {
return errors.New("missing slack token")
}
p.debug = debug
p.token = token
return nil
}
func (p *slackInput) Stream() (input.Conn, error) {
p.Lock()
defer p.Unlock()
if !p.running {
return nil, errors.New("not running")
}
// test auth
auth, err := p.api.AuthTest()
if err != nil {
return nil, err
}
rtm := p.api.NewRTM()
exit := make(chan bool)
go rtm.ManageConnection()
go func() {
select {
case <-p.exit:
select {
case <-exit:
return
default:
close(exit)
}
case <-exit:
}
rtm.Disconnect()
}()
conn := &slackConn{
auth: auth,
rtm: rtm,
exit: exit,
names: make(map[string]string),
}
go conn.run()
return conn, nil
}
func (p *slackInput) Start() error {
if len(p.token) == 0 {
return errors.New("missing slack token")
}
p.Lock()
defer p.Unlock()
if p.running {
return nil
}
api := slack.New(p.token, slack.OptionDebug(p.debug))
// test auth
_, err := api.AuthTest()
if err != nil {
return err
}
p.api = api
p.exit = make(chan bool)
p.running = true
return nil
}
func (p *slackInput) Stop() error {
p.Lock()
defer p.Unlock()
if !p.running {
return nil
}
close(p.exit)
p.running = false
return nil
}
func (p *slackInput) String() string {
return "slack"
}
func NewInput() input.Input {
return &slackInput{}
}
-18
View File
@@ -1,18 +0,0 @@
# Telegram Messenger input for micro bot
[Telegram](https://telegram.org) support for micro bot based on [telegram-bot-api](https://github.com/go-telegram-bot-api/telegram-bot-api).
## Options
### --telegram_token (required)
Sets bot's token for interacting with API.
Head over to Telegram's [API documentation](https://core.telegram.org/bots/api)
to learn how to create bots and how the API works.
### --telegram_debug
Sets the debug flag to make the bot's output verbose.
### --telegram_whitelist
Sets a list of comma-separated nicknames (without @ symbol in the beginning) for interacting with bot. Only these users can use the bot.
-113
View File
@@ -1,113 +0,0 @@
package telegram
import (
"errors"
"strings"
"sync"
"github.com/forestgiant/sliceutil"
"github.com/micro/go-micro/v2/agent/input"
"github.com/micro/go-micro/v2/util/log"
tgbotapi "gopkg.in/telegram-bot-api.v4"
)
type telegramConn struct {
input *telegramInput
recv <-chan tgbotapi.Update
exit chan bool
syncCond *sync.Cond
mutex sync.Mutex
}
func newConn(input *telegramInput) (*telegramConn, error) {
conn := &telegramConn{
input: input,
}
conn.syncCond = sync.NewCond(&conn.mutex)
go conn.run()
return conn, nil
}
func (tc *telegramConn) run() {
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates, err := tc.input.api.GetUpdatesChan(u)
if err != nil {
return
}
tc.recv = updates
tc.syncCond.Signal()
select {
case <-tc.exit:
return
}
}
func (tc *telegramConn) Close() error {
return nil
}
func (tc *telegramConn) Recv(event *input.Event) error {
if event == nil {
return errors.New("event cannot be nil")
}
for {
if tc.recv == nil {
tc.mutex.Lock()
tc.syncCond.Wait()
}
update := <-tc.recv
if update.Message == nil || (len(tc.input.whitelist) > 0 && !sliceutil.Contains(tc.input.whitelist, update.Message.From.UserName)) {
continue
}
if event.Meta == nil {
event.Meta = make(map[string]interface{})
}
event.Type = input.TextEvent
event.From = update.Message.From.UserName
event.To = tc.input.api.Self.UserName
event.Data = []byte(update.Message.Text)
event.Meta["chatId"] = update.Message.Chat.ID
event.Meta["chatType"] = update.Message.Chat.Type
event.Meta["messageId"] = update.Message.MessageID
return nil
}
}
func (tc *telegramConn) Send(event *input.Event) error {
messageText := strings.TrimSpace(string(event.Data))
chatId := event.Meta["chatId"].(int64)
chatType := ChatType(event.Meta["chatType"].(string))
msgConfig := tgbotapi.NewMessage(chatId, messageText)
msgConfig.ParseMode = tgbotapi.ModeHTML
if sliceutil.Contains([]ChatType{Group, Supergroup}, chatType) {
msgConfig.ReplyToMessageID = event.Meta["messageId"].(int)
}
_, err := tc.input.api.Send(msgConfig)
if err != nil {
// probably it could be because of nested HTML tags -- telegram doesn't allow nested tags
log.Log("[telegram][Send] error:", err)
msgConfig.Text = "This bot couldn't send the response (Internal error)"
tc.input.api.Send(msgConfig)
}
return nil
}
-101
View File
@@ -1,101 +0,0 @@
package telegram
import (
"errors"
"strings"
"sync"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2/agent/input"
tgbotapi "gopkg.in/telegram-bot-api.v4"
)
type telegramInput struct {
sync.Mutex
debug bool
token string
whitelist []string
api *tgbotapi.BotAPI
}
type ChatType string
const (
Private ChatType = "private"
Group ChatType = "group"
Supergroup ChatType = "supergroup"
)
func init() {
input.Inputs["telegram"] = &telegramInput{}
}
func (ti *telegramInput) Flags() []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
Name: "telegram_debug",
EnvVars: []string{"MICRO_TELEGRAM_DEBUG"},
Usage: "Telegram debug output",
},
&cli.StringFlag{
Name: "telegram_token",
EnvVars: []string{"MICRO_TELEGRAM_TOKEN"},
Usage: "Telegram token",
},
&cli.StringFlag{
Name: "telegram_whitelist",
EnvVars: []string{"MICRO_TELEGRAM_WHITELIST"},
Usage: "Telegram bot's users (comma-separated values)",
},
}
}
func (ti *telegramInput) Init(ctx *cli.Context) error {
ti.debug = ctx.Bool("telegram_debug")
ti.token = ctx.String("telegram_token")
whitelist := ctx.String("telegram_whitelist")
if whitelist != "" {
ti.whitelist = strings.Split(whitelist, ",")
}
if len(ti.token) == 0 {
return errors.New("missing telegram token")
}
return nil
}
func (ti *telegramInput) Stream() (input.Conn, error) {
ti.Lock()
defer ti.Unlock()
return newConn(ti)
}
func (ti *telegramInput) Start() error {
ti.Lock()
defer ti.Unlock()
api, err := tgbotapi.NewBotAPI(ti.token)
if err != nil {
return err
}
ti.api = api
api.Debug = ti.debug
return nil
}
func (ti *telegramInput) Stop() error {
return nil
}
func (p *telegramInput) String() string {
return "telegram"
}
-216
View File
@@ -1,216 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: github.com/micro/go-micro/v2/agent/proto/bot.proto
package go_micro_bot
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type HelpRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HelpRequest) Reset() { *m = HelpRequest{} }
func (m *HelpRequest) String() string { return proto.CompactTextString(m) }
func (*HelpRequest) ProtoMessage() {}
func (*HelpRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_018e8d5b14a89d12, []int{0}
}
func (m *HelpRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HelpRequest.Unmarshal(m, b)
}
func (m *HelpRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HelpRequest.Marshal(b, m, deterministic)
}
func (m *HelpRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_HelpRequest.Merge(m, src)
}
func (m *HelpRequest) XXX_Size() int {
return xxx_messageInfo_HelpRequest.Size(m)
}
func (m *HelpRequest) XXX_DiscardUnknown() {
xxx_messageInfo_HelpRequest.DiscardUnknown(m)
}
var xxx_messageInfo_HelpRequest proto.InternalMessageInfo
type HelpResponse struct {
Usage string `protobuf:"bytes,1,opt,name=usage,proto3" json:"usage,omitempty"`
Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HelpResponse) Reset() { *m = HelpResponse{} }
func (m *HelpResponse) String() string { return proto.CompactTextString(m) }
func (*HelpResponse) ProtoMessage() {}
func (*HelpResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_018e8d5b14a89d12, []int{1}
}
func (m *HelpResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HelpResponse.Unmarshal(m, b)
}
func (m *HelpResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HelpResponse.Marshal(b, m, deterministic)
}
func (m *HelpResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_HelpResponse.Merge(m, src)
}
func (m *HelpResponse) XXX_Size() int {
return xxx_messageInfo_HelpResponse.Size(m)
}
func (m *HelpResponse) XXX_DiscardUnknown() {
xxx_messageInfo_HelpResponse.DiscardUnknown(m)
}
var xxx_messageInfo_HelpResponse proto.InternalMessageInfo
func (m *HelpResponse) GetUsage() string {
if m != nil {
return m.Usage
}
return ""
}
func (m *HelpResponse) GetDescription() string {
if m != nil {
return m.Description
}
return ""
}
type ExecRequest struct {
Args []string `protobuf:"bytes,1,rep,name=args,proto3" json:"args,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ExecRequest) Reset() { *m = ExecRequest{} }
func (m *ExecRequest) String() string { return proto.CompactTextString(m) }
func (*ExecRequest) ProtoMessage() {}
func (*ExecRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_018e8d5b14a89d12, []int{2}
}
func (m *ExecRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ExecRequest.Unmarshal(m, b)
}
func (m *ExecRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ExecRequest.Marshal(b, m, deterministic)
}
func (m *ExecRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ExecRequest.Merge(m, src)
}
func (m *ExecRequest) XXX_Size() int {
return xxx_messageInfo_ExecRequest.Size(m)
}
func (m *ExecRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ExecRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ExecRequest proto.InternalMessageInfo
func (m *ExecRequest) GetArgs() []string {
if m != nil {
return m.Args
}
return nil
}
type ExecResponse struct {
Result []byte `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"`
Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ExecResponse) Reset() { *m = ExecResponse{} }
func (m *ExecResponse) String() string { return proto.CompactTextString(m) }
func (*ExecResponse) ProtoMessage() {}
func (*ExecResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_018e8d5b14a89d12, []int{3}
}
func (m *ExecResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ExecResponse.Unmarshal(m, b)
}
func (m *ExecResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ExecResponse.Marshal(b, m, deterministic)
}
func (m *ExecResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ExecResponse.Merge(m, src)
}
func (m *ExecResponse) XXX_Size() int {
return xxx_messageInfo_ExecResponse.Size(m)
}
func (m *ExecResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ExecResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ExecResponse proto.InternalMessageInfo
func (m *ExecResponse) GetResult() []byte {
if m != nil {
return m.Result
}
return nil
}
func (m *ExecResponse) GetError() string {
if m != nil {
return m.Error
}
return ""
}
func init() {
proto.RegisterType((*HelpRequest)(nil), "go.micro.bot.HelpRequest")
proto.RegisterType((*HelpResponse)(nil), "go.micro.bot.HelpResponse")
proto.RegisterType((*ExecRequest)(nil), "go.micro.bot.ExecRequest")
proto.RegisterType((*ExecResponse)(nil), "go.micro.bot.ExecResponse")
}
func init() {
proto.RegisterFile("github.com/micro/go-micro/v2/agent/proto/bot.proto", fileDescriptor_018e8d5b14a89d12)
}
var fileDescriptor_018e8d5b14a89d12 = []byte{
// 246 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x50, 0x4d, 0x4b, 0xc4, 0x30,
0x10, 0xdd, 0xea, 0xba, 0xb2, 0xd3, 0x7a, 0x09, 0x22, 0x75, 0x4f, 0x35, 0xa7, 0xbd, 0x98, 0x80,
0x5e, 0x05, 0x0f, 0xa2, 0x78, 0xee, 0x3f, 0x68, 0xbb, 0x43, 0x2c, 0x6c, 0x3b, 0x35, 0x99, 0x82,
0xff, 0xc1, 0x3f, 0x2d, 0x4d, 0x72, 0x08, 0xc5, 0xdb, 0x7b, 0x79, 0xe1, 0x7d, 0x0c, 0x68, 0xd3,
0xf3, 0xd7, 0xdc, 0xaa, 0x8e, 0x06, 0x3d, 0xf4, 0x9d, 0x25, 0x6d, 0xe8, 0x31, 0x80, 0xc6, 0xe0,
0xc8, 0x7a, 0xb2, 0xc4, 0xa4, 0x5b, 0x62, 0xe5, 0x91, 0x28, 0x0c, 0x29, 0xaf, 0xab, 0x96, 0x58,
0xde, 0x40, 0xfe, 0x89, 0xe7, 0xa9, 0xc6, 0xef, 0x19, 0x1d, 0xcb, 0x0f, 0x28, 0x02, 0x75, 0x13,
0x8d, 0x0e, 0xc5, 0x2d, 0x5c, 0xcd, 0xae, 0x31, 0x58, 0x66, 0x55, 0x76, 0xdc, 0xd7, 0x81, 0x88,
0x0a, 0xf2, 0x13, 0xba, 0xce, 0xf6, 0x13, 0xf7, 0x34, 0x96, 0x17, 0x5e, 0x4b, 0x9f, 0xe4, 0x03,
0xe4, 0xef, 0x3f, 0xd8, 0x45, 0x5b, 0x21, 0x60, 0xdb, 0x58, 0xe3, 0xca, 0xac, 0xba, 0x3c, 0xee,
0x6b, 0x8f, 0xe5, 0x0b, 0x14, 0xe1, 0x4b, 0x8c, 0xba, 0x83, 0x9d, 0x45, 0x37, 0x9f, 0xd9, 0x67,
0x15, 0x75, 0x64, 0x4b, 0x05, 0xb4, 0x96, 0x6c, 0x8c, 0x09, 0xe4, 0xe9, 0x37, 0x83, 0xeb, 0x37,
0x1a, 0x86, 0x66, 0x3c, 0x89, 0x57, 0xd8, 0x2e, 0xa5, 0xc5, 0xbd, 0x4a, 0xa7, 0xa9, 0x64, 0xd7,
0xe1, 0xf0, 0x9f, 0x14, 0x82, 0xe5, 0x66, 0x31, 0x58, 0xaa, 0xac, 0x0d, 0x92, 0x05, 0x6b, 0x83,
0xb4, 0xb9, 0xdc, 0xb4, 0x3b, 0x7f, 0xda, 0xe7, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0xbd,
0x39, 0x29, 0x8d, 0x01, 0x00, 0x00,
}
-108
View File
@@ -1,108 +0,0 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: github.com/micro/go-micro/v2/agent/proto/bot.proto
package go_micro_bot
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
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 _ context.Context
var _ client.Option
var _ server.Option
// Client API for Command service
type CommandService interface {
Help(ctx context.Context, in *HelpRequest, opts ...client.CallOption) (*HelpResponse, error)
Exec(ctx context.Context, in *ExecRequest, opts ...client.CallOption) (*ExecResponse, error)
}
type commandService struct {
c client.Client
name string
}
func NewCommandService(name string, c client.Client) CommandService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.bot"
}
return &commandService{
c: c,
name: name,
}
}
func (c *commandService) Help(ctx context.Context, in *HelpRequest, opts ...client.CallOption) (*HelpResponse, error) {
req := c.c.NewRequest(c.name, "Command.Help", in)
out := new(HelpResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *commandService) Exec(ctx context.Context, in *ExecRequest, opts ...client.CallOption) (*ExecResponse, error) {
req := c.c.NewRequest(c.name, "Command.Exec", in)
out := new(ExecResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Command service
type CommandHandler interface {
Help(context.Context, *HelpRequest, *HelpResponse) error
Exec(context.Context, *ExecRequest, *ExecResponse) error
}
func RegisterCommandHandler(s server.Server, hdlr CommandHandler, opts ...server.HandlerOption) error {
type command interface {
Help(ctx context.Context, in *HelpRequest, out *HelpResponse) error
Exec(ctx context.Context, in *ExecRequest, out *ExecResponse) error
}
type Command struct {
command
}
h := &commandHandler{hdlr}
return s.Handle(s.NewHandler(&Command{h}, opts...))
}
type commandHandler struct {
CommandHandler
}
func (h *commandHandler) Help(ctx context.Context, in *HelpRequest, out *HelpResponse) error {
return h.CommandHandler.Help(ctx, in, out)
}
func (h *commandHandler) Exec(ctx context.Context, in *ExecRequest, out *ExecResponse) error {
return h.CommandHandler.Exec(ctx, in, out)
}
-25
View File
@@ -1,25 +0,0 @@
syntax = "proto3";
package go.micro.bot;
service Command {
rpc Help(HelpRequest) returns (HelpResponse) {};
rpc Exec(ExecRequest) returns (ExecResponse) {};
}
message HelpRequest {
}
message HelpResponse {
string usage = 1;
string description = 2;
}
message ExecRequest {
repeated string args = 1;
}
message ExecResponse {
bytes result = 1;
string error = 2;
}
+37 -5
View File
@@ -5,10 +5,27 @@ import (
"regexp"
"strings"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/server"
"github.com/asim/go-micro/v3/registry"
"github.com/asim/go-micro/v3/server"
)
type Api interface {
// Initialise options
Init(...Option) error
// Get the options
Options() Options
// Register a http handler
Register(*Endpoint) error
// Register a route
Deregister(*Endpoint) error
// Implemenation of api
String() string
}
type Options struct{}
type Option func(*Options) error
// Endpoint is a mapping between an RPC method and HTTP endpoint
type Endpoint struct {
// RPC Method e.g. Greeter.Hello
@@ -23,6 +40,12 @@ type Endpoint struct {
Method []string
// HTTP Path e.g /greeter. Expect POSIX regex
Path []string
// Body destination
// "*" or "" - top level message value
// "string" - inner message value
Body string
// Stream flag
Stream bool
}
// Service represents an API service
@@ -105,9 +128,18 @@ func Validate(e *Endpoint) error {
}
for _, p := range e.Path {
_, err := regexp.CompilePOSIX(p)
if err != nil {
return err
ps := p[0]
pe := p[len(p)-1]
if ps == '^' && pe == '$' {
_, err := regexp.CompilePOSIX(p)
if err != nil {
return err
}
} else if ps == '^' && pe != '$' {
return errors.New("invalid path")
} else if ps != '^' && pe == '$' {
return errors.New("invalid path")
}
}
+39
View File
@@ -111,3 +111,42 @@ func TestEncoding(t *testing.T) {
}
}
}
func TestValidate(t *testing.T) {
epPcre := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"^/test/?$"},
}
if err := Validate(epPcre); err != nil {
t.Fatal(err)
}
epGpath := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test/{id}"},
}
if err := Validate(epGpath); err != nil {
t.Fatal(err)
}
epPcreInvalid := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test/?$"},
}
if err := Validate(epPcreInvalid); err == nil {
t.Fatalf("invalid pcre %v", epPcreInvalid.Path[0])
}
}
+14 -8
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/asim/go-micro/v3/api"
"github.com/asim/go-micro/v3/api/handler"
api "github.com/asim/go-micro/v3/api/proto"
"github.com/asim/go-micro/v3/client"
"github.com/asim/go-micro/v3/errors"
"github.com/asim/go-micro/v3/selector"
"github.com/asim/go-micro/v3/util/ctx"
)
type apiHandler struct {
@@ -24,6 +24,12 @@ const (
// API handler is the default handler which takes api.Request and returns api.Response
func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if a.opts.MaxRecvSize > 0 {
bsize = a.opts.MaxRecvSize
}
r.Body = http.MaxBytesReader(w, r.Body, bsize)
request, err := requestToProto(r)
if err != nil {
er := errors.InternalServerError("go.micro.api", err.Error())
@@ -59,7 +65,7 @@ func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// create request and response
c := a.opts.Service.Client()
c := a.opts.Client
req := c.NewRequest(service.Name, service.Endpoint.Name, request)
rsp := &api.Response{}
+15 -7
View File
@@ -2,15 +2,20 @@ package api
import (
"fmt"
"io/ioutil"
"mime"
"net"
"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/asim/go-micro/v3/api/proto"
"github.com/asim/go-micro/v3/registry"
"github.com/asim/go-micro/v3/selector"
"github.com/oxtoacart/bpool"
)
var (
// need to calculate later to specify useful defaults
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
func requestToProto(r *http.Request) (*api.Request, error) {
@@ -39,9 +44,12 @@ func requestToProto(r *http.Request) (*api.Request, error) {
case "application/x-www-form-urlencoded":
// expect form vals in Post data
default:
data, _ := ioutil.ReadAll(r.Body)
req.Body = string(data)
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err = buf.ReadFrom(r.Body); err != nil {
return nil, err
}
req.Body = buf.String()
}
}
-268
View File
@@ -1,268 +0,0 @@
// Package broker provides a go-micro/broker handler
package broker
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"strings"
"sync"
"time"
"github.com/gorilla/websocket"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/util/log"
)
const (
Handler = "broker"
pingTime = (readDeadline * 9) / 10
readLimit = 16384
readDeadline = 60 * time.Second
writeDeadline = 10 * time.Second
)
type brokerHandler struct {
opts handler.Options
u websocket.Upgrader
}
type conn struct {
b broker.Broker
cType string
topic string
queue string
exit chan bool
sync.Mutex
ws *websocket.Conn
}
var (
once sync.Once
contentType = "text/plain"
)
func checkOrigin(r *http.Request) bool {
origin := r.Header["Origin"]
if len(origin) == 0 {
return true
}
u, err := url.Parse(origin[0])
if err != nil {
return false
}
return u.Host == r.Host
}
func (c *conn) close() {
select {
case <-c.exit:
return
default:
close(c.exit)
}
}
func (c *conn) readLoop() {
defer func() {
c.close()
c.ws.Close()
}()
// set read limit/deadline
c.ws.SetReadLimit(readLimit)
c.ws.SetReadDeadline(time.Now().Add(readDeadline))
// set close handler
ch := c.ws.CloseHandler()
c.ws.SetCloseHandler(func(code int, text string) error {
err := ch(code, text)
c.close()
return err
})
// set pong handler
c.ws.SetPongHandler(func(string) error {
c.ws.SetReadDeadline(time.Now().Add(readDeadline))
return nil
})
for {
_, message, err := c.ws.ReadMessage()
if err != nil {
return
}
c.b.Publish(c.topic, &broker.Message{
Header: map[string]string{"Content-Type": c.cType},
Body: message,
})
}
}
func (c *conn) write(mType int, data []byte) error {
c.Lock()
c.ws.SetWriteDeadline(time.Now().Add(writeDeadline))
err := c.ws.WriteMessage(mType, data)
c.Unlock()
return err
}
func (c *conn) writeLoop() {
ticker := time.NewTicker(pingTime)
var opts []broker.SubscribeOption
if len(c.queue) > 0 {
opts = append(opts, broker.Queue(c.queue))
}
subscriber, err := c.b.Subscribe(c.topic, func(p broker.Event) error {
b, err := json.Marshal(p.Message())
if err != nil {
return nil
}
return c.write(websocket.TextMessage, b)
}, opts...)
defer func() {
subscriber.Unsubscribe()
ticker.Stop()
c.ws.Close()
}()
if err != nil {
log.Log(err.Error())
return
}
for {
select {
case <-ticker.C:
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
return
}
case <-c.exit:
return
}
}
}
func (b *brokerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
br := b.opts.Service.Client().Options().Broker
// Setup the broker
once.Do(func() {
br.Init()
br.Connect()
})
// Parse
r.ParseForm()
topic := r.Form.Get("topic")
// Can't do anything without a topic
if len(topic) == 0 {
http.Error(w, "Topic not specified", 400)
return
}
// Post assumed to be Publish
if r.Method == "POST" {
// Create a broker message
msg := &broker.Message{
Header: make(map[string]string),
}
// Set header
for k, v := range r.Header {
msg.Header[k] = strings.Join(v, ", ")
}
// Read body
b, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// Set body
msg.Body = b
// Publish
br.Publish(topic, msg)
return
}
// now back to our regularly scheduled programming
if r.Method != "GET" {
http.Error(w, "Method not allowed", 405)
return
}
queue := r.Form.Get("queue")
ws, err := b.u.Upgrade(w, r, nil)
if err != nil {
log.Log(err.Error())
return
}
cType := r.Header.Get("Content-Type")
if len(cType) == 0 {
cType = contentType
}
c := &conn{
b: br,
cType: cType,
topic: topic,
queue: queue,
exit: make(chan bool),
ws: ws,
}
go c.writeLoop()
c.readLoop()
}
func (b *brokerHandler) String() string {
return "broker"
}
func NewHandler(opts ...handler.Option) handler.Handler {
return &brokerHandler{
u: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
ReadBufferSize: 1024,
WriteBufferSize: 1024,
},
opts: handler.NewOptions(opts...),
}
}
func WithCors(cors map[string]bool, opts ...handler.Option) handler.Handler {
return &brokerHandler{
u: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
if origin := r.Header.Get("Origin"); cors[origin] {
return true
} else if len(origin) > 0 && cors["*"] {
return true
} else if checkOrigin(r) {
return true
}
return false
},
ReadBufferSize: 1024,
WriteBufferSize: 1024,
},
opts: handler.NewOptions(opts...),
}
}
-94
View File
@@ -1,94 +0,0 @@
// Package cloudevents provides a cloudevents handler publishing the event using the go-micro/client
package cloudevents
import (
"net/http"
"path"
"regexp"
"strings"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/util/ctx"
)
type event struct {
options handler.Options
}
var (
Handler = "cloudevents"
versionRe = regexp.MustCompilePOSIX("^v[0-9]+$")
)
func eventName(parts []string) string {
return strings.Join(parts, ".")
}
func evRoute(ns, p string) (string, string) {
p = path.Clean(p)
p = strings.TrimPrefix(p, "/")
if len(p) == 0 {
return ns, "event"
}
parts := strings.Split(p, "/")
// no path
if len(parts) == 0 {
// topic: namespace
// action: event
return strings.Trim(ns, "."), "event"
}
// Treat /v[0-9]+ as versioning
// /v1/foo/bar => topic: v1.foo action: bar
if len(parts) >= 2 && versionRe.Match([]byte(parts[0])) {
topic := ns + "." + strings.Join(parts[:2], ".")
action := eventName(parts[1:])
return topic, action
}
// /foo => topic: ns.foo action: foo
// /foo/bar => topic: ns.foo action: bar
topic := ns + "." + strings.Join(parts[:1], ".")
action := eventName(parts[1:])
return topic, action
}
func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// request to topic:event
// create event
// publish to topic
topic, _ := evRoute(e.options.Namespace, r.URL.Path)
// create event
ev, err := FromRequest(r)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// get client
c := e.options.Service.Client()
// create publication
p := c.NewMessage(topic, ev)
// publish event
if err := c.Publish(ctx.FromRequest(r), p); err != nil {
http.Error(w, err.Error(), 500)
return
}
}
func (e *event) String() string {
return "cloudevents"
}
func NewHandler(opts ...handler.Option) handler.Handler {
return &event{
options: handler.NewOptions(opts...),
}
}
-282
View File
@@ -1,282 +0,0 @@
/*
* From: https://github.com/serverless/event-gateway/blob/master/event/event.go
* Modified: Strip to handler requirements
*
* Copyright 2017 Serverless, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package cloudevents
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"mime"
"net/http"
"strings"
"time"
"unicode"
"github.com/google/uuid"
validator "gopkg.in/go-playground/validator.v9"
)
const (
// TransformationVersion is indicative of the revision of how Event Gateway transforms a request into CloudEvents format.
TransformationVersion = "0.1"
// CloudEventsVersion currently supported by Event Gateway
CloudEventsVersion = "0.1"
)
// Event is a default event structure. All data that passes through the Event Gateway
// is formatted to a format defined CloudEvents v0.1 spec.
type Event struct {
EventType string `json:"eventType" validate:"required"`
EventTypeVersion string `json:"eventTypeVersion,omitempty"`
CloudEventsVersion string `json:"cloudEventsVersion" validate:"required"`
Source string `json:"source" validate:"uri,required"`
EventID string `json:"eventID" validate:"required"`
EventTime *time.Time `json:"eventTime,omitempty"`
SchemaURL string `json:"schemaURL,omitempty"`
Extensions map[string]interface{} `json:"extensions,omitempty"`
ContentType string `json:"contentType,omitempty"`
Data interface{} `json:"data"`
}
// New return new instance of Event.
func New(eventType string, mimeType string, payload interface{}) *Event {
now := time.Now()
event := &Event{
EventType: eventType,
CloudEventsVersion: CloudEventsVersion,
Source: "https://micro.mu",
EventID: uuid.New().String(),
EventTime: &now,
ContentType: mimeType,
Data: payload,
Extensions: map[string]interface{}{
"eventgateway": map[string]interface{}{
"transformed": "true",
"transformation-version": TransformationVersion,
},
},
}
event.Data = normalizePayload(event.Data, event.ContentType)
return event
}
// FromRequest takes an HTTP request and returns an Event along with path. Most of the implementation
// is based on https://github.com/cloudevents/spec/blob/master/http-transport-binding.md.
// This function also supports legacy mode where event type is sent in Event header.
func FromRequest(r *http.Request) (*Event, error) {
contentType := r.Header.Get("Content-Type")
mimeType, _, err := mime.ParseMediaType(contentType)
if err != nil {
if err.Error() != "mime: no media type" {
return nil, err
}
mimeType = "application/octet-stream"
}
// Read request body
body := []byte{}
if r.Body != nil {
body, err = ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
}
var event *Event
if mimeType == mimeCloudEventsJSON { // CloudEvents Structured Content Mode
return parseAsCloudEvent(mimeType, body)
} else if isCloudEventsBinaryContentMode(r.Header) { // CloudEvents Binary Content Mode
return parseAsCloudEventBinary(r.Header, body)
} else if isLegacyMode(r.Header) {
if mimeType == mimeJSON { // CloudEvent in Legacy Mode
event, err = parseAsCloudEvent(mimeType, body)
if err != nil {
return New(string(r.Header.Get("event")), mimeType, body), nil
}
return event, err
}
return New(string(r.Header.Get("event")), mimeType, body), nil
}
return New("http.request", mimeJSON, newHTTPRequestData(r, body)), nil
}
// Validate Event struct
func (e *Event) Validate() error {
validate := validator.New()
err := validate.Struct(e)
if err != nil {
return fmt.Errorf("CloudEvent not valid: %v", err)
}
return nil
}
func isLegacyMode(headers http.Header) bool {
if headers.Get("Event") != "" {
return true
}
return false
}
func isCloudEventsBinaryContentMode(headers http.Header) bool {
if headers.Get("CE-EventType") != "" &&
headers.Get("CE-CloudEventsVersion") != "" &&
headers.Get("CE-Source") != "" &&
headers.Get("CE-EventID") != "" {
return true
}
return false
}
func parseAsCloudEventBinary(headers http.Header, payload interface{}) (*Event, error) {
event := &Event{
EventType: headers.Get("CE-EventType"),
EventTypeVersion: headers.Get("CE-EventTypeVersion"),
CloudEventsVersion: headers.Get("CE-CloudEventsVersion"),
Source: headers.Get("CE-Source"),
EventID: headers.Get("CE-EventID"),
ContentType: headers.Get("Content-Type"),
Data: payload,
}
err := event.Validate()
if err != nil {
return nil, err
}
if headers.Get("CE-EventTime") != "" {
val, err := time.Parse(time.RFC3339, headers.Get("CE-EventTime"))
if err != nil {
return nil, err
}
event.EventTime = &val
}
if val := headers.Get("CE-SchemaURL"); len(val) > 0 {
event.SchemaURL = val
}
event.Extensions = map[string]interface{}{}
for key, val := range flatten(headers) {
if strings.HasPrefix(key, "Ce-X-") {
key = strings.TrimLeft(key, "Ce-X-")
// Make first character lowercase
runes := []rune(key)
runes[0] = unicode.ToLower(runes[0])
event.Extensions[string(runes)] = val
}
}
event.Data = normalizePayload(event.Data, event.ContentType)
return event, nil
}
func flatten(h http.Header) map[string]string {
headers := map[string]string{}
for key, header := range h {
headers[key] = header[0]
if len(header) > 1 {
headers[key] = strings.Join(header, ", ")
}
}
return headers
}
func parseAsCloudEvent(mime string, payload interface{}) (*Event, error) {
body, ok := payload.([]byte)
if ok {
event := &Event{}
err := json.Unmarshal(body, event)
if err != nil {
return nil, err
}
err = event.Validate()
if err != nil {
return nil, err
}
event.Data = normalizePayload(event.Data, event.ContentType)
return event, nil
}
return nil, errors.New("couldn't cast to []byte")
}
const (
mimeJSON = "application/json"
mimeFormMultipart = "multipart/form-data"
mimeFormURLEncoded = "application/x-www-form-urlencoded"
mimeCloudEventsJSON = "application/cloudevents+json"
)
// normalizePayload takes anything, checks if it's []byte array and depending on provided mime
// type converts it to either string or map[string]interface to avoid having base64 string after
// JSON marshaling.
func normalizePayload(payload interface{}, mime string) interface{} {
if bytePayload, ok := payload.([]byte); ok && len(bytePayload) > 0 {
switch {
case mime == mimeJSON || strings.HasSuffix(mime, "+json"):
var result map[string]interface{}
err := json.Unmarshal(bytePayload, &result)
if err != nil {
return payload
}
return result
case strings.HasPrefix(mime, mimeFormMultipart), mime == mimeFormURLEncoded:
return string(bytePayload)
}
}
return payload
}
// HTTPRequestData is a event schema used for sending events to HTTP subscriptions.
type HTTPRequestData struct {
Headers map[string]string `json:"headers"`
Query map[string][]string `json:"query"`
Body interface{} `json:"body"`
Host string `json:"host"`
Path string `json:"path"`
Method string `json:"method"`
Params map[string]string `json:"params"`
}
// NewHTTPRequestData returns a new instance of HTTPRequestData
func newHTTPRequestData(r *http.Request, eventData interface{}) *HTTPRequestData {
req := &HTTPRequestData{
Headers: flatten(r.Header),
Query: r.URL.Query(),
Body: eventData,
Host: r.Host,
Path: r.URL.Path,
Method: r.Method,
}
req.Body = normalizePayload(req.Body, r.Header.Get("content-type"))
return req
}
+24 -11
View File
@@ -4,21 +4,25 @@ package event
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"path"
"regexp"
"strings"
"time"
"github.com/asim/go-micro/v3/api/handler"
proto "github.com/asim/go-micro/v3/api/proto"
"github.com/asim/go-micro/v3/util/ctx"
"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/oxtoacart/bpool"
)
var (
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
type event struct {
options handler.Options
opts handler.Options
}
var (
@@ -64,11 +68,18 @@ func evRoute(ns, p string) (string, string) {
}
func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if e.opts.MaxRecvSize > 0 {
bsize = e.opts.MaxRecvSize
}
r.Body = http.MaxBytesReader(w, r.Body, bsize)
// request to topic:event
// create event
// publish to topic
topic, action := evRoute(e.options.Namespace, r.URL.Path)
topic, action := evRoute(e.opts.Namespace, r.URL.Path)
// create event
ev := &proto.Event{
@@ -96,16 +107,18 @@ func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bytes, _ := json.Marshal(r.URL.Query())
ev.Data = string(bytes)
} else {
b, err := ioutil.ReadAll(r.Body)
if err != nil {
// Read body
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err := buf.ReadFrom(r.Body); err != nil {
http.Error(w, err.Error(), 500)
return
}
ev.Data = string(b)
ev.Data = buf.String()
}
// get client
c := e.options.Service.Client()
c := e.opts.Client
// create publication
p := c.NewMessage(topic, ev)
@@ -123,6 +136,6 @@ func (e *event) String() string {
func NewHandler(opts ...handler.Option) handler.Handler {
return &event{
options: handler.NewOptions(opts...),
opts: handler.NewOptions(opts...),
}
}
-16
View File
@@ -1,16 +0,0 @@
// Package file serves file relative to the current directory
package file
import (
"net/http"
)
type Handler struct{}
func (h *Handler) Serve(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "."+r.URL.Path)
}
func (h *Handler) String() string {
return "file"
}
+3 -3
View File
@@ -8,9 +8,9 @@ import (
"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/asim/go-micro/v3/api"
"github.com/asim/go-micro/v3/api/handler"
"github.com/asim/go-micro/v3/selector"
)
const (
+13 -8
View File
@@ -6,15 +6,16 @@ import (
"net/http/httptest"
"testing"
"github.com/micro/go-micro/v2/api/handler"
"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/asim/go-micro/v3/api/handler"
"github.com/asim/go-micro/v3/api/resolver"
"github.com/asim/go-micro/v3/api/resolver/vpath"
"github.com/asim/go-micro/v3/api/router"
regRouter "github.com/asim/go-micro/v3/api/router/registry"
"github.com/asim/go-micro/v3/registry"
)
func testHttp(t *testing.T, path, service, ns string) {
r := memory.NewRegistry()
r := registry.NewMemoryRegistry()
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
@@ -54,8 +55,10 @@ func testHttp(t *testing.T, path, service, ns string) {
// initialise the handler
rt := regRouter.NewRouter(
router.WithHandler("http"),
router.WithNamespace(ns),
router.WithRegistry(r),
router.WithResolver(vpath.NewResolver(
resolver.WithNamespace(resolver.StaticNamespace(ns)),
)),
)
p := NewHandler(handler.WithRouter(rt))
@@ -116,6 +119,8 @@ func TestHttpHandler(t *testing.T) {
}
for _, d := range testData {
testHttp(t, d.path, d.service, d.namespace)
t.Run(d.service, func(t *testing.T) {
testHttp(t, d.path, d.service, d.namespace)
})
}
}
+25 -11
View File
@@ -1,14 +1,19 @@
package handler
import (
"github.com/micro/go-micro/v2"
"github.com/micro/go-micro/v2/api/router"
"github.com/asim/go-micro/v3/api/router"
"github.com/asim/go-micro/v3/client"
)
var (
DefaultMaxRecvSize int64 = 1024 * 1024 * 100 // 10Mb
)
type Options struct {
Namespace string
Router router.Router
Service micro.Service
MaxRecvSize int64
Namespace string
Router router.Router
Client client.Client
}
type Option func(o *Options)
@@ -20,9 +25,8 @@ func NewOptions(opts ...Option) Options {
o(&options)
}
// create service if its blank
if options.Service == nil {
WithService(micro.NewService())(&options)
if options.Client == nil {
WithClient(client.NewClient())(&options)
}
// set namespace if blank
@@ -30,6 +34,10 @@ func NewOptions(opts ...Option) Options {
WithNamespace("go.micro.api")(&options)
}
if options.MaxRecvSize == 0 {
options.MaxRecvSize = DefaultMaxRecvSize
}
return options
}
@@ -47,9 +55,15 @@ func WithRouter(r router.Router) Option {
}
}
// WithService specifies a micro.Service
func WithService(s micro.Service) Option {
func WithClient(c client.Client) Option {
return func(o *Options) {
o.Service = s
o.Client = c
}
}
// WithmaxRecvSize specifies max body size
func WithMaxRecvSize(size int64) Option {
return func(o *Options) {
o.MaxRecvSize = size
}
}
-211
View File
@@ -1,211 +0,0 @@
// Package registry is a go-micro/registry handler
package registry
import (
"encoding/json"
"io/ioutil"
"net/http"
"strconv"
"time"
"github.com/gorilla/websocket"
"github.com/micro/go-micro/v2/api/handler"
"github.com/micro/go-micro/v2/registry"
)
const (
Handler = "registry"
pingTime = (readDeadline * 9) / 10
readLimit = 16384
readDeadline = 60 * time.Second
writeDeadline = 10 * time.Second
)
type registryHandler struct {
opts handler.Options
reg registry.Registry
}
func (rh *registryHandler) add(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
b, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
defer r.Body.Close()
var opts []registry.RegisterOption
// parse ttl
if ttl := r.Form.Get("ttl"); len(ttl) > 0 {
d, err := time.ParseDuration(ttl)
if err == nil {
opts = append(opts, registry.RegisterTTL(d))
}
}
var service *registry.Service
err = json.Unmarshal(b, &service)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
err = rh.reg.Register(service, opts...)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
}
func (rh *registryHandler) del(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
b, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
defer r.Body.Close()
var service *registry.Service
err = json.Unmarshal(b, &service)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
err = rh.reg.Deregister(service)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
}
func (rh *registryHandler) get(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
service := r.Form.Get("service")
var s []*registry.Service
var err error
if len(service) == 0 {
//
upgrade := r.Header.Get("Upgrade")
connect := r.Header.Get("Connection")
// watch if websockets
if upgrade == "websocket" && connect == "Upgrade" {
rw, err := rh.reg.Watch()
if err != nil {
http.Error(w, err.Error(), 500)
return
}
watch(rw, w, r)
return
}
// otherwise list services
s, err = rh.reg.ListServices()
} else {
s, err = rh.reg.GetService(service)
}
if err != nil {
http.Error(w, err.Error(), 500)
return
}
if s == nil || (len(service) > 0 && (len(s) == 0 || len(s[0].Name) == 0)) {
http.Error(w, "Service not found", 404)
return
}
b, err := json.Marshal(s)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Length", strconv.Itoa(len(b)))
w.Write(b)
}
func ping(ws *websocket.Conn, exit chan bool) {
ticker := time.NewTicker(pingTime)
for {
select {
case <-ticker.C:
ws.SetWriteDeadline(time.Now().Add(writeDeadline))
err := ws.WriteMessage(websocket.PingMessage, []byte{})
if err != nil {
return
}
case <-exit:
return
}
}
}
func watch(rw registry.Watcher, w http.ResponseWriter, r *http.Request) {
upgrader := websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// we need an exit chan
exit := make(chan bool)
defer func() {
close(exit)
}()
// ping the socket
go ping(ws, exit)
for {
// get next result
r, err := rw.Next()
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// write to client
ws.SetWriteDeadline(time.Now().Add(writeDeadline))
if err := ws.WriteJSON(r); err != nil {
return
}
}
}
func (rh *registryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
rh.get(w, r)
case "POST":
rh.add(w, r)
case "DELETE":
rh.del(w, r)
}
}
func (rh *registryHandler) String() string {
return "registry"
}
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.NewOptions(opts...)
return &registryHandler{
opts: options,
reg: options.Service.Client().Options().Registry,
}
}
+254 -41
View File
@@ -4,23 +4,27 @@ package rpc
import (
"encoding/json"
"io"
"io/ioutil"
"net/http"
"net/textproto"
"strconv"
"strings"
"github.com/joncalhoun/qson"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/handler"
proto "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/registry"
"github.com/micro/go-micro/v2/util/ctx"
"github.com/asim/go-micro/v3/api"
"github.com/asim/go-micro/v3/api/handler"
"github.com/asim/go-micro/v3/api/internal/proto"
"github.com/asim/go-micro/v3/client"
"github.com/asim/go-micro/v3/codec"
"github.com/asim/go-micro/v3/codec/jsonrpc"
"github.com/asim/go-micro/v3/codec/protorpc"
"github.com/asim/go-micro/v3/errors"
"github.com/asim/go-micro/v3/logger"
"github.com/asim/go-micro/v3/metadata"
"github.com/asim/go-micro/v3/registry"
"github.com/asim/go-micro/v3/selector"
"github.com/asim/go-micro/v3/util/ctx"
"github.com/asim/go-micro/v3/util/qson"
jsonpatch "github.com/evanphx/json-patch/v5"
"github.com/oxtoacart/bpool"
)
const (
@@ -44,6 +48,8 @@ var (
"application/proto-rpc",
"application/octet-stream",
}
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
type rpcHandler struct {
@@ -68,6 +74,13 @@ func strategy(services []*registry.Service) selector.Strategy {
}
func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if h.opts.MaxRecvSize > 0 {
bsize = h.opts.MaxRecvSize
}
r.Body = http.MaxBytesReader(w, r.Body, bsize)
defer r.Body.Close()
var service *api.Service
@@ -88,12 +101,6 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
// only allow post when we have the router
if r.Method != "GET" && (h.opts.Router != nil && r.Method != "POST") {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
ct := r.Header.Get("Content-Type")
// Strip charset from Content-Type (like `application/json; charset=UTF-8`)
@@ -102,11 +109,42 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// micro client
c := h.opts.Service.Client()
c := h.opts.Client
// 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))
// walk the standard call path
// get payload
br, err := requestPayload(r)
if err != nil {
@@ -114,9 +152,6 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
// create context
cx := ctx.FromRequest(r)
var rsp []byte
switch {
@@ -145,7 +180,12 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// marshall response
rsp, _ = response.Marshal()
rsp, err = response.Marshal()
if err != nil {
writeError(w, r, err)
return
}
default:
// if json codec is not present set to json
if !hasCodec(ct, jsonCodecs) {
@@ -168,7 +208,6 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
&request,
client.WithContentType(ct),
)
// make the call
if err := c.Call(cx, req, &response, client.WithSelectOption(so)); err != nil {
writeError(w, r, err)
@@ -176,7 +215,11 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// marshall response
rsp, _ = response.MarshalJSON()
rsp, err = response.MarshalJSON()
if err != nil {
writeError(w, r, err)
return
}
}
// write the response
@@ -200,49 +243,203 @@ func hasCodec(ct string, codecs []string) bool {
// If the request is a GET the query string parameters are extracted and marshaled to JSON and the raw bytes are returned.
// If the request method is a POST the request body is read and returned
func requestPayload(r *http.Request) ([]byte, error) {
var err error
// we have to decode json-rpc and proto-rpc because we suck
// well actually because there's no proxy codec right now
switch r.Header.Get("Content-Type") {
case "application/json-rpc":
ct := r.Header.Get("Content-Type")
switch {
case strings.Contains(ct, "application/json-rpc"):
msg := codec.Message{
Type: codec.Request,
Header: make(map[string]string),
}
c := jsonrpc.NewCodec(&buffer{r.Body})
if err := c.ReadHeader(&msg, codec.Request); err != nil {
if err = c.ReadHeader(&msg, codec.Request); err != nil {
return nil, err
}
var raw json.RawMessage
if err := c.ReadBody(&raw); err != nil {
if err = c.ReadBody(&raw); err != nil {
return nil, err
}
return ([]byte)(raw), nil
case "application/proto-rpc", "application/octet-stream":
case strings.Contains(ct, "application/proto-rpc"), strings.Contains(ct, "application/octet-stream"):
msg := codec.Message{
Type: codec.Request,
Header: make(map[string]string),
}
c := protorpc.NewCodec(&buffer{r.Body})
if err := c.ReadHeader(&msg, codec.Request); err != nil {
if err = c.ReadHeader(&msg, codec.Request); err != nil {
return nil, err
}
var raw proto.Message
if err := c.ReadBody(&raw); err != nil {
if err = c.ReadBody(&raw); err != nil {
return nil, err
}
b, _ := raw.Marshal()
return b, nil
return raw.Marshal()
case strings.Contains(ct, "application/www-x-form-urlencoded"):
r.ParseForm()
// generate a new set of values from the form
vals := make(map[string]string)
for k, v := range r.Form {
vals[k] = strings.Join(v, ",")
}
// marshal
return json.Marshal(vals)
// TODO: application/grpc
}
// otherwise as per usual
ctx := r.Context()
// dont user meadata.FromContext as it mangles names
md, ok := metadata.FromContext(ctx)
if !ok {
md = make(map[string]string)
}
// allocate maximum
matches := make(map[string]interface{}, len(md))
bodydst := ""
// get fields from url path
for k, v := range md {
k = strings.ToLower(k)
// filter own keys
if strings.HasPrefix(k, "x-api-field-") {
matches[strings.TrimPrefix(k, "x-api-field-")] = v
delete(md, k)
} else if k == "x-api-body" {
bodydst = v
delete(md, k)
}
}
// map of all fields
req := make(map[string]interface{}, len(md))
// get fields from url values
if len(r.URL.RawQuery) > 0 {
umd := make(map[string]interface{})
err = qson.Unmarshal(&umd, r.URL.RawQuery)
if err != nil {
return nil, err
}
for k, v := range umd {
matches[k] = v
}
}
// restore context without fields
*r = *r.Clone(metadata.NewContext(ctx, md))
for k, v := range matches {
ps := strings.Split(k, ".")
if len(ps) == 1 {
req[k] = v
continue
}
em := make(map[string]interface{})
em[ps[len(ps)-1]] = v
for i := len(ps) - 2; i > 0; i-- {
nm := make(map[string]interface{})
nm[ps[i]] = em
em = nm
}
if vm, ok := req[ps[0]]; ok {
// nested map
nm := vm.(map[string]interface{})
for vk, vv := range em {
nm[vk] = vv
}
req[ps[0]] = nm
} else {
req[ps[0]] = em
}
}
pathbuf := []byte("{}")
if len(req) > 0 {
pathbuf, err = json.Marshal(req)
if err != nil {
return nil, err
}
}
urlbuf := []byte("{}")
out, err := jsonpatch.MergeMergePatches(urlbuf, pathbuf)
if err != nil {
return nil, err
}
switch r.Method {
case "GET":
if len(r.URL.RawQuery) > 0 {
return qson.ToJSON(r.URL.RawQuery)
// empty response
if strings.Contains(ct, "application/json") && string(out) == "{}" {
return out, nil
} else if string(out) == "{}" && !strings.Contains(ct, "application/json") {
return []byte{}, nil
}
case "PATCH", "POST":
return ioutil.ReadAll(r.Body)
return out, nil
case "PATCH", "POST", "PUT", "DELETE":
bodybuf := []byte("{}")
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err := buf.ReadFrom(r.Body); err != nil {
return nil, err
}
if b := buf.Bytes(); len(b) > 0 {
bodybuf = b
}
if bodydst == "" || bodydst == "*" {
if out, err = jsonpatch.MergeMergePatches(out, bodybuf); err == nil {
return out, nil
}
}
var jsonbody map[string]interface{}
if json.Valid(bodybuf) {
if err = json.Unmarshal(bodybuf, &jsonbody); err != nil {
return nil, err
}
}
dstmap := make(map[string]interface{})
ps := strings.Split(bodydst, ".")
if len(ps) == 1 {
if jsonbody != nil {
dstmap[ps[0]] = jsonbody
} else {
// old unexpected behaviour
dstmap[ps[0]] = bodybuf
}
} else {
em := make(map[string]interface{})
if jsonbody != nil {
em[ps[len(ps)-1]] = jsonbody
} else {
// old unexpected behaviour
em[ps[len(ps)-1]] = bodybuf
}
for i := len(ps) - 2; i > 0; i-- {
nm := make(map[string]interface{})
nm[ps[i]] = em
em = nm
}
dstmap[ps[0]] = em
}
bodyout, err := json.Marshal(dstmap)
if err != nil {
return nil, err
}
if out, err = jsonpatch.MergeMergePatches(out, bodyout); err == nil {
return out, nil
}
//fallback to previous unknown behaviour
return bodybuf, nil
}
return []byte{}, nil
@@ -274,7 +471,12 @@ func writeError(w http.ResponseWriter, r *http.Request, err error) {
w.Header().Set("grpc-message", ce.Detail)
}
w.Write([]byte(ce.Error()))
_, werr := w.Write([]byte(ce.Error()))
if werr != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(werr)
}
}
}
func writeResponse(w http.ResponseWriter, r *http.Request, rsp []byte) {
@@ -289,8 +491,19 @@ func writeResponse(w http.ResponseWriter, r *http.Request, rsp []byte) {
w.Header().Set("grpc-message", "")
}
// write 204 status if rsp is nil
if len(rsp) == 0 {
w.WriteHeader(http.StatusNoContent)
}
// write response
w.Write(rsp)
_, err := w.Write(rsp)
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
}
}
func NewHandler(opts ...handler.Option) handler.Handler {
+28 -1
View File
@@ -4,10 +4,11 @@ import (
"bytes"
"encoding/json"
"net/http"
"reflect"
"testing"
go_api "github.com/asim/go-micro/v3/api/proto"
"github.com/golang/protobuf/proto"
go_api "github.com/micro/go-micro/v2/api/proto"
)
func TestRequestPayloadFromRequest(t *testing.T) {
@@ -27,6 +28,32 @@ func TestRequestPayloadFromRequest(t *testing.T) {
t.Fatal("Failed to marshal proto to JSON ", err)
}
type jsonUrl struct {
Key1 string `json:"key1"`
Key2 string `json:"key2"`
Name string `json:"name"`
}
jUrl := &jsonUrl{Key1: "val1", Key2: "val2", Name: "Test"}
t.Run("extracting a json from a POST request with url params", func(t *testing.T) {
r, err := http.NewRequest("POST", "http://localhost/my/path?key1=val1&key2=val2", bytes.NewReader(jsonBytes))
if err != nil {
t.Fatalf("Failed to created http.Request: %v", err)
}
extByte, err := requestPayload(r)
if err != nil {
t.Fatalf("Failed to extract payload from request: %v", err)
}
extJUrl := &jsonUrl{}
if err := json.Unmarshal(extByte, extJUrl); err != nil {
t.Fatalf("Failed to unmarshal payload from request: %v", err)
}
if !reflect.DeepEqual(extJUrl, jUrl) {
t.Fatalf("Expected %v and %v to match", extJUrl, jUrl)
}
})
t.Run("extracting a proto from a POST request", func(t *testing.T) {
r, err := http.NewRequest("POST", "http://localhost/my/path", bytes.NewReader(protoBytes))
if err != nil {
+259
View File
@@ -0,0 +1,259 @@
package rpc
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"strings"
"time"
"github.com/asim/go-micro/v3/api"
"github.com/asim/go-micro/v3/client"
raw "github.com/asim/go-micro/v3/codec/bytes"
"github.com/asim/go-micro/v3/logger"
"github.com/asim/go-micro/v3/selector"
"github.com/gobwas/httphead"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
)
// serveWebsocket will stream rpc back over websockets assuming json
func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request, service *api.Service, c client.Client) {
var op ws.OpCode
ct := r.Header.Get("Content-Type")
// Strip charset from Content-Type (like `application/json; charset=UTF-8`)
if idx := strings.IndexRune(ct, ';'); idx >= 0 {
ct = ct[:idx]
}
// check proto from request
switch ct {
case "application/json":
op = ws.OpText
default:
op = ws.OpBinary
}
hdr := make(http.Header)
if proto, ok := r.Header["Sec-Websocket-Protocol"]; ok {
for _, p := range proto {
switch p {
case "binary":
hdr["Sec-WebSocket-Protocol"] = []string{"binary"}
op = ws.OpBinary
}
}
}
payload, err := requestPayload(r)
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
return
}
upgrader := ws.HTTPUpgrader{Timeout: 5 * time.Second,
Protocol: func(proto string) bool {
if strings.Contains(proto, "binary") {
return true
}
// fallback to support all protocols now
return true
},
Extension: func(httphead.Option) bool {
// disable extensions for compatibility
return false
},
Header: hdr,
}
conn, rw, _, err := upgrader.Upgrade(r, w)
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
return
}
defer func() {
if err := conn.Close(); err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
return
}
}()
var request interface{}
if !bytes.Equal(payload, []byte(`{}`)) {
switch ct {
case "application/json", "":
m := json.RawMessage(payload)
request = &m
default:
request = &raw.Frame{Data: payload}
}
}
// we always need to set content type for message
if ct == "" {
ct = "application/json"
}
req := c.NewRequest(
service.Name,
service.Endpoint.Name,
request,
client.WithContentType(ct),
client.StreamingRequest(),
)
so := selector.WithStrategy(strategy(service.Services))
// create a new stream
stream, err := c.Stream(ctx, req, client.WithSelectOption(so))
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
return
}
if request != nil {
if err = stream.Send(request); err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
return
}
}
go writeLoop(rw, stream)
rsp := stream.Response()
// receive from stream and send to client
for {
select {
case <-ctx.Done():
return
case <-stream.Context().Done():
return
default:
// read backend response body
buf, err := rsp.Read()
if err != nil {
// wants to avoid import grpc/status.Status
if strings.Contains(err.Error(), "context canceled") {
return
}
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
return
}
// write the response
if err := wsutil.WriteServerMessage(rw, op, buf); err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
return
}
if err = rw.Flush(); err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
return
}
}
}
}
// writeLoop
func writeLoop(rw io.ReadWriter, stream client.Stream) {
// close stream when done
defer stream.Close()
for {
select {
case <-stream.Context().Done():
return
default:
buf, op, err := wsutil.ReadClientData(rw)
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
}
}
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
return
}
switch op {
default:
// not relevant
continue
case ws.OpText, ws.OpBinary:
break
}
// send to backend
// default to trying json
// if the extracted payload isn't empty lets use it
request := &raw.Frame{Data: buf}
if err := stream.Send(request); err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Error(err)
}
return
}
}
}
}
func isStream(r *http.Request, srv *api.Service) bool {
// check if it's a web socket
if !isWebSocket(r) {
return false
}
// check if the endpoint supports streaming
for _, service := range srv.Services {
for _, ep := range service.Endpoints {
// skip if it doesn't match the name
if ep.Name != srv.Endpoint.Name {
continue
}
// matched if the name
if v := ep.Metadata["stream"]; v == "true" {
return true
}
}
}
return false
}
func isWebSocket(r *http.Request) bool {
contains := func(key, val string) bool {
vv := strings.Split(r.Header.Get(key), ",")
for _, v := range vv {
if val == strings.ToLower(strings.TrimSpace(v)) {
return true
}
}
return false
}
if contains("Connection", "upgrade") && contains("Upgrade", "websocket") {
return true
}
return false
}
-25
View File
@@ -1,25 +0,0 @@
// Package udp reads and write from a udp connection
package udp
import (
"io"
"net"
"net/http"
)
type Handler struct{}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c, err := net.Dial("udp", r.Host)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
go io.Copy(c, r.Body)
// write response
io.Copy(w, c)
}
func (h *Handler) String() string {
return "udp"
}
-30
View File
@@ -1,30 +0,0 @@
// Package unix reads from a unix socket expecting it to be in /tmp/path
package unix
import (
"fmt"
"io"
"net"
"net/http"
"path/filepath"
)
type Handler struct{}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
sock := fmt.Sprintf("%s.sock", filepath.Clean(r.URL.Path))
path := filepath.Join("/tmp", sock)
c, err := net.Dial("unix", path)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
go io.Copy(c, r.Body)
// write response
io.Copy(w, c)
}
func (h *Handler) String() string {
return "unix"
}
+3 -3
View File
@@ -11,9 +11,9 @@ import (
"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/asim/go-micro/v3/api"
"github.com/asim/go-micro/v3/api/handler"
"github.com/asim/go-micro/v3/selector"
)
const (
+33 -36
View File
@@ -1,5 +1,5 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: github.com/micro/go-micro/v2/api/proto/api.proto
// source: api/proto/api.proto
package go_api
@@ -32,7 +32,7 @@ func (m *Pair) Reset() { *m = Pair{} }
func (m *Pair) String() string { return proto.CompactTextString(m) }
func (*Pair) ProtoMessage() {}
func (*Pair) Descriptor() ([]byte, []int) {
return fileDescriptor_7b6696ef87ec1943, []int{0}
return fileDescriptor_2df576b66d12087a, []int{0}
}
func (m *Pair) XXX_Unmarshal(b []byte) error {
@@ -86,7 +86,7 @@ func (m *Request) Reset() { *m = Request{} }
func (m *Request) String() string { return proto.CompactTextString(m) }
func (*Request) ProtoMessage() {}
func (*Request) Descriptor() ([]byte, []int) {
return fileDescriptor_7b6696ef87ec1943, []int{1}
return fileDescriptor_2df576b66d12087a, []int{1}
}
func (m *Request) XXX_Unmarshal(b []byte) error {
@@ -171,7 +171,7 @@ func (m *Response) Reset() { *m = Response{} }
func (m *Response) String() string { return proto.CompactTextString(m) }
func (*Response) ProtoMessage() {}
func (*Response) Descriptor() ([]byte, []int) {
return fileDescriptor_7b6696ef87ec1943, []int{2}
return fileDescriptor_2df576b66d12087a, []int{2}
}
func (m *Response) XXX_Unmarshal(b []byte) error {
@@ -235,7 +235,7 @@ func (m *Event) Reset() { *m = Event{} }
func (m *Event) String() string { return proto.CompactTextString(m) }
func (*Event) ProtoMessage() {}
func (*Event) Descriptor() ([]byte, []int) {
return fileDescriptor_7b6696ef87ec1943, []int{3}
return fileDescriptor_2df576b66d12087a, []int{3}
}
func (m *Event) XXX_Unmarshal(b []byte) error {
@@ -303,36 +303,33 @@ func init() {
proto.RegisterMapType((map[string]*Pair)(nil), "go.api.Event.HeaderEntry")
}
func init() {
proto.RegisterFile("github.com/micro/go-micro/v2/api/proto/api.proto", fileDescriptor_7b6696ef87ec1943)
}
func init() { proto.RegisterFile("api/proto/api.proto", fileDescriptor_2df576b66d12087a) }
var fileDescriptor_7b6696ef87ec1943 = []byte{
// 408 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0x4d, 0x8f, 0xd3, 0x30,
0x10, 0x55, 0xe2, 0x24, 0xbb, 0x99, 0x22, 0x84, 0x7c, 0x40, 0x66, 0x59, 0xa1, 0x2a, 0xa7, 0x0a,
0xa9, 0x29, 0xec, 0x72, 0x40, 0x5c, 0xa1, 0x5a, 0x8e, 0x2b, 0xff, 0x03, 0x77, 0x63, 0x25, 0x16,
0x4d, 0x1c, 0x62, 0xa7, 0x52, 0x7f, 0x1c, 0x07, 0x7e, 0x06, 0xff, 0x06, 0x79, 0xec, 0x7e, 0x50,
0x95, 0x0b, 0xf4, 0xf6, 0x62, 0xbf, 0x79, 0xf3, 0xe6, 0x8d, 0x03, 0xf3, 0x5a, 0xd9, 0x66, 0x5c,
0x95, 0x4f, 0xba, 0x5d, 0xb4, 0xea, 0x69, 0xd0, 0x8b, 0x5a, 0xcf, 0x3d, 0x10, 0xbd, 0x5a, 0xf4,
0x83, 0xb6, 0x88, 0x4a, 0x44, 0x34, 0xab, 0x75, 0x29, 0x7a, 0x55, 0xbc, 0x83, 0xe4, 0x51, 0xa8,
0x81, 0xbe, 0x00, 0xf2, 0x4d, 0x6e, 0x59, 0x34, 0x8d, 0x66, 0x39, 0x77, 0x90, 0xbe, 0x84, 0x6c,
0x23, 0xd6, 0xa3, 0x34, 0x2c, 0x9e, 0x92, 0x59, 0xce, 0xc3, 0x57, 0xf1, 0x93, 0xc0, 0x15, 0x97,
0xdf, 0x47, 0x69, 0xac, 0xe3, 0xb4, 0xd2, 0x36, 0xba, 0x0a, 0x85, 0xe1, 0x8b, 0x52, 0x48, 0x7a,
0x61, 0x1b, 0x16, 0xe3, 0x29, 0x62, 0x7a, 0x0f, 0x59, 0x23, 0x45, 0x25, 0x07, 0x46, 0xa6, 0x64,
0x36, 0xb9, 0x7b, 0x5d, 0x7a, 0x0b, 0x65, 0x10, 0x2b, 0xbf, 0xe2, 0xed, 0xb2, 0xb3, 0xc3, 0x96,
0x07, 0x2a, 0x7d, 0x0b, 0xa4, 0x96, 0x96, 0x25, 0x58, 0xc1, 0x4e, 0x2b, 0x1e, 0xa4, 0xf5, 0x74,
0x47, 0xa2, 0x73, 0x48, 0x7a, 0x6d, 0x2c, 0x4b, 0x91, 0xfc, 0xea, 0x94, 0xfc, 0xa8, 0x4d, 0x60,
0x23, 0xcd, 0x79, 0x5c, 0xe9, 0x6a, 0xcb, 0x32, 0xef, 0xd1, 0x61, 0x97, 0xc2, 0x38, 0xac, 0xd9,
0x95, 0x4f, 0x61, 0x1c, 0xd6, 0x37, 0x0f, 0x30, 0x39, 0xf2, 0x75, 0x26, 0xa6, 0x02, 0x52, 0x0c,
0x06, 0x67, 0x9d, 0xdc, 0x3d, 0xdb, 0xb5, 0x75, 0xa9, 0x72, 0x7f, 0xf5, 0x29, 0xfe, 0x18, 0xdd,
0x7c, 0x81, 0xeb, 0x9d, 0xdd, 0xff, 0x50, 0x59, 0x42, 0xbe, 0x9f, 0xe3, 0xdf, 0x65, 0x8a, 0x1f,
0x11, 0x5c, 0x73, 0x69, 0x7a, 0xdd, 0x19, 0x49, 0xdf, 0x00, 0x18, 0x2b, 0xec, 0x68, 0x3e, 0xeb,
0x4a, 0xa2, 0x5a, 0xca, 0x8f, 0x4e, 0xe8, 0x87, 0xfd, 0xe2, 0x62, 0x4c, 0xf6, 0xf6, 0x90, 0xac,
0x57, 0x38, 0xbb, 0xb9, 0x5d, 0xbc, 0xe4, 0x10, 0xef, 0xc5, 0xc2, 0x2c, 0x7e, 0x45, 0x90, 0x2e,
0x37, 0xb2, 0xc3, 0x2d, 0x76, 0xa2, 0x95, 0x41, 0x04, 0x31, 0x7d, 0x0e, 0xb1, 0xaa, 0xc2, 0xdb,
0x8b, 0x55, 0x45, 0x6f, 0x21, 0xb7, 0xaa, 0x95, 0xc6, 0x8a, 0xb6, 0x47, 0x3f, 0x84, 0x1f, 0x0e,
0xe8, 0xfb, 0xfd, 0x78, 0xc9, 0x9f, 0x0f, 0x07, 0x1b, 0xfc, 0x6d, 0xb6, 0x4a, 0x58, 0xc1, 0x52,
0xdf, 0xd4, 0xe1, 0x8b, 0xcd, 0xb6, 0xca, 0xf0, 0x07, 0xbd, 0xff, 0x1d, 0x00, 0x00, 0xff, 0xff,
0x97, 0xf3, 0x59, 0x6e, 0xd1, 0x03, 0x00, 0x00,
var fileDescriptor_2df576b66d12087a = []byte{
// 393 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0xcd, 0xce, 0xd3, 0x30,
0x10, 0x54, 0xe2, 0x24, 0x6d, 0xb6, 0x08, 0x21, 0x23, 0x21, 0x53, 0x2a, 0x54, 0xe5, 0x54, 0x21,
0x91, 0x42, 0xcb, 0x01, 0x71, 0x85, 0xaa, 0x1c, 0x2b, 0xbf, 0x81, 0xab, 0x58, 0x6d, 0x44, 0x13,
0x9b, 0xd8, 0xa9, 0xd4, 0x87, 0xe3, 0xc0, 0x63, 0xf0, 0x36, 0xc8, 0x1b, 0xf7, 0xe7, 0xab, 0xfa,
0x5d, 0xbe, 0xaf, 0xb7, 0x89, 0x3d, 0x3b, 0x3b, 0x3b, 0xeb, 0xc0, 0x6b, 0xa1, 0xcb, 0xa9, 0x6e,
0x94, 0x55, 0x53, 0xa1, 0xcb, 0x1c, 0x11, 0x4d, 0x36, 0x2a, 0x17, 0xba, 0xcc, 0x3e, 0x41, 0xb4,
0x12, 0x65, 0x43, 0x5f, 0x01, 0xf9, 0x25, 0x0f, 0x2c, 0x18, 0x07, 0x93, 0x94, 0x3b, 0x48, 0xdf,
0x40, 0xb2, 0x17, 0xbb, 0x56, 0x1a, 0x16, 0x8e, 0xc9, 0x24, 0xe5, 0xfe, 0x2b, 0xfb, 0x4b, 0xa0,
0xc7, 0xe5, 0xef, 0x56, 0x1a, 0xeb, 0x38, 0x95, 0xb4, 0x5b, 0x55, 0xf8, 0x42, 0xff, 0x45, 0x29,
0x44, 0x5a, 0xd8, 0x2d, 0x0b, 0xf1, 0x14, 0x31, 0x9d, 0x43, 0xb2, 0x95, 0xa2, 0x90, 0x0d, 0x23,
0x63, 0x32, 0x19, 0xcc, 0xde, 0xe5, 0x9d, 0x85, 0xdc, 0x8b, 0xe5, 0x3f, 0xf1, 0x76, 0x51, 0xdb,
0xe6, 0xc0, 0x3d, 0x95, 0x7e, 0x00, 0xb2, 0x91, 0x96, 0x45, 0x58, 0xc1, 0xae, 0x2b, 0x96, 0xd2,
0x76, 0x74, 0x47, 0xa2, 0x1f, 0x21, 0xd2, 0xca, 0x58, 0x16, 0x23, 0xf9, 0xed, 0x35, 0x79, 0xa5,
0x8c, 0x67, 0x23, 0xcd, 0x79, 0x5c, 0xab, 0xe2, 0xc0, 0x92, 0xce, 0xa3, 0xc3, 0x2e, 0x85, 0xb6,
0xd9, 0xb1, 0x5e, 0x97, 0x42, 0xdb, 0xec, 0x86, 0x4b, 0x18, 0x5c, 0xf8, 0xba, 0x11, 0x53, 0x06,
0x31, 0x06, 0x83, 0xb3, 0x0e, 0x66, 0x2f, 0x8e, 0x6d, 0x5d, 0xaa, 0xbc, 0xbb, 0xfa, 0x16, 0x7e,
0x0d, 0x86, 0x3f, 0xa0, 0x7f, 0xb4, 0xfb, 0x0c, 0x95, 0x05, 0xa4, 0xa7, 0x39, 0x9e, 0x2e, 0x93,
0xfd, 0x09, 0xa0, 0xcf, 0xa5, 0xd1, 0xaa, 0x36, 0x92, 0xbe, 0x07, 0x30, 0x56, 0xd8, 0xd6, 0x7c,
0x57, 0x85, 0x44, 0xb5, 0x98, 0x5f, 0x9c, 0xd0, 0x2f, 0xa7, 0xc5, 0x85, 0x98, 0xec, 0xe8, 0x9c,
0x6c, 0xa7, 0x70, 0x73, 0x73, 0xc7, 0x78, 0xc9, 0x39, 0xde, 0xbb, 0x85, 0x99, 0xfd, 0x0b, 0x20,
0x5e, 0xec, 0x65, 0x8d, 0x5b, 0xac, 0x45, 0x25, 0xbd, 0x08, 0x62, 0xfa, 0x12, 0xc2, 0xb2, 0xf0,
0x6f, 0x2f, 0x2c, 0x0b, 0x3a, 0x82, 0xd4, 0x96, 0x95, 0x34, 0x56, 0x54, 0x1a, 0xfd, 0x10, 0x7e,
0x3e, 0xa0, 0x9f, 0x4f, 0xe3, 0x45, 0x0f, 0x1f, 0x0e, 0x36, 0x78, 0x6c, 0xb6, 0x42, 0x58, 0xc1,
0xe2, 0xae, 0xa9, 0xc3, 0x77, 0x9b, 0x6d, 0x9d, 0xe0, 0x0f, 0x3a, 0xff, 0x1f, 0x00, 0x00, 0xff,
0xff, 0xd4, 0x6d, 0x70, 0x51, 0xb7, 0x03, 0x00, 0x00,
}
+1 -1
View File
@@ -1,5 +1,5 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: github.com/micro/go-micro/v2/api/proto/api.proto
// source: api/proto/api.proto
package go_api
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"net/http"
"strings"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/asim/go-micro/v3/api/resolver"
)
type Resolver struct{}
+5 -3
View File
@@ -4,10 +4,12 @@ package host
import (
"net/http"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/asim/go-micro/v3/api/resolver"
)
type Resolver struct{}
type Resolver struct {
opts resolver.Options
}
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
return &resolver.Endpoint{
@@ -23,5 +25,5 @@ func (r *Resolver) String() string {
}
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{}
return &Resolver{opts: resolver.NewOptions(opts...)}
}
-45
View File
@@ -1,45 +0,0 @@
// Package micro provides a micro rpc resolver which prefixes a namespace
package micro
import (
"net/http"
"github.com/micro/go-micro/v2/api/resolver"
)
// default resolver for legacy purposes
// it uses proxy routing to resolve names
// /foo becomes namespace.foo
// /v1/foo becomes namespace.v1.foo
type Resolver struct {
Options resolver.Options
}
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
var name, method string
switch r.Options.Handler {
// internal handlers
case "meta", "api", "rpc", "micro":
name, method = apiRoute(req.URL.Path)
default:
method = req.Method
name = proxyRoute(req.URL.Path)
}
return &resolver.Endpoint{
Name: name,
Method: method,
}, nil
}
func (r *Resolver) String() string {
return "micro"
}
// NewResolver creates a new micro resolver
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{
Options: resolver.NewOptions(opts...),
}
}
-90
View File
@@ -1,90 +0,0 @@
package micro
import (
"path"
"regexp"
"strings"
)
var (
proxyRe = regexp.MustCompile("^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$")
versionRe = regexp.MustCompilePOSIX("^v[0-9]+$")
)
// Translates /foo/bar/zool into api service go.micro.api.foo method Bar.Zool
// Translates /foo/bar into api service go.micro.api.foo method Foo.Bar
func apiRoute(p string) (string, string) {
p = path.Clean(p)
p = strings.TrimPrefix(p, "/")
parts := strings.Split(p, "/")
// If we've got two or less parts
// Use first part as service
// Use all parts as method
if len(parts) <= 2 {
name := parts[0]
return name, methodName(parts)
}
// Treat /v[0-9]+ as versioning where we have 3 parts
// /v1/foo/bar => service: v1.foo method: Foo.bar
if len(parts) == 3 && versionRe.Match([]byte(parts[0])) {
name := strings.Join(parts[:len(parts)-1], ".")
return name, methodName(parts[len(parts)-2:])
}
// Service is everything minus last two parts
// Method is the last two parts
name := strings.Join(parts[:len(parts)-2], ".")
return name, methodName(parts[len(parts)-2:])
}
func proxyRoute(p string) string {
parts := strings.Split(p, "/")
if len(parts) < 2 {
return ""
}
var service string
var alias string
// /[service]/methods
if len(parts) > 2 {
// /v1/[service]
if versionRe.MatchString(parts[1]) {
service = parts[1] + "." + parts[2]
alias = parts[2]
} else {
service = parts[1]
alias = parts[1]
}
// /[service]
} else {
service = parts[1]
alias = parts[1]
}
// check service name is valid
if !proxyRe.MatchString(alias) {
return ""
}
return service
}
func methodName(parts []string) string {
for i, part := range parts {
parts[i] = toCamel(part)
}
return strings.Join(parts, ".")
}
func toCamel(s string) string {
words := strings.Split(s, "-")
var out string
for _, word := range words {
out += strings.Title(word)
}
return out
}
-130
View File
@@ -1,130 +0,0 @@
package micro
import (
"testing"
)
func TestApiRoute(t *testing.T) {
testData := []struct {
path string
service string
method string
}{
{
"/foo/bar",
"foo",
"Foo.Bar",
},
{
"/foo/foo/bar",
"foo",
"Foo.Bar",
},
{
"/foo/bar/baz",
"foo",
"Bar.Baz",
},
{
"/foo/bar/baz-xyz",
"foo",
"Bar.BazXyz",
},
{
"/foo/bar/baz/cat",
"foo.bar",
"Baz.Cat",
},
{
"/foo/bar/baz/cat/car",
"foo.bar.baz",
"Cat.Car",
},
{
"/foo/fooBar/bazCat",
"foo",
"FooBar.BazCat",
},
{
"/v1/foo/bar",
"v1.foo",
"Foo.Bar",
},
{
"/v1/foo/bar/baz",
"v1.foo",
"Bar.Baz",
},
{
"/v1/foo/bar/baz/cat",
"v1.foo.bar",
"Baz.Cat",
},
}
for _, d := range testData {
s, m := apiRoute(d.path)
if d.service != s {
t.Fatalf("Expected service: %s for path: %s got: %s %s", d.service, d.path, s, m)
}
if d.method != m {
t.Fatalf("Expected service: %s for path: %s got: %s", d.method, d.path, m)
}
}
}
func TestProxyRoute(t *testing.T) {
testData := []struct {
path string
service string
}{
// no namespace
{
"/f",
"f",
},
{
"/f",
"f",
},
{
"/f-b",
"f-b",
},
{
"/foo/bar",
"foo",
},
{
"/foo-bar",
"foo-bar",
},
{
"/foo-bar-baz",
"foo-bar-baz",
},
{
"/foo/bar/bar",
"foo",
},
{
"/v1/foo/bar",
"v1.foo",
},
{
"/v1/foo/bar/baz",
"v1.foo",
},
{
"/v1/foo/bar/baz/cat",
"v1.foo",
},
}
for _, d := range testData {
s := proxyRoute(d.path)
if d.service != s {
t.Fatalf("Expected service: %s for path: %s got: %s", d.service, d.path, s)
}
}
}
+11 -2
View File
@@ -1,11 +1,20 @@
package resolver
import (
"net/http"
)
// 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
}
@@ -16,8 +25,8 @@ func WithHandler(h string) Option {
}
}
// WithNamespace sets the namespace being used
func WithNamespace(n string) Option {
// WithNamespace sets the function which determines the namespace for a request
func WithNamespace(n func(*http.Request) string) Option {
return func(o *Options) {
o.Namespace = n
}
+10 -6
View File
@@ -2,22 +2,26 @@
package path
import (
"errors"
"net/http"
"strings"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/asim/go-micro/v3/api/resolver"
)
type Resolver struct{}
type Resolver struct {
opts resolver.Options
}
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
if req.URL.Path == "/" {
return nil, errors.New("unknown name")
return nil, resolver.ErrNotFound
}
parts := strings.Split(req.URL.Path[1:], "/")
ns := r.opts.Namespace(req)
return &resolver.Endpoint{
Name: parts[0],
Name: ns + "." + parts[0],
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
@@ -29,5 +33,5 @@ func (r *Resolver) String() string {
}
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{}
return &Resolver{opts: resolver.NewOptions(opts...)}
}
+14 -1
View File
@@ -2,9 +2,15 @@
package resolver
import (
"errors"
"net/http"
)
var (
ErrNotFound = errors.New("not found")
ErrInvalidPath = errors.New("invalid path")
)
// Resolver resolves requests to endpoints
type Resolver interface {
Resolve(r *http.Request) (*Endpoint, error)
@@ -25,7 +31,14 @@ type Endpoint struct {
type Options struct {
Handler string
Namespace 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
}
}
+18 -8
View File
@@ -7,10 +7,16 @@ import (
"regexp"
"strings"
"github.com/micro/go-micro/v2/api/resolver"
"github.com/asim/go-micro/v3/api/resolver"
)
type Resolver struct{}
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{opts: resolver.NewOptions(opts...)}
}
type Resolver struct {
opts resolver.Options
}
var (
re = regexp.MustCompile("^v[0-9]+$")
@@ -22,10 +28,9 @@ func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
}
parts := strings.Split(req.URL.Path[1:], "/")
if len(parts) == 1 {
return &resolver.Endpoint{
Name: parts[0],
Name: r.withNamespace(req, parts...),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
@@ -35,7 +40,7 @@ func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
// /v1/foo
if re.MatchString(parts[0]) {
return &resolver.Endpoint{
Name: parts[1],
Name: r.withNamespace(req, parts[0:2]...),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
@@ -43,7 +48,7 @@ func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
}
return &resolver.Endpoint{
Name: parts[0],
Name: r.withNamespace(req, parts[0]),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
@@ -54,6 +59,11 @@ func (r *Resolver) String() string {
return "path"
}
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{}
func (r *Resolver) withNamespace(req *http.Request, parts ...string) string {
ns := r.opts.Namespace(req)
if len(ns) == 0 {
return strings.Join(parts, ".")
}
return strings.Join(append([]string{ns}, parts...), ".")
}
+7 -15
View File
@@ -1,16 +1,15 @@
package router
import (
"github.com/micro/go-micro/v2/api/resolver"
"github.com/micro/go-micro/v2/api/resolver/micro"
"github.com/micro/go-micro/v2/registry"
"github.com/asim/go-micro/v3/api/resolver"
"github.com/asim/go-micro/v3/api/resolver/vpath"
"github.com/asim/go-micro/v3/registry"
)
type Options struct {
Namespace string
Handler string
Registry registry.Registry
Resolver resolver.Resolver
Handler string
Registry registry.Registry
Resolver resolver.Resolver
}
type Option func(o *Options)
@@ -26,9 +25,8 @@ func NewOptions(opts ...Option) Options {
}
if options.Resolver == nil {
options.Resolver = micro.NewResolver(
options.Resolver = vpath.NewResolver(
resolver.WithHandler(options.Handler),
resolver.WithNamespace(options.Namespace),
)
}
@@ -41,12 +39,6 @@ func WithHandler(h string) Option {
}
}
func WithNamespace(ns string) Option {
return func(o *Options) {
o.Namespace = ns
}
}
func WithRegistry(r registry.Registry) Option {
return func(o *Options) {
o.Registry = r
+178 -73
View File
@@ -4,19 +4,28 @@ package registry
import (
"errors"
"fmt"
"log"
"net/http"
"regexp"
"strings"
"sync"
"time"
"github.com/micro/go-micro/v2/api"
"github.com/micro/go-micro/v2/api/router"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/cache"
"github.com/asim/go-micro/v3/api"
"github.com/asim/go-micro/v3/api/router"
"github.com/asim/go-micro/v3/api/router/util"
"github.com/asim/go-micro/v3/logger"
"github.com/asim/go-micro/v3/metadata"
"github.com/asim/go-micro/v3/registry"
"github.com/asim/go-micro/v3/registry/cache"
)
// endpoint struct, that holds compiled pcre
type endpoint struct {
hostregs []*regexp.Regexp
pathregs []util.Pattern
pcreregs []*regexp.Regexp
}
// router is the default router
type registryRouter struct {
exit chan bool
@@ -27,28 +36,8 @@ type registryRouter struct {
sync.RWMutex
eps map[string]*api.Service
}
func setNamespace(ns, name string) string {
ns = strings.TrimSpace(ns)
name = strings.TrimSpace(name)
// no namespace
if len(ns) == 0 {
return name
}
switch {
// has - suffix
case strings.HasSuffix(ns, "-"):
return strings.Replace(ns+name, ".", "-", -1)
// has . suffix
case strings.HasSuffix(ns, "."):
return ns + name
}
// default join .
return strings.Join([]string{ns, name}, ".")
// compiled regexp for host and path
ceps map[string]*endpoint
}
func (r *registryRouter) isClosed() bool {
@@ -68,7 +57,9 @@ func (r *registryRouter) refresh() {
services, err := r.opts.Registry.ListServices()
if err != nil {
attempts++
log.Println("Error listing endpoints", err)
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("unable to list services: %v", err)
}
time.Sleep(time.Duration(attempts) * time.Second)
continue
}
@@ -77,18 +68,18 @@ func (r *registryRouter) refresh() {
// for each service, get service and store endpoints
for _, s := range services {
// only get services for this namespace
if !strings.HasPrefix(s.Name, r.opts.Namespace) {
continue
}
service, err := r.rc.GetService(s.Name)
if err != nil {
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("unable to get service: %v", err)
}
continue
}
r.store(service)
}
// refresh list in 10 minutes... cruft
// use registry watching
select {
case <-time.After(time.Minute * 10):
case <-r.exit:
@@ -100,13 +91,16 @@ func (r *registryRouter) refresh() {
// process watch event
func (r *registryRouter) process(res *registry.Result) {
// skip these things
if res == nil || res.Service == nil || !strings.HasPrefix(res.Service.Name, r.opts.Namespace) {
if res == nil || res.Service == nil {
return
}
// get entry from cache
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)
}
return
}
@@ -128,14 +122,17 @@ func (r *registryRouter) store(services []*registry.Service) {
names[service.Name] = true
// map per endpoint
for _, endpoint := range service.Endpoints {
for _, sep := range service.Endpoints {
// create a key service:endpoint_name
key := fmt.Sprintf("%s:%s", service.Name, endpoint.Name)
key := fmt.Sprintf("%s.%s", service.Name, sep.Name)
// decode endpoint
end := api.Decode(endpoint.Metadata)
end := api.Decode(sep.Metadata)
// if we got nothing skip
if err := api.Validate(end); err != nil {
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
logger.Tracef("endpoint validation failed: %v", err)
}
continue
}
@@ -170,8 +167,57 @@ func (r *registryRouter) store(services []*registry.Service) {
}
// now set the eps we have
for name, endpoint := range eps {
r.eps[name] = endpoint
for name, ep := range eps {
r.eps[name] = ep
cep := &endpoint{}
for _, h := range ep.Endpoint.Host {
if h == "" || h == "*" {
continue
}
hostreg, err := regexp.CompilePOSIX(h)
if err != nil {
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
logger.Tracef("endpoint have invalid host regexp: %v", err)
}
continue
}
cep.hostregs = append(cep.hostregs, hostreg)
}
for _, p := range ep.Endpoint.Path {
var pcreok bool
if p[0] == '^' && p[len(p)-1] == '$' {
pcrereg, err := regexp.CompilePOSIX(p)
if err == nil {
cep.pcreregs = append(cep.pcreregs, pcrereg)
pcreok = true
}
}
rule, err := util.Parse(p)
if err != nil && !pcreok {
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
logger.Tracef("endpoint have invalid path pattern: %v", err)
}
continue
} else if err != nil && pcreok {
continue
}
tpl := rule.Compile()
pathreg, err := util.NewPattern(tpl.Version, tpl.OpCodes, tpl.Pool, "")
if err != nil {
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
logger.Tracef("endpoint have invalid path pattern: %v", err)
}
continue
}
cep.pathregs = append(cep.pathregs, pathreg)
}
r.ceps[name] = cep
}
}
@@ -188,7 +234,9 @@ func (r *registryRouter) watch() {
w, err := r.opts.Registry.Watch()
if err != nil {
attempts++
log.Println("Error watching endpoints", err)
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("error watching endpoints: %v", err)
}
time.Sleep(time.Duration(attempts) * time.Second)
continue
}
@@ -211,7 +259,9 @@ func (r *registryRouter) watch() {
// process next event
res, err := w.Next()
if err != nil {
log.Println("Error getting next endpoint", err)
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
logger.Errorf("error getting next endoint: %v", err)
}
close(ch)
break
}
@@ -235,6 +285,14 @@ func (r *registryRouter) Close() error {
return nil
}
func (r *registryRouter) Register(ep *api.Endpoint) error {
return nil
}
func (r *registryRouter) Deregister(ep *api.Endpoint) error {
return nil
}
func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) {
if r.isClosed() {
return nil, errors.New("router closed")
@@ -243,60 +301,106 @@ func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) {
r.RLock()
defer r.RUnlock()
var idx int
if len(req.URL.Path) > 0 && req.URL.Path != "/" {
idx = 1
}
path := strings.Split(req.URL.Path[idx:], "/")
// use the first match
// TODO: weighted matching
for _, e := range r.eps {
for n, e := range r.eps {
cep, ok := r.ceps[n]
if !ok {
continue
}
ep := e.Endpoint
// match
var pathMatch, hostMatch, methodMatch bool
// 1. try method GET, POST, PUT, etc
// 2. try host example.com, foobar.com, etc
// 3. try path /foo/bar, /bar/baz, etc
// 1. try match method
var mMatch, hMatch, pMatch bool
// 1. try method
for _, m := range ep.Method {
if req.Method == m {
methodMatch = true
if m == req.Method {
mMatch = true
break
}
}
// no match on method pass
if len(ep.Method) > 0 && !methodMatch {
if !mMatch {
continue
}
// 2. try match host
for _, h := range ep.Host {
if req.Host == h {
hostMatch = true
break
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api method match %s", req.Method)
}
// no match on host pass
if len(ep.Host) > 0 && !hostMatch {
// 2. try host
if len(ep.Host) == 0 {
hMatch = true
} else {
for idx, h := range ep.Host {
if h == "" || h == "*" {
hMatch = true
break
} else {
if cep.hostregs[idx].MatchString(req.URL.Host) {
hMatch = true
break
}
}
}
}
if !hMatch {
continue
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api host match %s", req.URL.Host)
}
// 3. try match paths
for _, p := range ep.Path {
re, err := regexp.CompilePOSIX(p)
if err == nil && re.MatchString(req.URL.Path) {
pathMatch = true
// 3. try path via google.api path matching
for _, pathreg := range cep.pathregs {
matches, err := pathreg.Match(path, "")
if err != nil {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api gpath not match %s != %v", path, pathreg)
}
continue
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api gpath match %s = %v", path, pathreg)
}
pMatch = true
ctx := req.Context()
md, ok := metadata.FromContext(ctx)
if !ok {
md = make(metadata.Metadata)
}
for k, v := range matches {
md[fmt.Sprintf("x-api-field-%s", k)] = v
}
md["x-api-body"] = ep.Body
*req = *req.Clone(metadata.NewContext(ctx, md))
break
}
if !pMatch {
// 4. try path via pcre path matching
for _, pathreg := range cep.pcreregs {
if !pathreg.MatchString(req.URL.Path) {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api pcre path not match %s != %v", path, pathreg)
}
continue
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api pcre path match %s != %v", path, pathreg)
}
pMatch = true
break
}
}
// no match pass
if len(ep.Path) > 0 && !pathMatch {
if !pMatch {
continue
}
// TODO: Percentage traffic
// we got here, so its a match
return e, nil
}
@@ -327,7 +431,7 @@ func (r *registryRouter) Route(req *http.Request) (*api.Service, error) {
}
// service name
name := setNamespace(r.opts.Namespace, rp.Name)
name := rp.Name
// get service
services, err := r.rc.GetService(name)
@@ -381,6 +485,7 @@ func newRouter(opts ...router.Option) *registryRouter {
opts: options,
rc: cache.New(options.Registry),
eps: make(map[string]*api.Service),
ceps: make(map[string]*endpoint),
}
go r.watch()
go r.refresh()
+23 -170
View File
@@ -1,181 +1,34 @@
package registry
import (
"fmt"
"net/http"
"net/url"
"testing"
"github.com/micro/go-micro/v2/api"
"github.com/asim/go-micro/v3/registry"
"github.com/stretchr/testify/assert"
)
func TestSetNamespace(t *testing.T) {
testCases := []struct {
namespace string
name string
expected string
}{
// default dotted path
func TestStoreRegex(t *testing.T) {
router := newRouter()
router.store([]*registry.Service{
{
"go.micro.api",
"foo",
"go.micro.api.foo",
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{},
},
// dotted end
{
"go.micro.api.",
"foo",
"go.micro.api.foo",
},
// dashed end
{
"go-micro-api-",
"foo",
"go-micro-api-foo",
},
// no namespace
{
"",
"foo",
"foo",
},
{
"go-micro-api-",
"v2.foo",
"go-micro-api-v2-foo",
},
}
for _, test := range testCases {
name := setNamespace(test.namespace, test.name)
if name != test.expected {
t.Fatalf("expected name %s got %s", test.expected, name)
}
}
}
func TestRouter(t *testing.T) {
r := newRouter()
compare := func(expect, got []string) bool {
// no data to compare, return true
if len(expect) == 0 && len(got) == 0 {
return true
}
// no data expected but got some return false
if len(expect) == 0 && len(got) > 0 {
return false
}
// compare expected with what we got
for _, e := range expect {
var seen bool
for _, g := range got {
if e == g {
seen = true
break
}
}
if !seen {
return false
}
}
// we're done, return true
return true
}
testData := []struct {
e *api.Endpoint
r *http.Request
m bool
}{
{
e: &api.Endpoint{
Name: "Foo.Bar",
Host: []string{"example.com"},
Method: []string{"GET"},
Path: []string{"/foo"},
},
r: &http.Request{
Host: "example.com",
Method: "GET",
URL: &url.URL{
Path: "/foo",
},
},
m: true,
},
{
e: &api.Endpoint{
Name: "Bar.Baz",
Host: []string{"example.com", "foo.com"},
Method: []string{"GET", "POST"},
Path: []string{"/foo/bar"},
},
r: &http.Request{
Host: "foo.com",
Method: "POST",
URL: &url.URL{
Path: "/foo/bar",
},
},
m: true,
},
{
e: &api.Endpoint{
Name: "Test.Cruft",
Host: []string{"example.com", "foo.com"},
Method: []string{"GET", "POST"},
Path: []string{"/xyz"},
},
r: &http.Request{
Host: "fail.com",
Method: "DELETE",
URL: &url.URL{
Path: "/test/fail",
},
},
m: false,
},
}
for _, d := range testData {
key := fmt.Sprintf("%s:%s", "test.service", d.e.Name)
r.eps[key] = &api.Service{
Endpoint: d.e,
}
}
for _, d := range testData {
e, err := r.Endpoint(d.r)
if d.m && err != nil {
t.Fatalf("expected match, got %v", err)
}
if !d.m && err == nil {
t.Fatal("expected error got match")
}
// skip testing the non match
if !d.m {
continue
}
ep := e.Endpoint
// test the match
if d.e.Name != ep.Name {
t.Fatalf("expected %v got %v", d.e.Name, ep.Name)
}
if ok := compare(d.e.Method, ep.Method); !ok {
t.Fatalf("expected %v got %v", d.e.Method, ep.Method)
}
if ok := compare(d.e.Path, ep.Path); !ok {
t.Fatalf("expected %v got %v", d.e.Path, ep.Path)
}
if ok := compare(d.e.Host, ep.Host); !ok {
t.Fatalf("expected %v got %v", d.e.Host, ep.Host)
}
}
},
)
assert.Len(t, router.ceps["Foobar.foo"].pcreregs, 1)
}
+5 -1
View File
@@ -4,7 +4,7 @@ package router
import (
"net/http"
"github.com/micro/go-micro/v2/api"
"github.com/asim/go-micro/v3/api"
)
// Router is used to determine an endpoint for a request
@@ -15,6 +15,10 @@ type Router interface {
Close() error
// Endpoint returns an api.Service endpoint or an error if it does not exist
Endpoint(r *http.Request) (*api.Service, error)
// Register endpoint in router
Register(ep *api.Endpoint) error
// Deregister endpoint from router
Deregister(ep *api.Endpoint) error
// Route returns an api.Service route
Route(r *http.Request) (*api.Service, error)
}
+356
View File
@@ -0,0 +1,356 @@
package static
import (
"errors"
"fmt"
"net/http"
"regexp"
"strings"
"sync"
"github.com/asim/go-micro/v3/api"
"github.com/asim/go-micro/v3/api/router"
"github.com/asim/go-micro/v3/api/router/util"
"github.com/asim/go-micro/v3/logger"
"github.com/asim/go-micro/v3/metadata"
"github.com/asim/go-micro/v3/registry"
rutil "github.com/asim/go-micro/v3/util/registry"
)
type endpoint struct {
apiep *api.Endpoint
hostregs []*regexp.Regexp
pathregs []util.Pattern
pcreregs []*regexp.Regexp
}
// router is the default router
type staticRouter struct {
exit chan bool
opts router.Options
sync.RWMutex
eps map[string]*endpoint
}
func (r *staticRouter) isClosed() bool {
select {
case <-r.exit:
return true
default:
return false
}
}
/*
// watch for endpoint changes
func (r *staticRouter) watch() {
var attempts int
for {
if r.isClosed() {
return
}
// watch for changes
w, err := r.opts.Registry.Watch()
if err != nil {
attempts++
log.Println("Error watching endpoints", err)
time.Sleep(time.Duration(attempts) * time.Second)
continue
}
ch := make(chan bool)
go func() {
select {
case <-ch:
w.Stop()
case <-r.exit:
w.Stop()
}
}()
// reset if we get here
attempts = 0
for {
// process next event
res, err := w.Next()
if err != nil {
log.Println("Error getting next endpoint", err)
close(ch)
break
}
r.process(res)
}
}
}
*/
func (r *staticRouter) Register(ep *api.Endpoint) error {
if err := api.Validate(ep); err != nil {
return err
}
var pathregs []util.Pattern
var hostregs []*regexp.Regexp
var pcreregs []*regexp.Regexp
for _, h := range ep.Host {
if h == "" || h == "*" {
continue
}
hostreg, err := regexp.CompilePOSIX(h)
if err != nil {
return err
}
hostregs = append(hostregs, hostreg)
}
for _, p := range ep.Path {
var pcreok bool
// pcre only when we have start and end markers
if p[0] == '^' && p[len(p)-1] == '$' {
pcrereg, err := regexp.CompilePOSIX(p)
if err == nil {
pcreregs = append(pcreregs, pcrereg)
pcreok = true
}
}
rule, err := util.Parse(p)
if err != nil && !pcreok {
return err
} else if err != nil && pcreok {
continue
}
tpl := rule.Compile()
pathreg, err := util.NewPattern(tpl.Version, tpl.OpCodes, tpl.Pool, "")
if err != nil {
return err
}
pathregs = append(pathregs, pathreg)
}
r.Lock()
r.eps[ep.Name] = &endpoint{
apiep: ep,
pcreregs: pcreregs,
pathregs: pathregs,
hostregs: hostregs,
}
r.Unlock()
return nil
}
func (r *staticRouter) Deregister(ep *api.Endpoint) error {
if err := api.Validate(ep); err != nil {
return err
}
r.Lock()
delete(r.eps, ep.Name)
r.Unlock()
return nil
}
func (r *staticRouter) Options() router.Options {
return r.opts
}
func (r *staticRouter) Close() error {
select {
case <-r.exit:
return nil
default:
close(r.exit)
}
return nil
}
func (r *staticRouter) Endpoint(req *http.Request) (*api.Service, error) {
ep, err := r.endpoint(req)
if err != nil {
return nil, err
}
epf := strings.Split(ep.apiep.Name, ".")
services, err := r.opts.Registry.GetService(epf[0])
if err != nil {
return nil, err
}
// hack for stream endpoint
if ep.apiep.Stream {
svcs := rutil.Copy(services)
for _, svc := range svcs {
if len(svc.Endpoints) == 0 {
e := &registry.Endpoint{}
e.Name = strings.Join(epf[1:], ".")
e.Metadata = make(map[string]string)
e.Metadata["stream"] = "true"
svc.Endpoints = append(svc.Endpoints, e)
}
for _, e := range svc.Endpoints {
e.Name = strings.Join(epf[1:], ".")
e.Metadata = make(map[string]string)
e.Metadata["stream"] = "true"
}
}
services = svcs
}
svc := &api.Service{
Name: epf[0],
Endpoint: &api.Endpoint{
Name: strings.Join(epf[1:], "."),
Handler: "rpc",
Host: ep.apiep.Host,
Method: ep.apiep.Method,
Path: ep.apiep.Path,
Body: ep.apiep.Body,
Stream: ep.apiep.Stream,
},
Services: services,
}
return svc, nil
}
func (r *staticRouter) endpoint(req *http.Request) (*endpoint, error) {
if r.isClosed() {
return nil, errors.New("router closed")
}
r.RLock()
defer r.RUnlock()
var idx int
if len(req.URL.Path) > 0 && req.URL.Path != "/" {
idx = 1
}
path := strings.Split(req.URL.Path[idx:], "/")
// use the first match
// TODO: weighted matching
for _, ep := range r.eps {
var mMatch, hMatch, pMatch bool
// 1. try method
for _, m := range ep.apiep.Method {
if m == req.Method {
mMatch = true
break
}
}
if !mMatch {
continue
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api method match %s", req.Method)
}
// 2. try host
if len(ep.apiep.Host) == 0 {
hMatch = true
} else {
for idx, h := range ep.apiep.Host {
if h == "" || h == "*" {
hMatch = true
break
} else {
if ep.hostregs[idx].MatchString(req.URL.Host) {
hMatch = true
break
}
}
}
}
if !hMatch {
continue
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api host match %s", req.URL.Host)
}
// 3. try google.api path
for _, pathreg := range ep.pathregs {
matches, err := pathreg.Match(path, "")
if err != nil {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api gpath not match %s != %v", path, pathreg)
}
continue
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api gpath match %s = %v", path, pathreg)
}
pMatch = true
ctx := req.Context()
md, ok := metadata.FromContext(ctx)
if !ok {
md = make(metadata.Metadata)
}
for k, v := range matches {
md[fmt.Sprintf("x-api-field-%s", k)] = v
}
md["x-api-body"] = ep.apiep.Body
*req = *req.Clone(metadata.NewContext(ctx, md))
break
}
if !pMatch {
// 4. try path via pcre path matching
for _, pathreg := range ep.pcreregs {
if !pathreg.MatchString(req.URL.Path) {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("api pcre path not match %s != %v", req.URL.Path, pathreg)
}
continue
}
pMatch = true
break
}
}
if !pMatch {
continue
}
// TODO: Percentage traffic
// we got here, so its a match
return ep, nil
}
// no match
return nil, fmt.Errorf("endpoint not found for %v", req.URL)
}
func (r *staticRouter) Route(req *http.Request) (*api.Service, error) {
if r.isClosed() {
return nil, errors.New("router closed")
}
// try get an endpoint
ep, err := r.Endpoint(req)
if err != nil {
return nil, err
}
return ep, nil
}
func NewRouter(opts ...router.Option) *staticRouter {
options := router.NewOptions(opts...)
r := &staticRouter{
exit: make(chan bool),
opts: options,
eps: make(map[string]*endpoint),
}
//go r.watch()
//go r.refresh()
return r
}
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2015, Gengo, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Gengo, Inc. nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+115
View File
@@ -0,0 +1,115 @@
package util
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/compile.go
const (
opcodeVersion = 1
)
// Template is a compiled representation of path templates.
type Template struct {
// Version is the version number of the format.
Version int
// OpCodes is a sequence of operations.
OpCodes []int
// Pool is a constant pool
Pool []string
// Verb is a VERB part in the template.
Verb string
// Fields is a list of field paths bound in this template.
Fields []string
// Original template (example: /v1/a_bit_of_everything)
Template string
}
// Compiler compiles utilities representation of path templates into marshallable operations.
// They can be unmarshalled by runtime.NewPattern.
type Compiler interface {
Compile() Template
}
type op struct {
// code is the opcode of the operation
code OpCode
// str is a string operand of the code.
// operand is ignored if str is not empty.
str string
// operand is a numeric operand of the code.
operand int
}
func (w wildcard) compile() []op {
return []op{
{code: OpPush},
}
}
func (w deepWildcard) compile() []op {
return []op{
{code: OpPushM},
}
}
func (l literal) compile() []op {
return []op{
{
code: OpLitPush,
str: string(l),
},
}
}
func (v variable) compile() []op {
var ops []op
for _, s := range v.segments {
ops = append(ops, s.compile()...)
}
ops = append(ops, op{
code: OpConcatN,
operand: len(v.segments),
}, op{
code: OpCapture,
str: v.path,
})
return ops
}
func (t template) Compile() Template {
var rawOps []op
for _, s := range t.segments {
rawOps = append(rawOps, s.compile()...)
}
var (
ops []int
pool []string
fields []string
)
consts := make(map[string]int)
for _, op := range rawOps {
ops = append(ops, int(op.code))
if op.str == "" {
ops = append(ops, op.operand)
} else {
if _, ok := consts[op.str]; !ok {
consts[op.str] = len(pool)
pool = append(pool, op.str)
}
ops = append(ops, consts[op.str])
}
if op.code == OpCapture {
fields = append(fields, op.str)
}
}
return Template{
Version: opcodeVersion,
OpCodes: ops,
Pool: pool,
Verb: t.verb,
Fields: fields,
Template: t.template,
}
}
+122
View File
@@ -0,0 +1,122 @@
package util
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/compile_test.go
import (
"reflect"
"testing"
)
const (
operandFiller = 0
)
func TestCompile(t *testing.T) {
for _, spec := range []struct {
segs []segment
verb string
ops []int
pool []string
fields []string
}{
{},
{
segs: []segment{
wildcard{},
},
ops: []int{int(OpPush), operandFiller},
},
{
segs: []segment{
deepWildcard{},
},
ops: []int{int(OpPushM), operandFiller},
},
{
segs: []segment{
literal("v1"),
},
ops: []int{int(OpLitPush), 0},
pool: []string{"v1"},
},
{
segs: []segment{
literal("v1"),
},
verb: "LOCK",
ops: []int{int(OpLitPush), 0},
pool: []string{"v1"},
},
{
segs: []segment{
variable{
path: "name.nested",
segments: []segment{
wildcard{},
},
},
},
ops: []int{
int(OpPush), operandFiller,
int(OpConcatN), 1,
int(OpCapture), 0,
},
pool: []string{"name.nested"},
fields: []string{"name.nested"},
},
{
segs: []segment{
literal("obj"),
variable{
path: "name.nested",
segments: []segment{
literal("a"),
wildcard{},
literal("b"),
},
},
variable{
path: "obj",
segments: []segment{
deepWildcard{},
},
},
},
ops: []int{
int(OpLitPush), 0,
int(OpLitPush), 1,
int(OpPush), operandFiller,
int(OpLitPush), 2,
int(OpConcatN), 3,
int(OpCapture), 3,
int(OpPushM), operandFiller,
int(OpConcatN), 1,
int(OpCapture), 0,
},
pool: []string{"obj", "a", "b", "name.nested"},
fields: []string{"name.nested", "obj"},
},
} {
tmpl := template{
segments: spec.segs,
verb: spec.verb,
}
compiled := tmpl.Compile()
if got, want := compiled.Version, opcodeVersion; got != want {
t.Errorf("tmpl.Compile().Version = %d; want %d; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.OpCodes, spec.ops; !reflect.DeepEqual(got, want) {
t.Errorf("tmpl.Compile().OpCodes = %v; want %v; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.Pool, spec.pool; !reflect.DeepEqual(got, want) {
t.Errorf("tmpl.Compile().Pool = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.Verb, spec.verb; got != want {
t.Errorf("tmpl.Compile().Verb = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.Fields, spec.fields; !reflect.DeepEqual(got, want) {
t.Errorf("tmpl.Compile().Fields = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
}
}
+363
View File
@@ -0,0 +1,363 @@
package util
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/parse.go
import (
"fmt"
"strings"
"github.com/asim/go-micro/v3/logger"
)
// InvalidTemplateError indicates that the path template is not valid.
type InvalidTemplateError struct {
tmpl string
msg string
}
func (e InvalidTemplateError) Error() string {
return fmt.Sprintf("%s: %s", e.msg, e.tmpl)
}
// Parse parses the string representation of path template
func Parse(tmpl string) (Compiler, error) {
if !strings.HasPrefix(tmpl, "/") {
return template{}, InvalidTemplateError{tmpl: tmpl, msg: "no leading /"}
}
tokens, verb := tokenize(tmpl[1:])
p := parser{tokens: tokens}
segs, err := p.topLevelSegments()
if err != nil {
return template{}, InvalidTemplateError{tmpl: tmpl, msg: err.Error()}
}
return template{
segments: segs,
verb: verb,
template: tmpl,
}, nil
}
func tokenize(path string) (tokens []string, verb string) {
if path == "" {
return []string{eof}, ""
}
const (
init = iota
field
nested
)
var (
st = init
)
for path != "" {
var idx int
switch st {
case init:
idx = strings.IndexAny(path, "/{")
case field:
idx = strings.IndexAny(path, ".=}")
case nested:
idx = strings.IndexAny(path, "/}")
}
if idx < 0 {
tokens = append(tokens, path)
break
}
switch r := path[idx]; r {
case '/', '.':
case '{':
st = field
case '=':
st = nested
case '}':
st = init
}
if idx == 0 {
tokens = append(tokens, path[idx:idx+1])
} else {
tokens = append(tokens, path[:idx], path[idx:idx+1])
}
path = path[idx+1:]
}
l := len(tokens)
t := tokens[l-1]
if idx := strings.LastIndex(t, ":"); idx == 0 {
tokens, verb = tokens[:l-1], t[1:]
} else if idx > 0 {
tokens[l-1], verb = t[:idx], t[idx+1:]
}
tokens = append(tokens, eof)
return tokens, verb
}
// parser is a parser of the template syntax defined in github.com/googleapis/googleapis/google/api/http.proto.
type parser struct {
tokens []string
accepted []string
}
// topLevelSegments is the target of this parser.
func (p *parser) topLevelSegments() ([]segment, error) {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Parsing %q", p.tokens)
}
segs, err := p.segments()
if err != nil {
return nil, err
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("accept segments: %q; %q", p.accepted, p.tokens)
}
if _, err := p.accept(typeEOF); err != nil {
return nil, fmt.Errorf("unexpected token %q after segments %q", p.tokens[0], strings.Join(p.accepted, ""))
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("accept eof: %q; %q", p.accepted, p.tokens)
}
return segs, nil
}
func (p *parser) segments() ([]segment, error) {
s, err := p.segment()
if err != nil {
return nil, err
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("accept segment: %q; %q", p.accepted, p.tokens)
}
segs := []segment{s}
for {
if _, err := p.accept("/"); err != nil {
return segs, nil
}
s, err := p.segment()
if err != nil {
return segs, err
}
segs = append(segs, s)
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("accept segment: %q; %q", p.accepted, p.tokens)
}
}
}
func (p *parser) segment() (segment, error) {
if _, err := p.accept("*"); err == nil {
return wildcard{}, nil
}
if _, err := p.accept("**"); err == nil {
return deepWildcard{}, nil
}
if l, err := p.literal(); err == nil {
return l, nil
}
v, err := p.variable()
if err != nil {
return nil, fmt.Errorf("segment neither wildcards, literal or variable: %v", err)
}
return v, err
}
func (p *parser) literal() (segment, error) {
lit, err := p.accept(typeLiteral)
if err != nil {
return nil, err
}
return literal(lit), nil
}
func (p *parser) variable() (segment, error) {
if _, err := p.accept("{"); err != nil {
return nil, err
}
path, err := p.fieldPath()
if err != nil {
return nil, err
}
var segs []segment
if _, err := p.accept("="); err == nil {
segs, err = p.segments()
if err != nil {
return nil, fmt.Errorf("invalid segment in variable %q: %v", path, err)
}
} else {
segs = []segment{wildcard{}}
}
if _, err := p.accept("}"); err != nil {
return nil, fmt.Errorf("unterminated variable segment: %s", path)
}
return variable{
path: path,
segments: segs,
}, nil
}
func (p *parser) fieldPath() (string, error) {
c, err := p.accept(typeIdent)
if err != nil {
return "", err
}
components := []string{c}
for {
if _, err = p.accept("."); err != nil {
return strings.Join(components, "."), nil
}
c, err := p.accept(typeIdent)
if err != nil {
return "", fmt.Errorf("invalid field path component: %v", err)
}
components = append(components, c)
}
}
// A termType is a type of terminal symbols.
type termType string
// These constants define some of valid values of termType.
// They improve readability of parse functions.
//
// You can also use "/", "*", "**", "." or "=" as valid values.
const (
typeIdent = termType("ident")
typeLiteral = termType("literal")
typeEOF = termType("$")
)
const (
// eof is the terminal symbol which always appears at the end of token sequence.
eof = "\u0000"
)
// accept tries to accept a token in "p".
// This function consumes a token and returns it if it matches to the specified "term".
// If it doesn't match, the function does not consume any tokens and return an error.
func (p *parser) accept(term termType) (string, error) {
t := p.tokens[0]
switch term {
case "/", "*", "**", ".", "=", "{", "}":
if t != string(term) && t != "/" {
return "", fmt.Errorf("expected %q but got %q", term, t)
}
case typeEOF:
if t != eof {
return "", fmt.Errorf("expected EOF but got %q", t)
}
case typeIdent:
if err := expectIdent(t); err != nil {
return "", err
}
case typeLiteral:
if err := expectPChars(t); err != nil {
return "", err
}
default:
return "", fmt.Errorf("unknown termType %q", term)
}
p.tokens = p.tokens[1:]
p.accepted = append(p.accepted, t)
return t, nil
}
// expectPChars determines if "t" consists of only pchars defined in RFC3986.
//
// https://www.ietf.org/rfc/rfc3986.txt, P.49
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
// / "*" / "+" / "," / ";" / "="
// pct-encoded = "%" HEXDIG HEXDIG
func expectPChars(t string) error {
const (
init = iota
pct1
pct2
)
st := init
for _, r := range t {
if st != init {
if !isHexDigit(r) {
return fmt.Errorf("invalid hexdigit: %c(%U)", r, r)
}
switch st {
case pct1:
st = pct2
case pct2:
st = init
}
continue
}
// unreserved
switch {
case 'A' <= r && r <= 'Z':
continue
case 'a' <= r && r <= 'z':
continue
case '0' <= r && r <= '9':
continue
}
switch r {
case '-', '.', '_', '~':
// unreserved
case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=':
// sub-delims
case ':', '@':
// rest of pchar
case '%':
// pct-encoded
st = pct1
default:
return fmt.Errorf("invalid character in path segment: %q(%U)", r, r)
}
}
if st != init {
return fmt.Errorf("invalid percent-encoding in %q", t)
}
return nil
}
// expectIdent determines if "ident" is a valid identifier in .proto schema ([[:alpha:]_][[:alphanum:]_]*).
func expectIdent(ident string) error {
if ident == "" {
return fmt.Errorf("empty identifier")
}
for pos, r := range ident {
switch {
case '0' <= r && r <= '9':
if pos == 0 {
return fmt.Errorf("identifier starting with digit: %s", ident)
}
continue
case 'A' <= r && r <= 'Z':
continue
case 'a' <= r && r <= 'z':
continue
case r == '_':
continue
default:
return fmt.Errorf("invalid character %q(%U) in identifier: %s", r, r, ident)
}
}
return nil
}
func isHexDigit(r rune) bool {
switch {
case '0' <= r && r <= '9':
return true
case 'A' <= r && r <= 'F':
return true
case 'a' <= r && r <= 'f':
return true
}
return false
}
+321
View File
@@ -0,0 +1,321 @@
package util
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/parse_test.go
import (
"flag"
"fmt"
"reflect"
"testing"
"github.com/asim/go-micro/v3/logger"
)
func TestTokenize(t *testing.T) {
for _, spec := range []struct {
src string
tokens []string
}{
{
src: "",
tokens: []string{eof},
},
{
src: "v1",
tokens: []string{"v1", eof},
},
{
src: "v1/b",
tokens: []string{"v1", "/", "b", eof},
},
{
src: "v1/endpoint/*",
tokens: []string{"v1", "/", "endpoint", "/", "*", eof},
},
{
src: "v1/endpoint/**",
tokens: []string{"v1", "/", "endpoint", "/", "**", eof},
},
{
src: "v1/b/{bucket_name=*}",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "*", "}",
eof,
},
},
{
src: "v1/b/{bucket_name=buckets/*}",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "buckets", "/", "*", "}",
eof,
},
},
{
src: "v1/b/{bucket_name=buckets/*}/o",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
"o",
eof,
},
},
{
src: "v1/b/{bucket_name=buckets/*}/o/{name}",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
"o", "/", "{", "name", "}",
eof,
},
},
{
src: "v1/a=b&c=d;e=f:g/endpoint.rdf",
tokens: []string{
"v1", "/",
"a=b&c=d;e=f:g", "/",
"endpoint.rdf",
eof,
},
},
} {
tokens, verb := tokenize(spec.src)
if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
t.Errorf("tokenize(%q) = %q, _; want %q, _", spec.src, got, want)
}
if got, want := verb, ""; got != want {
t.Errorf("tokenize(%q) = _, %q; want _, %q", spec.src, got, want)
}
src := fmt.Sprintf("%s:%s", spec.src, "LOCK")
tokens, verb = tokenize(src)
if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
t.Errorf("tokenize(%q) = %q, _; want %q, _", src, got, want)
}
if got, want := verb, "LOCK"; got != want {
t.Errorf("tokenize(%q) = _, %q; want _, %q", src, got, want)
}
}
}
func TestParseSegments(t *testing.T) {
flag.Set("v", "3")
for _, spec := range []struct {
tokens []string
want []segment
}{
{
tokens: []string{"v1", eof},
want: []segment{
literal("v1"),
},
},
{
tokens: []string{"/", eof},
want: []segment{
wildcard{},
},
},
{
tokens: []string{"-._~!$&'()*+,;=:@", eof},
want: []segment{
literal("-._~!$&'()*+,;=:@"),
},
},
{
tokens: []string{"%e7%ac%ac%e4%b8%80%e7%89%88", eof},
want: []segment{
literal("%e7%ac%ac%e4%b8%80%e7%89%88"),
},
},
{
tokens: []string{"v1", "/", "*", eof},
want: []segment{
literal("v1"),
wildcard{},
},
},
{
tokens: []string{"v1", "/", "**", eof},
want: []segment{
literal("v1"),
deepWildcard{},
},
},
{
tokens: []string{"{", "name", "}", eof},
want: []segment{
variable{
path: "name",
segments: []segment{
wildcard{},
},
},
},
},
{
tokens: []string{"{", "name", "=", "*", "}", eof},
want: []segment{
variable{
path: "name",
segments: []segment{
wildcard{},
},
},
},
},
{
tokens: []string{"{", "field", ".", "nested", ".", "nested2", "=", "*", "}", eof},
want: []segment{
variable{
path: "field.nested.nested2",
segments: []segment{
wildcard{},
},
},
},
},
{
tokens: []string{"{", "name", "=", "a", "/", "b", "/", "*", "}", eof},
want: []segment{
variable{
path: "name",
segments: []segment{
literal("a"),
literal("b"),
wildcard{},
},
},
},
},
{
tokens: []string{
"v1", "/",
"{",
"name", ".", "nested", ".", "nested2",
"=",
"a", "/", "b", "/", "*",
"}", "/",
"o", "/",
"{",
"another_name",
"=",
"a", "/", "b", "/", "*", "/", "c",
"}", "/",
"**",
eof},
want: []segment{
literal("v1"),
variable{
path: "name.nested.nested2",
segments: []segment{
literal("a"),
literal("b"),
wildcard{},
},
},
literal("o"),
variable{
path: "another_name",
segments: []segment{
literal("a"),
literal("b"),
wildcard{},
literal("c"),
},
},
deepWildcard{},
},
},
} {
p := parser{tokens: spec.tokens}
segs, err := p.topLevelSegments()
if err != nil {
t.Errorf("parser{%q}.segments() failed with %v; want success", spec.tokens, err)
continue
}
if got, want := segs, spec.want; !reflect.DeepEqual(got, want) {
t.Errorf("parser{%q}.segments() = %#v; want %#v", spec.tokens, got, want)
}
if got := p.tokens; len(got) > 0 {
t.Errorf("p.tokens = %q; want []; spec.tokens=%q", got, spec.tokens)
}
}
}
func TestParseSegmentsWithErrors(t *testing.T) {
flag.Set("v", "3")
for _, spec := range []struct {
tokens []string
}{
{
// double slash
tokens: []string{"//", eof},
},
{
// invalid literal
tokens: []string{"a?b", eof},
},
{
// invalid percent-encoding
tokens: []string{"%", eof},
},
{
// invalid percent-encoding
tokens: []string{"%2", eof},
},
{
// invalid percent-encoding
tokens: []string{"a%2z", eof},
},
{
// empty segments
tokens: []string{eof},
},
{
// unterminated variable
tokens: []string{"{", "name", eof},
},
{
// unterminated variable
tokens: []string{"{", "name", "=", eof},
},
{
// unterminated variable
tokens: []string{"{", "name", "=", "*", eof},
},
{
// empty component in field path
tokens: []string{"{", "name", ".", "}", eof},
},
{
// empty component in field path
tokens: []string{"{", "name", ".", ".", "nested", "}", eof},
},
{
// invalid character in identifier
tokens: []string{"{", "field-name", "}", eof},
},
{
// no slash between segments
tokens: []string{"v1", "endpoint", eof},
},
{
// no slash between segments
tokens: []string{"v1", "{", "name", "}", eof},
},
} {
p := parser{tokens: spec.tokens}
segs, err := p.topLevelSegments()
if err == nil {
t.Errorf("parser{%q}.segments() succeeded; want InvalidTemplateError; accepted %#v", spec.tokens, segs)
continue
}
logger.Info(err)
}
}
+24
View File
@@ -0,0 +1,24 @@
package util
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/utilities/pattern.go
// An OpCode is a opcode of compiled path patterns.
type OpCode int
// These constants are the valid values of OpCode.
const (
// OpNop does nothing
OpNop = OpCode(iota)
// OpPush pushes a component to stack
OpPush
// OpLitPush pushes a component to stack if it matches to the literal
OpLitPush
// OpPushM concatenates the remaining components and pushes it to stack
OpPushM
// OpConcatN pops N items from stack, concatenates them and pushes it back to stack
OpConcatN
// OpCapture pops an item and binds it to the variable
OpCapture
// OpEnd is the least positive invalid opcode.
OpEnd
)
+283
View File
@@ -0,0 +1,283 @@
package util
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/runtime/pattern.go
import (
"errors"
"fmt"
"strings"
"github.com/asim/go-micro/v3/logger"
)
var (
// ErrNotMatch indicates that the given HTTP request path does not match to the pattern.
ErrNotMatch = errors.New("not match to the path pattern")
// ErrInvalidPattern indicates that the given definition of Pattern is not valid.
ErrInvalidPattern = errors.New("invalid pattern")
)
type rop struct {
code OpCode
operand int
}
// Pattern is a template pattern of http request paths defined in github.com/googleapis/googleapis/google/api/http.proto.
type Pattern struct {
// ops is a list of operations
ops []rop
// pool is a constant pool indexed by the operands or vars.
pool []string
// vars is a list of variables names to be bound by this pattern
vars []string
// stacksize is the max depth of the stack
stacksize int
// tailLen is the length of the fixed-size segments after a deep wildcard
tailLen int
// verb is the VERB part of the path pattern. It is empty if the pattern does not have VERB part.
verb string
// assumeColonVerb indicates whether a path suffix after a final
// colon may only be interpreted as a verb.
assumeColonVerb bool
}
type patternOptions struct {
assumeColonVerb bool
}
// PatternOpt is an option for creating Patterns.
type PatternOpt func(*patternOptions)
// NewPattern returns a new Pattern from the given definition values.
// "ops" is a sequence of op codes. "pool" is a constant pool.
// "verb" is the verb part of the pattern. It is empty if the pattern does not have the part.
// "version" must be 1 for now.
// It returns an error if the given definition is invalid.
func NewPattern(version int, ops []int, pool []string, verb string, opts ...PatternOpt) (Pattern, error) {
options := patternOptions{
assumeColonVerb: true,
}
for _, o := range opts {
o(&options)
}
if version != 1 {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("unsupported version: %d", version)
}
return Pattern{}, ErrInvalidPattern
}
l := len(ops)
if l%2 != 0 {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("odd number of ops codes: %d", l)
}
return Pattern{}, ErrInvalidPattern
}
var (
typedOps []rop
stack, maxstack int
tailLen int
pushMSeen bool
vars []string
)
for i := 0; i < l; i += 2 {
op := rop{code: OpCode(ops[i]), operand: ops[i+1]}
switch op.code {
case OpNop:
continue
case OpPush:
if pushMSeen {
tailLen++
}
stack++
case OpPushM:
if pushMSeen {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debug("pushM appears twice")
}
return Pattern{}, ErrInvalidPattern
}
pushMSeen = true
stack++
case OpLitPush:
if op.operand < 0 || len(pool) <= op.operand {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("negative literal index: %d", op.operand)
}
return Pattern{}, ErrInvalidPattern
}
if pushMSeen {
tailLen++
}
stack++
case OpConcatN:
if op.operand <= 0 {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("negative concat size: %d", op.operand)
}
return Pattern{}, ErrInvalidPattern
}
stack -= op.operand
if stack < 0 {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debug("stack underflow")
}
return Pattern{}, ErrInvalidPattern
}
stack++
case OpCapture:
if op.operand < 0 || len(pool) <= op.operand {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("variable name index out of bound: %d", op.operand)
}
return Pattern{}, ErrInvalidPattern
}
v := pool[op.operand]
op.operand = len(vars)
vars = append(vars, v)
stack--
if stack < 0 {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debug("stack underflow")
}
return Pattern{}, ErrInvalidPattern
}
default:
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("invalid opcode: %d", op.code)
}
return Pattern{}, ErrInvalidPattern
}
if maxstack < stack {
maxstack = stack
}
typedOps = append(typedOps, op)
}
return Pattern{
ops: typedOps,
pool: pool,
vars: vars,
stacksize: maxstack,
tailLen: tailLen,
verb: verb,
assumeColonVerb: options.assumeColonVerb,
}, nil
}
// MustPattern is a helper function which makes it easier to call NewPattern in variable initialization.
func MustPattern(p Pattern, err error) Pattern {
if err != nil {
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Fatalf("Pattern initialization failed: %v", err)
}
}
return p
}
// Match examines components if it matches to the Pattern.
// If it matches, the function returns a mapping from field paths to their captured values.
// If otherwise, the function returns an error.
func (p Pattern) Match(components []string, verb string) (map[string]string, error) {
if p.verb != verb {
if p.assumeColonVerb || p.verb != "" {
return nil, ErrNotMatch
}
if len(components) == 0 {
components = []string{":" + verb}
} else {
components = append([]string{}, components...)
components[len(components)-1] += ":" + verb
}
verb = ""
}
var pos int
stack := make([]string, 0, p.stacksize)
captured := make([]string, len(p.vars))
l := len(components)
for _, op := range p.ops {
switch op.code {
case OpNop:
continue
case OpPush, OpLitPush:
if pos >= l {
return nil, ErrNotMatch
}
c := components[pos]
if op.code == OpLitPush {
if lit := p.pool[op.operand]; c != lit {
return nil, ErrNotMatch
}
}
stack = append(stack, c)
pos++
case OpPushM:
end := len(components)
if end < pos+p.tailLen {
return nil, ErrNotMatch
}
end -= p.tailLen
stack = append(stack, strings.Join(components[pos:end], "/"))
pos = end
case OpConcatN:
n := op.operand
l := len(stack) - n
stack = append(stack[:l], strings.Join(stack[l:], "/"))
case OpCapture:
n := len(stack) - 1
captured[op.operand] = stack[n]
stack = stack[:n]
}
}
if pos < l {
return nil, ErrNotMatch
}
bindings := make(map[string]string)
for i, val := range captured {
bindings[p.vars[i]] = val
}
return bindings, nil
}
// Verb returns the verb part of the Pattern.
func (p Pattern) Verb() string { return p.verb }
func (p Pattern) String() string {
var stack []string
for _, op := range p.ops {
switch op.code {
case OpNop:
continue
case OpPush:
stack = append(stack, "*")
case OpLitPush:
stack = append(stack, p.pool[op.operand])
case OpPushM:
stack = append(stack, "**")
case OpConcatN:
n := op.operand
l := len(stack) - n
stack = append(stack[:l], strings.Join(stack[l:], "/"))
case OpCapture:
n := len(stack) - 1
stack[n] = fmt.Sprintf("{%s=%s}", p.vars[op.operand], stack[n])
}
}
segs := strings.Join(stack, "/")
if p.verb != "" {
return fmt.Sprintf("/%s:%s", segs, p.verb)
}
return "/" + segs
}
// AssumeColonVerbOpt indicates whether a path suffix after a final
// colon may only be interpreted as a verb.
func AssumeColonVerbOpt(val bool) PatternOpt {
return PatternOpt(func(o *patternOptions) {
o.assumeColonVerb = val
})
}
+62
View File
@@ -0,0 +1,62 @@
package util
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/types.go
import (
"fmt"
"strings"
)
type template struct {
segments []segment
verb string
template string
}
type segment interface {
fmt.Stringer
compile() (ops []op)
}
type wildcard struct{}
type deepWildcard struct{}
type literal string
type variable struct {
path string
segments []segment
}
func (wildcard) String() string {
return "*"
}
func (deepWildcard) String() string {
return "**"
}
func (l literal) String() string {
return string(l)
}
func (v variable) String() string {
var segs []string
for _, s := range v.segments {
segs = append(segs, s.String())
}
return fmt.Sprintf("{%s=%s}", v.path, strings.Join(segs, "/"))
}
func (t template) String() string {
var segs []string
for _, s := range t.segments {
segs = append(segs, s.String())
}
str := strings.Join(segs, "/")
if t.verb != "" {
str = fmt.Sprintf("%s:%s", str, t.verb)
}
return "/" + str
}
+93
View File
@@ -0,0 +1,93 @@
package util
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/types_test.go
import (
"fmt"
"testing"
)
func TestTemplateStringer(t *testing.T) {
for _, spec := range []struct {
segs []segment
want string
}{
{
segs: []segment{
literal("v1"),
},
want: "/v1",
},
{
segs: []segment{
wildcard{},
},
want: "/*",
},
{
segs: []segment{
deepWildcard{},
},
want: "/**",
},
{
segs: []segment{
variable{
path: "name",
segments: []segment{
literal("a"),
},
},
},
want: "/{name=a}",
},
{
segs: []segment{
variable{
path: "name",
segments: []segment{
literal("a"),
wildcard{},
literal("b"),
},
},
},
want: "/{name=a/*/b}",
},
{
segs: []segment{
literal("v1"),
variable{
path: "name",
segments: []segment{
literal("a"),
wildcard{},
literal("b"),
},
},
literal("c"),
variable{
path: "field.nested",
segments: []segment{
wildcard{},
literal("d"),
},
},
wildcard{},
literal("e"),
deepWildcard{},
},
want: "/v1/{name=a/*/b}/c/{field.nested=*/d}/*/e/**",
},
} {
tmpl := template{segments: spec.segs}
if got, want := tmpl.String(), spec.want; got != want {
t.Errorf("%#v.String() = %q; want %q", tmpl, got, want)
}
tmpl.verb = "LOCK"
if got, want := tmpl.String(), fmt.Sprintf("%s:LOCK", spec.want); got != want {
t.Errorf("%#v.String() = %q; want %q", tmpl, got, want)
}
}
}
+5 -1
View File
@@ -2,6 +2,7 @@
package acme
import (
"crypto/tls"
"errors"
"net"
)
@@ -14,7 +15,10 @@ var (
// Provider is a ACME provider interface
type Provider interface {
NewListener(...string) (net.Listener, error)
// Listen returns a new listener
Listen(...string) (net.Listener, error)
// TLSConfig returns a tls config
TLSConfig(...string) (*tls.Config, error)
}
// The Let's Encrypt ACME endpoints
+28 -5
View File
@@ -3,21 +3,44 @@
package autocert
import (
"crypto/tls"
"net"
"os"
"github.com/micro/go-micro/v2/api/server/acme"
"github.com/asim/go-micro/v3/api/server/acme"
"github.com/asim/go-micro/v3/logger"
"golang.org/x/crypto/acme/autocert"
)
// autoCertACME is the ACME provider from golang.org/x/crypto/acme/autocert
type autocertProvider struct{}
// NewListener implements acme.Provider
func (a *autocertProvider) NewListener(ACMEHosts ...string) (net.Listener, error) {
return autocert.NewListener(ACMEHosts...), nil
// Listen implements acme.Provider
func (a *autocertProvider) Listen(hosts ...string) (net.Listener, error) {
return autocert.NewListener(hosts...), nil
}
// TLSConfig returns a new tls config
func (a *autocertProvider) TLSConfig(hosts ...string) (*tls.Config, error) {
// create a new manager
m := &autocert.Manager{
Prompt: autocert.AcceptTOS,
}
if len(hosts) > 0 {
m.HostPolicy = autocert.HostWhitelist(hosts...)
}
dir := cacheDir()
if err := os.MkdirAll(dir, 0700); err != nil {
if logger.V(logger.InfoLevel, logger.DefaultLogger) {
logger.Infof("warning: autocert not using a cache: %v", err)
}
} else {
m.Cache = autocert.DirCache(dir)
}
return m.TLSConfig(), nil
}
// New returns an autocert acme.Provider
func New() acme.Provider {
func NewProvider() acme.Provider {
return &autocertProvider{}
}
+2 -2
View File
@@ -5,9 +5,9 @@ import (
)
func TestAutocert(t *testing.T) {
l := New()
l := NewProvider()
if _, ok := l.(*autocertProvider); !ok {
t.Error("New() didn't return an autocertProvider")
t.Error("NewProvider() didn't return an autocertProvider")
}
// TODO: Travis CI doesn't let us bind :443
// if _, err := l.NewListener(); err != nil {
+37
View File
@@ -0,0 +1,37 @@
package autocert
import (
"os"
"path/filepath"
"runtime"
)
func homeDir() string {
if runtime.GOOS == "windows" {
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
}
if h := os.Getenv("HOME"); h != "" {
return h
}
return "/"
}
func cacheDir() string {
const base = "golang-autocert"
switch runtime.GOOS {
case "darwin":
return filepath.Join(homeDir(), "Library", "Caches", base)
case "windows":
for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
if v := os.Getenv(ev); v != "" {
return filepath.Join(v, base)
}
}
// Worst case:
return filepath.Join(homeDir(), base)
}
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
return filepath.Join(xdg, base)
}
return filepath.Join(homeDir(), ".cache", base)
}
-58
View File
@@ -1,58 +0,0 @@
// Package certmagic is the ACME provider from github.com/mholt/certmagic
package certmagic
import (
"log"
"math/rand"
"net"
"time"
"github.com/mholt/certmagic"
"github.com/micro/go-micro/v2/api/server/acme"
)
type certmagicProvider struct {
opts acme.Options
}
func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, error) {
certmagic.Default.CA = c.opts.CA
if c.opts.ChallengeProvider != nil {
// Enabling DNS Challenge disables the other challenges
certmagic.Default.DNSProvider = c.opts.ChallengeProvider
}
if c.opts.OnDemand {
certmagic.Default.OnDemand = new(certmagic.OnDemandConfig)
}
if c.opts.Cache != nil {
// already validated by new()
certmagic.Default.Storage = c.opts.Cache.(certmagic.Storage)
}
// If multiple instances of the provider are running, inject some
// randomness so they don't collide
rand.Seed(time.Now().UnixNano())
randomDuration := (7 * 24 * time.Hour) + (time.Duration(rand.Intn(504)) * time.Hour)
certmagic.Default.RenewDurationBefore = randomDuration
return certmagic.Listen(ACMEHosts)
}
// New returns a certmagic provider
func New(options ...acme.Option) acme.Provider {
opts := acme.DefaultOptions()
for _, o := range options {
o(&opts)
}
if opts.Cache != nil {
if _, ok := opts.Cache.(certmagic.Storage); !ok {
log.Fatal("ACME: cache provided doesn't implement certmagic's Storage interface")
}
}
return &certmagicProvider{
opts: opts,
}
}
-224
View File
@@ -1,224 +0,0 @@
package certmagic
import (
"net/http"
"os"
"reflect"
"sort"
"testing"
"time"
"github.com/go-acme/lego/v3/providers/dns/cloudflare"
"github.com/mholt/certmagic"
"github.com/micro/go-micro/v2/api/server/acme"
cfstore "github.com/micro/go-micro/v2/store/cloudflare"
"github.com/micro/go-micro/v2/sync/lock/memory"
)
func TestCertMagic(t *testing.T) {
if len(os.Getenv("IN_TRAVIS_CI")) != 0 {
t.Skip("Travis doesn't let us bind :443")
}
l, err := New().NewListener()
if err != nil {
t.Fatal(err.Error())
}
l.Close()
c := cloudflare.NewDefaultConfig()
c.AuthEmail = ""
c.AuthKey = ""
c.AuthToken = "test"
c.ZoneToken = "test"
p, err := cloudflare.NewDNSProviderConfig(c)
if err != nil {
t.Fatal(err.Error())
}
l, err = New(acme.AcceptToS(true),
acme.CA(acme.LetsEncryptStagingCA),
acme.ChallengeProvider(p),
).NewListener()
if err != nil {
t.Fatal(err.Error())
}
l.Close()
}
func TestStorageImplementation(t *testing.T) {
apiToken, accountID := os.Getenv("CF_API_TOKEN"), os.Getenv("CF_ACCOUNT_ID")
kvID := os.Getenv("KV_NAMESPACE_ID")
if len(apiToken) == 0 || len(accountID) == 0 || len(kvID) == 0 {
t.Skip("No Cloudflare API keys available, skipping test")
}
var s certmagic.Storage
st := cfstore.NewStore(
cfstore.Token(apiToken),
cfstore.Account(accountID),
cfstore.Namespace(kvID),
)
s = &storage{
lock: memory.NewLock(),
store: st,
}
// Test Lock
if err := s.Lock("test"); err != nil {
t.Fatal(err)
}
// Test Unlock
if err := s.Unlock("test"); err != nil {
t.Fatal(err)
}
// Test data
testdata := []struct {
key string
value []byte
}{
{key: "/foo/a", value: []byte("lorem")},
{key: "/foo/b", value: []byte("ipsum")},
{key: "/foo/c", value: []byte("dolor")},
{key: "/foo/d", value: []byte("sit")},
{key: "/bar/a", value: []byte("amet")},
{key: "/bar/b", value: []byte("consectetur")},
{key: "/bar/c", value: []byte("adipiscing")},
{key: "/bar/d", value: []byte("elit")},
{key: "/foo/bar/a", value: []byte("sed")},
{key: "/foo/bar/b", value: []byte("do")},
{key: "/foo/bar/c", value: []byte("eiusmod")},
{key: "/foo/bar/d", value: []byte("tempor")},
{key: "/foo/bar/baz/a", value: []byte("incididunt")},
{key: "/foo/bar/baz/b", value: []byte("ut")},
{key: "/foo/bar/baz/c", value: []byte("labore")},
{key: "/foo/bar/baz/d", value: []byte("et")},
// a duplicate just in case there's any edge cases
{key: "/foo/a", value: []byte("lorem")},
}
// Test Store
for _, d := range testdata {
if err := s.Store(d.key, d.value); err != nil {
t.Fatal(err.Error())
}
}
// Test Load
for _, d := range testdata {
if value, err := s.Load(d.key); err != nil {
t.Fatal(err.Error())
} else {
if !reflect.DeepEqual(value, d.value) {
t.Fatalf("Load %s: expected %v, got %v", d.key, d.value, value)
}
}
}
// Test Exists
for _, d := range testdata {
if !s.Exists(d.key) {
t.Fatalf("%s should exist, but doesn't\n", d.key)
}
}
// Test List
if list, err := s.List("/", true); err != nil {
t.Fatal(err.Error())
} else {
var expected []string
for i, d := range testdata {
if i != len(testdata)-1 {
// Don't store the intentionally duplicated key
expected = append(expected, d.key)
}
}
sort.Strings(expected)
sort.Strings(list)
if !reflect.DeepEqual(expected, list) {
t.Fatalf("List: Expected %v, got %v\n", expected, list)
}
}
if list, err := s.List("/foo", false); err != nil {
t.Fatal(err.Error())
} else {
sort.Strings(list)
expected := []string{"/foo/a", "/foo/b", "/foo/bar", "/foo/c", "/foo/d"}
if !reflect.DeepEqual(expected, list) {
t.Fatalf("List: expected %s, got %s\n", expected, list)
}
}
// Test Stat
for _, d := range testdata {
info, err := s.Stat(d.key)
if err != nil {
t.Fatal(err.Error())
} else {
if info.Key != d.key {
t.Fatalf("Stat().Key: expected %s, got %s\n", d.key, info.Key)
}
if info.Size != int64(len(d.value)) {
t.Fatalf("Stat().Size: expected %d, got %d\n", len(d.value), info.Size)
}
if time.Since(info.Modified) > time.Minute {
t.Fatalf("Stat().Modified: expected time since last modified to be < 1 minute, got %v\n", time.Since(info.Modified))
}
}
}
// Test Delete
for _, d := range testdata {
if err := s.Delete(d.key); err != nil {
t.Fatal(err.Error())
}
}
// New interface doesn't return an error, so call it in case any log.Fatal
// happens
New(acme.Cache(s))
}
// Full test with a real zone, with against LE staging
func TestE2e(t *testing.T) {
apiToken, accountID := os.Getenv("CF_API_TOKEN"), os.Getenv("CF_ACCOUNT_ID")
kvID := os.Getenv("KV_NAMESPACE_ID")
if len(apiToken) == 0 || len(accountID) == 0 || len(kvID) == 0 {
t.Skip("No Cloudflare API keys available, skipping test")
}
testLock := memory.NewLock()
testStore := cfstore.NewStore(
cfstore.Token(apiToken),
cfstore.Account(accountID),
cfstore.Namespace(kvID),
)
testStorage := NewStorage(testLock, testStore)
conf := cloudflare.NewDefaultConfig()
conf.AuthToken = apiToken
conf.ZoneToken = apiToken
testChallengeProvider, err := cloudflare.NewDNSProviderConfig(conf)
if err != nil {
t.Fatal(err.Error())
}
testProvider := New(
acme.AcceptToS(true),
acme.Cache(testStorage),
acme.CA(acme.LetsEncryptStagingCA),
acme.ChallengeProvider(testChallengeProvider),
acme.OnDemand(false),
)
listener, err := testProvider.NewListener("*.micro.mu", "micro.mu")
if err != nil {
t.Fatal(err.Error())
}
go http.Serve(listener, http.NotFoundHandler())
time.Sleep(10 * time.Minute)
}
+1 -1
View File
@@ -1,6 +1,6 @@
package acme
import "github.com/go-acme/lego/v3/challenge"
import "github.com/go-acme/lego/v4/challenge"
// Option (or Options) are passed to New() to configure providers
type Option func(o *Options)
+57
View File
@@ -0,0 +1,57 @@
package cors
import (
"net/http"
)
type Config struct {
AllowOrigin string
AllowCredentials bool
AllowMethods string
AllowHeaders string
}
// CombinedCORSHandler wraps a server and provides CORS headers
func CombinedCORSHandler(h http.Handler, config *Config) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if config != nil {
SetHeaders(w, r, config)
}
if r.Method == "OPTIONS" {
return
}
h.ServeHTTP(w, r)
})
}
// SetHeaders sets the CORS headers
func SetHeaders(w http.ResponseWriter, _ *http.Request, config *Config) {
set := func(w http.ResponseWriter, k, v string) {
if v := w.Header().Get(k); len(v) > 0 {
return
}
w.Header().Set(k, v)
}
//For forward-compatible code, default values may not be provided in the future
if config.AllowCredentials {
set(w, "Access-Control-Allow-Credentials", "true")
} else {
set(w, "Access-Control-Allow-Credentials", "false")
}
if config.AllowOrigin == "" {
set(w, "Access-Control-Allow-Origin", "*")
} else {
set(w, "Access-Control-Allow-Origin", config.AllowOrigin)
}
if config.AllowMethods == "" {
set(w, "Access-Control-Allow-Methods", "POST, PATCH, GET, OPTIONS, PUT, DELETE")
} else {
set(w, "Access-Control-Allow-Methods", config.AllowMethods)
}
if config.AllowHeaders == "" {
set(w, "Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
} else {
set(w, "Access-Control-Allow-Headers", config.AllowHeaders)
}
}
+31 -8
View File
@@ -8,9 +8,10 @@ import (
"os"
"sync"
"github.com/asim/go-micro/v3/api/server"
"github.com/asim/go-micro/v3/api/server/cors"
"github.com/asim/go-micro/v3/logger"
"github.com/gorilla/handlers"
"github.com/micro/go-micro/v2/api/server"
"github.com/micro/go-micro/v2/util/log"
)
type httpServer struct {
@@ -22,9 +23,14 @@ type httpServer struct {
exit chan chan error
}
func NewServer(address string) server.Server {
func NewServer(address string, opts ...server.Option) server.Server {
var options server.Options
for _, o := range opts {
o(&options)
}
return &httpServer{
opts: server.Options{},
opts: options,
mux: http.NewServeMux(),
address: address,
exit: make(chan chan error),
@@ -45,7 +51,22 @@ func (s *httpServer) Init(opts ...server.Option) error {
}
func (s *httpServer) Handle(path string, handler http.Handler) {
s.mux.Handle(path, handlers.CombinedLoggingHandler(os.Stdout, handler))
// TODO: move this stuff out to one place with ServeHTTP
// apply the wrappers, e.g. auth
for _, wrapper := range s.opts.Wrappers {
handler = wrapper(handler)
}
// wrap with cors
if s.opts.EnableCORS {
handler = cors.CombinedCORSHandler(handler, s.opts.CORSConfig)
}
// wrap with logger
handler = handlers.CombinedLoggingHandler(os.Stdout, handler)
s.mux.Handle(path, handler)
}
func (s *httpServer) Start() error {
@@ -54,7 +75,7 @@ func (s *httpServer) Start() error {
if s.opts.EnableACME && s.opts.ACMEProvider != nil {
// should we check the address to make sure its using :443?
l, err = s.opts.ACMEProvider.NewListener(s.opts.ACMEHosts...)
l, err = s.opts.ACMEProvider.Listen(s.opts.ACMEHosts...)
} else if s.opts.EnableTLS && s.opts.TLSConfig != nil {
l, err = tls.Listen("tcp", s.address, s.opts.TLSConfig)
} else {
@@ -65,7 +86,9 @@ func (s *httpServer) Start() error {
return err
}
log.Logf("HTTP API Listening on %s", l.Addr().String())
if logger.V(logger.InfoLevel, logger.DefaultLogger) {
logger.Infof("HTTP API Listening on %s", l.Addr().String())
}
s.mtx.Lock()
s.address = l.Addr().String()
@@ -74,7 +97,7 @@ func (s *httpServer) Start() error {
go func() {
if err := http.Serve(l, s.mux); err != nil {
// temporary fix
//log.Fatal(err)
//logger.Fatal(err)
}
}()
+73
View File
@@ -2,6 +2,8 @@ package http
import (
"fmt"
"github.com/asim/go-micro/v3/api/server"
"github.com/asim/go-micro/v3/api/server/cors"
"io/ioutil"
"net/http"
"testing"
@@ -39,3 +41,74 @@ func TestHTTPServer(t *testing.T) {
t.Fatal(err)
}
}
func TestCORSHTTPServer(t *testing.T) {
testResponse := "hello world"
testAllowOrigin := "*"
testAllowCredentials := true
testAllowMethods := "GET"
testAllowHeaders := "Accept, Content-Type, Content-Length"
s := NewServer("localhost:0",
server.EnableCORS(true),
server.CORSConfig(&cors.Config{
AllowCredentials: testAllowCredentials,
AllowOrigin: testAllowOrigin,
AllowMethods: testAllowMethods,
AllowHeaders: testAllowHeaders,
}),
)
s.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, testResponse)
}))
if err := s.Start(); err != nil {
t.Fatal(err)
}
rsp, err := http.Get(fmt.Sprintf("http://%s/", s.Address()))
if err != nil {
t.Fatal(err)
}
defer rsp.Body.Close()
b, err := ioutil.ReadAll(rsp.Body)
if err != nil {
t.Fatal(err)
}
if string(b) != testResponse {
t.Fatalf("Unexpected response, got %s, expected %s", string(b), testResponse)
}
allowCredentials := rsp.Header.Get("Access-Control-Allow-Credentials")
getTestCredentialsStr := func() string {
if testAllowCredentials == true {
return "true"
} else {
return "false"
}
}
if getTestCredentialsStr() != allowCredentials {
t.Fatalf("Unexpected Access-Control-Allow-Credentials, got %s, expected %s", allowCredentials, getTestCredentialsStr())
}
allowOrigin := rsp.Header.Get("Access-Control-Allow-Origin")
if testAllowOrigin != allowOrigin {
t.Fatalf("Unexpected Access-Control-Allow-Origins, got %s, expected %s", allowOrigin, testAllowOrigin)
}
allowMethods := rsp.Header.Get("Access-Control-Allow-Methods")
if testAllowMethods != allowMethods {
t.Fatalf("Unexpected Access-Control-Allow-Methods, got %s, expected %s", allowMethods, testAllowMethods)
}
allowHeaders := rsp.Header.Get("Access-Control-Allow-Headers")
if testAllowHeaders != allowHeaders {
t.Fatalf("Unexpected Access-Control-Allow-Headers, got %s, expected %s", allowHeaders, testAllowHeaders)
}
if err := s.Stop(); err != nil {
t.Fatal(err)
}
}
+34 -1
View File
@@ -2,18 +2,45 @@ package server
import (
"crypto/tls"
"github.com/asim/go-micro/v3/api/server/cors"
"net/http"
"github.com/micro/go-micro/v2/api/server/acme"
"github.com/asim/go-micro/v3/api/resolver"
"github.com/asim/go-micro/v3/api/server/acme"
)
type Option func(o *Options)
type Options struct {
EnableACME bool
EnableCORS bool
CORSConfig *cors.Config
ACMEProvider acme.Provider
EnableTLS bool
ACMEHosts []string
TLSConfig *tls.Config
Resolver resolver.Resolver
Wrappers []Wrapper
}
type Wrapper func(h http.Handler) http.Handler
func WrapHandler(w Wrapper) Option {
return func(o *Options) {
o.Wrappers = append(o.Wrappers, w)
}
}
func EnableCORS(b bool) Option {
return func(o *Options) {
o.EnableCORS = b
}
}
func CORSConfig(c *cors.Config) Option {
return func(o *Options) {
o.CORSConfig = c
}
}
func EnableACME(b bool) Option {
@@ -45,3 +72,9 @@ func TLSConfig(t *tls.Config) Option {
o.TLSConfig = t
}
}
func Resolver(r resolver.Resolver) Option {
return func(o *Options) {
o.Resolver = r
}
}
+118 -21
View File
@@ -2,39 +2,136 @@
package auth
import (
"context"
"errors"
"time"
)
// Auth providers authentication and authorization
const (
// BearerScheme used for Authorization header
BearerScheme = "Bearer "
// ScopePublic is the scope applied to a rule to allow access to the public
ScopePublic = ""
// ScopeAccount is the scope applied to a rule to limit to users with any valid account
ScopeAccount = "*"
)
var (
// ErrInvalidToken is when the token provided is not valid
ErrInvalidToken = errors.New("invalid token provided")
// ErrForbidden is when a user does not have the necessary scope to access a resource
ErrForbidden = errors.New("resource forbidden")
)
// Auth provides authentication and authorization
type Auth interface {
// Generate a new auth token
Generate(string) (*Token, error)
// Revoke an authorization token
Revoke(*Token) error
// Init the auth
Init(opts ...Option)
// Options set for auth
Options() Options
// Generate a new account
Generate(id string, opts ...GenerateOption) (*Account, error)
// Inspect a token
Inspect(token string) (*Account, error)
// Token generated using refresh token or credentials
Token(opts ...TokenOption) (*Token, error)
// String returns the name of the implementation
String() string
}
// Rules manages access to resources
type Rules interface {
// Verify an account has access to a resource using the rules
Verify(acc *Account, res *Resource, opts ...VerifyOption) error
// Grant access to a resource
Grant(*Token, *Service) error
// Verify a token can access a resource
Verify(*Token, *Service) error
Grant(rule *Rule) error
// Revoke access to a resource
Revoke(rule *Rule) error
// List returns all the rules used to verify requests
List(...ListOption) ([]*Rule, error)
}
// Service is some thing to provide access to
type Service struct {
// Name of the resource
Name string
// Endpoint is the specific endpoint
Endpoint string
// Account provided by an auth provider
type Account struct {
// ID of the account e.g. email
ID string `json:"id"`
// Type of the account, e.g. service
Type string `json:"type"`
// Issuer of the account
Issuer string `json:"issuer"`
// Any other associated metadata
Metadata map[string]string `json:"metadata"`
// Scopes the account has access to
Scopes []string `json:"scopes"`
// Secret for the account, e.g. the password
Secret string `json:"secret"`
}
// Token providers by an auth provider
// Token can be short or long lived
type Token struct {
// Unique token id
Id string `json: "id"`
// The token to be used for accessing resources
AccessToken string `json:"access_token"`
// RefreshToken to be used to generate a new token
RefreshToken string `json:"refresh_token"`
// Time of token creation
Created time.Time `json:"created"`
// Time of token expiry
Expiry time.Time `json:"expiry"`
// Roles associated with the token
Roles []string `json:"roles"`
// Any other associated metadata
Metadata map[string]string `json:"metadata"`
}
// Expired returns a boolean indicating if the token needs to be refreshed
func (t *Token) Expired() bool {
return t.Expiry.Unix() < time.Now().Unix()
}
// Resource is an entity such as a user or
type Resource struct {
// Name of the resource, e.g. go.micro.service.notes
Name string `json:"name"`
// Type of resource, e.g. service
Type string `json:"type"`
// Endpoint resource e.g NotesService.Create
Endpoint string `json:"endpoint"`
}
// Access defines the type of access a rule grants
type Access int
const (
// AccessGranted to a resource
AccessGranted Access = iota
// AccessDenied to a resource
AccessDenied
)
// Rule is used to verify access to a resource
type Rule struct {
// ID of the rule, e.g. "public"
ID string
// Scope the rule requires, a blank scope indicates open to the public and * indicates the rule
// applies to any valid account
Scope string
// Resource the rule applies to
Resource *Resource
// Access determines if the rule grants or denies access to the resource
Access Access
// Priority the rule should take when verifying a request, the higher the value the sooner the
// rule will be applied
Priority int32
}
type accountKey struct{}
// AccountFromContext gets the account from the context, which
// is set by the auth wrapper at the start of a call. If the account
// is not set, a nil account will be returned. The error is only returned
// when there was a problem retrieving an account
func AccountFromContext(ctx context.Context) (*Account, bool) {
acc, ok := ctx.Value(accountKey{}).(*Account)
return acc, ok
}
// ContextWithAccount sets the account in the context
func ContextWithAccount(ctx context.Context, account *Account) context.Context {
return context.WithValue(ctx, accountKey{}, account)
}
+91
View File
@@ -0,0 +1,91 @@
package auth
import (
"github.com/google/uuid"
)
var (
DefaultAuth = NewAuth()
)
func NewAuth(opts ...Option) Auth {
options := Options{}
for _, o := range opts {
o(&options)
}
return &noop{
opts: options,
}
}
func NewRules() Rules {
return new(noopRules)
}
type noop struct {
opts Options
}
type noopRules struct{}
// 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 *noopRules) Grant(rule *Rule) error {
return nil
}
// Revoke access to a resource
func (n *noopRules) Revoke(rule *Rule) error {
return nil
}
// Rules used to verify requests
// Verify an account has access to a resource
func (n *noopRules) Verify(acc *Account, res *Resource, opts ...VerifyOption) error {
return nil
}
func (n *noopRules) List(opts ...ListOption) ([]*Rule, error) {
return []*Rule{}, 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
}
+207
View File
@@ -0,0 +1,207 @@
package auth
import (
"context"
"time"
)
func NewOptions(opts ...Option) Options {
var options Options
for _, o := range opts {
o(&options)
}
return options
}
type Options struct {
// Namespace the service belongs to
Namespace string
// ID is the services auth ID
ID string
// Secret is used to authenticate the service
Secret string
// Token is the services token used to authenticate itself
Token *Token
// PublicKey for decoding JWTs
PublicKey string
// PrivateKey for encoding JWTs
PrivateKey string
// Addrs sets the addresses of auth
Addrs []string
}
type Option func(o *Options)
// Addrs is the auth addresses to use
func Addrs(addrs ...string) Option {
return func(o *Options) {
o.Addrs = addrs
}
}
// Namespace the service belongs to
func Namespace(n string) Option {
return func(o *Options) {
o.Namespace = n
}
}
// PublicKey is the JWT public key
func PublicKey(key string) Option {
return func(o *Options) {
o.PublicKey = key
}
}
// PrivateKey is the JWT private key
func PrivateKey(key string) Option {
return func(o *Options) {
o.PrivateKey = key
}
}
// Credentials sets the auth credentials
func Credentials(id, secret string) Option {
return func(o *Options) {
o.ID = id
o.Secret = secret
}
}
// ClientToken sets the auth token to use when making requests
func ClientToken(token *Token) Option {
return func(o *Options) {
o.Token = token
}
}
type GenerateOptions struct {
// Metadata associated with the account
Metadata map[string]string
// Scopes the account has access too
Scopes []string
// Provider of the account, e.g. oauth
Provider string
// Type of the account, e.g. user
Type string
// Secret used to authenticate the account
Secret string
}
type GenerateOption func(o *GenerateOptions)
// WithSecret for the generated account
func WithSecret(s string) GenerateOption {
return func(o *GenerateOptions) {
o.Secret = s
}
}
// WithType for the generated account
func WithType(t string) GenerateOption {
return func(o *GenerateOptions) {
o.Type = t
}
}
// WithMetadata for the generated account
func WithMetadata(md map[string]string) GenerateOption {
return func(o *GenerateOptions) {
o.Metadata = md
}
}
// WithProvider for the generated account
func WithProvider(p string) GenerateOption {
return func(o *GenerateOptions) {
o.Provider = p
}
}
// WithScopes for the generated account
func WithScopes(s ...string) GenerateOption {
return func(o *GenerateOptions) {
o.Scopes = s
}
}
// NewGenerateOptions from a slice of options
func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
var options GenerateOptions
for _, o := range opts {
o(&options)
}
return options
}
type TokenOptions struct {
// ID for the account
ID string
// Secret for the account
Secret string
// RefreshToken is used to refesh a token
RefreshToken string
// Expiry is the time the token should live for
Expiry time.Duration
}
type TokenOption func(o *TokenOptions)
// WithExpiry for the token
func WithExpiry(ex time.Duration) TokenOption {
return func(o *TokenOptions) {
o.Expiry = ex
}
}
func WithCredentials(id, secret string) TokenOption {
return func(o *TokenOptions) {
o.ID = id
o.Secret = secret
}
}
func WithToken(rt string) TokenOption {
return func(o *TokenOptions) {
o.RefreshToken = rt
}
}
// NewTokenOptions from a slice of options
func NewTokenOptions(opts ...TokenOption) TokenOptions {
var options TokenOptions
for _, o := range opts {
o(&options)
}
// set defualt expiry of token
if options.Expiry == 0 {
options.Expiry = time.Minute
}
return options
}
type VerifyOptions struct {
Context context.Context
}
type VerifyOption func(o *VerifyOptions)
func VerifyContext(ctx context.Context) VerifyOption {
return func(o *VerifyOptions) {
o.Context = ctx
}
}
type ListOptions struct {
Context context.Context
}
type ListOption func(o *ListOptions)
func RulesContext(ctx context.Context) ListOption {
return func(o *ListOptions) {
o.Context = ctx
}
}
+91
View File
@@ -0,0 +1,91 @@
package auth
import (
"fmt"
"sort"
"strings"
)
// Verify an account has access to a resource using the rules provided. If the account does not have
// access an error will be returned. If there are no rules provided which match the resource, an error
// will be returned
func Verify(rules []*Rule, acc *Account, res *Resource) error {
// the rule is only to be applied if the type matches the resource or is catch-all (*)
validTypes := []string{"*", res.Type}
// the rule is only to be applied if the name matches the resource or is catch-all (*)
validNames := []string{"*", res.Name}
// rules can have wildcard excludes on endpoints since this can also be a path for web services,
// e.g. /foo/* would include /foo/bar. We also want to check for wildcards and the exact endpoint
validEndpoints := []string{"*", res.Endpoint}
if comps := strings.Split(res.Endpoint, "/"); len(comps) > 1 {
for i := 1; i < len(comps)+1; i++ {
wildcard := fmt.Sprintf("%v/*", strings.Join(comps[0:i], "/"))
validEndpoints = append(validEndpoints, wildcard)
}
}
// filter the rules to the ones which match the criteria above
filteredRules := make([]*Rule, 0)
for _, rule := range rules {
if !include(validTypes, rule.Resource.Type) {
continue
}
if !include(validNames, rule.Resource.Name) {
continue
}
if !include(validEndpoints, rule.Resource.Endpoint) {
continue
}
filteredRules = append(filteredRules, rule)
}
// sort the filtered rules by priority, highest to lowest
sort.SliceStable(filteredRules, func(i, j int) bool {
return filteredRules[i].Priority > filteredRules[j].Priority
})
// 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 == ScopePublic && rule.Access == AccessDenied {
return ErrForbidden
} else if rule.Scope == ScopePublic && rule.Access == AccessGranted {
return nil
}
// all further checks require an account
if acc == nil {
continue
}
// this rule applies to any account
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 == AccessDenied {
return ErrForbidden
} else if include(acc.Scopes, rule.Scope) && rule.Access == AccessGranted {
return nil
}
}
// if no rules matched then return forbidden
return ErrForbidden
}
// include is a helper function which checks to see if the slice contains the value. includes is
// not case sensitive.
func include(slice []string, val string) bool {
for _, s := range slice {
if strings.EqualFold(s, val) {
return true
}
}
return false
}
+288
View File
@@ -0,0 +1,288 @@
package auth
import (
"testing"
)
func TestVerify(t *testing.T) {
srvResource := &Resource{
Type: "service",
Name: "go.micro.service.foo",
Endpoint: "Foo.Bar",
}
webResource := &Resource{
Type: "service",
Name: "go.micro.web.foo",
Endpoint: "/foo/bar",
}
catchallResource := &Resource{
Type: "*",
Name: "*",
Endpoint: "*",
}
tt := []struct {
Name string
Rules []*Rule
Account *Account
Resource *Resource
Error error
}{
{
Name: "NoRules",
Rules: []*Rule{},
Account: nil,
Resource: srvResource,
Error: ErrForbidden,
},
{
Name: "CatchallPublicAccount",
Account: &Account{},
Resource: srvResource,
Rules: []*Rule{
&Rule{
Scope: "",
Resource: catchallResource,
},
},
},
{
Name: "CatchallPublicNoAccount",
Resource: srvResource,
Rules: []*Rule{
&Rule{
Scope: "",
Resource: catchallResource,
},
},
},
{
Name: "CatchallPrivateAccount",
Account: &Account{},
Resource: srvResource,
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
},
},
},
{
Name: "CatchallPrivateNoAccount",
Resource: srvResource,
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
},
},
Error: ErrForbidden,
},
{
Name: "CatchallServiceRuleMatch",
Resource: srvResource,
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &Resource{
Type: srvResource.Type,
Name: srvResource.Name,
Endpoint: "*",
},
},
},
},
{
Name: "CatchallServiceRuleNoMatch",
Resource: srvResource,
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &Resource{
Type: srvResource.Type,
Name: "wrongname",
Endpoint: "*",
},
},
},
Error: ErrForbidden,
},
{
Name: "ExactRuleValidScope",
Resource: srvResource,
Account: &Account{
Scopes: []string{"neededscope"},
},
Rules: []*Rule{
&Rule{
Scope: "neededscope",
Resource: srvResource,
},
},
},
{
Name: "ExactRuleInvalidScope",
Resource: srvResource,
Account: &Account{
Scopes: []string{"neededscope"},
},
Rules: []*Rule{
&Rule{
Scope: "invalidscope",
Resource: srvResource,
},
},
Error: ErrForbidden,
},
{
Name: "CatchallDenyWithAccount",
Resource: srvResource,
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: AccessDenied,
},
},
Error: ErrForbidden,
},
{
Name: "CatchallDenyWithNoAccount",
Resource: srvResource,
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: AccessDenied,
},
},
Error: ErrForbidden,
},
{
Name: "RulePriorityGrantFirst",
Resource: srvResource,
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: AccessGranted,
Priority: 1,
},
&Rule{
Scope: "*",
Resource: catchallResource,
Access: AccessDenied,
Priority: 0,
},
},
},
{
Name: "RulePriorityDenyFirst",
Resource: srvResource,
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: catchallResource,
Access: AccessGranted,
Priority: 0,
},
&Rule{
Scope: "*",
Resource: catchallResource,
Access: AccessDenied,
Priority: 1,
},
},
Error: ErrForbidden,
},
{
Name: "WebExactEndpointValid",
Resource: webResource,
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: webResource,
},
},
},
{
Name: "WebExactEndpointInalid",
Resource: webResource,
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "invalidendpoint",
},
},
},
Error: ErrForbidden,
},
{
Name: "WebWildcardEndpoint",
Resource: webResource,
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "*",
},
},
},
},
{
Name: "WebWildcardPathEndpointValid",
Resource: webResource,
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "/foo/*",
},
},
},
},
{
Name: "WebWildcardPathEndpointInvalid",
Resource: webResource,
Account: &Account{},
Rules: []*Rule{
&Rule{
Scope: "*",
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "/bar/*",
},
},
},
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 {
t.Errorf("Expected %v but got %v", tc.Error, err)
}
})
}
}
+1
View File
@@ -28,6 +28,7 @@ type Event interface {
Topic() string
Message() *Message
Ack() error
Error() error
}
// Subscriber is a convenience return type for the Subscribe method
-459
View File
@@ -1,459 +0,0 @@
package broker
import (
"context"
"errors"
"net"
"net/url"
"strconv"
"strings"
"sync"
"time"
"github.com/micro/go-micro/v2/codec/json"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/util/addr"
"github.com/micro/go-micro/v2/util/log"
"github.com/nats-io/nats-server/v2/server"
nats "github.com/nats-io/nats.go"
)
type natsBroker struct {
sync.Once
sync.RWMutex
// indicate if we're connected
connected bool
// address to bind routes to
addrs []string
// servers for the client
servers []string
// client connection and nats opts
conn *nats.Conn
opts Options
nopts nats.Options
// should we drain the connection
drain bool
closeCh chan (error)
// embedded server
server *server.Server
// configure to use local server
local bool
// server exit channel
exit chan bool
}
type subscriber struct {
s *nats.Subscription
opts SubscribeOptions
}
type publication struct {
t string
m *Message
}
func (p *publication) Topic() string {
return p.t
}
func (p *publication) Message() *Message {
return p.m
}
func (p *publication) Ack() error {
// nats does not support acking
return nil
}
func (s *subscriber) Options() SubscribeOptions {
return s.opts
}
func (s *subscriber) Topic() string {
return s.s.Subject
}
func (s *subscriber) Unsubscribe() error {
return s.s.Unsubscribe()
}
func (n *natsBroker) Address() string {
n.RLock()
defer n.RUnlock()
if n.server != nil {
return n.server.ClusterAddr().String()
}
if n.conn != nil && n.conn.IsConnected() {
return n.conn.ConnectedUrl()
}
if len(n.addrs) > 0 {
return n.addrs[0]
}
return "127.0.0.1:-1"
}
func (n *natsBroker) setAddrs(addrs []string) []string {
//nolint:prealloc
var cAddrs []string
for _, addr := range addrs {
if len(addr) == 0 {
continue
}
if !strings.HasPrefix(addr, "nats://") {
addr = "nats://" + addr
}
cAddrs = append(cAddrs, addr)
}
// if there's no address and we weren't told to
// embed a local server then use the default url
if len(cAddrs) == 0 && !n.local {
cAddrs = []string{nats.DefaultURL}
}
return cAddrs
}
// serve stats a local nats server if needed
func (n *natsBroker) serve(exit chan bool) error {
// local server address
host := "127.0.0.1"
port := -1
// cluster address
caddr := "0.0.0.0"
cport := -1
// with no address we just default it
// this is a local client address
if len(n.addrs) > 0 {
address := n.addrs[0]
if strings.HasPrefix(address, "nats://") {
address = strings.TrimPrefix(address, "nats://")
}
// parse out the address
h, p, err := net.SplitHostPort(address)
if err == nil {
caddr = h
cport, _ = strconv.Atoi(p)
}
}
// 1. create new server
// 2. register the server
// 3. connect to other servers
// set cluster opts
cOpts := server.ClusterOpts{
Host: caddr,
Port: cport,
}
// get the routes for other nodes
var routes []*url.URL
// get existing nats servers to connect to
services, err := n.opts.Registry.GetService("go.micro.nats.broker")
if err == nil {
for _, service := range services {
for _, node := range service.Nodes {
u, err := url.Parse("nats://" + node.Address)
if err != nil {
log.Log(err)
continue
}
// append to the cluster routes
routes = append(routes, u)
}
}
}
// try get existing server
s := n.server
if s != nil {
// stop the existing server
s.Shutdown()
}
s, err = server.NewServer(&server.Options{
// Specify the host
Host: host,
// Use a random port
Port: port,
// Set the cluster ops
Cluster: cOpts,
// Set the routes
Routes: routes,
NoLog: true,
NoSigs: true,
MaxControlLine: 2048,
TLSConfig: n.opts.TLSConfig,
})
if err != nil {
return err
}
// save the server
n.server = s
// start the server
go s.Start()
var ready bool
// wait till its ready for connections
for i := 0; i < 3; i++ {
if s.ReadyForConnections(time.Second) {
ready = true
break
}
}
if !ready {
return errors.New("server not ready")
}
// set the client address
n.servers = []string{s.ClientURL()}
go func() {
var advertise string
// parse out the address
_, port, err := net.SplitHostPort(s.ClusterAddr().String())
if err == nil {
addr, _ := addr.Extract("")
advertise = net.JoinHostPort(addr, port)
} else {
s.ClusterAddr().String()
}
// register the cluster address
for {
select {
case <-exit:
// deregister on exit
n.opts.Registry.Deregister(&registry.Service{
Name: "go.micro.nats.broker",
Version: "v2",
Nodes: []*registry.Node{
{Id: s.ID(), Address: advertise},
},
})
s.Shutdown()
return
default:
// register the broker
n.opts.Registry.Register(&registry.Service{
Name: "go.micro.nats.broker",
Version: "v2",
Nodes: []*registry.Node{
{Id: s.ID(), Address: advertise},
},
}, registry.RegisterTTL(time.Minute))
time.Sleep(time.Minute)
}
}
}()
return nil
}
func (n *natsBroker) Connect() error {
n.Lock()
defer n.Unlock()
if !n.connected {
// create exit chan
n.exit = make(chan bool)
// start the local server
if err := n.serve(n.exit); err != nil {
return err
}
// set to connected
n.connected = true
}
status := nats.CLOSED
if n.conn != nil {
status = n.conn.Status()
}
switch status {
case nats.CONNECTED, nats.RECONNECTING, nats.CONNECTING:
return nil
default: // DISCONNECTED or CLOSED or DRAINING
opts := n.nopts
opts.Servers = n.servers
opts.Secure = n.opts.Secure
opts.TLSConfig = n.opts.TLSConfig
// secure might not be set
if n.opts.TLSConfig != nil {
opts.Secure = true
}
c, err := opts.Connect()
if err != nil {
return err
}
n.conn = c
return nil
}
}
func (n *natsBroker) Disconnect() error {
n.RLock()
defer n.RUnlock()
if !n.connected {
return nil
}
// drain the connection if specified
if n.drain {
n.conn.Drain()
return <-n.closeCh
}
// close the client connection
n.conn.Close()
// shutdown the local server
// and deregister
select {
case <-n.exit:
default:
close(n.exit)
}
// set not connected
n.connected = false
return nil
}
func (n *natsBroker) Init(opts ...Option) error {
n.setOption(opts...)
return nil
}
func (n *natsBroker) Options() Options {
return n.opts
}
func (n *natsBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
b, err := n.opts.Codec.Marshal(msg)
if err != nil {
return err
}
n.RLock()
defer n.RUnlock()
return n.conn.Publish(topic, b)
}
func (n *natsBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
if n.conn == nil {
return nil, errors.New("not connected")
}
opt := SubscribeOptions{
AutoAck: true,
Context: context.Background(),
}
for _, o := range opts {
o(&opt)
}
fn := func(msg *nats.Msg) {
var m Message
if err := n.opts.Codec.Unmarshal(msg.Data, &m); err != nil {
return
}
handler(&publication{m: &m, t: msg.Subject})
}
var sub *nats.Subscription
var err error
n.RLock()
if len(opt.Queue) > 0 {
sub, err = n.conn.QueueSubscribe(topic, opt.Queue, fn)
} else {
sub, err = n.conn.Subscribe(topic, fn)
}
n.RUnlock()
if err != nil {
return nil, err
}
return &subscriber{s: sub, opts: opt}, nil
}
func (n *natsBroker) String() string {
return "eats"
}
func (n *natsBroker) setOption(opts ...Option) {
for _, o := range opts {
o(&n.opts)
}
n.Once.Do(func() {
n.nopts = nats.GetDefaultOptions()
})
// local embedded server
n.local = true
// set to drain
n.drain = true
if !n.opts.Secure {
n.opts.Secure = n.nopts.Secure
}
if n.opts.TLSConfig == nil {
n.opts.TLSConfig = n.nopts.TLSConfig
}
n.addrs = n.setAddrs(n.opts.Addrs)
}
func (n *natsBroker) onClose(conn *nats.Conn) {
n.closeCh <- nil
}
func (n *natsBroker) onAsyncError(conn *nats.Conn, sub *nats.Subscription, err error) {
// There are kinds of different async error nats might callback, but we are interested
// in ErrDrainTimeout only here.
if err == nats.ErrDrainTimeout {
n.closeCh <- err
}
}
func NewBroker(opts ...Option) Broker {
options := Options{
// Default codec
Codec: json.Marshaler{},
Context: context.Background(),
Registry: registry.DefaultRegistry,
}
n := &natsBroker{
opts: options,
}
n.setOption(opts...)
return n
}
+711
View File
@@ -0,0 +1,711 @@
// 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/asim/go-micro/v3/codec/json"
merr "github.com/asim/go-micro/v3/errors"
"github.com/asim/go-micro/v3/registry"
"github.com/asim/go-micro/v3/registry/cache"
maddr "github.com/asim/go-micro/v3/util/addr"
mnet "github.com/asim/go-micro/v3/util/net"
mls "github.com/asim/go-micro/v3/util/tls"
"github.com/google/uuid"
"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...)
}
+383
View File
@@ -0,0 +1,383 @@
package broker_test
import (
"sync"
"testing"
"time"
"github.com/asim/go-micro/v3/broker"
"github.com/asim/go-micro/v3/registry"
"github.com/google/uuid"
)
var (
// mock data
testData = map[string][]*registry.Service{
"foo": {
{
Name: "foo",
Version: "1.0.0",
Nodes: []*registry.Node{
{
Id: "foo-1.0.0-123",
Address: "localhost:9999",
},
{
Id: "foo-1.0.0-321",
Address: "localhost:9999",
},
},
},
{
Name: "foo",
Version: "1.0.1",
Nodes: []*registry.Node{
{
Id: "foo-1.0.1-321",
Address: "localhost:6666",
},
},
},
{
Name: "foo",
Version: "1.0.3",
Nodes: []*registry.Node{
{
Id: "foo-1.0.3-345",
Address: "localhost:8888",
},
},
},
},
}
)
func newTestRegistry() registry.Registry {
return registry.NewMemoryRegistry(registry.Services(testData))
}
func sub(be *testing.B, c int) {
be.StopTimer()
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
topic := uuid.New().String()
if err := b.Init(); err != nil {
be.Fatalf("Unexpected init error: %v", err)
}
if err := b.Connect(); err != nil {
be.Fatalf("Unexpected connect error: %v", err)
}
msg := &broker.Message{
Header: map[string]string{
"Content-Type": "application/json",
},
Body: []byte(`{"message": "Hello World"}`),
}
var subs []broker.Subscriber
done := make(chan bool, c)
for i := 0; i < c; i++ {
sub, err := b.Subscribe(topic, func(p broker.Event) error {
done <- true
m := p.Message()
if string(m.Body) != string(msg.Body) {
be.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
}
return nil
}, broker.Queue("shared"))
if err != nil {
be.Fatalf("Unexpected subscribe error: %v", err)
}
subs = append(subs, sub)
}
for i := 0; i < be.N; i++ {
be.StartTimer()
if err := b.Publish(topic, msg); err != nil {
be.Fatalf("Unexpected publish error: %v", err)
}
<-done
be.StopTimer()
}
for _, sub := range subs {
sub.Unsubscribe()
}
if err := b.Disconnect(); err != nil {
be.Fatalf("Unexpected disconnect error: %v", err)
}
}
func pub(be *testing.B, c int) {
be.StopTimer()
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
topic := uuid.New().String()
if err := b.Init(); err != nil {
be.Fatalf("Unexpected init error: %v", err)
}
if err := b.Connect(); err != nil {
be.Fatalf("Unexpected connect error: %v", err)
}
msg := &broker.Message{
Header: map[string]string{
"Content-Type": "application/json",
},
Body: []byte(`{"message": "Hello World"}`),
}
done := make(chan bool, c*4)
sub, err := b.Subscribe(topic, func(p broker.Event) error {
done <- true
m := p.Message()
if string(m.Body) != string(msg.Body) {
be.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
}
return nil
}, broker.Queue("shared"))
if err != nil {
be.Fatalf("Unexpected subscribe error: %v", err)
}
var wg sync.WaitGroup
ch := make(chan int, c*4)
be.StartTimer()
for i := 0; i < c; i++ {
go func() {
for range ch {
if err := b.Publish(topic, msg); err != nil {
be.Fatalf("Unexpected publish error: %v", err)
}
select {
case <-done:
case <-time.After(time.Second):
}
wg.Done()
}
}()
}
for i := 0; i < be.N; i++ {
wg.Add(1)
ch <- i
}
wg.Wait()
be.StopTimer()
sub.Unsubscribe()
close(ch)
close(done)
if err := b.Disconnect(); err != nil {
be.Fatalf("Unexpected disconnect error: %v", err)
}
}
func TestBroker(t *testing.T) {
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
if err := b.Init(); err != nil {
t.Fatalf("Unexpected init error: %v", err)
}
if err := b.Connect(); err != nil {
t.Fatalf("Unexpected connect error: %v", err)
}
msg := &broker.Message{
Header: map[string]string{
"Content-Type": "application/json",
},
Body: []byte(`{"message": "Hello World"}`),
}
done := make(chan bool)
sub, err := b.Subscribe("test", func(p broker.Event) error {
m := p.Message()
if string(m.Body) != string(msg.Body) {
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
}
close(done)
return nil
})
if err != nil {
t.Fatalf("Unexpected subscribe error: %v", err)
}
if err := b.Publish("test", msg); err != nil {
t.Fatalf("Unexpected publish error: %v", err)
}
<-done
sub.Unsubscribe()
if err := b.Disconnect(); err != nil {
t.Fatalf("Unexpected disconnect error: %v", err)
}
}
func TestConcurrentSubBroker(t *testing.T) {
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
if err := b.Init(); err != nil {
t.Fatalf("Unexpected init error: %v", err)
}
if err := b.Connect(); err != nil {
t.Fatalf("Unexpected connect error: %v", err)
}
msg := &broker.Message{
Header: map[string]string{
"Content-Type": "application/json",
},
Body: []byte(`{"message": "Hello World"}`),
}
var subs []broker.Subscriber
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
sub, err := b.Subscribe("test", func(p broker.Event) error {
defer wg.Done()
m := p.Message()
if string(m.Body) != string(msg.Body) {
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
}
return nil
})
if err != nil {
t.Fatalf("Unexpected subscribe error: %v", err)
}
wg.Add(1)
subs = append(subs, sub)
}
if err := b.Publish("test", msg); err != nil {
t.Fatalf("Unexpected publish error: %v", err)
}
wg.Wait()
for _, sub := range subs {
sub.Unsubscribe()
}
if err := b.Disconnect(); err != nil {
t.Fatalf("Unexpected disconnect error: %v", err)
}
}
func TestConcurrentPubBroker(t *testing.T) {
m := newTestRegistry()
b := broker.NewBroker(broker.Registry(m))
if err := b.Init(); err != nil {
t.Fatalf("Unexpected init error: %v", err)
}
if err := b.Connect(); err != nil {
t.Fatalf("Unexpected connect error: %v", err)
}
msg := &broker.Message{
Header: map[string]string{
"Content-Type": "application/json",
},
Body: []byte(`{"message": "Hello World"}`),
}
var wg sync.WaitGroup
sub, err := b.Subscribe("test", func(p broker.Event) error {
defer wg.Done()
m := p.Message()
if string(m.Body) != string(msg.Body) {
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
}
return nil
})
if err != nil {
t.Fatalf("Unexpected subscribe error: %v", err)
}
for i := 0; i < 10; i++ {
wg.Add(1)
if err := b.Publish("test", msg); err != nil {
t.Fatalf("Unexpected publish error: %v", err)
}
}
wg.Wait()
sub.Unsubscribe()
if err := b.Disconnect(); err != nil {
t.Fatalf("Unexpected disconnect error: %v", err)
}
}
func BenchmarkSub1(b *testing.B) {
sub(b, 1)
}
func BenchmarkSub8(b *testing.B) {
sub(b, 8)
}
func BenchmarkSub32(b *testing.B) {
sub(b, 32)
}
func BenchmarkSub64(b *testing.B) {
sub(b, 64)
}
func BenchmarkSub128(b *testing.B) {
sub(b, 128)
}
func BenchmarkPub1(b *testing.B) {
pub(b, 1)
}
func BenchmarkPub8(b *testing.B) {
pub(b, 8)
}
func BenchmarkPub32(b *testing.B) {
pub(b, 32)
}
func BenchmarkPub64(b *testing.B) {
pub(b, 64)
}
func BenchmarkPub128(b *testing.B) {
pub(b, 128)
}
+25 -5
View File
@@ -4,14 +4,19 @@ import (
"context"
"crypto/tls"
"github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v2/registry"
"github.com/asim/go-micro/v3/codec"
"github.com/asim/go-micro/v3/registry"
)
type Options struct {
Addrs []string
Secure bool
Codec codec.Marshaler
Addrs []string
Secure bool
Codec codec.Marshaler
// Handler executed when error happens in broker mesage
// processing
ErrorHandler Handler
TLSConfig *tls.Config
// Registry used for clustering
Registry registry.Registry
@@ -44,6 +49,13 @@ type Option func(*Options)
type PublishOption func(*PublishOptions)
// PublishContext set context
func PublishContext(ctx context.Context) PublishOption {
return func(o *PublishOptions) {
o.Context = ctx
}
}
type SubscribeOption func(*SubscribeOptions)
func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions {
@@ -81,6 +93,14 @@ func DisableAutoAck() SubscribeOption {
}
}
// ErrorHandler will catch all broker errors that cant be handled
// in normal way, for example Codec errors
func ErrorHandler(h Handler) Option {
return func(o *Options) {
o.ErrorHandler = h
}
}
// Queue sets the name of the queue to share messages on
func Queue(name string) SubscribeOption {
return func(o *SubscribeOptions) {
-229
View File
@@ -1,229 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: micro/go-micro/broker/service/proto/broker.proto
package go_micro_broker
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
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_178fdc60944ff5e5, []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_178fdc60944ff5e5, []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_178fdc60944ff5e5, []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_178fdc60944ff5e5, []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("micro/go-micro/broker/service/proto/broker.proto", fileDescriptor_178fdc60944ff5e5)
}
var fileDescriptor_178fdc60944ff5e5 = []byte{
// 305 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x51, 0x4d, 0x4f, 0xc2, 0x40,
0x14, 0x64, 0x41, 0x68, 0x78, 0x18, 0x25, 0x1b, 0x62, 0x1a, 0x2e, 0x62, 0xe3, 0x81, 0x8b, 0x5b,
0x52, 0x2f, 0x6a, 0x8c, 0x07, 0x23, 0x89, 0x07, 0x4d, 0xcc, 0x7a, 0xf3, 0xd6, 0x2d, 0x2f, 0xa5,
0x81, 0xba, 0x65, 0xb7, 0x25, 0xe9, 0x1f, 0xf1, 0xe4, 0x8f, 0x35, 0xec, 0x16, 0x3f, 0x68, 0xf0,
0x36, 0xf3, 0x76, 0x76, 0xde, 0x64, 0x1e, 0x4c, 0xd2, 0x24, 0x52, 0xd2, 0x8f, 0xe5, 0x85, 0x05,
0x42, 0xc9, 0x05, 0x2a, 0x5f, 0xa3, 0x5a, 0x27, 0x11, 0xfa, 0x99, 0x92, 0xf9, 0x76, 0xc8, 0x0c,
0xa1, 0xc7, 0xb1, 0x64, 0x46, 0xcb, 0xec, 0xd8, 0x73, 0xa0, 0x3d, 0x4d, 0xb3, 0xbc, 0xf4, 0xde,
0xe0, 0xe8, 0xa5, 0x10, 0xcb, 0x44, 0xcf, 0x39, 0xae, 0x0a, 0xd4, 0x39, 0x1d, 0x40, 0x3b, 0x97,
0x59, 0x12, 0xb9, 0x64, 0x44, 0xc6, 0x5d, 0x6e, 0x09, 0x0d, 0xc0, 0x49, 0x51, 0xeb, 0x30, 0x46,
0xb7, 0x39, 0x22, 0xe3, 0x5e, 0xe0, 0xb2, 0x1d, 0x4f, 0xf6, 0x6c, 0xdf, 0xf9, 0x56, 0xe8, 0xdd,
0x41, 0xff, 0xb5, 0x10, 0x3a, 0x52, 0x89, 0xc0, 0xff, 0xdd, 0x07, 0xd0, 0x5e, 0x15, 0x58, 0x58,
0xef, 0x2e, 0xb7, 0xc4, 0xfb, 0x20, 0xe0, 0x54, 0xa6, 0xf4, 0x16, 0x3a, 0x73, 0x0c, 0x67, 0xa8,
0x5c, 0x32, 0x6a, 0x8d, 0x7b, 0xc1, 0xf9, 0xbe, 0xf5, 0xec, 0xd1, 0xc8, 0xa6, 0xef, 0xb9, 0x2a,
0x79, 0xf5, 0x87, 0x52, 0x38, 0x10, 0x72, 0x56, 0x1a, 0xfb, 0x43, 0x6e, 0xf0, 0xf0, 0x1a, 0x7a,
0xbf, 0xa4, 0xb4, 0x0f, 0xad, 0x05, 0x96, 0x55, 0xac, 0x0d, 0xdc, 0x84, 0x5a, 0x87, 0xcb, 0x9f,
0x50, 0x86, 0xdc, 0x34, 0xaf, 0x48, 0xf0, 0x49, 0xa0, 0x73, 0x6f, 0xb6, 0xd2, 0x07, 0x70, 0xaa,
0xfe, 0xe8, 0x69, 0x2d, 0xd2, 0xdf, 0x66, 0x87, 0x27, 0x35, 0x81, 0xbd, 0x41, 0x83, 0x3e, 0x41,
0xf7, 0xbb, 0x29, 0x7a, 0x56, 0x93, 0xed, 0xb6, 0x38, 0xdc, 0x5b, 0xbe, 0xd7, 0x98, 0x10, 0xd1,
0x31, 0x47, 0xbf, 0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x60, 0x8c, 0x40, 0xd5, 0x28, 0x02, 0x00,
0x00,
}
-173
View File
@@ -1,173 +0,0 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/broker/service/proto/broker.proto
package go_micro_broker
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
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 _ context.Context
var _ client.Option
var _ server.Option
// 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 {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.broker"
}
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 {
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) 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 {
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) 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)
}
-25
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;
}
-134
View File
@@ -1,134 +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/util/log"
)
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 {
log.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)
}
log.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:
log.Debugf("Unsubscribed from topic %s", topic)
return
default:
// run the subscriber
log.Debugf("Streaming from broker %v to topic [%s] queue [%s]", b.Addrs, topic, options.Queue)
if err := sub.run(); err != nil {
log.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 {
log.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,
}
}
-101
View File
@@ -1,101 +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/util/log"
)
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
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 *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 {
log.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
}
// TODO: handle error
s.handler(&serviceEvent{
topic: s.topic,
message: &broker.Message{
Header: msg.Header,
Body: msg.Body,
},
})
}
}
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
}
+65
View File
@@ -0,0 +1,65 @@
package cache
import (
"context"
"errors"
"time"
)
var (
// DefaultCache is the default cache.
DefaultCache Cache = NewCache()
// DefaultExpiration is the default duration for items stored in
// the cache to expire.
DefaultExpiration time.Duration = 0
// ErrItemExpired is returned in Cache.Get when the item found in the cache
// has expired.
ErrItemExpired error = errors.New("item has expired")
// ErrKeyNotFound is returned in Cache.Get and Cache.Delete when the
// provided key could not be found in cache.
ErrKeyNotFound error = errors.New("key not found in cache")
)
// Cache is the interface that wraps the cache.
//
// Context specifies the context for the cache.
// Get gets a cached value by key.
// Put stores a key-value pair into cache.
// Delete removes a key from cache.
type Cache interface {
Context(ctx context.Context) Cache
Get(key string) (interface{}, time.Time, error)
Put(key string, val interface{}, d time.Duration) error
Delete(key string) error
}
// Item represents an item stored in the cache.
type Item struct {
Value interface{}
Expiration int64
}
// Expired returns true if the item has expired.
func (i *Item) Expired() bool {
if i.Expiration == 0 {
return false
}
return time.Now().UnixNano() > i.Expiration
}
// NewCache returns a new cache.
func NewCache(opts ...Option) Cache {
options := NewOptions(opts...)
items := make(map[string]Item)
if len(options.Items) > 0 {
items = options.Items
}
return &memCache{
opts: options,
items: items,
}
}
+111
View File
@@ -0,0 +1,111 @@
package cache
import (
"context"
"testing"
"time"
)
var (
ctx context.Context = context.TODO()
key string = "test"
val interface{} = "hello go-micro"
)
// TestMemCache tests the in-memory cache implementation.
func TestCache(t *testing.T) {
t.Run("CacheGetMiss", func(t *testing.T) {
if _, _, err := NewCache().Context(ctx).Get(key); err == nil {
t.Error("expected to get no value from cache")
}
})
t.Run("CacheGetHit", func(t *testing.T) {
c := NewCache()
if err := c.Context(ctx).Put(key, val, 0); err != nil {
t.Error(err)
}
if a, _, err := c.Context(ctx).Get(key); err != nil {
t.Errorf("Expected a value, got err: %s", err)
} else if a != val {
t.Errorf("Expected '%v', got '%v'", val, a)
}
})
t.Run("CacheGetExpired", func(t *testing.T) {
c := NewCache()
e := 20 * time.Millisecond
if err := c.Context(ctx).Put(key, val, e); err != nil {
t.Error(err)
}
<-time.After(25 * time.Millisecond)
if _, _, err := c.Context(ctx).Get(key); err == nil {
t.Error("expected to get no value from cache")
}
})
t.Run("CacheGetValid", func(t *testing.T) {
c := NewCache()
e := 25 * time.Millisecond
if err := c.Context(ctx).Put(key, val, e); err != nil {
t.Error(err)
}
<-time.After(20 * time.Millisecond)
if _, _, err := c.Context(ctx).Get(key); err != nil {
t.Errorf("expected a value, got err: %s", err)
}
})
t.Run("CacheDeleteMiss", func(t *testing.T) {
if err := NewCache().Context(ctx).Delete(key); err == nil {
t.Error("expected to delete no value from cache")
}
})
t.Run("CacheDeleteHit", func(t *testing.T) {
c := NewCache()
if err := c.Context(ctx).Put(key, val, 0); err != nil {
t.Error(err)
}
if err := c.Context(ctx).Delete(key); err != nil {
t.Errorf("Expected to delete an item, got err: %s", err)
}
if _, _, err := c.Context(ctx).Get(key); err == nil {
t.Errorf("Expected error")
}
})
}
func TestCacheWithOptions(t *testing.T) {
t.Run("CacheWithExpiration", func(t *testing.T) {
c := NewCache(Expiration(20 * time.Millisecond))
if err := c.Context(ctx).Put(key, val, 0); err != nil {
t.Error(err)
}
<-time.After(25 * time.Millisecond)
if _, _, err := c.Context(ctx).Get(key); err == nil {
t.Error("expected to get no value from cache")
}
})
t.Run("CacheWithItems", func(t *testing.T) {
c := NewCache(Items(map[string]Item{key: {val, 0}}))
if a, _, err := c.Context(ctx).Get(key); err != nil {
t.Errorf("Expected a value, got err: %s", err)
} else if a != val {
t.Errorf("Expected '%v', got '%v'", val, a)
}
})
}
+68
View File
@@ -0,0 +1,68 @@
package cache
import (
"context"
"sync"
"time"
)
type memCache struct {
opts Options
sync.RWMutex
ctx context.Context
items map[string]Item
}
func (c *memCache) Context(ctx context.Context) Cache {
c.ctx = ctx
return c
}
func (c *memCache) Get(key string) (interface{}, time.Time, error) {
c.RWMutex.Lock()
defer c.RWMutex.Unlock()
item, found := c.items[key]
if !found {
return nil, time.Time{}, ErrKeyNotFound
}
if item.Expired() {
return nil, time.Time{}, ErrItemExpired
}
return item.Value, time.Unix(0, item.Expiration), nil
}
func (c *memCache) Put(key string, val interface{}, d time.Duration) error {
var e int64
if d == DefaultExpiration {
d = c.opts.Expiration
}
if d > 0 {
e = time.Now().Add(d).UnixNano()
}
c.RWMutex.Lock()
defer c.RWMutex.Unlock()
c.items[key] = Item{
Value: val,
Expiration: e,
}
return nil
}
func (c *memCache) Delete(key string) error {
c.RWMutex.Lock()
defer c.RWMutex.Unlock()
_, found := c.items[key]
if !found {
return ErrKeyNotFound
}
delete(c.items, key)
return nil
}
+40
View File
@@ -0,0 +1,40 @@
package cache
import "time"
// Options represents the options for the cache.
type Options struct {
Expiration time.Duration
Items map[string]Item
}
// Option manipulates the Options passed.
type Option func(o *Options)
// Expiration sets the duration for items stored in the cache to expire.
func Expiration(d time.Duration) Option {
return func(o *Options) {
o.Expiration = d
}
}
// Items initializes the cache with preconfigured items.
func Items(i map[string]Item) Option {
return func(o *Options) {
o.Items = i
}
}
// NewOptions returns a new options struct.
func NewOptions(opts ...Option) Options {
options := Options{
Expiration: DefaultExpiration,
Items: make(map[string]Item),
}
for _, o := range opts {
o(&options)
}
return options
}
+43
View File
@@ -0,0 +1,43 @@
package cache
import (
"testing"
"time"
)
func TestOptions(t *testing.T) {
testData := map[string]struct {
set bool
expiration time.Duration
items map[string]Item
}{
"DefaultOptions": {false, DefaultExpiration, map[string]Item{}},
"ModifiedOptions": {true, time.Second, map[string]Item{"test": {"hello go-micro", 0}}},
}
for k, d := range testData {
t.Run(k, func(t *testing.T) {
var opts Options
if d.set {
opts = NewOptions(
Expiration(d.expiration),
Items(d.items),
)
} else {
opts = NewOptions()
}
// test options
for _, o := range []Options{opts} {
if o.Expiration != d.expiration {
t.Fatalf("Expected expiration '%v', got '%v'", d.expiration, o.Expiration)
}
if o.Items["test"] != d.items["test"] {
t.Fatalf("Expected items %#v, got %#v", d.items, o.Items)
}
}
})
}
}

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