Compare commits

..

271 Commits
upstream ... v4

Author SHA1 Message Date
vtolstov
9bb2f8cffa Apply Code Coverage Badge 2025-09-23 10:34:26 +00:00
24801750a7 integrate request builder into HTTP client for googleapis support (#157)
Some checks failed
coverage / build (push) Successful in 2m19s
test / test (push) Failing after 17m15s
2025-09-23 13:30:15 +03:00
vtolstov
b37fca95cf Apply Code Coverage Badge 2025-06-05 09:23:41 +00:00
4c4a024e19 fix for headers with hyphens (#155)
All checks were successful
coverage / build (push) Successful in 52s
test / test (push) Successful in 2m44s
2025-06-05 12:18:50 +03:00
b93e634873 fixed working with headers and cookies (#154)
Some checks failed
coverage / build (push) Successful in 3m33s
test / test (push) Failing after 16m33s
sync / sync (push) Successful in 11s
2025-06-04 23:12:06 +03:00
4a6414b2b8 changed embedded mutex to private field (#153)
Some checks failed
test / test (push) Failing after 16m53s
coverage / build (push) Failing after 17m7s
sync / sync (push) Successful in 13s
2025-05-25 01:14:50 +03:00
938ddd2ab3 added commit hash check to avoid unnecessary repository cloning (#152)
All checks were successful
sync / sync (push) Successful in 18s
2025-05-05 19:25:44 +03:00
04e0597b73 fixup workflows
All checks were successful
sync / sync (push) Successful in 35s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-04 22:52:28 +03:00
4d55c7b90a [v4] rename .gitea to .github (#148)
Some checks failed
sync / sync (push) Has been cancelled
* rename .gitea to .github

* attempt to fix lint/test job
2025-04-28 10:26:29 +03:00
fbe82a3905 improve sync
All checks were successful
sync / sync (push) Successful in 43s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-04-27 21:07:40 +03:00
vtolstov
14490a419e Apply Code Coverage Badge 2025-04-27 13:31:31 +00:00
d434261168 update deps
All checks were successful
coverage / build (push) Successful in 2m48s
sync / sync (push) Successful in 1m25s
test / test (push) Successful in 5m4s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-04-27 16:29:22 +03:00
2e384a5b75 Merge pull request 'prepare v4' (#125) from devstigneev/micro-client-http:prepare_v4_1 into v4
All checks were successful
test / test (push) Successful in 1m53s
move forward to v4

Reviewed-on: #125
2025-02-28 14:39:02 +03:00
d9e7ac75ef prepare v4
All checks were successful
lint / lint (pull_request) Successful in 1m38s
test / test (pull_request) Successful in 3m9s
2025-02-27 18:57:14 +03:00
a9829b5da0 Merge pull request 'move set content-type in client publish' (#123) from devstigneev/micro-client-http:v3 into v3
All checks were successful
test / test (push) Successful in 2m56s
Reviewed-on: #123
2025-01-18 15:38:23 +03:00
a770c498e7 move set content-type in client publish
All checks were successful
lint / lint (pull_request) Successful in 1m25s
test / test (pull_request) Successful in 2m56s
2025-01-17 17:57:44 +03:00
fa5602486a lintering && fix call hook (#122)
All checks were successful
test / test (push) Successful in 4m10s
Reviewed-on: #122
Co-authored-by: Evstigneev Denis <danteevstigneev@yandex.ru>
Co-committed-by: Evstigneev Denis <danteevstigneev@yandex.ru>
2024-12-18 20:43:01 +03:00
d5690ef2b7 Update workflows (#121)
All checks were successful
test / test (push) Successful in 2m31s
Co-authored-by: Aleksandr Tolstikhin <atolstikhin@mtsbank.ru>
Reviewed-on: #121
Co-authored-by: Александр Толстихин <tolstihin1996@mail.ru>
Co-committed-by: Александр Толстихин <tolstihin1996@mail.ru>
2024-12-11 00:35:07 +03:00
134031e18b update for latest micro logger changes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-10-12 13:26:11 +03:00
9743a7f9b1 update deps
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-09-20 18:45:01 +03:00
2225b95e3e try to fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-05-20 08:55:24 +03:00
b76b8d3114 fixup path filling
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-05-04 15:05:21 +03:00
b92b2541e0 fixup path filling
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-05-04 13:41:59 +03:00
9d955fc079 lower deps
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-05-04 09:18:37 +03:00
1be75ea264 fill nested fields in path
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-05-02 14:40:12 +03:00
d4a07099a2 add metrics and tracing
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-04-23 06:51:47 +03:00
a648bf77f4 fixup logger
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-04-09 23:57:44 +03:00
0eb663fffe fixup logger
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-04-09 23:46:36 +03:00
Nurzhan Ilyassov
fd803b1fe0 update tests 2024-03-31 20:21:45 +03:00
Nurzhan Ilyassov
4c2178305d fix nil nmsg in case of empty request body 2024-03-31 20:21:45 +03:00
9149aeb3de Merge pull request 'fixup md' (#116) from pubmdfix into v3
Reviewed-on: #116
2023-12-21 00:16:22 +03:00
dcadd64941 fixup md
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-12-21 00:16:02 +03:00
8207d4154f Merge pull request 'fix MessageMetadata option' (#115) from client-metadata into v3
Reviewed-on: #115
2023-10-26 03:16:36 +03:00
d7524cbe01 fix MessageMetadata option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-26 03:16:09 +03:00
0e7b8e73a8 Merge pull request 'fix request/response md handling' (#113) from reqrsp-md into v3
Reviewed-on: #113
2023-07-11 00:55:15 +03:00
35146aa717 fix request/response md handling
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-07-11 00:54:23 +03:00
3f44a41d30 Merge pull request 'simplify rsp parse' (#108) from logrsp into v3
Reviewed-on: #108
2023-03-16 19:17:12 +03:00
090100e522 simplify rsp parse
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-16 19:16:35 +03:00
0bf8053d72 Merge pull request 'logging: add debug logging for request and response' (#107) from logging into v3
Reviewed-on: #107
2023-03-15 22:26:58 +03:00
42bdd79b3d add debug logging for request and response
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-15 22:26:14 +03:00
5fa952c86a Merge pull request #107 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.10.16
Bump go.unistack.org/micro/v3 from 3.10.14 to 3.10.16
2023-03-14 22:00:27 +03:00
dependabot[bot]
d861c31a05 Bump go.unistack.org/micro/v3 from 3.10.14 to 3.10.16
Bumps go.unistack.org/micro/v3 from 3.10.14 to 3.10.16.

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-14 18:58:37 +00:00
e7dc59157d Merge pull request #106 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.10.14
Bump go.unistack.org/micro/v3 from 3.10.13 to 3.10.14
2023-02-27 22:08:00 +03:00
dependabot[bot]
7f7081e6e8 Bump go.unistack.org/micro/v3 from 3.10.13 to 3.10.14
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.10.13 to 3.10.14.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.10.13...v3.10.14)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 19:06:37 +00:00
d441f151e0 Merge pull request #105 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.10.13
Bump go.unistack.org/micro/v3 from 3.10.12 to 3.10.13
2023-02-22 21:13:36 +03:00
dependabot[bot]
6a6be26ef1 Bump go.unistack.org/micro/v3 from 3.10.12 to 3.10.13
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.10.12 to 3.10.13.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.10.12...v3.10.13)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-22 18:11:53 +00:00
d1d5e0f4ac Merge pull request #104 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.10.12
Bump go.unistack.org/micro/v3 from 3.10.11 to 3.10.12
2023-02-21 22:32:42 +03:00
dependabot[bot]
3f8976ee62 Bump go.unistack.org/micro/v3 from 3.10.11 to 3.10.12
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.10.11 to 3.10.12.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.10.11...v3.10.12)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-21 19:30:23 +00:00
2078f2e700 Merge pull request #103 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.10.11
Bump go.unistack.org/micro/v3 from 3.10.10 to 3.10.11
2023-02-14 22:00:49 +03:00
dependabot[bot]
007d45fc7b Bump go.unistack.org/micro/v3 from 3.10.10 to 3.10.11
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.10.10 to 3.10.11.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.10.10...v3.10.11)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-14 18:58:54 +00:00
6c84752ccd Merge pull request #102 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.10.10
Bump go.unistack.org/micro/v3 from 3.10.9 to 3.10.10
2023-02-13 22:17:58 +03:00
dependabot[bot]
54d21ba6e1 Bump go.unistack.org/micro/v3 from 3.10.9 to 3.10.10
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.10.9 to 3.10.10.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.10.9...v3.10.10)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 19:16:23 +00:00
5b1f1df17a Merge pull request #101 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.10.9
Bump go.unistack.org/micro/v3 from 3.10.6 to 3.10.9
2023-02-08 21:05:25 +03:00
dependabot[bot]
11d7ee54fd Bump go.unistack.org/micro/v3 from 3.10.6 to 3.10.9
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.10.6 to 3.10.9.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.10.6...v3.10.9)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-08 18:03:34 +00:00
41d606b4e8 Merge pull request #100 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.10.6
Bump go.unistack.org/micro/v3 from 3.10.5 to 3.10.6
2023-02-06 21:08:51 +03:00
dependabot[bot]
5f6f06c239 Bump go.unistack.org/micro/v3 from 3.10.5 to 3.10.6
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.10.5 to 3.10.6.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.10.5...v3.10.6)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 18:06:46 +00:00
dependabot[bot]
f80bb95119 Bump dependabot/fetch-metadata from 1.3.5 to 1.3.6 (#97)
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.5 to 1.3.6.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.5...v1.3.6)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-30 21:21:22 +03:00
dependabot[bot]
cd23f93d57 Bump golangci/golangci-lint-action from 3.3.1 to 3.4.0 (#96)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.3.1 to 3.4.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.3.1...v3.4.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-30 18:14:03 +00:00
f625d5c958 Merge pull request #98 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.10.5
Bump go.unistack.org/micro/v3 from 3.10.4 to 3.10.5
2023-01-30 21:08:56 +03:00
dependabot[bot]
8b068fb7ab Bump go.unistack.org/micro/v3 from 3.10.4 to 3.10.5
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.10.4 to 3.10.5.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.10.4...v3.10.5)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-30 18:06:50 +00:00
3a1e9ec707 Merge pull request #95 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.10.4
Bump go.unistack.org/micro/v3 from 3.10.1 to 3.10.4
2023-01-18 21:04:54 +03:00
dependabot[bot]
4d6a98782c Bump go.unistack.org/micro/v3 from 3.10.1 to 3.10.4
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.10.1 to 3.10.4.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.10.1...v3.10.4)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-18 18:03:06 +00:00
af838ec6e5 Merge pull request #94 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.10.1
Bump go.unistack.org/micro/v3 from 3.10.0 to 3.10.1
2023-01-17 21:05:06 +03:00
dependabot[bot]
0c90ea28d9 Bump go.unistack.org/micro/v3 from 3.10.0 to 3.10.1
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.10.0 to 3.10.1.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.10.0...v3.10.1)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-17 18:03:12 +00:00
90e327726b Merge pull request #93 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.10.0
Bump go.unistack.org/micro/v3 from 3.9.18 to 3.10.0
2023-01-09 21:21:55 +03:00
dependabot[bot]
6e29ac652b Bump go.unistack.org/micro/v3 from 3.9.18 to 3.10.0
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.9.18 to 3.10.0.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.9.18...v3.10.0)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-09 18:20:15 +00:00
f8b0fe881f Merge pull request #92 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.9.18
Bump go.unistack.org/micro/v3 from 3.9.17 to 3.9.18
2022-12-28 21:09:44 +03:00
dependabot[bot]
2941a335f0 Bump go.unistack.org/micro/v3 from 3.9.17 to 3.9.18
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.9.17 to 3.9.18.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.9.17...v3.9.18)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-28 18:07:29 +00:00
712ba79b55 Merge pull request #91 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.9.17
Bump go.unistack.org/micro/v3 from 3.9.15 to 3.9.17
2022-12-26 21:05:49 +03:00
dependabot[bot]
3df3d6bfa3 Bump go.unistack.org/micro/v3 from 3.9.15 to 3.9.17
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.9.15 to 3.9.17.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.9.15...v3.9.17)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-26 18:04:17 +00:00
bed23083cf Merge pull request #90 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.9.15
Bump go.unistack.org/micro/v3 from 3.9.13 to 3.9.15
2022-11-28 21:08:44 +03:00
dependabot[bot]
79175c96aa Bump go.unistack.org/micro/v3 from 3.9.13 to 3.9.15
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.9.13 to 3.9.15.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.9.13...v3.9.15)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 18:06:45 +00:00
dependabot[bot]
c23192f2b6 Bump hmarr/auto-approve-action from 2 to 3 (#89)
Bumps [hmarr/auto-approve-action](https://github.com/hmarr/auto-approve-action) from 2 to 3.
- [Release notes](https://github.com/hmarr/auto-approve-action/releases)
- [Commits](https://github.com/hmarr/auto-approve-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: hmarr/auto-approve-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-28 16:25:58 +03:00
2bf129d6ab Merge pull request #88 from unistack-org/ctx_dialer
add context dialer support
2022-11-17 00:06:59 +03:00
4bf60486e0 add context dialer support
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-11-17 00:05:09 +03:00
d048278b84 Merge pull request #87 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.9.13
Bump go.unistack.org/micro/v3 from 3.9.11 to 3.9.13
2022-11-14 21:05:48 +03:00
dependabot[bot]
1cf18ac2a9 Bump go.unistack.org/micro/v3 from 3.9.11 to 3.9.13
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.9.11 to 3.9.13.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.9.11...v3.9.13)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-14 18:03:58 +00:00
dependabot[bot]
251f6f70c4 Bump golangci/golangci-lint-action from 3.2.0 to 3.3.1 (#86)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.2.0 to 3.3.1.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.2.0...v3.3.1)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-14 07:22:37 +03:00
dependabot[bot]
26bf526741 Bump dependabot/fetch-metadata from 1.3.4 to 1.3.5 (#85)
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.4 to 1.3.5.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.4...v1.3.5)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-14 07:04:31 +03:00
dependabot[bot]
a86c824eb7 Bump dependabot/fetch-metadata from 1.3.3 to 1.3.4 (#83)
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.3 to 1.3.4.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.3...v1.3.4)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-04 11:43:33 +03:00
dependabot[bot]
f8cd67b8cd Bump dependabot/fetch-metadata from 1.3.1 to 1.3.3 (#81)
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.1 to 1.3.3.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.1...v1.3.3)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-28 17:45:04 +03:00
feb5fec0dd Merge pull request #82 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.9.11
Bump go.unistack.org/micro/v3 from 3.9.10 to 3.9.11
2022-07-11 21:20:31 +03:00
dependabot[bot]
37996fbe90 Bump go.unistack.org/micro/v3 from 3.9.10 to 3.9.11
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.9.10 to 3.9.11.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.9.10...v3.9.11)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-11 18:18:37 +00:00
08a22e02f9 Merge pull request #79 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.9.10
Bump go.unistack.org/micro/v3 from 3.9.8 to 3.9.10
2022-06-27 21:40:09 +03:00
dependabot[bot]
3358ce8aa2 Bump go.unistack.org/micro/v3 from 3.9.8 to 3.9.10
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.9.8 to 3.9.10.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.9.8...v3.9.10)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-27 18:38:09 +00:00
dependabot[bot]
74158bd60b Bump golangci/golangci-lint-action from 3.1.0 to 3.2.0 (#78)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.1.0...v3.2.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-25 22:52:35 +03:00
930530cbae Merge pull request #77 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.9.8
Bump go.unistack.org/micro/v3 from 3.9.7 to 3.9.8
2022-05-03 21:15:55 +03:00
dependabot[bot]
24a8dbdc4b Bump go.unistack.org/micro/v3 from 3.9.7 to 3.9.8
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.9.7 to 3.9.8.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.9.7...v3.9.8)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-03 18:14:10 +00:00
c3c580163d Merge pull request #76 from unistack-org/backoff
use backoff for lookup too
2022-05-01 23:34:28 +03:00
4ad526253a use backoff for lookup too
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-01 23:32:37 +03:00
dependabot[bot]
3eca811f81 Bump github/codeql-action from 1 to 2 (#75)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1 to 2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-25 21:44:56 +03:00
dependabot[bot]
367fe6d579 Bump dependabot/fetch-metadata from 1.3.0 to 1.3.1 (#74)
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.0...v1.3.1)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-21 12:48:37 +03:00
b86a166d08 Merge pull request #73 from unistack-org/dependabot/github_actions/actions/setup-go-3
Bump actions/setup-go from 2 to 3
2022-04-11 22:58:06 +03:00
dependabot[bot]
ecd2711714 Bump actions/setup-go from 2 to 3
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-08 18:17:08 +00:00
0e82845989 Merge pull request #72 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.9.7
Bump go.unistack.org/micro/v3 from 3.9.5 to 3.9.7
2022-03-30 21:24:05 +03:00
dependabot[bot]
670a31c9ef Bump go.unistack.org/micro/v3 from 3.9.5 to 3.9.7
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.9.5 to 3.9.7.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.9.5...v3.9.7)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-30 18:22:04 +00:00
6f136a039b Merge pull request #71 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.9.5
Bump go.unistack.org/micro/v3 from 3.9.2 to 3.9.5
2022-03-28 21:29:16 +03:00
dependabot[bot]
c8516877ee Bump go.unistack.org/micro/v3 from 3.9.2 to 3.9.5
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.9.2 to 3.9.5.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.9.2...v3.9.5)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-28 18:27:27 +00:00
2075368ba3 Merge pull request #70 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.9.2
Bump go.unistack.org/micro/v3 from 3.9.1 to 3.9.2
2022-03-25 21:24:30 +03:00
dependabot[bot]
fbef71bae8 Bump go.unistack.org/micro/v3 from 3.9.1 to 3.9.2
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.9.1 to 3.9.2.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.9.1...v3.9.2)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-25 18:23:00 +00:00
a946d63cae Merge pull request #68 from unistack-org/dependabot/github_actions/actions/cache-3
Bump actions/cache from 2 to 3
2022-03-22 23:14:45 +03:00
dependabot[bot]
bc47181f45 Bump actions/cache from 2 to 3
Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-22 18:33:08 +00:00
e29671add6 Merge pull request #69 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.9.1
Bump go.unistack.org/micro/v3 from 3.9.0 to 3.9.1
2022-03-22 21:32:31 +03:00
dependabot[bot]
dede9e6488 Bump go.unistack.org/micro/v3 from 3.9.0 to 3.9.1
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.9.0 to 3.9.1.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.9.0...v3.9.1)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-22 18:26:02 +00:00
2f658787d4 Merge pull request #67 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.9.0
Bump go.unistack.org/micro/v3 from 3.8.21 to 3.9.0
2022-03-21 21:43:35 +03:00
dependabot[bot]
b8ad04a42a Bump go.unistack.org/micro/v3 from 3.8.21 to 3.9.0
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.21 to 3.9.0.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.21...v3.9.0)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-21 18:41:56 +00:00
766cbd8ae0 Merge pull request #66 from unistack-org/wrappers
add additional wrappers support
2022-03-10 12:30:10 +03:00
847352a8d3 Merge branch 'master' into wrappers 2022-03-10 12:28:56 +03:00
147c2d756e add additional wrappers support
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-10 12:27:37 +03:00
f42994e5c0 update go version
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-07 13:45:05 +03:00
08b3e7e23e Merge pull request #64 from unistack-org/message
add message metadata support
2022-03-05 19:04:45 +03:00
2f70890e6c Merge branch 'master' into message 2022-03-05 19:02:56 +03:00
e9be505c59 fix workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 19:02:47 +03:00
13749e7a4d Merge branch 'master' into message 2022-03-05 19:01:26 +03:00
91cf06abbd fix workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 19:01:13 +03:00
a1f29249d3 Merge branch 'master' into message 2022-03-05 18:58:02 +03:00
db9c686306 fix workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 18:57:54 +03:00
b74add18c1 Merge branch 'master' into message 2022-03-05 18:54:53 +03:00
5d3cbfc7d3 fix workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 18:54:28 +03:00
f42e8883ae Merge branch 'master' into message 2022-03-05 18:49:30 +03:00
93344a3f7b fix workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 18:49:01 +03:00
2e218748e8 fix workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 18:47:38 +03:00
178d68aae9 add message metadata support
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 18:46:00 +03:00
2061b2dbb6 update worklow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 18:45:15 +03:00
fa9e204032 update
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 18:38:35 +03:00
bce6f8c3e7 fix workflows
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 18:32:28 +03:00
1ee46a0aac update workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 18:25:01 +03:00
4f749c5bb1 change workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 18:20:30 +03:00
8231f4f7f6 Merge pull request #57 from unistack-org/master
merge master
2022-03-03 17:47:50 +03:00
241114aeb1 Merge pull request #56 from unistack-org/fix_exported
fixup for older go
2022-03-03 17:47:30 +03:00
e4541d8b44 fixup for older go
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-03 17:47:12 +03:00
b658f5091a Merge pull request #55 from unistack-org/master
merge master
2022-03-02 17:52:34 +03:00
c21d30e4a0 Merge pull request #52 from unistack-org/dependabot/github_actions/dependabot/fetch-metadata-1.3.0
Bump dependabot/fetch-metadata from 1.2.1 to 1.3.0
2022-03-02 17:52:10 +03:00
1349df4d97 Merge pull request #53 from unistack-org/dependabot/github_actions/actions/checkout-3
Bump actions/checkout from 2 to 3
2022-03-02 17:52:01 +03:00
6dfc8b8a01 Merge pull request #54 from unistack-org/isExported
fixup for unexported fields
2022-03-02 17:51:49 +03:00
d280ea260e fixup for unexported fields
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-02 17:51:29 +03:00
dependabot[bot]
e9295b1d12 Bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-01 18:19:22 +00:00
dependabot[bot]
75b102666f Bump dependabot/fetch-metadata from 1.2.1 to 1.3.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.2.1 to 1.3.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.2.1...v1.3.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-01 18:19:19 +00:00
18b95d7dde Merge pull request #51 from unistack-org/dependabot/github_actions/golangci/golangci-lint-action-3.1.0
Bump golangci/golangci-lint-action from 2 to 3.1.0
2022-03-01 00:38:46 +03:00
dependabot[bot]
6d3d8ccd14 Bump golangci/golangci-lint-action from 2 to 3.1.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 2 to 3.1.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v2...v3.1.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-28 18:24:50 +00:00
f37c8fd5d4 Merge pull request #49 from unistack-org/dependabot/github_actions/dependabot/fetch-metadata-1.2.1
Bump dependabot/fetch-metadata from 1.1.1 to 1.2.1
2022-02-25 09:41:34 +03:00
dependabot[bot]
c0bfe416b1 Bump dependabot/fetch-metadata from 1.1.1 to 1.2.1
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.1.1 to 1.2.1.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.1.1...v1.2.1)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-23 18:16:56 +00:00
ce520397c4 Merge pull request #47 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.8.21
Bump go.unistack.org/micro/v3 from 3.8.20 to 3.8.21
2022-01-31 21:22:52 +03:00
dependabot[bot]
3cd4e16c36 Bump go.unistack.org/micro/v3 from 3.8.20 to 3.8.21
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.20 to 3.8.21.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.20...v3.8.21)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-31 18:21:09 +00:00
3dccbcba98 Merge pull request #46 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.8.20
Bump go.unistack.org/micro/v3 from 3.8.19 to 3.8.20
2022-01-26 21:17:26 +03:00
dependabot[bot]
7572ef631d Bump go.unistack.org/micro/v3 from 3.8.19 to 3.8.20
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.19 to 3.8.20.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.19...v3.8.20)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-26 18:15:32 +00:00
030d906f87 Merge pull request #45 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.8.19
Bump go.unistack.org/micro/v3 from 3.8.18 to 3.8.19
2022-01-25 21:17:30 +03:00
dependabot[bot]
bfefab250f Bump go.unistack.org/micro/v3 from 3.8.18 to 3.8.19
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.18 to 3.8.19.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.18...v3.8.19)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-25 18:15:35 +00:00
82b7b5e4fb Merge pull request #44 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.8.18
Bump go.unistack.org/micro/v3 from 3.8.15 to 3.8.18
2022-01-21 21:21:18 +03:00
dependabot[bot]
980bea6e4f Bump go.unistack.org/micro/v3 from 3.8.15 to 3.8.18
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.15 to 3.8.18.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.15...v3.8.18)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-21 18:19:33 +00:00
a7ce52d564 Merge pull request #43 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.8.15
Bump go.unistack.org/micro/v3 from 3.8.14 to 3.8.15
2022-01-19 21:19:09 +03:00
dependabot[bot]
4771d5e12b Bump go.unistack.org/micro/v3 from 3.8.14 to 3.8.15
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.14 to 3.8.15.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.14...v3.8.15)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-19 18:17:23 +00:00
6be01f2cb2 Merge branch 'master' into v3 2022-01-12 17:48:26 +03:00
3829766afd update for latest micro
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-01-12 17:47:58 +03:00
a2939066a5 Merge pull request #41 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.8.13
Bump go.unistack.org/micro/v3 from 3.8.12 to 3.8.13
2021-12-28 21:16:44 +03:00
dependabot[bot]
e6718f368a Bump go.unistack.org/micro/v3 from 3.8.12 to 3.8.13
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.12 to 3.8.13.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.12...v3.8.13)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-28 18:15:08 +00:00
381f3e051e Merge pull request #40 from unistack-org/master
merge master
2021-12-16 15:38:33 +03:00
757c458f60 fix for latest micro
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-12-16 15:37:49 +03:00
a2f089f890 Merge pull request #39 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.8.11
Bump go.unistack.org/micro/v3 from 3.8.10 to 3.8.11
2021-11-24 21:19:29 +03:00
dependabot[bot]
d85e3fdce2 Bump go.unistack.org/micro/v3 from 3.8.10 to 3.8.11
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.10 to 3.8.11.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.10...v3.8.11)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-24 18:17:26 +00:00
5f370d1a31 Merge pull request #38 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.8.10
Bump go.unistack.org/micro/v3 from 3.8.9 to 3.8.10
2021-11-19 21:17:58 +03:00
dependabot[bot]
2b47343e07 Bump go.unistack.org/micro/v3 from 3.8.9 to 3.8.10
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.9 to 3.8.10.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.9...v3.8.10)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-19 18:16:11 +00:00
00c1d3618f Merge pull request #37 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.8.9
Bump go.unistack.org/micro/v3 from 3.8.7 to 3.8.9
2021-11-18 21:23:11 +03:00
dependabot[bot]
0f80c9f7ba Bump go.unistack.org/micro/v3 from 3.8.7 to 3.8.9
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.7 to 3.8.9.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.7...v3.8.9)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-18 18:21:19 +00:00
a275ffa001 Merge pull request #36 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.8.7
Bump go.unistack.org/micro/v3 from 3.8.6 to 3.8.7
2021-10-28 21:22:57 +03:00
dependabot[bot]
2800d4c78b Bump go.unistack.org/micro/v3 from 3.8.6 to 3.8.7
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.6 to 3.8.7.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.6...v3.8.7)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-28 18:21:01 +00:00
a609f15425 Merge pull request #35 from unistack-org/dependabot/go_modules/go.unistack.org/micro/v3-3.8.6
Bump go.unistack.org/micro/v3 from 3.8.5 to 3.8.6
2021-10-27 16:34:53 +03:00
dependabot[bot]
83cc922771 Bump go.unistack.org/micro/v3 from 3.8.5 to 3.8.6
Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.5 to 3.8.6.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.5...v3.8.6)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-27 13:29:38 +00:00
2bba395ba3 fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 16:28:34 +03:00
a360ef0e8f fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 16:25:28 +03:00
5f0e4915f6 fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 16:18:11 +03:00
f02efaf18c fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 16:08:18 +03:00
279d111d39 fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 16:07:12 +03:00
928455a651 fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 15:56:36 +03:00
6df77d317d update automerge 2021-10-27 15:56:06 +03:00
e8f7fab079 Merge branch 'v3' 2021-10-27 15:49:00 +03:00
ae6f29e540 update
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 15:44:35 +03:00
de6cdb4e4d update
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 15:37:42 +03:00
0398d969d5 github automerge
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 15:37:42 +03:00
10419bc74a github automerge
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 15:37:42 +03:00
93ce60f59c fix Header and Cookie fill
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 15:37:42 +03:00
61c082cb2f github automerge
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 15:24:55 +03:00
22f1b6551a github automerge
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 15:23:26 +03:00
73b7d998c0 fix Header and Cookie fill
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 13:13:33 +03:00
4aca8e1ce0 merge v3 branch (#34)
* guard import

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

* Bump go.unistack.org/micro/v3 from 3.8.4 to 3.8.5 (#32)

Bumps [go.unistack.org/micro/v3](https://github.com/unistack-org/micro) from 3.8.4 to 3.8.5.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.8.4...v3.8.5)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-26 19:01:46 +00:00
3592052ac9 guard import
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-25 20:35:02 +03:00
fd5479f6e7 add ability to send headers and cookies
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-25 19:59:37 +03:00
4fe11b430e fix path query requests with templates
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-23 08:58:55 +03:00
5dc57579c3 update workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-21 21:46:43 +03:00
dependabot[bot]
f974460ff3 Bump github.com/unistack-org/micro/v3 from 3.7.0 to 3.7.1 (#28)
Bumps [github.com/unistack-org/micro/v3](https://github.com/unistack-org/micro) from 3.7.0 to 3.7.1.
- [Release notes](https://github.com/unistack-org/micro/releases)
- [Commits](https://github.com/unistack-org/micro/compare/v3.7.0...v3.7.1)

---
updated-dependencies:
- dependency-name: github.com/unistack-org/micro/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-17 08:27:55 +03:00
5c54b82aaf fixup workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-17 08:22:44 +03:00
a5bf3e2edc update workflows
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-17 07:47:48 +03:00
3a2bac8723 enable automerge
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-16 11:03:10 +03:00
dependabot[bot]
9a72dba9df Bump github.com/unistack-org/micro/v3 from 3.6.2 to 3.7.0 (#27) 2021-09-06 15:03:55 +00:00
00e853e3ee add github stuff
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-06 15:23:36 +03:00
5aff0014bb add github stuff
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-06 10:32:59 +03:00
807b4dc41a fix for url query params in url
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-01 02:25:29 +03:00
a1f432fac9 fix parsing of url query with all methods
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-01 02:06:01 +03:00
dd7e872858 update micro
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-17 01:23:04 +03:00
57c2a5b95f nolint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-31 15:01:44 +03:00
73fc3d34f0 minor tweaks
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-31 14:59:09 +03:00
09e7282b2b fix path template parsing
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-30 23:02:50 +03:00
5f029fd432 use metadata header names
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-23 12:22:27 +03:00
8555ebdd5c remove debug
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-14 22:16:01 +03:00
273da35b92 on error try to return original message to client
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-14 17:05:33 +03:00
556e8dd568 fix lint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-09 23:10:48 +03:00
f3573e651b fix field setting
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-09 18:22:40 +03:00
8074f9f617 fix field setting
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-09 18:19:30 +03:00
e497b5fa89 rework newRequest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-09 16:27:49 +03:00
520dc29f89 fixup header filling after making new request
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-09 12:25:26 +03:00
59d6c26003 support metadata option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-09 11:00:19 +03:00
fade40754a update deps
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-05 16:23:00 +03:00
f39d449ca2 lint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-05 16:10:38 +03:00
7cab3c18a7 add ability to wrap any struct to error interface
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-05 16:02:35 +03:00
7098c252dc allow to publish only body
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-06-11 15:21:36 +03:00
3cbc879769 fix stream timeout
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-29 22:38:38 +03:00
05add422d1 lint fixes (#25)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-25 16:26:36 +03:00
Renovate Bot
b4970ee807 Update module github.com/unistack-org/micro/v3 to v3.3.16 2021-04-20 13:42:24 +00:00
Renovate Bot
f4c91686f4 Update module github.com/unistack-org/micro/v3 to v3.3.15 2021-04-19 14:41:02 +00:00
ee9b7493e3 fix repeated detection
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-19 10:08:56 +03:00
Renovate Bot
0182d6ab56 Update module github.com/unistack-org/micro/v3 to v3.3.14 2021-04-19 04:33:26 +00:00
d99e97090c support repeated url values
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-19 02:10:24 +03:00
8de7912a91 detect response content-type
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-18 15:45:42 +03:00
6ccb40bab0 minor content-type fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-16 17:09:29 +03:00
2b16a8a7a6 allow to set content type with charset
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-10 12:46:34 +03:00
Renovate Bot
d051256839 Update module github.com/unistack-org/micro/v3 to v3.3.13 2021-04-10 00:54:11 +00:00
68b32989fc allow to override content-type
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-09 23:12:25 +03:00
21a41a8e03 fix message body parsing
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-09 22:55:01 +03:00
9150958044 fix x-www-form-urlencoded requests
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-03 11:55:22 +03:00
Renovate Bot
e358db44ca Update module github.com/unistack-org/micro/v3 to v3.3.10 2021-04-01 00:36:29 +00:00
5cdd48329e metadata.FromContext => metadata.FromOutgoingContext
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-31 10:43:38 +03:00
Renovate Bot
01b5e1db54 Update module github.com/unistack-org/micro/v3 to v3.3.9 2021-03-30 01:17:00 +00:00
Renovate Bot
900e08458b Update module github.com/unistack-org/micro/v3 to v3.3.8 2021-03-29 01:30:57 +00:00
Renovate Bot
f4fff1c77a Update module github.com/unistack-org/micro/v3 to v3.3.4 2021-03-26 16:34:13 +00:00
4bbf97a309 add minor dev comment
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-26 15:52:52 +03:00
02f29b0ef3 fix tls issues
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-26 15:48:57 +03:00
Renovate Bot
ba69a7dfcd Update module github.com/unistack-org/micro/v3 to v3.3.2 2021-03-25 14:38:19 +00:00
Renovate Bot
45aee3c441 Update module github.com/unistack-org/micro/v3 to v3.3.1 2021-03-24 23:34:28 +00:00
1787f44c54 cleanup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-24 15:49:20 +03:00
1bbdf03f60 add aditional check for nil message passed
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-24 10:44:53 +03:00
Renovate Bot
2bb1299e56 Update module github.com/unistack-org/micro/v3 to v3.2.26 2021-03-23 17:35:57 +00:00
fd1acd56cb add tag support
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-23 00:29:27 +03:00
573c1f50e2 rewrite query param and not marshal empty struct
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-21 17:44:48 +03:00
602aed03bd support path params not only for get requests
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-19 14:17:53 +03:00
1919ef3830 update micro
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-16 19:11:29 +03:00
Renovate Bot
b878d401c3 Update module github.com/unistack-org/micro/v3 to v3.2.22 2021-03-15 00:33:56 +00:00
Renovate Bot
eb08ecc606 Update module github.com/unistack-org/micro/v3 to v3.2.20 2021-03-06 22:27:09 +00:00
Renovate Bot
f12ae0cea7 Update module github.com/unistack-org/micro/v3 to v3.2.17 2021-03-05 16:54:39 +00:00
Renovate Bot
b28c2dd79e Update module github.com/unistack-org/micro/v3 to v3.2.16 2021-03-04 12:32:12 +00:00
Renovate Bot
f61cef37d5 Update module github.com/unistack-org/micro/v3 to v3.2.15 2021-03-02 19:59:50 +00:00
1da1d3ec0f Merge pull request #7 from unistack-org/renovate/all
Update module unistack-org/micro/v3 to v3.2.14
2021-02-18 19:09:12 +03:00
Renovate Bot
a4bf3bc345 Update module unistack-org/micro/v3 to v3.2.14 2021-02-18 15:48:32 +00:00
826fab0493 update workflows
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-18 18:45:59 +03:00
Renovate Bot
48fb59468e Update module unistack-org/micro/v3 to v3.2.11 2021-02-12 15:41:37 +00:00
c22bb154d5 update renovate.json
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-12 12:40:30 +03:00
690686794d move renovate.json
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-10 13:18:51 +03:00
07bab6c2c2 Merge pull request #5 from unistack-org/renovate/golangci-golangci-lint-action-2.x
Update golangci/golangci-lint-action action to v2
2021-02-10 00:09:25 +03:00
2f55b38984 Merge pull request #3 from unistack-org/renovate/unistack-org-micro-v3-3.x
Update module unistack-org/micro/v3 to v3.2.8
2021-02-10 00:06:06 +03:00
c554ecfa43 Merge pull request #4 from unistack-org/renovate/actions-setup-go-2.x
Update actions/setup-go action to v2
2021-02-10 00:05:29 +03:00
Renovate Bot
fde1dee7d1 Update golangci/golangci-lint-action action to v2 2021-02-09 20:10:29 +00:00
Renovate Bot
359b97b177 Update actions/setup-go action to v2 2021-02-09 20:10:24 +00:00
Renovate Bot
258785f49b Update module unistack-org/micro/v3 to v3.2.8 2021-02-09 20:10:17 +00:00
8086a54aa4 Merge pull request #2 from unistack-org/renovate/configure
Configure Renovate
2021-02-09 23:09:27 +03:00
Renovate Bot
59fa196f1f Add renovate.json 2021-02-08 23:22:18 +00:00
ce49e7d867 extend test case
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-06 19:05:55 +03:00
4237630f98 export DefaultContentType
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-06 18:54:18 +03:00
b216aa3509 update for latest micro
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 15:11:04 +03:00
a1a3daa799 handle timeout errors
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-19 22:55:24 +03:00
d611a6aed5 initial support for path param
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-19 04:12:31 +03:00
7a29127d17 update micro to v3.1.1
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-16 01:58:43 +03:00
59d77f7b8c use own fork
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-10 14:48:10 +03:00
49 changed files with 10152 additions and 1827 deletions

18
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,18 @@
---
name: Bug report
about: For reporting bugs in micro
title: "[BUG]"
labels: ''
assignees: ''
---
**Describe the bug**
1. What are you trying to do?
2. What did you expect to happen?
3. What happens instead?
**How to reproduce the bug:**
If possible, please include a minimal code snippet here.

View File

@@ -0,0 +1,17 @@
---
name: Feature request / Enhancement
about: If you have a need not served by micro
title: "[FEATURE]"
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the feature request here.

8
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,8 @@
---
name: Question
about: Ask a question about micro
title: ''
labels: ''
assignees: ''
---

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

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

28
.github/autoapprove.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: "autoapprove"
on:
pull_request_target:
types: [assigned, opened, synchronize, reopened]
workflow_run:
workflows: ["prbuild"]
types:
- completed
permissions:
pull-requests: write
contents: write
jobs:
autoapprove:
runs-on: ubuntu-latest
steps:
- name: approve
run: [ "curl -o tea https://dl.gitea.com/tea/main/tea-main-linux-amd64",
"chmod +x ./tea",
"./tea login add --name unistack --token ${{ secrets.GITHUB_TOKEN }} --url https://git.unistack.org",
"./tea pr --repo ${{ github.event.repository.name }}"
]
if: github.actor == 'vtolstov'
id: approve
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

53
.github/workflows/job_coverage.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: coverage
on:
push:
branches: [ main, v3, v4 ]
paths-ignore:
- '.github/**'
- '.gitea/**'
pull_request:
branches: [ main, v3, v4 ]
jobs:
build:
if: github.server_url != 'https://github.com'
runs-on: ubuntu-latest
steps:
- name: checkout code
uses: actions/checkout@v4
with:
filter: 'blob:none'
- name: setup go
uses: actions/setup-go@v5
with:
cache-dependency-path: "**/*.sum"
go-version: 'stable'
- name: test coverage
run: |
go test -v -cover ./... -covermode=count -coverprofile coverage.out -coverpkg ./...
go tool cover -func coverage.out -o coverage.out
- name: coverage badge
uses: tj-actions/coverage-badge-go@v2
with:
green: 80
filename: coverage.out
- uses: stefanzweifel/git-auto-commit-action@v4
name: autocommit
with:
commit_message: Apply Code Coverage Badge
skip_fetch: false
skip_checkout: false
file_pattern: ./README.md
- name: push
if: steps.auto-commit-action.outputs.changes_detected == 'true'
uses: ad-m/github-push-action@master
with:
github_token: ${{ github.token }}
branch: ${{ github.ref }}

29
.github/workflows/job_lint.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: lint
on:
pull_request:
types: [opened, reopened, synchronize]
branches: [ master, v3, v4 ]
paths-ignore:
- '.github/**'
- '.gitea/**'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: checkout code
uses: actions/checkout@v4
with:
filter: 'blob:none'
- name: setup go
uses: actions/setup-go@v5
with:
cache-dependency-path: "**/*.sum"
go-version: 'stable'
- name: setup deps
run: go get -v ./...
- name: run lint
uses: golangci/golangci-lint-action@v6
with:
version: 'latest'

94
.github/workflows/job_sync.yml vendored Normal file
View File

@@ -0,0 +1,94 @@
name: sync
on:
schedule:
- cron: '*/5 * * * *'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
sync:
if: github.server_url != 'https://github.com'
runs-on: ubuntu-latest
steps:
- name: init
run: |
git config --global user.email "vtolstov <vtolstov@users.noreply.github.com>"
git config --global user.name "github-actions[bot]"
echo "machine git.unistack.org login vtolstov password ${{ secrets.TOKEN_GITEA }}" >> /root/.netrc
echo "machine github.com login vtolstov password ${{ secrets.TOKEN_GITHUB }}" >> /root/.netrc
- name: check master
id: check_master
run: |
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/master | cut -f1)
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/master | cut -f1)
echo "src_hash=$src_hash"
echo "dst_hash=$dst_hash"
if [ "$src_hash" != "$dst_hash" ]; then
echo "sync_needed=true" >> $GITHUB_OUTPUT
else
echo "sync_needed=false" >> $GITHUB_OUTPUT
fi
- name: sync master
if: steps.check_master.outputs.sync_needed == 'true'
run: |
git clone --filter=blob:none --filter=tree:0 --branch master --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
cd repo
git remote add --no-tags --fetch --track master upstream https://github.com/${GITHUB_REPOSITORY}
git pull --rebase upstream master
git push upstream master --progress
git push origin master --progress
cd ../
rm -rf repo
- name: check v3
id: check_v3
run: |
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/v3 | cut -f1)
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/v3 | cut -f1)
echo "src_hash=$src_hash"
echo "dst_hash=$dst_hash"
if [ "$src_hash" != "$dst_hash" ]; then
echo "sync_needed=true" >> $GITHUB_OUTPUT
else
echo "sync_needed=false" >> $GITHUB_OUTPUT
fi
- name: sync v3
if: steps.check_v3.outputs.sync_needed == 'true'
run: |
git clone --filter=blob:none --filter=tree:0 --branch v3 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
cd repo
git remote add --no-tags --fetch --track v3 upstream https://github.com/${GITHUB_REPOSITORY}
git pull --rebase upstream v3
git push upstream v3 --progress
git push origin v3 --progress
cd ../
rm -rf repo
- name: check v4
id: check_v4
run: |
src_hash=$(git ls-remote https://github.com/${GITHUB_REPOSITORY} refs/heads/v4 | cut -f1)
dst_hash=$(git ls-remote ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} refs/heads/v4 | cut -f1)
echo "src_hash=$src_hash"
echo "dst_hash=$dst_hash"
if [ "$src_hash" != "$dst_hash" ]; then
echo "sync_needed=true" >> $GITHUB_OUTPUT
else
echo "sync_needed=false" >> $GITHUB_OUTPUT
fi
- name: sync v4
if: steps.check_v4.outputs.sync_needed == 'true'
run: |
git clone --filter=blob:none --filter=tree:0 --branch v4 --single-branch ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} repo
cd repo
git remote add --no-tags --fetch --track v4 upstream https://github.com/${GITHUB_REPOSITORY}
git pull --rebase upstream v4
git push upstream v4 --progress
git push origin v4 --progress
cd ../
rm -rf repo

31
.github/workflows/job_test.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: test
on:
pull_request:
types: [opened, reopened, synchronize]
branches: [ master, v3, v4 ]
push:
branches: [ master, v3, v4 ]
paths-ignore:
- '.github/**'
- '.gitea/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: checkout code
uses: actions/checkout@v4
with:
filter: 'blob:none'
- name: setup go
uses: actions/setup-go@v5
with:
cache-dependency-path: "**/*.sum"
go-version: 'stable'
- name: setup deps
run: go get -v ./...
- name: run test
env:
INTEGRATION_TESTS: yes
run: go test -mod readonly -v ./...

50
.github/workflows/job_tests.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: test
on:
pull_request:
types: [opened, reopened, synchronize]
branches: [ master, v3, v4 ]
push:
branches: [ master, v3, v4 ]
paths-ignore:
- '.github/**'
- '.gitea/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: checkout code
uses: actions/checkout@v4
with:
filter: 'blob:none'
- name: checkout tests
uses: actions/checkout@v4
with:
ref: master
filter: 'blob:none'
repository: unistack-org/micro-tests
path: micro-tests
- name: setup go
uses: actions/setup-go@v5
with:
cache-dependency-path: "**/*.sum"
go-version: 'stable'
- name: setup go work
env:
GOWORK: ${{ github.workspace }}/go.work
run: |
go work init
go work use .
go work use micro-tests
- name: setup deps
env:
GOWORK: ${{ github.workspace }}/go.work
run: go get -v ./...
- name: run tests
env:
INTEGRATION_TESTS: yes
GOWORK: ${{ github.workspace }}/go.work
run: |
cd micro-tests
go test -mod readonly -v ./... || true

39
.gitignore vendored Normal file
View File

@@ -0,0 +1,39 @@
# Develop tools
/.vscode/
/.idea/
.idea
.vscode
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Folders
_obj
_test
_build
.DS_Store
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# vim temp files
*~
*.swp
*.swo

5
.golangci.yml Normal file
View File

@@ -0,0 +1,5 @@
run:
concurrency: 8
timeout: 5m
issues-exit-code: 1
tests: true

1
.synced Normal file
View File

@@ -0,0 +1 @@
7e753b1f84b06797ce8c4be8e4b1941b45f8bbb7

192
LICENSE Normal file
View File

@@ -0,0 +1,192 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2015-2020 Asim Aslam.
Copyright 2019-2020 Unistack LLC.
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.

151
README.md
View File

@@ -1,18 +1,26 @@
# HTTP Client # HTTP Client
![Coverage](https://img.shields.io/badge/Coverage-42.7%25-yellow)
This plugin is a http client for go-micro. This plugin is an HTTP client for [Micro](https://pkg.go.dev/go.unistack.org/micro/v4).
It implements the [micro.Client](https://pkg.go.dev/go.unistack.org/micro/v4/client#Client) interface.
## Overview ## Overview
The http client wraps `net/http` to provide a robust go-micro client with service discovery, load balancing and streaming. The HTTP client wraps `net/http` to provide a robust client with service discovery, load balancing and
It complies with the [go-micro.Client](https://godoc.org/github.com/micro/go-micro/client#Client) interface. implements HTTP rules defined in the [google/api/http.proto](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto) specification.
## Limitations
* Streaming is not yet implemented.
* Only protobuf-generated messages are supported.
## Usage ## Usage
### Use directly
```go ```go
import "github.com/micro/go-plugins/client/http" import (
"go.unistack.org/micro/v4"
http "go.unistack.org/micro-client-http/v4"
)
service := micro.NewService( service := micro.NewService(
micro.Name("my.service"), micro.Name("my.service"),
@@ -20,42 +28,127 @@ service := micro.NewService(
) )
``` ```
### Use with flags ### Simple call
```go ```go
import _ "github.com/micro/go-plugins/client/http" import (
"go.unistack.org/micro/v4/client"
http "go.unistack.org/micro-client-http/v4"
jsoncodec "go.unistack.org/micro-codec-json/v4"
)
c := http.NewClient(
client.Codec("application/json", jsoncodec.NewCodec()),
)
req := c.NewRequest(
"user-service",
"/user/{user_id}/order/{order_id}",
&protoReq{UserId: "123", OrderId: 456},
)
rsp := new(protoRsp)
err := c.Call(
ctx,
req,
rsp,
client.WithAddress("example.com"),
)
``` ```
```shell ### Call with specific options
go run main.go --client=http
```
### Call Service
Assuming you have a http service "my.service" with path "/foo/bar"
```go ```go
// new client import (
client := http.NewClient() "go.unistack.org/micro/v4/client"
http "go.unistack.org/micro-client-http/v4"
)
// create request/response err := c.Call(
request := client.NewRequest("my.service", "/foo/bar", protoRequest{}) ctx,
response := new(protoResponse) req,
rsp,
// call service client.WithAddress("example.com"),
err := client.Call(context.TODO(), request, response) http.Method("POST"),
http.Path("/user/{user_id}/order/{order_id}"),
http.Body("*"), // <- use all fields from the proto request as HTTP request body or specify a single field name to use only that field (see Google API HTTP spec: google/api/http.proto)
)
``` ```
Look at http_test.go for detailed use. ### Call with request headers
### Encoding
Default protobuf with content-type application/proto
```go ```go
client.NewRequest("service", "/path", protoRequest{}) import (
"go.unistack.org/micro/v4/metadata"
http "go.unistack.org/micro-client-http/v4"
)
ctx := metadata.NewOutgoingContext(ctx, metadata.Pairs(
"Authorization", "Bearer token",
"My-Header", "My-Header-Value",
))
err := c.Call(
ctx,
req,
rsp,
http.Header("Authorization", "true", "My-Header", "false"), // <- call option that declares required/optional headers
)
``` ```
Json with content-type application/json ### Call with response headers
```go ```go
client.NewJsonRequest("service", "/path", jsonRequest{}) import (
"go.unistack.org/micro/v4/metadata"
http "go.unistack.org/micro-client-http/v4"
)
respMetadata := metadata.Metadata{}
err := c.Call(
ctx,
req,
rsp,
client.WithResponseMetadata(&respMetadata), // <- metadata with response headers
)
``` ```
### Call with cookies
```go
import (
"go.unistack.org/micro/v4/metadata"
http "go.unistack.org/micro-client-http/v4"
)
ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(
"Cookie", "session_id=abc123; theme=dark",
))
err := c.Call(
ctx,
req,
rsp,
http.Cookie("session_id", "true", "theme", "false"), // <- call option that declares required/optional cookies
)
```
### Call with error mapping
```go
import (
http "go.unistack.org/micro-client-http/v4"
jsoncodec "go.unistack.org/micro-codec-json/v4"
)
err := c.Call(
ctx,
req,
rsp,
http.ErrorMap(map[string]any{
"default": &protoDefaultError{}, // <- default case
"403": &protoSpecialError{}, // <- key is the HTTP status code that is mapped to this error
}),
)
```

View File

@@ -1,14 +0,0 @@
package http
import (
"bytes"
)
type buffer struct {
*bytes.Buffer
}
func (b *buffer) Close() error {
b.Buffer.Reset()
return nil
}

116
builder/body.go Normal file
View File

@@ -0,0 +1,116 @@
package builder
import (
"fmt"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
)
func buildSingleFieldBody(msg proto.Message, fieldName string) (proto.Message, error) {
msgReflect := msg.ProtoReflect()
fd, found := findFieldByName(msgReflect, fieldName)
if !found || fd == nil {
return nil, fmt.Errorf("field %s not found", fieldName)
}
if !msgReflect.Has(fd) {
return nil, fmt.Errorf("field %s is not set", fieldName)
}
val := msgReflect.Get(fd)
if fd.Kind() == protoreflect.MessageKind {
return val.Message().Interface(), nil
}
newMsg := proto.Clone(msg)
newMsgReflect := newMsg.ProtoReflect()
newMsgReflect.Range(func(f protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
if f != fd {
newMsgReflect.Clear(f)
}
return true
})
return newMsg, nil
}
func buildFullBody(msg proto.Message, usedFieldsPath *usedFields) (proto.Message, error) {
var (
msgReflect = msg.ProtoReflect()
newMsg = msgReflect.New().Interface()
newMsgReflect = newMsg.ProtoReflect()
)
fields := msgReflect.Descriptor().Fields()
for i := 0; i < fields.Len(); i++ {
fd := fields.Get(i)
fieldName := fd.JSONName()
if usedFieldsPath.hasTopLevelKey(fieldName) {
continue
}
val := msgReflect.Get(fd)
if !val.IsValid() {
continue
}
// Note: order of the cases is important!
switch {
case fd.IsList():
list := val.List()
newList := newMsgReflect.Mutable(fd).List()
if fd.Kind() == protoreflect.MessageKind {
for j := 0; j < list.Len(); j++ {
elem, err := buildFullBody(list.Get(j).Message().Interface(), usedFieldsPath)
if err != nil {
return nil, fmt.Errorf("recursive build full body: %w", err)
}
newList.Append(protoreflect.ValueOfMessage(elem.ProtoReflect()))
}
} else {
for j := 0; j < list.Len(); j++ {
newList.Append(list.Get(j))
}
}
case fd.IsMap():
var (
m = val.Map()
newMap = newMsgReflect.Mutable(fd).Map()
rangeErr error
)
m.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
if fd.MapValue().Kind() == protoreflect.MessageKind {
elem, err := buildFullBody(v.Message().Interface(), usedFieldsPath)
if err != nil {
rangeErr = fmt.Errorf("recursive build full body: %w", err)
return false
}
newMap.Set(k, protoreflect.ValueOfMessage(elem.ProtoReflect()))
} else {
newMap.Set(k, v)
}
return true
})
if rangeErr != nil {
return nil, fmt.Errorf("map range error: %w", rangeErr)
}
case fd.Kind() == protoreflect.MessageKind:
elem, err := buildFullBody(val.Message().Interface(), usedFieldsPath)
if err != nil {
return nil, fmt.Errorf("recursive build full body: %w", err)
}
newMsgReflect.Set(fd, protoreflect.ValueOfMessage(elem.ProtoReflect()))
default:
newMsgReflect.Set(fd, val)
}
}
return newMsg, nil
}

22
builder/body_option.go Normal file
View File

@@ -0,0 +1,22 @@
package builder
const (
singleWildcard string = "*"
doubleWildcard string = "**"
)
type bodyOption string
func (o bodyOption) String() string { return string(o) }
func (o bodyOption) isFullBody() bool {
return o.String() == singleWildcard
}
func (o bodyOption) isWithoutBody() bool {
return o == ""
}
func (o bodyOption) isSingleField() bool {
return o != "" && o.String() != singleWildcard
}

View File

@@ -0,0 +1,79 @@
package builder
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestBodyOption_String(t *testing.T) {
tests := []struct {
name string
opt bodyOption
want string
}{
{"empty", bodyOption(""), ""},
{"star", bodyOption("*"), "*"},
{"field", bodyOption("field"), "field"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, tt.opt.String())
})
}
}
func TestBodyOption_isFullBody(t *testing.T) {
tests := []struct {
name string
opt bodyOption
want bool
}{
{"empty", bodyOption(""), false},
{"star", bodyOption("*"), true},
{"field", bodyOption("field"), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, tt.opt.isFullBody())
})
}
}
func TestBodyOption_isWithoutBody(t *testing.T) {
tests := []struct {
name string
opt bodyOption
want bool
}{
{"empty", bodyOption(""), true},
{"star", bodyOption("*"), false},
{"field", bodyOption("field"), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, tt.opt.isWithoutBody())
})
}
}
func TestBodyOption_isSingleField(t *testing.T) {
tests := []struct {
name string
opt bodyOption
want bool
}{
{"empty", bodyOption(""), false},
{"star", bodyOption("*"), false},
{"field", bodyOption("field"), true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, tt.opt.isSingleField())
})
}
}

114
builder/helpers.go Normal file
View File

@@ -0,0 +1,114 @@
package builder
import (
"encoding/base64"
"fmt"
"strconv"
"strings"
"google.golang.org/protobuf/reflect/protoreflect"
)
// findFieldByPath resolves a dot-separated field path in a protobuf message and returns the protoreflect value and its descriptor.
func findFieldByPath(msg protoreflect.Message, fieldPath string) (protoreflect.Value, protoreflect.FieldDescriptor, bool) {
var (
current = msg
parts = strings.Split(fieldPath, ".")
partsCount = len(parts) - 1
)
for i, part := range parts {
fd, ok := findFieldByName(current, part)
if !ok {
return protoreflect.Value{}, nil, false
}
val := current.Get(fd)
if i == partsCount { // it's last part
return val, fd, true
}
if fd.Kind() != protoreflect.MessageKind {
return protoreflect.Value{}, nil, false
}
current = val.Message()
}
return protoreflect.Value{}, nil, false
}
// findFieldByName find a field name in a protobuf message and returns the protoreflect field descriptor.
func findFieldByName(msg protoreflect.Message, fieldName string) (protoreflect.FieldDescriptor, bool) {
fields := msg.Descriptor().Fields()
for i := 0; i < fields.Len(); i++ {
fd := fields.Get(i)
if fd.JSONName() == fieldName {
return fd, true
}
}
return nil, false
}
// isZeroValue checks if protoreflect.Value is zero for the field.
func isZeroValue(val protoreflect.Value, fd protoreflect.FieldDescriptor) bool {
if fd.IsList() {
return val.List().Len() == 0
}
if fd.IsMap() {
return val.Map().Len() == 0
}
switch fd.Kind() {
case protoreflect.StringKind:
return val.String() == ""
case protoreflect.BytesKind:
return len(val.Bytes()) == 0
case protoreflect.BoolKind:
return !val.Bool()
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind,
protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
return val.Int() == 0
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind,
protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
return val.Uint() == 0
case protoreflect.FloatKind, protoreflect.DoubleKind:
return val.Float() == 0
case protoreflect.EnumKind:
return val.Enum() == 0
case protoreflect.MessageKind:
return !val.Message().IsValid()
default:
return !val.IsValid()
}
}
// stringifyValue converts protoreflect.Value to string for path/query substitution.
func stringifyValue(val protoreflect.Value, fd protoreflect.FieldDescriptor) (string, error) {
switch fd.Kind() {
case protoreflect.StringKind:
return val.String(), nil
case protoreflect.BytesKind:
return base64.StdEncoding.EncodeToString(val.Bytes()), nil
case protoreflect.BoolKind:
if val.Bool() {
return "true", nil
}
return "false", nil
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind,
protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
return fmt.Sprintf("%d", val.Int()), nil
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind,
protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
return fmt.Sprintf("%d", val.Uint()), nil
case protoreflect.FloatKind, protoreflect.DoubleKind:
return strconv.FormatFloat(val.Float(), 'g', -1, 64), nil
case protoreflect.EnumKind:
ed := fd.Enum().Values().ByNumber(val.Enum())
if ed != nil {
return string(ed.Name()), nil
}
return fmt.Sprintf("%d", val.Enum()), nil
default:
return "", fmt.Errorf("unsupported field kind: %s", fd.Kind())
}
}

313
builder/path_template.go Normal file
View File

@@ -0,0 +1,313 @@
package builder
import (
"errors"
"fmt"
"net/url"
"strings"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
)
// -------------------------- Path template representation -------------------------
// pathSegment is a helper interface for elements of a path.
type pathSegment interface {
isSegment() bool
}
// pathTemplate represents a parsed URL path template.
type pathTemplate struct {
// literalPrefix is the fixed part of the path before the first {var},
// e.g. "/v1/users/" for "/v1/users/{user_id}/orders:get".
// It is removed from segments, so segments contain only the remaining path literals and variables.
literalPrefix string
// segments is a sequence of pathLiteral or pathVar representing the rest of the path after literalPrefix.
segments []pathSegment
// customVerb is an optional ":verb" suffix, e.g. ":get".
customVerb string
}
// pathLiteral represents a fixed literal segment in a path template, e.g., "/v1/users/".
type pathLiteral struct {
text string
}
func (p pathLiteral) isSegment() bool { return true }
// pathVar represents a variable segment in a path template, e.g., "{user.id}".
type pathVar struct {
// fieldPath is the dotted path to the field in the struct, e.g., "user.id".
fieldPath string
// pattern is the optional pattern after '=', e.g., "*" or "**/orders".
// It specifies how the variable can match parts of the URL path.
pattern string
// multiSegment is true if the pattern can match multiple path segments
// (contains '/' or "**").
multiSegment bool
}
func (p pathVar) isSegment() bool { return true }
// ----------------------------- Path template parsing -----------------------------
// parsePathTemplate parses a URL path template into a pathTemplate.
// It extracts:
// 1. literalPrefix — fixed part before the first variable,
// 2. segments — sequence of pathLiteral and pathVar,
// 3. customVerb — optional ":verb" suffix.
//
// Complexity: time O(n), memory O(n).
//
// Example:
//
// input: "/v1/users/{user_id}/orders:get"
// output: pathTemplate{
// literalPrefix: "/v1/users/",
// segments: [{user_id}, "/orders"],
// customVerb: ":get",
// }
func parsePathTemplate(input string) (*pathTemplate, error) {
// Step 1: extract custom verb after the last colon, e.g. ":get"
var customVerb string
if i := strings.LastIndex(input, ":"); i >= 0 && i > strings.LastIndex(input, "/") {
customVerb = input[i:]
input = input[:i]
}
var (
segments []pathSegment
buf strings.Builder
)
// Step 2: iterate over the input and split into segments
for i := 0; i < len(input); {
if input[i] != '{' {
buf.WriteByte(input[i])
i++
continue
}
// Add literal before '{' if any
if buf.Len() > 0 {
segments = append(segments, pathLiteral{text: buf.String()})
buf.Reset()
}
// Find closing '}'
start := i + 1
offset := strings.IndexByte(input[start:], '}') // relative offset from start
if offset < 0 {
return nil, fmt.Errorf("unclosed '{' in path: %s", input)
}
end := start + offset
token := input[start:end]
i = end + 1 // jump past '}'
// Split field path and optional pattern
var fieldPath, pattern string
if k := strings.IndexByte(token, '='); k >= 0 {
fieldPath = strings.TrimSpace(token[:k])
pattern = strings.TrimSpace(token[k+1:])
} else {
fieldPath = strings.TrimSpace(token)
}
if fieldPath == "" {
return nil, fmt.Errorf("empty variable in path: %s", input)
}
pv := pathVar{
fieldPath: fieldPath,
pattern: pattern,
multiSegment: isMultiSegmentPattern(pattern),
}
segments = append(segments, pv)
}
// Step 3: add any trailing literal after last '}'
if buf.Len() > 0 {
segments = append(segments, pathLiteral{text: buf.String()})
}
// Step 4: extract literalPrefix if the first segment is a literal
var literalPrefix string
if len(segments) > 0 {
if pl, ok := segments[0].(pathLiteral); ok {
literalPrefix = pl.text
segments = segments[1:] // remove from segments to avoid duplication
}
}
// Step 5: return fully parsed pathTemplate
return &pathTemplate{
literalPrefix: literalPrefix,
segments: segments,
customVerb: customVerb,
}, nil
}
// isMultiSegmentPattern returns true if pattern can match multiple path segments (contains '/' or '**').
// Examples:
// | Pattern | Result | Usecase |
// |----------------|--------|------------------------------------------|
// | "" | false | {var} => single segment |
// | "*" | false | {var=*} => single segment |
// | "**" | true | {var=**} => multiple segments |
// | "foo/*" | true | {var=foo/*} => multiple segments |
// | "foo/**" | true | {var=foo/**} => multiple segments |
// | "users/*/orders"| true | {users/*/orders} => multiple segments |
func isMultiSegmentPattern(pattern string) bool {
if pattern == "" {
return false
}
if pattern == singleWildcard {
return false
}
return strings.Contains(pattern, "/") || strings.Contains(pattern, doubleWildcard)
}
// ----------------------------- Path template resolving -----------------------------
// resolvePathPlaceholders expands placeholders in a path template using values from proto.Message.
// Placeholders must be bound to non-repeated scalar fields (not lists, maps, or messages).
//
// Example:
//
// tmpl: "/v1/users/{user_id}/orders:get"
// msg: &pb.Message{UserId: 12345}
//
// path: "/v1/users/12345/orders:get"
// usedFields: {"user_id"}
func resolvePathPlaceholders(tmpl *pathTemplate, msg proto.Message) (path string, usedFields *usedFields, err error) {
usedFields = newUsedFields()
var sb strings.Builder
sb.WriteString(tmpl.literalPrefix)
msgReflect := msg.ProtoReflect()
for _, segment := range tmpl.segments {
switch s := segment.(type) {
case pathLiteral:
sb.WriteString(s.text)
case pathVar:
val, fd, ok := findFieldByPath(msgReflect, s.fieldPath)
if !ok {
return "", nil, fmt.Errorf("path placeholder %s not found", s.fieldPath)
}
if isZeroValue(val, fd) {
// it's the only case that allows zero-value matches.
if s.pattern == doubleWildcard {
usedFields.add(s.fieldPath)
continue
}
return "", nil, fmt.Errorf("path placeholder %s has zero value", s.fieldPath)
}
// must be scalar (non-repeated, non-map, non-message)
if fd.IsList() || fd.IsMap() || fd.Kind() == protoreflect.MessageKind {
return "", nil, fmt.Errorf("path placeholder %s must be scalar", s.fieldPath)
}
usedFields.add(s.fieldPath)
var strVal string
strVal, err = stringifyValue(val, fd)
if err != nil {
return "", nil, fmt.Errorf("stringify placeholder %s: %w", s.fieldPath, err)
}
if err = validatePattern(s.pattern, strVal); err != nil {
return "", nil, fmt.Errorf("validate pattern, %s:%s: %w", s.fieldPath, strVal, err)
}
parts := strings.Split(strVal, "/")
for i := range parts {
parts[i] = url.PathEscape(parts[i])
}
sb.WriteString(strings.Join(parts, "/"))
}
}
sb.WriteString(tmpl.customVerb)
return sb.String(), usedFields, nil
}
// validatePattern checks whether input matches the given path pattern.
//
// Rules:
// - "" or "*" => exactly one segment, no "/" allowed
// - "**" => zero or more segments (may include "/")
// - composite patterns like "*/orders/*" must match literally
//
// Example for composite pattern case:
//
// pattern: "*/orders/*"
// input: "42/orders/123"
//
// patternSegments = ["*", "orders", "*"]
// valueParts = ["42", "orders", "123"]
//
// Match:
// "*" -> "42"
// "orders" -> "orders"
// "*" -> "123"
func validatePattern(pattern, input string) error {
var (
parts = strings.Split(input, "/")
lenParts = len(parts)
)
if pattern == "" || pattern == singleWildcard {
if lenParts != 1 {
return errors.New("must be a single path segment")
}
return nil
}
if pattern == doubleWildcard {
if lenParts < 1 {
return errors.New("must contain at least one segment")
}
return nil
}
var (
patternSegments = strings.Split(pattern, "/")
patternIndex int
)
for i := 0; i < len(patternSegments); i++ {
switch patternSegments[i] {
case singleWildcard:
if patternIndex >= lenParts || parts[patternIndex] == "" {
return fmt.Errorf("segment %d must not be empty", patternIndex)
}
patternIndex++
case doubleWildcard:
if patternIndex >= lenParts {
return fmt.Errorf("must contain at least one segment at position %d", patternIndex)
}
return nil
default:
if patternIndex >= lenParts || parts[patternIndex] != patternSegments[i] {
return fmt.Errorf("expected literal %s at position %d", patternSegments[i], patternIndex)
}
patternIndex++
}
}
if patternIndex != lenParts {
return errors.New("extra segments in value")
}
return nil
}

View File

@@ -0,0 +1,21 @@
package builder
import "sync"
var (
pathTemplateCache = make(map[string]*pathTemplate)
pathTemplateCacheMu sync.RWMutex
)
func getCachedPathTemplate(path string) (*pathTemplate, bool) {
pathTemplateCacheMu.RLock()
defer pathTemplateCacheMu.RUnlock()
tmpl, ok := pathTemplateCache[path]
return tmpl, ok
}
func setPathTemplateCache(path string, tmpl *pathTemplate) {
pathTemplateCacheMu.Lock()
defer pathTemplateCacheMu.Unlock()
pathTemplateCache[path] = tmpl
}

View File

@@ -0,0 +1,15 @@
package proto
import "google.golang.org/protobuf/encoding/protojson"
var marshaler = protojson.MarshalOptions{}
func (m *Test_Client_Call_DefaultError) Error() string {
buf, _ := marshaler.Marshal(m)
return string(buf)
}
func (m *Test_Client_Call_SpecialError) Error() string {
buf, _ := marshaler.Marshal(m)
return string(buf)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,330 @@
syntax = "proto3";
package proto;
option go_package = "go.unistack.org/micro-client-http/v4/proto;proto";
message TestRequestBuilder {}
message Test_PathOnly {
message PrimitiveCase {
string userId = 1 [json_name = "user_id"];
int64 orderId = 2 [json_name = "order_id"];
}
message NestedCase {
User user = 1;
Order order = 2;
message User {
string id = 1;
}
message Order {
int64 id = 1;
Product product = 2;
message Product {
int64 id = 1;
}
}
}
message MultipleCase {
string userId = 1 [json_name = "user_id"];
Order order = 2;
message Order {
string id = 1;
}
}
message RepeatedCase {
repeated string userId = 1 [json_name = "user_id"];
int64 orderId = 2 [json_name = "order_id"];
}
message NonPrimitiveMessageCase {
User userId = 1 [json_name = "user_id"];
int64 orderId = 2 [json_name = "order_id"];
message User {
string id = 1;
}
}
message NonPrimitiveMapCase {
map<string, string> userId = 1 [json_name = "user_id"];
int64 orderId = 2 [json_name = "order_id"];
}
message PatternCase {
string pattern = 1;
}
message CompositePatternCase {
string pattern = 1;
string orderId = 2 [json_name = "order_id"];
string productId = 3 [json_name = "product_id"];
}
}
message Test_QueryOnly {
message PrimitiveCase {
string userId = 1 [json_name = "user_id"];
int64 orderId = 2 [json_name = "order_id"];
bool flag = 3;
}
message RepeatedCase {
repeated string strings = 1;
repeated int64 integers = 2;
}
message NestedMessageCase {
string userId = 1 [json_name = "user_id"];
Filter filter = 2;
message Filter {
int64 age = 1;
string name = 2;
SubFilter subFilter = 3 [json_name = "sub_filter"];
message SubFilter {
int64 subAge = 1 [json_name = "sub_age"];
string subName = 2 [json_name = "sub_name"];
}
}
}
message NestedMapCase {
string userId = 1 [json_name = "user_id"];
map<string, string> firstFilter = 2 [json_name = "first_filter"];
map<string, SubFilter> secondFilter = 4 [json_name = "second_filter"];
message SubFilter {
int64 subAge = 1 [json_name = "sub_age"];
string subName = 2 [json_name = "sub_name"];
}
}
message MultipleCase {
string userId = 1 [json_name = "user_id"];
repeated string strings = 2;
Filter firstFilter = 3 [json_name = "first_filter"];
map<string, SubFilter> secondFilter = 4 [json_name = "second_filter"];
message Filter {
int64 age = 1;
SubFilter subFilter = 2 [json_name = "sub_filter"];
}
message SubFilter {
int64 subAge = 1 [json_name = "sub_age"];
}
}
message RepeatedMessageCase {
repeated Filter filters = 1;
message Filter {
int64 age = 1;
}
}
}
message Test_BodyOnly {
message PrimitiveCase {
string userId = 1 [json_name = "user_id"];
int64 orderId = 2 [json_name = "order_id"];
bool flag = 3;
repeated string strings = 4;
Product product = 6;
message Product {
string id = 1;
string name = 2;
}
}
message NestedCase {
string userId = 1 [json_name = "user_id"];
Filter first_filter = 2 [json_name = "first_filter"];
Filter second_filter = 3 [json_name = "second_filter"];
message Filter {
int64 age = 1;
string name = 2;
SubFilter subFilter = 3 [json_name = "sub_filter"];
message SubFilter {
int64 subAge = 1 [json_name = "sub_age"];
string subName = 2 [json_name = "sub_name"];
}
}
}
message RepeatedMessageCase {
string userId = 1 [json_name = "user_id"];
repeated Product products = 2 [json_name = "products"];
message Product {
string id = 1;
string name = 2;
}
}
message MapCase {
map<string, string> firstFilter = 1 [json_name = "first_filter"];
map<string, SubFilter> secondFilter = 2 [json_name = "second_filter"];
message SubFilter {
int64 subAge = 1 [json_name = "sub_age"];
string subName = 2 [json_name = "sub_name"];
}
}
message MultipleCase {
string userId = 1 [json_name = "user_id"];
repeated SubFilter firstFilter = 2 [json_name = "first_filter"];
map<string, SubFilter> secondFilter = 3 [json_name = "second_filter"];
SubFilter thirdFilter = 4 [json_name = "third_filter"];
message SubFilter {
int64 subAge = 1 [json_name = "sub_age"];
string subName = 2 [json_name = "sub_name"];
}
}
}
message Test_Mixed {
message PrimitiveCase {
string userId = 1 [json_name = "user_id"];
int64 orderId = 2 [json_name = "order_id"];
Product product = 3;
message Product {
string id = 1;
string name = 2;
}
}
message NestedCase {
string userId = 1 [json_name = "user_id"];
Filter first_filter = 2 [json_name = "first_filter"];
Filter second_filter = 3 [json_name = "second_filter"];
message Filter {
int64 age = 1;
string name = 2;
SubFilter subFilter = 3 [json_name = "sub_filter"];
message SubFilter {
int64 subAge = 1 [json_name = "sub_age"];
string subName = 2 [json_name = "sub_name"];
}
}
}
message RepeatedMessageCase {
string userId = 1 [json_name = "user_id"];
repeated Product products = 2 [json_name = "products"];
message Product {
string id = 1;
string name = 2;
}
}
message MapCase {
map<string, string> firstFilter = 1 [json_name = "first_filter"];
map<string, SubFilter> secondFilter = 2 [json_name = "second_filter"];
message SubFilter {
int64 subAge = 1 [json_name = "sub_age"];
string subName = 2 [json_name = "sub_name"];
}
}
message MultipleCase {
string userId = 1 [json_name = "user_id"];
repeated SubFilter firstFilter = 2 [json_name = "first_filter"];
map<string, SubFilter> secondFilter = 3 [json_name = "second_filter"];
SubFilter thirdFilter = 4 [json_name = "third_filter"];
message SubFilter {
int64 subAge = 1 [json_name = "sub_age"];
string subName = 2 [json_name = "sub_name"];
}
}
}
message Benchmark {
message Case5 {
string field1 = 1;
string field2 = 2;
string field3 = 3;
string field4 = 4;
string field5 = 5;
}
message Case10 {
string field1 = 1;
string field2 = 2;
string field3 = 3;
string field4 = 4;
string field5 = 5;
string field6 = 6;
string field7 = 7;
string field8 = 8;
string field9 = 9;
string field10 = 10;
}
message Case30 {
string field1 = 1;
string field2 = 2;
string field3 = 3;
string field4 = 4;
string field5 = 5;
string field6 = 6;
string field7 = 7;
string field8 = 8;
string field9 = 9;
string field10 = 10;
string field11 = 11;
string field12 = 12;
string field13 = 13;
string field14 = 14;
string field15 = 15;
string field16 = 16;
string field17 = 17;
string field18 = 18;
string field19 = 19;
string field20 = 20;
string field21 = 21;
string field22 = 22;
string field23 = 23;
string field24 = 24;
string field25 = 25;
string field26 = 26;
string field27 = 27;
string field28 = 28;
string field29 = 29;
string field30 = 30;
}
}
message Test_Client_Call {
message Request {
string userId = 1 [json_name = "user_id"];
int64 orderId = 2 [json_name = "order_id"];
}
message Response {
string id = 1;
string name = 2;
}
message DefaultError {
string code = 1;
string msg = 2;
}
message SpecialError {
string code = 1;
string msg = 2;
string warning = 3;
}
}

194
builder/query.go Normal file
View File

@@ -0,0 +1,194 @@
package builder
import (
"fmt"
"net/url"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
)
func buildQuery(msg proto.Message, usedFieldsPath *usedFields, usedFieldBody string) (url.Values, error) {
var (
query = url.Values{}
msgReflect = msg.ProtoReflect()
)
fields := msgReflect.Descriptor().Fields()
for i := 0; i < fields.Len(); i++ {
var (
fd = fields.Get(i)
fieldName = fd.JSONName()
)
if usedFieldsPath.hasFullKey(fieldName) {
continue
}
if fieldName == usedFieldBody {
continue
}
val := msgReflect.Get(fd)
if isZeroValue(val, fd) {
continue
}
// Note: order of the cases is important!
switch {
case fd.IsList():
if fd.Kind() == protoreflect.MessageKind {
return nil, fmt.Errorf("repeated message field %s cannot be mapped to URL query parameters", fieldName)
}
list := val.List()
for j := 0; j < list.Len(); j++ {
strVal, err := stringifyValue(list.Get(j), fd)
if err != nil {
return nil, fmt.Errorf("stringify value for query %s: %w", fieldName, err)
}
query.Add(fieldName, strVal)
}
case fd.IsMap():
var (
m = val.Map()
rangeErr error
)
m.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
key := fmt.Sprintf("%s.%s", fieldName, k.String())
if fd.MapValue().Kind() == protoreflect.MessageKind {
flattened, err := flattenMsgForQuery(key, v.Message())
if err != nil {
rangeErr = fmt.Errorf("flatten msg for query %s: %w", fieldName, err)
return false
}
for _, item := range flattened {
if item.val == "" {
continue
}
if usedFieldsPath.hasFullKey(item.key) {
continue
}
query.Add(item.key, item.val)
}
} else {
strVal, err := stringifyValue(v, fd.MapValue())
if err != nil {
rangeErr = fmt.Errorf("stringify value for map %s: %w", fieldName, err)
return false
}
query.Add(key, strVal)
}
return true
})
if rangeErr != nil {
return nil, fmt.Errorf("map range error: %w", rangeErr)
}
case fd.Kind() == protoreflect.MessageKind:
flattened, err := flattenMsgForQuery(fieldName, val.Message())
if err != nil {
return nil, fmt.Errorf("flatten msg for query %s: %w", fieldName, err)
}
for _, item := range flattened {
if item.val == "" {
continue
}
if usedFieldsPath.hasFullKey(item.key) {
continue
}
query.Add(item.key, item.val)
}
default:
strVal, err := stringifyValue(val, fd)
if err != nil {
return nil, fmt.Errorf("stringify value for primitive %s: %w", fieldName, err)
}
query.Add(fieldName, strVal)
}
}
return query, nil
}
type flattenItem struct {
key string
val string
}
// flattenMsgForQuery flattens a non-repeated message value under a given prefix.
func flattenMsgForQuery(prefix string, msg protoreflect.Message) ([]flattenItem, error) {
var out []flattenItem
fields := msg.Descriptor().Fields()
for i := 0; i < fields.Len(); i++ {
var (
fd = fields.Get(i)
val = msg.Get(fd)
)
if isZeroValue(val, fd) {
continue
}
key := fmt.Sprintf("%s.%s", prefix, fd.JSONName())
switch {
case fd.IsList():
if fd.Kind() == protoreflect.MessageKind {
return nil, fmt.Errorf("repeated message field %s cannot be flattened for query", key)
}
list := val.List()
for j := 0; j < list.Len(); j++ {
strVal, err := stringifyValue(list.Get(j), fd)
if err != nil {
return nil, fmt.Errorf("stringify query %s: %w", key, err)
}
out = append(out, flattenItem{key: key, val: strVal})
}
case fd.Kind() == protoreflect.MessageKind:
nested, err := flattenMsgForQuery(key, val.Message())
if err != nil {
return nil, fmt.Errorf("flatten msg for query %s: %w", key, err)
}
out = append(out, nested...)
case fd.IsMap():
var mapErr error
val.Map().Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
keyStr := k.String()
if fd.MapValue().Kind() == protoreflect.MessageKind {
child, err := flattenMsgForQuery(keyStr, v.Message())
if err != nil {
mapErr = fmt.Errorf("flatten map value %s: %w", key, err)
return false
}
out = append(out, child...)
} else {
strVal, err := stringifyValue(v, fd.MapValue())
if err != nil {
mapErr = fmt.Errorf("stringify query %s: %w", keyStr, err)
return false
}
out = append(out, flattenItem{key: keyStr, val: strVal})
}
return true
})
if mapErr != nil {
return nil, mapErr
}
default:
strVal, err := stringifyValue(val, fd)
if err != nil {
return nil, fmt.Errorf("stringify query %s: %w", key, err)
}
out = append(out, flattenItem{key: key, val: strVal})
}
}
return out, nil
}

180
builder/request_builder.go Normal file
View File

@@ -0,0 +1,180 @@
// Package builder implements google.api.http-style request building (gRPC JSON transcoding)
// for HTTP requests, closely following the google.api.http spec.
// See full spec for details: https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
package builder
import (
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
)
type RequestBuilder struct {
path string // e.g. "/v1/{name=projects/*/topics/*}:publish" or "/users/{user.id}"
method string // GET, POST, PATCH, etc. (not used in mapping rules, but convenient for callers)
bodyOption bodyOption // "", "*", or top-level field name
msg proto.Message // request struct
}
func NewRequestBuilder(
path string,
method string,
bodyOpt string,
msg proto.Message,
) (
*RequestBuilder,
error,
) {
rb := &RequestBuilder{
path: path,
method: method,
bodyOption: bodyOption(bodyOpt),
msg: msg,
}
if err := rb.validate(); err != nil {
return nil, fmt.Errorf("validate: %w", err)
}
return rb, nil
}
// Build applies mapping rules and returns:
//
// resolvedPath — path with placeholders substituted and query appended
// newMsg — same concrete type as input, filtered to contain only the body fields
// err — if mapping/validation failed
func (b *RequestBuilder) Build() (resolvedPath string, newMsg proto.Message, err error) {
tmpl, isCached := getCachedPathTemplate(b.path)
if !isCached {
tmpl, err = parsePathTemplate(b.path)
if err != nil {
return "", nil, fmt.Errorf("parse path template: %w", err)
}
setPathTemplateCache(b.path, tmpl)
}
var usedFieldsPath *usedFields
resolvedPath, usedFieldsPath, err = resolvePathPlaceholders(tmpl, b.msg)
if err != nil {
return "", nil, fmt.Errorf("resolve path placeholders: %w", err)
}
// if all set fields are already used in path, no need to process query/body
if allFieldsUsed(b.msg, usedFieldsPath) {
return resolvedPath, initZeroMsg(b.msg), nil
}
switch {
case b.bodyOption.isWithoutBody():
var query url.Values
query, err = buildQuery(b.msg, usedFieldsPath, "")
if err != nil {
return "", nil, fmt.Errorf("build query: %w", err)
}
return resolvedPath + encodeQuery(query), initZeroMsg(b.msg), nil
case b.bodyOption.isSingleField():
fieldBody := b.bodyOption.String()
newMsg, err = buildSingleFieldBody(b.msg, fieldBody)
if err != nil {
return "", nil, fmt.Errorf("build single field body: %w", err)
}
var query url.Values
query, err = buildQuery(b.msg, usedFieldsPath, fieldBody)
if err != nil {
return "", nil, fmt.Errorf("build query: %w", err)
}
return resolvedPath + encodeQuery(query), newMsg, nil
case b.bodyOption.isFullBody():
newMsg, err = buildFullBody(b.msg, usedFieldsPath)
if err != nil {
return "", nil, fmt.Errorf("build full body: %w", err)
}
return resolvedPath, newMsg, nil
default:
return "", nil, fmt.Errorf("unsupported body option %s", b.bodyOption.String())
}
}
func (b *RequestBuilder) validate() error {
if b.path == "" {
return errors.New("path is empty")
}
if err := validateHTTPMethod(b.method); err != nil {
return fmt.Errorf("validate http method: %w", err)
}
if err := validateHTTPMethodAndBody(b.method, b.bodyOption); err != nil {
return fmt.Errorf("validate http method and body: %w", err)
}
if b.msg == nil {
return errors.New("msg is nil")
}
return nil
}
func validateHTTPMethod(method string) error {
switch strings.ToUpper(method) {
case http.MethodGet,
http.MethodHead,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
http.MethodConnect,
http.MethodOptions,
http.MethodTrace:
return nil
default:
return errors.New("invalid http method")
}
}
func validateHTTPMethodAndBody(method string, bodyOpt bodyOption) error {
switch method {
case http.MethodGet, http.MethodDelete, http.MethodHead, http.MethodOptions:
if !bodyOpt.isWithoutBody() {
return fmt.Errorf("%s method must not have a body", method)
}
}
return nil
}
func allFieldsUsed(msg proto.Message, used *usedFields) bool {
if used.len() == 0 {
return false
}
count := 0
msg.ProtoReflect().Range(func(protoreflect.FieldDescriptor, protoreflect.Value) bool {
count++
return true
})
return used.len() == count
}
func encodeQuery(query url.Values) string {
if len(query) == 0 {
return ""
}
enc := query.Encode()
if enc == "" {
return ""
}
return "?" + enc
}
func initZeroMsg(msg proto.Message) proto.Message {
return msg.ProtoReflect().New().Interface()
}

View File

@@ -0,0 +1,149 @@
package builder
import (
"fmt"
"math/rand"
"strings"
"testing"
"time"
"google.golang.org/protobuf/proto"
pb "go.unistack.org/micro-client-http/v4/builder/proto"
)
// sink prevents the compiler from optimizing away parsePathTemplate results.
var sink *pathTemplate
func BenchmarkParsePathTemplate(b *testing.B) {
r := rand.New(rand.NewSource(1))
benchInput := func(size int) string {
sb := strings.Builder{}
sb.Grow(size * 10)
for i := 0; i < size; i++ {
name := fmt.Sprintf("var%d", r.Intn(1000))
if r.Intn(5) == 0 {
sb.WriteString(fmt.Sprintf("{%s=**}", name))
} else {
sb.WriteString(fmt.Sprintf("{%s}", name))
}
}
return sb.String()
}
sizes := []int{1_000, 10_000, 50_000, 100_000}
for _, size := range sizes {
input := benchInput(size)
b.Run(fmt.Sprintf("N=%d", size), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
var err error
sink, err = parsePathTemplate(input)
if err != nil && testing.Verbose() {
b.Fatal(err)
}
}
})
}
}
func BenchmarkRequestBuilder(b *testing.B) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
makeMsg := func(fieldCount int) proto.Message {
switch fieldCount {
case 5:
return &pb.Benchmark_Case5{
Field1: fmt.Sprintf("value%d", r.Intn(1000)),
Field2: fmt.Sprintf("value%d", r.Intn(1000)),
Field3: fmt.Sprintf("value%d", r.Intn(1000)),
Field4: fmt.Sprintf("value%d", r.Intn(1000)),
Field5: fmt.Sprintf("value%d", r.Intn(1000)),
}
case 10:
return &pb.Benchmark_Case10{
Field1: fmt.Sprintf("value%d", r.Intn(1000)),
Field2: fmt.Sprintf("value%d", r.Intn(1000)),
Field3: fmt.Sprintf("value%d", r.Intn(1000)),
Field4: fmt.Sprintf("value%d", r.Intn(1000)),
Field5: fmt.Sprintf("value%d", r.Intn(1000)),
Field6: fmt.Sprintf("value%d", r.Intn(1000)),
Field7: fmt.Sprintf("value%d", r.Intn(1000)),
Field8: fmt.Sprintf("value%d", r.Intn(1000)),
Field9: fmt.Sprintf("value%d", r.Intn(1000)),
Field10: fmt.Sprintf("value%d", r.Intn(1000)),
}
case 30:
return &pb.Benchmark_Case30{
Field1: fmt.Sprintf("value%d", r.Intn(1000)),
Field2: fmt.Sprintf("value%d", r.Intn(1000)),
Field3: fmt.Sprintf("value%d", r.Intn(1000)),
Field4: fmt.Sprintf("value%d", r.Intn(1000)),
Field5: fmt.Sprintf("value%d", r.Intn(1000)),
Field6: fmt.Sprintf("value%d", r.Intn(1000)),
Field7: fmt.Sprintf("value%d", r.Intn(1000)),
Field8: fmt.Sprintf("value%d", r.Intn(1000)),
Field9: fmt.Sprintf("value%d", r.Intn(1000)),
Field10: fmt.Sprintf("value%d", r.Intn(1000)),
Field11: fmt.Sprintf("value%d", r.Intn(1000)),
Field12: fmt.Sprintf("value%d", r.Intn(1000)),
Field13: fmt.Sprintf("value%d", r.Intn(1000)),
Field14: fmt.Sprintf("value%d", r.Intn(1000)),
Field15: fmt.Sprintf("value%d", r.Intn(1000)),
Field16: fmt.Sprintf("value%d", r.Intn(1000)),
Field17: fmt.Sprintf("value%d", r.Intn(1000)),
Field18: fmt.Sprintf("value%d", r.Intn(1000)),
Field19: fmt.Sprintf("value%d", r.Intn(1000)),
Field20: fmt.Sprintf("value%d", r.Intn(1000)),
Field21: fmt.Sprintf("value%d", r.Intn(1000)),
Field22: fmt.Sprintf("value%d", r.Intn(1000)),
Field23: fmt.Sprintf("value%d", r.Intn(1000)),
Field24: fmt.Sprintf("value%d", r.Intn(1000)),
Field25: fmt.Sprintf("value%d", r.Intn(1000)),
Field26: fmt.Sprintf("value%d", r.Intn(1000)),
Field27: fmt.Sprintf("value%d", r.Intn(1000)),
Field28: fmt.Sprintf("value%d", r.Intn(1000)),
Field29: fmt.Sprintf("value%d", r.Intn(1000)),
Field30: fmt.Sprintf("value%d", r.Intn(1000)),
}
default:
b.Fatal("undefined field count")
return nil
}
}
tests := []struct {
name string
pathTmpl string
bodyOption string
}{
{"all fields in path", "/resource/{field1}/{field2}", ""},
{"single field body", "/resource/{field1}", "field4"},
{"full body", "/resource", "*"},
}
for _, fields := range []int{5, 10, 30} {
for _, tt := range tests {
b.Run(fmt.Sprintf("%s_%d_fields", tt.name, fields), func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
msg := makeMsg(fields)
rb, err := NewRequestBuilder(tt.pathTmpl, "POST", tt.bodyOption, msg)
if err != nil {
b.Fatalf("new request builder: %v", err)
}
_, _, err = rb.Build()
if err != nil {
b.Fatalf("build: %v", err)
}
}
})
}
}
}

File diff suppressed because it is too large Load Diff

44
builder/used_fields.go Normal file
View File

@@ -0,0 +1,44 @@
package builder
import "strings"
// usedFields stores keys and their top-level parts,
// turning top-level lookups from O(N) into O(1).
type usedFields struct {
full map[string]struct{}
top map[string]struct{}
}
func newUsedFields() *usedFields {
return &usedFields{
full: make(map[string]struct{}),
top: make(map[string]struct{}),
}
}
// add inserts a new key and updates the top-level index.
func (u *usedFields) add(key string) {
u.full[key] = struct{}{}
top := key
if i := strings.IndexByte(key, '.'); i != -1 {
top = key[:i]
}
u.top[top] = struct{}{}
}
// hasTopLevelKey checks if a top-level key exists.
func (u *usedFields) hasTopLevelKey(top string) bool {
_, ok := u.top[top]
return ok
}
// hasFullKey checks if an exact key exists.
func (u *usedFields) hasFullKey(key string) bool {
_, ok := u.full[key]
return ok
}
// len returns the number of full keys stored in the set.
func (u *usedFields) len() int {
return len(u.full)
}

View File

@@ -0,0 +1,78 @@
package builder
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestNewUsedFields(t *testing.T) {
u := newUsedFields()
require.NotNil(t, u)
require.NotNil(t, u.full)
require.NotNil(t, u.top)
require.Len(t, u.full, 0)
require.Len(t, u.top, 0)
}
func TestUsedFields_Add(t *testing.T) {
u := newUsedFields()
u.add("user.name")
u.add("profile")
_, ok := u.full["user.name"]
require.True(t, ok)
_, ok = u.full["profile"]
require.True(t, ok)
_, ok = u.top["user"]
require.True(t, ok)
_, ok = u.top["profile"]
require.True(t, ok)
require.Len(t, u.full, 2)
require.Len(t, u.top, 2)
}
func TestUsedFields_HasFullKey(t *testing.T) {
u := newUsedFields()
u.add("user.name")
require.True(t, u.hasFullKey("user.name"))
require.False(t, u.hasFullKey("user.email"))
}
func TestUsedFields_HasTopLevelKey(t *testing.T) {
u := newUsedFields()
u.add("user.name")
u.add("settings.theme")
require.True(t, u.hasTopLevelKey("user"))
require.True(t, u.hasTopLevelKey("settings"))
require.False(t, u.hasTopLevelKey("profile"))
}
func TestUsedFields_AddDuplicate(t *testing.T) {
u := newUsedFields()
u.add("user.name")
u.add("user.name")
require.True(t, u.hasFullKey("user.name"))
require.True(t, u.hasTopLevelKey("user"))
require.Len(t, u.full, 1)
require.Len(t, u.top, 1)
}
func TestUsedFields_Len(t *testing.T) {
u := newUsedFields()
u.add("user.name")
u.add("profile")
u.add("user.name")
u.add("profile")
require.Equal(t, u.len(), 2)
}

122
client.go Normal file
View File

@@ -0,0 +1,122 @@
package http
import (
"context"
"net/http"
"strconv"
"sync"
"time"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/errors"
"go.unistack.org/micro/v4/options"
"go.unistack.org/micro/v4/semconv"
"go.unistack.org/micro/v4/tracer"
)
var DefaultContentType = "application/json"
type Client struct {
funcCall client.FuncCall
funcStream client.FuncStream
httpClient *http.Client
opts client.Options
mu sync.RWMutex
}
func NewClient(opts ...client.Option) *Client {
clientOpts := client.NewOptions(opts...)
if len(clientOpts.ContentType) == 0 {
clientOpts.ContentType = DefaultContentType
}
c := &Client{opts: clientOpts}
dialer, ok := httpDialerFromOpts(clientOpts)
if !ok {
dialer = defaultHTTPDialer()
}
c.httpClient, ok = httpClientFromOpts(clientOpts)
if !ok {
c.httpClient = defaultHTTPClient(dialer, clientOpts.TLSConfig)
}
c.funcCall = c.fnCall
c.funcStream = c.fnStream
return c
}
func (c *Client) Name() string {
return c.opts.Name
}
func (c *Client) Init(opts ...client.Option) error {
for _, o := range opts {
o(&c.opts)
}
c.opts.Hooks.EachPrev(func(hook options.Hook) {
switch h := hook.(type) {
case client.HookCall:
c.funcCall = h(c.funcCall)
case client.HookStream:
c.funcStream = h(c.funcStream)
}
})
return nil
}
func (c *Client) Options() client.Options {
return c.opts
}
func (c *Client) NewRequest(service, method string, req any, opts ...client.RequestOption) client.Request {
reqOpts := client.NewRequestOptions(opts...)
if reqOpts.ContentType == "" {
reqOpts.ContentType = c.opts.ContentType
}
return &httpRequest{
service: service,
method: method,
request: req,
opts: reqOpts,
}
}
func (c *Client) Call(ctx context.Context, req client.Request, rsp any, opts ...client.CallOption) error {
ts := time.Now()
c.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Inc()
var sp tracer.Span
ctx, sp = c.opts.Tracer.Start(ctx, req.Endpoint()+" rpc-client",
tracer.WithSpanKind(tracer.SpanKindClient),
tracer.WithSpanLabels("endpoint", req.Endpoint()),
)
err := c.funcCall(ctx, req, rsp, opts...)
c.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Dec()
te := time.Since(ts)
c.opts.Meter.Summary(semconv.ClientRequestLatencyMicroseconds, "endpoint", req.Endpoint()).Update(te.Seconds())
c.opts.Meter.Histogram(semconv.ClientRequestDurationSeconds, "endpoint", req.Endpoint()).Update(te.Seconds())
if me := errors.FromError(err); me == nil {
sp.Finish()
c.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "success", "code", strconv.Itoa(int(200))).Inc()
} else {
sp.SetStatus(tracer.SpanStatusError, err.Error())
c.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "failure", "code", strconv.Itoa(int(me.Code))).Inc()
}
return err
}
func (c *Client) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
return c.funcStream(ctx, req, opts...)
}
func (c *Client) String() string {
return "http"
}

263
client_helpers.go Normal file
View File

@@ -0,0 +1,263 @@
package http
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/metadata"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"go.unistack.org/micro-client-http/v4/builder"
)
func buildHTTPRequest(
ctx context.Context,
addr string,
path string,
ct string,
cf codec.Codec,
msg any,
opts client.CallOptions,
log logger.Logger,
) (
*http.Request,
error,
) {
protoMsg, ok := msg.(proto.Message)
if !ok {
return nil, errors.New("msg must be a proto message type")
}
var (
method = http.MethodPost
bodyOpt string
parameters = map[string]map[string]string{}
)
if opts.Context != nil {
if v, ok := methodFromOpts(opts); ok {
method = v
}
if v, ok := pathFromOpts(opts); ok {
path = v
}
if v, ok := bodyFromOpts(opts); ok {
bodyOpt = v
}
if h, ok := headerFromOpts(opts); ok && len(h) > 0 {
m, ok := parameters["header"]
if !ok {
m = make(map[string]string)
parameters["header"] = m
}
for idx := 0; idx+1 < len(h); idx += 2 {
m[h[idx]] = h[idx+1]
}
}
if c, ok := cookieFromOpts(opts); ok && len(c) > 0 {
m, ok := parameters["cookie"]
if !ok {
m = make(map[string]string)
parameters["cookie"] = m
}
for idx := 0; idx+1 < len(c); idx += 2 {
m[c[idx]] = c[idx+1]
}
}
}
reqBuilder, err := builder.NewRequestBuilder(path, method, bodyOpt, protoMsg)
if err != nil {
return nil, fmt.Errorf("new request builder: %w", err)
}
resolvedPath, newMsg, err := reqBuilder.Build()
if err != nil {
return nil, fmt.Errorf("build request: %w", err)
}
resolvedURL := joinURL(addr, resolvedPath)
u, err := normalizeURL(resolvedURL)
if err != nil {
return nil, fmt.Errorf("normalize url: %w", err)
}
body, err := marshallMsg(cf, newMsg)
if err != nil {
return nil, fmt.Errorf("marshal msg: %w", err)
}
var hreq *http.Request
if len(body) > 0 {
hreq, err = http.NewRequestWithContext(ctx, method, u.String(), io.NopCloser(bytes.NewBuffer(body)))
hreq.ContentLength = int64(len(body))
} else {
hreq, err = http.NewRequestWithContext(ctx, method, u.String(), nil)
}
if err != nil {
return nil, fmt.Errorf("new http request: %w", err)
}
setHeadersAndCookies(ctx, hreq, ct, opts)
if err = validateHeadersAndCookies(hreq, parameters); err != nil {
return nil, fmt.Errorf("validate headers and cookies: %w", err)
}
if log.V(logger.DebugLevel) {
log.Debug(
ctx,
fmt.Sprintf("request %s to %s with headers %v body %s", method, u.String(), hreq.Header, body),
)
}
return hreq, nil
}
func joinURL(addr, resolvedPath string) string {
if addr == "" {
return resolvedPath
}
if resolvedPath == "" {
return addr
}
switch {
case strings.HasSuffix(addr, "/") && strings.HasPrefix(resolvedPath, "/"):
return addr + resolvedPath[1:]
case !strings.HasSuffix(addr, "/") && !strings.HasPrefix(resolvedPath, "/"):
return addr + "/" + resolvedPath
default:
return addr + resolvedPath
}
}
func normalizeURL(raw string) (*url.URL, error) {
if !strings.Contains(raw, "://") {
raw = "http://" + raw
}
u, err := url.Parse(raw)
if err != nil {
return nil, fmt.Errorf("parse url: %w", err)
}
if u.Scheme != "http" && u.Scheme != "https" {
return nil, fmt.Errorf("invalid scheme: %q (must be http or https)", u.Scheme)
}
if u.Host == "" {
return nil, errors.New("missing host in url")
}
return u, nil
}
func marshallMsg(cf codec.Codec, msg proto.Message) ([]byte, error) {
if msg == nil {
return nil, nil
}
isEmpty := true
msg.ProtoReflect().Range(func(protoreflect.FieldDescriptor, protoreflect.Value) bool {
isEmpty = false
return false
})
if isEmpty {
return nil, nil
}
return cf.Marshal(msg)
}
func setHeadersAndCookies(ctx context.Context, r *http.Request, ct string, opts client.CallOptions) {
r.Header = make(http.Header)
r.Header.Set(metadata.HeaderContentType, ct)
r.Header.Set("Content-Length", fmt.Sprintf("%d", r.ContentLength))
if opts.AuthToken != "" {
r.Header.Set(metadata.HeaderAuthorization, opts.AuthToken)
}
if opts.StreamTimeout > time.Duration(0) {
r.Header.Set(metadata.HeaderTimeout, fmt.Sprintf("%d", opts.StreamTimeout))
}
if opts.RequestTimeout > time.Duration(0) {
r.Header.Set(metadata.HeaderTimeout, fmt.Sprintf("%d", opts.RequestTimeout))
}
if opts.RequestMetadata != nil {
for k, v := range opts.RequestMetadata {
if k == "Cookie" {
applyCookies(r, v)
continue
}
r.Header[k] = append(r.Header[k], v...)
}
}
if md, ok := metadata.FromOutgoingContext(ctx); ok {
for k, v := range md {
if k == "Cookie" {
applyCookies(r, v)
continue
}
r.Header[k] = append(r.Header[k], v...)
}
}
}
func applyCookies(r *http.Request, rawCookies []string) {
if len(rawCookies) == 0 {
return
}
raw := strings.Join(rawCookies, "; ")
tmp := http.Request{Header: http.Header{}}
tmp.Header.Set("Cookie", raw)
for _, c := range tmp.Cookies() {
r.AddCookie(c)
}
}
func validateHeadersAndCookies(r *http.Request, parameters map[string]map[string]string) error {
if headers, ok := parameters["header"]; ok {
for name, required := range headers {
if required == "true" && r.Header.Get(name) == "" {
return fmt.Errorf("missing required header: %s", name)
}
}
}
if cookies, ok := parameters["cookie"]; ok {
cookieMap := map[string]string{}
for _, c := range r.Cookies() {
cookieMap[c.Name] = c.Value
}
for name, required := range cookies {
if required == "true" {
if _, ok := cookieMap[name]; !ok {
return fmt.Errorf("missing required cookie: %s", name)
}
}
}
}
return nil
}

401
client_helpers_test.go Normal file
View File

@@ -0,0 +1,401 @@
package http
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/require"
jsoncodec "go.unistack.org/micro-codec-json/v4"
"google.golang.org/protobuf/proto"
pb "go.unistack.org/micro-client-http/v4/builder/proto"
)
func TestJoinURL(t *testing.T) {
tests := []struct {
name string
addr string
path string
want string
}{
{
name: "both without slash",
addr: "http://example.com",
path: "api/v1",
want: "http://example.com/api/v1",
},
{
name: "addr with slash, path without slash",
addr: "http://example.com/",
path: "api/v1",
want: "http://example.com/api/v1",
},
{
name: "addr without slash, path with slash",
addr: "http://example.com",
path: "/api/v1",
want: "http://example.com/api/v1",
},
{
name: "both with slash",
addr: "http://example.com/",
path: "/api/v1",
want: "http://example.com/api/v1",
},
{
name: "empty addr",
addr: "",
path: "/api/v1",
want: "/api/v1",
},
{
name: "empty path",
addr: "http://example.com",
path: "",
want: "http://example.com",
},
{
name: "both empty",
addr: "",
path: "",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, joinURL(tt.addr, tt.path))
})
}
}
func TestNormalizeURL(t *testing.T) {
tests := []struct {
name string
input string
want string
wantErr bool
}{
{
name: "host with port",
input: "localhost:8080",
want: "http://localhost:8080",
wantErr: false,
},
{
name: "http with host",
input: "http://example.com",
want: "http://example.com",
wantErr: false,
},
{
name: "http with no host",
input: "http://",
want: "",
wantErr: true,
},
{
name: "https with host",
input: "https://example.com",
want: "https://example.com",
wantErr: false,
},
{
name: "https with no host",
input: "https://",
want: "",
wantErr: true,
},
{
name: "invalid scheme",
input: "ftp://example.com",
want: "",
wantErr: true,
},
{
name: "IPv4 without scheme",
input: "127.0.0.1:9000",
want: "http://127.0.0.1:9000",
wantErr: false,
},
{
name: "IPv4 with scheme",
input: "http://127.0.0.1:8080",
want: "http://127.0.0.1:8080",
wantErr: false,
},
{
name: "IPv6 without scheme",
input: "[::1]:8080",
want: "http://[::1]:8080",
wantErr: false,
},
{
name: "IPv6 with scheme",
input: "https://[::1]:443",
want: "https://[::1]:443",
wantErr: false,
},
{
name: "hostname only",
input: "my-service",
want: "http://my-service",
wantErr: false,
},
{
name: "hostname with path",
input: "service.local/api/v1",
want: "http://service.local/api/v1",
wantErr: false,
},
{
name: "hostname with dash and port",
input: "api-service.local:8080",
want: "http://api-service.local:8080",
wantErr: false,
},
{
name: "just path",
input: "/api/v1",
want: "",
wantErr: true,
},
{
name: "empty string",
input: "",
want: "",
wantErr: true,
},
{
name: "http with query params",
input: "http://example.com?x=1&y=2",
want: "http://example.com?x=1&y=2",
wantErr: false,
},
{
name: "http with fragment",
input: "http://example.com/path#section1",
want: "http://example.com/path#section1",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := normalizeURL(tt.input)
if tt.wantErr {
require.Error(t, err)
require.Nil(t, result)
} else {
require.NoError(t, err)
require.Equal(t, tt.want, result.String())
}
})
}
}
func TestMarshallMsg(t *testing.T) {
type request = pb.Test_Client_Call_Request
tests := []struct {
name string
msg proto.Message
expected string
}{
{
name: "empty",
msg: &request{},
expected: "",
},
{
name: "nil",
msg: nil,
expected: "",
},
{
name: "valid",
msg: &request{UserId: "123", OrderId: 456},
expected: `{"userId":"123","orderId":456}`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := marshallMsg(jsoncodec.NewCodec(), tt.msg)
require.NoError(t, err)
require.Equal(t, tt.expected, string(result))
})
}
}
func TestApplyCookies(t *testing.T) {
tests := []struct {
name string
rawCookies []string
want []*http.Cookie
}{
{
name: "empty",
rawCookies: []string{},
want: []*http.Cookie{},
},
{
name: "single cookie",
rawCookies: []string{"session=abc123"},
want: []*http.Cookie{
{Name: "session", Value: "abc123"},
},
},
{
name: "multiple cookies separate items",
rawCookies: []string{"session=abc123", "user=john"},
want: []*http.Cookie{
{Name: "session", Value: "abc123"},
{Name: "user", Value: "john"},
},
},
{
name: "multiple cookies in one item",
rawCookies: []string{"a=1; b=2"},
want: []*http.Cookie{
{Name: "a", Value: "1"},
{Name: "b", Value: "2"},
},
},
{
name: "mix of combined and separate cookies",
rawCookies: []string{"a=1; b=2", "c=3"},
want: []*http.Cookie{
{Name: "a", Value: "1"},
{Name: "b", Value: "2"},
{Name: "c", Value: "3"},
},
},
{
name: "duplicate cookies",
rawCookies: []string{"session=abc123", "session=xyz"},
want: []*http.Cookie{
{Name: "session", Value: "abc123"},
{Name: "session", Value: "xyz"},
},
},
{
name: "cookie with spaces",
rawCookies: []string{"token=abc 123"},
want: []*http.Cookie{
{Name: "token", Value: "abc 123", Quoted: true},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest("GET", "/", nil)
applyCookies(req, tt.rawCookies)
require.Equal(t, tt.want, req.Cookies())
})
}
}
func TestValidateHeadersAndCookies(t *testing.T) {
tests := []struct {
name string
prepareRequest func() *http.Request
parameters map[string]map[string]string
wantErr bool
}{
{
name: "all required headers and cookies present",
prepareRequest: func() *http.Request {
req := httptest.NewRequest("GET", "/", nil)
req.Header.Set("My-Header-1", "Header-Value-1")
req.Header.Set("My-Header-2", "Header-Value-2")
req.AddCookie(&http.Cookie{Name: "session-1", Value: "abc-1"})
req.AddCookie(&http.Cookie{Name: "session-2", Value: "abc-2"})
return req
},
parameters: map[string]map[string]string{
"header": {"My-Header-1": "true", "My-Header-2": "true"},
"cookie": {"session-1": "true", "session-2": "true"},
},
wantErr: false,
},
{
name: "missing required header",
prepareRequest: func() *http.Request {
req := httptest.NewRequest("GET", "/", nil)
req.Header.Set("My-Header-1", "Header-Value-1")
req.AddCookie(&http.Cookie{Name: "session-1", Value: "abc-1"})
req.AddCookie(&http.Cookie{Name: "session-2", Value: "abc-2"})
return req
},
parameters: map[string]map[string]string{
"header": {"My-Header-1": "true", "My-Header-2": "true"},
"cookie": {"session-1": "true", "session-2": "true"},
},
wantErr: true,
},
{
name: "missing required cookie",
prepareRequest: func() *http.Request {
req := httptest.NewRequest("GET", "/", nil)
req.Header.Set("My-Header-1", "Header-Value-1")
req.Header.Set("My-Header-2", "Header-Value-2")
req.AddCookie(&http.Cookie{Name: "session-1", Value: "abc-1"})
return req
},
parameters: map[string]map[string]string{
"header": {"My-Header-1": "true", "My-Header-2": "true"},
"cookie": {"session-1": "true", "session-2": "true"},
},
wantErr: true,
},
{
name: "optional header and cookie not provided partially",
prepareRequest: func() *http.Request {
req := httptest.NewRequest("GET", "/", nil)
req.Header.Set("My-Header-1", "Header-Value-1")
req.AddCookie(&http.Cookie{Name: "session-1", Value: "abc-1"})
return req
},
parameters: map[string]map[string]string{
"header": {"My-Header-1": "true", "My-Header-2": "false"},
"cookie": {"session-1": "true", "session-2": "false"},
},
wantErr: false,
},
{
name: "optional header and cookie not provided",
prepareRequest: func() *http.Request {
req := httptest.NewRequest("GET", "/", nil)
return req
},
parameters: map[string]map[string]string{
"header": {"My-Header-1": "false", "My-Header-2": "false"},
"cookie": {"session-1": "false", "session-2": "false"},
},
wantErr: false,
},
{
name: "no headers or cookies required",
prepareRequest: func() *http.Request {
req := httptest.NewRequest("GET", "/", nil)
return req
},
parameters: map[string]map[string]string{},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateHeadersAndCookies(tt.prepareRequest(), tt.parameters)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}

13
client_stream.go Normal file
View File

@@ -0,0 +1,13 @@
package http
import (
"context"
"go.unistack.org/micro/v4/client"
)
// TODO: Add stream support in the future.
func (c *Client) fnStream(context.Context, client.Request, ...client.CallOption) (client.Stream, error) {
panic("not implemented")
}

273
client_unary_call.go Normal file
View File

@@ -0,0 +1,273 @@
package http
import (
"context"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strings"
"time"
"go.unistack.org/micro/v4/client"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/errors"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v4/selector"
)
func (c *Client) fnCall(ctx context.Context, req client.Request, rsp any, opts ...client.CallOption) error {
// make a copy of call opts
callOpts := c.opts.CallOptions
for _, opt := range opts {
opt(&callOpts)
}
// check if we already have a deadline
d, ok := ctx.Deadline()
if !ok {
var cancel context.CancelFunc
// no deadline so we create a new one
ctx, cancel = context.WithTimeout(ctx, callOpts.RequestTimeout)
defer cancel()
} else {
// got a deadline so no need to setup context,
// but we need to set the timeout we pass along
opt := client.WithRequestTimeout(time.Until(d))
opt(&callOpts)
}
// should we noop right here?
select {
case <-ctx.Done():
return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408)
default:
}
// make copy of call method
hcall := c.call
// use the router passed as a call option, or fallback to the rpc clients router
if callOpts.Router == nil {
callOpts.Router = c.opts.Router
}
if callOpts.Selector == nil {
callOpts.Selector = c.opts.Selector
}
// inject proxy address
// TODO: don't even bother using Lookup/Select in this case
if len(c.opts.Proxy) > 0 {
callOpts.Address = []string{c.opts.Proxy}
}
var next selector.Next
call := func(i int) error {
// call backoff first. Someone may want an initial start delay
t, err := callOpts.Backoff(ctx, req, i)
if err != nil {
return errors.InternalServerError("go.micro.client", "%+v", err)
}
// only sleep if greater than 0
if t.Seconds() > 0 {
time.Sleep(t)
}
if next == nil {
var routes []string
// lookup the route to send the reques to
// TODO apply any filtering here
routes, err = c.opts.Lookup(ctx, req, callOpts)
if err != nil {
return errors.InternalServerError("go.micro.client", "%+v", err)
}
// balance the list of nodes
next, err = callOpts.Selector.Select(routes)
if err != nil {
return errors.InternalServerError("go.micro.client", "%+v", err)
}
}
node := next()
// make the call
err = hcall(ctx, node, req, rsp, callOpts)
// record the result of the call to inform future routing decisions
if verr := c.opts.Selector.Record(node, err); verr != nil {
return errors.InternalServerError("go.micro.client", "%+v", verr)
}
// try and transform the error to micro error
if verr, ok := err.(*errors.Error); ok {
return verr
}
return err
}
ch := make(chan error, callOpts.Retries)
var gerr error
for i := 0; i <= callOpts.Retries; i++ {
go func() {
ch <- call(i)
}()
select {
case <-ctx.Done():
return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408)
case err := <-ch:
// if the call succeeded lets bail early
if err == nil {
return nil
}
retry, rerr := callOpts.Retry(ctx, req, i, err)
if rerr != nil {
return rerr
}
if !retry {
return err
}
gerr = err
}
}
return gerr
}
func (c *Client) call(ctx context.Context, addr string, req client.Request, rsp any, opts client.CallOptions) error {
ct := req.ContentType()
if len(opts.ContentType) > 0 {
ct = opts.ContentType
}
cf, err := c.newCodec(ct)
if err != nil {
return errors.BadRequest("go.micro.client", "%+v", err)
}
hreq, err := buildHTTPRequest(ctx, addr, req.Endpoint(), ct, cf, req.Body(), opts, c.opts.Logger)
if err != nil {
return errors.BadRequest("go.micro.client", "%+v", err)
}
hrsp, err := c.httpClient.Do(hreq)
if err != nil {
switch err := err.(type) {
case *url.Error:
if err, ok := err.Err.(net.Error); ok && err.Timeout() {
return errors.Timeout("go.micro.client", "%+v", err)
}
case net.Error:
if err.Timeout() {
return errors.Timeout("go.micro.client", "%+v", err)
}
}
return errors.InternalServerError("go.micro.client", "%+v", err)
}
defer hrsp.Body.Close()
return c.parseRsp(ctx, hrsp, rsp, opts)
}
func (c *Client) newCodec(ct string) (codec.Codec, error) {
c.mu.RLock()
defer c.mu.RUnlock()
if idx := strings.IndexRune(ct, ';'); idx >= 0 {
ct = ct[:idx]
}
if cf, ok := c.opts.Codecs[ct]; ok {
return cf, nil
}
return nil, codec.ErrUnknownContentType
}
func (c *Client) parseRsp(ctx context.Context, hrsp *http.Response, rsp any, opts client.CallOptions) error {
log := c.opts.Logger
select {
case <-ctx.Done():
return ctx.Err()
default:
}
var buf []byte
if opts.ResponseMetadata != nil {
for k, v := range hrsp.Header {
opts.ResponseMetadata.Set(k, strings.Join(v, ","))
}
}
if hrsp.StatusCode == http.StatusNoContent {
return nil
}
ct := DefaultContentType
if htype := hrsp.Header.Get(metadata.HeaderContentType); htype != "" {
ct = htype
}
if hrsp.Body != nil {
var err error
buf, err = io.ReadAll(hrsp.Body)
if err != nil {
return errors.InternalServerError("go.micro.client", "read body: %v", err)
}
}
cf, err := c.newCodec(ct)
if err != nil {
return errors.InternalServerError("go.micro.client", "unknown content-type %s: %v", ct, err)
}
if log.V(logger.DebugLevel) {
log.Debug(ctx, fmt.Sprintf("response with headers: %v and body: %s", hrsp.Header, buf))
}
if hrsp.StatusCode < http.StatusBadRequest {
if err = cf.Unmarshal(buf, rsp); err != nil {
return errors.InternalServerError("go.micro.client", "unmarshal response: %v", err)
}
return nil
}
var mappedErr any
errMap, ok := errorMapFromOpts(opts)
if ok && errMap != nil {
mappedErr, ok = errMap[fmt.Sprintf("%d", hrsp.StatusCode)]
if !ok {
mappedErr, ok = errMap["default"]
}
}
if !ok || mappedErr == nil {
return errors.New("go.micro.client", string(buf), int32(hrsp.StatusCode))
}
if err = cf.Unmarshal(buf, mappedErr); err != nil {
return errors.InternalServerError("go.micro.client", "unmarshal response: %v", err)
}
if v, ok := mappedErr.(error); ok {
return v
}
// if the error map item does not implement the error interface, wrap it
return &Error{err: mappedErr}
}

1284
client_unary_call_test.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,61 +0,0 @@
package http
import (
"encoding/json"
"github.com/golang/protobuf/proto"
"github.com/micro/go-micro/v2/codec"
"github.com/micro/go-micro/v2/codec/jsonrpc"
"github.com/micro/go-micro/v2/codec/protorpc"
)
type jsonCodec struct{}
type protoCodec struct{}
type Codec interface {
Marshal(v interface{}) ([]byte, error)
Unmarshal(b []byte, v interface{}) error
String() string
}
var (
defaultHTTPCodecs = map[string]Codec{
"application/json": jsonCodec{},
"application/proto": protoCodec{},
"application/protobuf": protoCodec{},
"application/octet-stream": protoCodec{},
}
defaultRPCCodecs = map[string]codec.NewCodec{
"application/json": jsonrpc.NewCodec,
"application/json-rpc": jsonrpc.NewCodec,
"application/protobuf": protorpc.NewCodec,
"application/proto-rpc": protorpc.NewCodec,
"application/octet-stream": protorpc.NewCodec,
}
)
func (protoCodec) Marshal(v interface{}) ([]byte, error) {
return proto.Marshal(v.(proto.Message))
}
func (protoCodec) Unmarshal(data []byte, v interface{}) error {
return proto.Unmarshal(data, v.(proto.Message))
}
func (protoCodec) String() string {
return "proto"
}
func (jsonCodec) Marshal(v interface{}) ([]byte, error) {
return json.Marshal(v)
}
func (jsonCodec) Unmarshal(data []byte, v interface{}) error {
return json.Unmarshal(data, v)
}
func (jsonCodec) String() string {
return "json"
}

12
error.go Normal file
View File

@@ -0,0 +1,12 @@
package http
import "fmt"
// Error is used when items in the error map do not implement the error interface and need to be wrapped.
type Error struct {
err any
}
func (e *Error) Error() string {
return fmt.Sprintf("%+v", e.err)
}

25
go.mod
View File

@@ -1,10 +1,25 @@
module github.com/micro/go-plugins/client/http/v2 module go.unistack.org/micro-client-http/v4
go 1.13 go 1.23.0
toolchain go1.24.2
require ( require (
github.com/golang/protobuf v1.4.0 github.com/stretchr/testify v1.11.1
github.com/micro/go-micro/v2 v2.9.1-0.20200716153311-f9bf56239306 go.unistack.org/micro-codec-json/v4 v4.1.0
go.unistack.org/micro/v4 v4.1.19
google.golang.org/protobuf v1.36.9
) )
replace github.com/coreos/etcd => github.com/ozonru/etcd v3.3.20-grpc1.27-origmodule+incompatible require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/matoous/go-nanoid v1.5.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/spf13/cast v1.9.2 // indirect
go.unistack.org/micro-proto/v4 v4.1.0 // indirect
golang.org/x/sys v0.35.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
google.golang.org/grpc v1.75.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

709
go.sum
View File

@@ -1,668 +1,47 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw=
github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
github.com/Azure/go-autorest/autorest/adal v0.2.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM=
github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhjOwRV2+0HXGmM0jxaEu+ZiXJFoVZOTfL/dmqbrD8=
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k=
github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75 h1:3ILjVyslFbc4jl1w5TWuvvslFD/nDfR2H8tVaMVLrEY=
github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ=
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bwmarrin/discordgo v0.20.2 h1:nA7jiTtqUA9lT93WL2jPjUp8ZTEInRujBdx1C9gkr20=
github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
github.com/caddyserver/certmagic v0.10.6/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ=
github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/ef-ds/deque v1.0.4-0.20190904040645-54cb57c252a1/go.mod h1:HvODWzv6Y6kBf3Ah2WzN1bHjDUezGLaAhwuWVwfpEJs=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c h1:pBgVXWDXju1m8W4lnEeIqTHPOzhTUO81a7yknM/xQR4=
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c/go.mod h1:pFdJbAhRf7rh6YYMUdIQGyzne6zYL1tCUW8QV2B3UfY=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-acme/lego/v3 v3.4.0/go.mod h1:xYbLDuxq3Hy4bMUT1t9JIuz6GWIWb3m5X+TeTHYaT7M=
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc=
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk=
github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.3/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg=
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= github.com/matoous/go-nanoid v1.5.1 h1:aCjdvTyO9LLnTIi0fgdXhOPPvOHjpXN6Ik9DaNjIct4=
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= github.com/matoous/go-nanoid v1.5.1/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/lucas-clemente/quic-go v0.14.1 h1:c1aKoBZKOPA+49q96B1wGkibyPP0AxYh45WuAoq+87E= github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/lucas-clemente/quic-go v0.14.1/go.mod h1:Vn3/Fb0/77b02SGhQk36KzOUmXgVpFfizUfW5WMaqyU= github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/marten-seemann/chacha20 v0.2.0 h1:f40vqzzx+3GdOmzQoItkLX5WLvHgPgyYqFFIO5Gh4hQ= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/marten-seemann/chacha20 v0.2.0/go.mod h1:HSdjFau7GzYRj+ahFNwsO3ouVJr1HFkWoEwNDb4TMtE= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI= go.unistack.org/micro-codec-json/v4 v4.1.0 h1:iydeSkt3ee7IPU0dHHKlGN97lw+YFQasBk9rdv0woYA=
github.com/marten-seemann/qtls v0.4.1 h1:YlT8QP3WCCvvok7MGEZkMldXbyqgr8oFg5/n8Gtbkks= go.unistack.org/micro-codec-json/v4 v4.1.0/go.mod h1:aUg86elSlURSynTAetDAAXj/VzFDwwcg92QNrRzcvrM=
github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc= go.unistack.org/micro-proto/v4 v4.1.0 h1:qPwL2n/oqh9RE3RTTDgt28XK3QzV597VugQPaw9lKUk=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= go.unistack.org/micro-proto/v4 v4.1.0/go.mod h1:ArmK7o+uFvxSY3dbJhKBBX4Pm1rhWdLEFf3LxBrMtec=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= go.unistack.org/micro/v4 v4.1.19 h1:LKpmSPYvX5B9AkFD7JqMU/U06v5yEWn2bsCG/YKZtZI=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= go.unistack.org/micro/v4 v4.1.19/go.mod h1:xleO2M5Yxh4s6I+RUcLrEpUjobefh+71ctrdIfn7TUs=
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
github.com/micro/cli/v2 v2.1.2 h1:43J1lChg/rZCC1rvdqZNFSQDrGT7qfMrtp6/ztpIkEM= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
github.com/micro/cli/v2 v2.1.2/go.mod h1:EguNh6DAoWKm9nmk+k/Rg0H3lQnDxqzu5x5srOtGtYg= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
github.com/micro/go-micro/v2 v2.9.1-0.20200716153311-f9bf56239306 h1:zm/cCJwRAySbM5DZdeqH4vf0F4Lvfe/XqC8AB9Vu5ow= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
github.com/micro/go-micro/v2 v2.9.1-0.20200716153311-f9bf56239306/go.mod h1:JgTt07BfD2x4mcTm/qZi8HZnxopoBYJxfpIETI8MKq8= google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=
github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0=
github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y=
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.6 h1:qAaHZaS8pRRNQLFaiBA1rq5WynyEGp9DFgmMfoaiXGY=
github.com/nats-io/nats-server/v2 v2.1.6/go.mod h1:BL1NOtaBQ5/y97djERRVWNouMW7GT3gxnmbE/eC8u8A=
github.com/nats-io/nats.go v1.9.2 h1:oDeERm3NcZVrPpdR/JpGdWHMv3oJ8yY30YwxKq+DU2s=
github.com/nats-io/nats.go v1.9.2/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE=
github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA=
github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nlopes/slack v0.6.1-0.20191106133607-d06c2a2b3249 h1:Pr5gZa2VcmktVwq0lyC39MsN5tz356vC/pQHKvq+QBo=
github.com/nlopes/slack v0.6.1-0.20191106133607-d06c2a2b3249/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk=
github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw=
github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ=
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/ozonru/etcd v3.3.20-grpc1.27-origmodule+incompatible h1:CAG0PUvo1fen+ZEfxKJjFIc8GuuN5RuaBuCAuaP2Hno=
github.com/ozonru/etcd v3.3.20-grpc1.27-origmodule+incompatible/go.mod h1:iIubILNIN6Jq9h8uiSLrN9L1tuj3iSSFwz3R61skm/A=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY=
github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc h1:yUaosFVTJwnltaHbSNC3i82I92quFs+OFPRl8kNMVwo=
github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY=
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361 h1:RIIXAeV6GvDBuADKumTODatUqANFZ+5BPMnzsy4hulY=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1 h1:aQktFqmDE2yjveXJlVIfslDFmFnUXSqG0i6KRcJAeMc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw=
gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/telegram-bot-api.v4 v4.6.4 h1:hpHWhzn4jTCsAJZZ2loNKfy2QWyPDRJVl3aTFXeMW8g=
gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

506
http.go
View File

@@ -1,506 +0,0 @@
// Package http provides a http client
package http
import (
"bufio"
"bytes"
"context"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"sync"
"time"
"github.com/micro/go-micro/v2/broker"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/cmd"
"github.com/micro/go-micro/v2/codec"
raw "github.com/micro/go-micro/v2/codec/bytes"
errors "github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/metadata"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/router"
"github.com/micro/go-micro/v2/selector"
"github.com/micro/go-micro/v2/transport"
)
func filterLabel(r []router.Route) []router.Route {
// selector.FilterLabel("protocol", "http")
return r
}
type httpClient struct {
once sync.Once
opts client.Options
}
func init() {
cmd.DefaultClients["http"] = NewClient
}
func (h *httpClient) call(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error {
// set the address
address := node.Address
header := make(http.Header)
if md, ok := metadata.FromContext(ctx); ok {
for k, v := range md {
header.Set(k, v)
}
}
// set timeout in nanoseconds
header.Set("Timeout", fmt.Sprintf("%d", opts.RequestTimeout))
// set the content type for the request
header.Set("Content-Type", req.ContentType())
// get codec
cf, err := h.newHTTPCodec(req.ContentType())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
// marshal request
b, err := cf.Marshal(req.Body())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
buf := &buffer{bytes.NewBuffer(b)}
defer buf.Close()
hreq := &http.Request{
Method: "POST",
URL: &url.URL{
Scheme: "http",
Host: address,
Path: req.Endpoint(),
},
Header: header,
Body: buf,
ContentLength: int64(len(b)),
Host: address,
}
// make the request
hrsp, err := http.DefaultClient.Do(hreq.WithContext(ctx))
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
defer hrsp.Body.Close()
// parse response
b, err = ioutil.ReadAll(hrsp.Body)
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
// unmarshal
if err := cf.Unmarshal(b, rsp); err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
return nil
}
func (h *httpClient) stream(ctx context.Context, node *registry.Node, req client.Request, opts client.CallOptions) (client.Stream, error) {
// set the address
address := node.Address
header := make(http.Header)
if md, ok := metadata.FromContext(ctx); ok {
for k, v := range md {
header.Set(k, v)
}
}
// set timeout in nanoseconds
header.Set("Timeout", fmt.Sprintf("%d", opts.RequestTimeout))
// set the content type for the request
header.Set("Content-Type", req.ContentType())
// get codec
cf, err := h.newHTTPCodec(req.ContentType())
if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error())
}
cc, err := net.Dial("tcp", address)
if err != nil {
return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error dialing: %v", err))
}
return &httpStream{
address: address,
context: ctx,
closed: make(chan bool),
conn: cc,
codec: cf,
header: header,
reader: bufio.NewReader(cc),
request: req,
}, nil
}
func (h *httpClient) newHTTPCodec(contentType string) (Codec, error) {
if c, ok := defaultHTTPCodecs[contentType]; ok {
return c, nil
}
return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType)
}
func (h *httpClient) newCodec(contentType string) (codec.NewCodec, error) {
if c, ok := h.opts.Codecs[contentType]; ok {
return c, nil
}
if cf, ok := defaultRPCCodecs[contentType]; ok {
return cf, nil
}
return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType)
}
func (h *httpClient) Init(opts ...client.Option) error {
for _, o := range opts {
o(&h.opts)
}
return nil
}
func (h *httpClient) Options() client.Options {
return h.opts
}
func (h *httpClient) NewMessage(topic string, msg interface{}, opts ...client.MessageOption) client.Message {
return newHTTPMessage(topic, msg, "application/proto", opts...)
}
func (h *httpClient) NewRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request {
return newHTTPRequest(service, method, req, h.opts.ContentType, reqOpts...)
}
func (h *httpClient) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
// make a copy of call opts
callOpts := h.opts.CallOptions
for _, opt := range opts {
opt(&callOpts)
}
// check if we already have a deadline
d, ok := ctx.Deadline()
if !ok {
// no deadline so we create a new one
ctx, _ = context.WithTimeout(ctx, callOpts.RequestTimeout)
} else {
// got a deadline so no need to setup context
// but we need to set the timeout we pass along
opt := client.WithRequestTimeout(d.Sub(time.Now()))
opt(&callOpts)
}
// should we noop right here?
select {
case <-ctx.Done():
return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408)
default:
}
// make copy of call method
hcall := h.call
// wrap the call in reverse
for i := len(callOpts.CallWrappers); i > 0; i-- {
hcall = callOpts.CallWrappers[i-1](hcall)
}
// return errors.New("go.micro.client", "request timeout", 408)
call := func(i int) error {
// call backoff first. Someone may want an initial start delay
t, err := callOpts.Backoff(ctx, req, i)
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
// only sleep if greater than 0
if t.Seconds() > 0 {
time.Sleep(t)
}
// use the router passed as a call option, or fallback to the rpc clients router
if callOpts.Router == nil {
callOpts.Router = h.opts.Router
}
// use the selector passed as a call option, or fallback to the rpc clients selector
if callOpts.Selector == nil {
callOpts.Selector = h.opts.Selector
}
callOpts.SelectOptions = append(callOpts.SelectOptions, selector.WithFilter(filterLabel))
// lookup the route to send the request via
route, err := client.LookupRoute(req, callOpts)
if err != nil {
return err
}
// pass a node to enable backwards compatability as changing the
// call func would be a breaking change.
// todo v3: change the call func to accept a route
node := &registry.Node{Address: route.Address, Metadata: route.Metadata}
node.Metadata["protocol"] = "http"
// make the call
err = hcall(ctx, node, req, rsp, callOpts)
h.opts.Selector.Record(*route, err)
return err
}
ch := make(chan error, callOpts.Retries)
var gerr error
for i := 0; i < callOpts.Retries; i++ {
go func() {
ch <- call(i)
}()
select {
case <-ctx.Done():
return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408)
case err := <-ch:
// if the call succeeded lets bail early
if err == nil {
return nil
}
retry, rerr := callOpts.Retry(ctx, req, i, err)
if rerr != nil {
return rerr
}
if !retry {
return err
}
gerr = err
}
}
return gerr
}
func (h *httpClient) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
// make a copy of call opts
callOpts := h.opts.CallOptions
for _, opt := range opts {
opt(&callOpts)
}
// check if we already have a deadline
d, ok := ctx.Deadline()
if !ok {
// no deadline so we create a new one
ctx, _ = context.WithTimeout(ctx, callOpts.RequestTimeout)
} else {
// got a deadline so no need to setup context
// but we need to set the timeout we pass along
opt := client.WithRequestTimeout(d.Sub(time.Now()))
opt(&callOpts)
}
// should we noop right here?
select {
case <-ctx.Done():
return nil, errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408)
default:
}
call := func(i int) (client.Stream, error) {
// call backoff first. Someone may want an initial start delay
t, err := callOpts.Backoff(ctx, req, i)
if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error())
}
// only sleep if greater than 0
if t.Seconds() > 0 {
time.Sleep(t)
}
// use the router passed as a call option, or fallback to the rpc clients router
if callOpts.Router == nil {
callOpts.Router = h.opts.Router
}
// use the selector passed as a call option, or fallback to the rpc clients selector
if callOpts.Selector == nil {
callOpts.Selector = h.opts.Selector
}
callOpts.SelectOptions = append(callOpts.SelectOptions, selector.WithFilter(filterLabel))
// lookup the route to send the request via
route, err := client.LookupRoute(req, callOpts)
if err != nil {
return nil, err
}
// pass a node to enable backwards compatability as changing the
// call func would be a breaking change.
// todo v3: change the call func to accept a route
node := &registry.Node{Address: route.Address, Metadata: route.Metadata}
node.Metadata["protocol"] = "http"
stream, err := h.stream(ctx, node, req, callOpts)
h.opts.Selector.Record(*route, err)
return stream, err
}
type response struct {
stream client.Stream
err error
}
ch := make(chan response, callOpts.Retries)
var grr error
var err error
for i := 0; i < callOpts.Retries; i++ {
go func() {
s, err := call(i)
ch <- response{s, err}
}()
select {
case <-ctx.Done():
return nil, errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408)
case rsp := <-ch:
// if the call succeeded lets bail early
if rsp.err == nil {
return rsp.stream, nil
}
retry, rerr := callOpts.Retry(ctx, req, i, err)
if rerr != nil {
return nil, rerr
}
if !retry {
return nil, rsp.err
}
grr = rsp.err
}
}
return nil, grr
}
func (h *httpClient) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
options := client.PublishOptions{
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
md, ok := metadata.FromContext(ctx)
if !ok {
md = make(map[string]string)
}
md["Content-Type"] = p.ContentType()
md["Micro-Topic"] = p.Topic()
cf, err := h.newCodec(p.ContentType())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
var body []byte
// passed in raw data
if d, ok := p.Payload().(*raw.Frame); ok {
body = d.Data
} else {
b := &buffer{bytes.NewBuffer(nil)}
if err := cf(b).Write(&codec.Message{Type: codec.Event}, p.Payload()); err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
body = b.Bytes()
}
h.once.Do(func() {
h.opts.Broker.Connect()
})
topic := p.Topic()
// get proxy
if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 {
options.Exchange = prx
}
// get the exchange
if len(options.Exchange) > 0 {
topic = options.Exchange
}
return h.opts.Broker.Publish(topic, &broker.Message{
Header: md,
Body: body,
})
}
func (h *httpClient) String() string {
return "http"
}
func newClient(opts ...client.Option) client.Client {
options := client.Options{
CallOptions: client.CallOptions{
Backoff: client.DefaultBackoff,
Retry: client.DefaultRetry,
Retries: client.DefaultRetries,
RequestTimeout: client.DefaultRequestTimeout,
DialTimeout: transport.DefaultDialTimeout,
},
}
for _, o := range opts {
o(&options)
}
if len(options.ContentType) == 0 {
options.ContentType = "application/proto"
}
if options.Broker == nil {
options.Broker = broker.DefaultBroker
}
if options.Router == nil {
options.Router = router.DefaultRouter
}
if options.Selector == nil {
options.Selector = selector.DefaultSelector
}
rc := &httpClient{
once: sync.Once{},
opts: options,
}
c := client.Client(rc)
// wrap in reverse
for i := len(options.Wrappers); i > 0; i-- {
c = options.Wrappers[i-1](c)
}
return c
}
func NewClient(opts ...client.Option) client.Client {
return newClient(opts...)
}

View File

@@ -1,271 +0,0 @@
package http
import (
"bufio"
"bytes"
"context"
"fmt"
"io/ioutil"
"net"
"net/http"
"testing"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-micro/v2/registry/memory"
"github.com/micro/go-micro/v2/router"
rrouter "github.com/micro/go-micro/v2/router/registry"
"github.com/micro/go-plugins/client/http/v2/test"
)
func TestHTTPClient(t *testing.T) {
r := memory.NewRegistry()
s := rrouter.NewRouter(router.Registry(r))
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer l.Close()
mux := http.NewServeMux()
mux.HandleFunc("/foo/bar", func(w http.ResponseWriter, r *http.Request) {
// only accept post
if r.Method != "POST" {
http.Error(w, "expect post method", 500)
return
}
// get codec
ct := r.Header.Get("Content-Type")
codec, ok := defaultHTTPCodecs[ct]
if !ok {
http.Error(w, "codec not found", 500)
return
}
b, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// extract message
msg := new(test.Message)
if err := codec.Unmarshal(b, msg); err != nil {
http.Error(w, err.Error(), 500)
return
}
// marshal response
b, err = codec.Marshal(msg)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// write response
w.Write(b)
})
go http.Serve(l, mux)
if err := r.Register(&registry.Service{
Name: "test.service",
Nodes: []*registry.Node{
{
Id: "test.service.1",
Address: l.Addr().String(),
Metadata: map[string]string{
"protocol": "http",
},
},
},
}); err != nil {
t.Fatal(err)
}
c := NewClient(client.Router(s))
for i := 0; i < 10; i++ {
msg := &test.Message{
Seq: int64(i),
Data: fmt.Sprintf("message %d", i),
}
req := c.NewRequest("test.service", "/foo/bar", msg)
rsp := new(test.Message)
err := c.Call(context.TODO(), req, rsp)
if err != nil {
t.Fatal(err)
}
if rsp.Seq != msg.Seq {
t.Fatalf("invalid seq %d for %d", rsp.Seq, msg.Seq)
}
}
}
func TestHTTPClientStream(t *testing.T) {
r := memory.NewRegistry()
s := rrouter.NewRouter(router.Registry(r))
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer l.Close()
mux := http.NewServeMux()
mux.HandleFunc("/foo/bar", func(w http.ResponseWriter, r *http.Request) {
// only accept post
if r.Method != "POST" {
http.Error(w, "expect post method", 500)
return
}
// hijack the connection
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "could not hijack conn", 500)
return
}
// hijacked
conn, bufrw, err := hj.Hijack()
if err != nil {
http.Error(w, err.Error(), 500)
return
}
defer conn.Close()
// read off the first request
// get codec
ct := r.Header.Get("Content-Type")
codec, ok := defaultHTTPCodecs[ct]
if !ok {
http.Error(w, "codec not found", 500)
return
}
b, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// extract message
msg := new(test.Message)
if err := codec.Unmarshal(b, msg); err != nil {
http.Error(w, err.Error(), 500)
return
}
// marshal response
b, err = codec.Marshal(msg)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// write response
rsp := &http.Response{
Header: r.Header,
Body: &buffer{bytes.NewBuffer(b)},
Status: "200 OK",
StatusCode: 200,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
ContentLength: int64(len(b)),
}
// write response
rsp.Write(bufrw)
bufrw.Flush()
reader := bufio.NewReader(conn)
for {
r, err := http.ReadRequest(reader)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
b, err = ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// extract message
msg := new(test.Message)
if err := codec.Unmarshal(b, msg); err != nil {
http.Error(w, err.Error(), 500)
return
}
// marshal response
b, err = codec.Marshal(msg)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
rsp := &http.Response{
Header: r.Header,
Body: &buffer{bytes.NewBuffer(b)},
Status: "200 OK",
StatusCode: 200,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
ContentLength: int64(len(b)),
}
// write response
rsp.Write(bufrw)
bufrw.Flush()
}
})
go http.Serve(l, mux)
if err := r.Register(&registry.Service{
Name: "test.service",
Nodes: []*registry.Node{
{
Id: "test.service.1",
Address: l.Addr().String(),
Metadata: map[string]string{
"protocol": "http",
},
},
},
}); err != nil {
t.Fatal(err)
}
c := NewClient(client.Router(s))
req := c.NewRequest("test.service", "/foo/bar", new(test.Message))
stream, err := c.Stream(context.TODO(), req)
if err != nil {
t.Fatal(err)
}
defer stream.Close()
for i := 0; i < 10; i++ {
msg := &test.Message{
Seq: int64(i),
Data: fmt.Sprintf("message %d", i),
}
err := stream.Send(msg)
if err != nil {
t.Fatal(err)
}
rsp := new(test.Message)
err = stream.Recv(rsp)
if err != nil {
t.Fatal(err)
}
if rsp.Seq != msg.Seq {
t.Fatalf("invalid seq %d for %d", rsp.Seq, msg.Seq)
}
}
}

View File

@@ -1,40 +0,0 @@
package http
import (
"github.com/micro/go-micro/v2/client"
)
type httpMessage struct {
topic string
contentType string
payload interface{}
}
func newHTTPMessage(topic string, payload interface{}, contentType string, opts ...client.MessageOption) client.Message {
var options client.MessageOptions
for _, o := range opts {
o(&options)
}
if len(options.ContentType) > 0 {
contentType = options.ContentType
}
return &httpMessage{
payload: payload,
topic: topic,
contentType: contentType,
}
}
func (h *httpMessage) ContentType() string {
return h.contentType
}
func (h *httpMessage) Topic() string {
return h.topic
}
func (h *httpMessage) Payload() interface{} {
return h.payload
}

148
options.go Normal file
View File

@@ -0,0 +1,148 @@
package http
import (
"context"
"crypto/tls"
"net"
"net/http"
"time"
"go.unistack.org/micro/v4/client"
)
// --------------------------------------------- HTTPClient option -----------------------------------------------------
type httpClientKey struct{}
func HTTPClient(c *http.Client) client.Option {
return client.SetOption(httpClientKey{}, c)
}
func httpClientFromOpts(opts client.Options) (*http.Client, bool) {
httpClient, ok := opts.Context.Value(httpClientKey{}).(*http.Client)
return httpClient, ok
}
func defaultHTTPClient(
dialer func(ctx context.Context, addr string) (net.Conn, error),
tlsConfig *tls.Config,
) *http.Client {
tr := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return dialer(ctx, addr)
},
ForceAttemptHTTP2: true,
MaxConnsPerHost: 100,
MaxIdleConns: 20,
IdleConnTimeout: 60 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: tlsConfig,
}
return &http.Client{Transport: tr}
}
// --------------------------------------------- HTTPDialer option -----------------------------------------------------
type httpDialerKey struct{}
func HTTPDialer(d *net.Dialer) client.Option {
return client.SetOption(httpDialerKey{}, d)
}
func httpDialerFromOpts(opts client.Options) (dialerFunc func(context.Context, string) (net.Conn, error), ok bool) {
var d *net.Dialer
if d, ok = opts.Context.Value(httpDialerKey{}).(*net.Dialer); ok {
dialerFunc = func(ctx context.Context, addr string) (net.Conn, error) {
return d.DialContext(ctx, "tcp", addr)
}
}
if opts.ContextDialer != nil {
dialerFunc, ok = opts.ContextDialer, true
}
return dialerFunc, ok
}
func defaultHTTPDialer() func(ctx context.Context, addr string) (net.Conn, error) {
return func(ctx context.Context, addr string) (net.Conn, error) {
d := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}
return d.DialContext(ctx, "tcp", addr)
}
}
// ----------------------------------------------- Method option -------------------------------------------------------
type methodKey struct{}
func Method(m string) client.CallOption {
return client.SetCallOption(methodKey{}, m)
}
func methodFromOpts(opts client.CallOptions) (string, bool) {
m, ok := opts.Context.Value(methodKey{}).(string)
return m, ok
}
// ------------------------------------------------ Path option --------------------------------------------------------
type pathKey struct{}
func Path(p string) client.CallOption {
return client.SetCallOption(pathKey{}, p)
}
func pathFromOpts(opts client.CallOptions) (string, bool) {
p, ok := opts.Context.Value(pathKey{}).(string)
return p, ok
}
// ------------------------------------------------ Body option --------------------------------------------------------
type bodyKey struct{}
func Body(b string) client.CallOption {
return client.SetCallOption(bodyKey{}, b)
}
func bodyFromOpts(opts client.CallOptions) (string, bool) {
b, ok := opts.Context.Value(bodyKey{}).(string)
return b, ok
}
// ---------------------------------------------- ErrorMap option ------------------------------------------------------
type errorMapKey struct{}
func ErrorMap(m map[string]any) client.CallOption {
return client.SetCallOption(errorMapKey{}, m)
}
func errorMapFromOpts(opts client.CallOptions) (map[string]any, bool) {
errMap, ok := opts.Context.Value(errorMapKey{}).(map[string]any)
return errMap, ok
}
// ------------------------------------------------ Cookie option ------------------------------------------------------
type cookieKey struct{}
func Cookie(cookies ...string) client.CallOption {
return client.SetCallOption(cookieKey{}, cookies)
}
func cookieFromOpts(opts client.CallOptions) ([]string, bool) {
c, ok := opts.Context.Value(cookieKey{}).([]string)
return c, ok
}
// ------------------------------------------------ Header option ------------------------------------------------------
type headerKey struct{}
func Header(headers ...string) client.CallOption {
return client.SetCallOption(headerKey{}, headers)
}
func headerFromOpts(opts client.CallOptions) ([]string, bool) {
h, ok := opts.Context.Value(headerKey{}).([]string)
return h, ok
}

View File

@@ -1,41 +1,17 @@
package http package http
import ( import (
"github.com/micro/go-micro/v2/client" "go.unistack.org/micro/v4/client"
"github.com/micro/go-micro/v2/codec" "go.unistack.org/micro/v4/codec"
) )
type httpRequest struct { type httpRequest struct {
service string service string
method string method string
contentType string request any
request interface{}
opts client.RequestOptions opts client.RequestOptions
} }
func newHTTPRequest(service, method string, request interface{}, contentType string, reqOpts ...client.RequestOption) client.Request {
var opts client.RequestOptions
for _, o := range reqOpts {
o(&opts)
}
if len(opts.ContentType) > 0 {
contentType = opts.ContentType
}
return &httpRequest{
service: service,
method: method,
request: request,
contentType: contentType,
opts: opts,
}
}
func (h *httpRequest) ContentType() string {
return h.contentType
}
func (h *httpRequest) Service() string { func (h *httpRequest) Service() string {
return h.service return h.service
} }
@@ -48,14 +24,18 @@ func (h *httpRequest) Endpoint() string {
return h.method return h.method
} }
func (h *httpRequest) Codec() codec.Writer { func (h *httpRequest) ContentType() string {
return nil return h.opts.ContentType
} }
func (h *httpRequest) Body() interface{} { func (h *httpRequest) Body() any {
return h.request return h.request
} }
func (h *httpRequest) Codec() codec.Codec {
return nil
}
func (h *httpRequest) Stream() bool { func (h *httpRequest) Stream() bool {
return h.opts.Stream return h.opts.Stream
} }

131
stream.go
View File

@@ -1,131 +0,0 @@
package http
import (
"bufio"
"bytes"
"context"
"errors"
"io/ioutil"
"net"
"net/http"
"net/url"
"sync"
"github.com/micro/go-micro/v2/client"
)
// Implements the streamer interface
type httpStream struct {
sync.RWMutex
address string
codec Codec
context context.Context
header http.Header
seq uint64
closed chan bool
err error
conn net.Conn
reader *bufio.Reader
request client.Request
}
var (
errShutdown = errors.New("connection is shut down")
)
func (h *httpStream) isClosed() bool {
select {
case <-h.closed:
return true
default:
return false
}
}
func (h *httpStream) Context() context.Context {
return h.context
}
func (h *httpStream) Request() client.Request {
return h.request
}
func (h *httpStream) Response() client.Response {
return nil
}
func (h *httpStream) Send(msg interface{}) error {
h.Lock()
defer h.Unlock()
if h.isClosed() {
h.err = errShutdown
return errShutdown
}
b, err := h.codec.Marshal(msg)
if err != nil {
return err
}
buf := &buffer{bytes.NewBuffer(b)}
defer buf.Close()
req := &http.Request{
Method: "POST",
URL: &url.URL{
Scheme: "http",
Host: h.address,
Path: h.request.Endpoint(),
},
Header: h.header,
Body: buf,
ContentLength: int64(len(b)),
Host: h.address,
}
return req.Write(h.conn)
}
func (h *httpStream) Recv(msg interface{}) error {
h.Lock()
defer h.Unlock()
if h.isClosed() {
h.err = errShutdown
return errShutdown
}
rsp, err := http.ReadResponse(h.reader, new(http.Request))
if err != nil {
return err
}
defer rsp.Body.Close()
b, err := ioutil.ReadAll(rsp.Body)
if err != nil {
return err
}
if rsp.StatusCode != 200 {
return errors.New(rsp.Status + ": " + string(b))
}
return h.codec.Unmarshal(b, msg)
}
func (h *httpStream) Error() error {
h.RLock()
defer h.RUnlock()
return h.err
}
func (h *httpStream) Close() error {
select {
case <-h.closed:
return nil
default:
close(h.closed)
return h.conn.Close()
}
}

View File

@@ -1,64 +0,0 @@
// Code generated by protoc-gen-go.
// source: github.com/micro/go-plugins/client/http/v2/proto/test.proto
// DO NOT EDIT!
/*
Package test is a generated protocol buffer package.
It is generated from these files:
github.com/micro/go-plugins/client/http/v2/proto/test.proto
It has these top-level messages:
Message
*/
package test
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.ProtoPackageIsVersion2 // please upgrade the proto package
type Message struct {
Seq int64 `protobuf:"varint,1,opt,name=seq" json:"seq,omitempty"`
Data string `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
}
func (m *Message) Reset() { *m = Message{} }
func (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {}
func (*Message) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func init() {
proto.RegisterType((*Message)(nil), "test.Message")
}
func init() {
proto.RegisterFile("github.com/micro/go-plugins/client/http/v2/proto/test.proto", fileDescriptor0)
}
var fileDescriptor0 = []byte{
// 131 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x1c, 0xcb, 0xb1, 0x0e, 0x82, 0x30,
0x10, 0x06, 0xe0, 0x54, 0x88, 0xc6, 0x4e, 0xa6, 0x13, 0x23, 0x71, 0x62, 0x91, 0x1b, 0x5c, 0x7c,
0x09, 0x17, 0xde, 0xa0, 0xd4, 0x4b, 0x69, 0x02, 0x5c, 0xe5, 0x7e, 0xde, 0x9f, 0xd0, 0xed, 0x5b,
0x3e, 0xfb, 0x89, 0x09, 0xd3, 0x3e, 0xf6, 0x41, 0x16, 0x5a, 0x52, 0xd8, 0x84, 0xa2, 0xbc, 0xf2,
0xbc, 0xc7, 0xb4, 0x2a, 0x85, 0x39, 0xf1, 0x0a, 0x9a, 0x80, 0x4c, 0x79, 0x13, 0x08, 0x81, 0x15,
0x7d, 0xa1, 0xab, 0x4f, 0x3f, 0xc9, 0xde, 0xbe, 0xac, 0xea, 0x23, 0xbb, 0x87, 0xad, 0x94, 0xff,
0x8d, 0x69, 0x4d, 0x57, 0x0d, 0x27, 0x9d, 0xb3, 0xf5, 0xcf, 0xc3, 0x37, 0x97, 0xd6, 0x74, 0xf7,
0xa1, 0x78, 0xbc, 0x96, 0xfd, 0x3e, 0x02, 0x00, 0x00, 0xff, 0xff, 0xff, 0x16, 0x8d, 0x95, 0x79,
0x00, 0x00, 0x00,
}

View File

@@ -1,8 +0,0 @@
syntax = "proto3";
package test;
message Message {
int64 seq = 1;
string data = 2;
}