Repository: go-kratos/kratos
Pull request #2731 (2023-03-13 19:02:52)

build(deps): bump github.com/hashicorp/consul/api from 1.19.1 to 1.20.0 in /contrib/registry/consul
Bumps [github.com/hashicorp/consul/api](https://github.com/hashicorp/consul) from 1.19.1 to 1.20.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/hashicorp/consul/blob/main/CHANGELOG.md">github.com/hashicorp/consul/api's changelog</a>.</em></p>
<blockquote>
<h2>1.15.1 (March 7, 2023)</h2>
<p>IMPROVEMENTS:</p>
<ul>
<li>cli: added <code>-append-policy-id</code>, <code>-append-policy-name</code>, <code>-append-role-name</code>, and <code>-append-role-id</code> flags to the <code>consul token update</code> command.
These flags allow updates to a token's policies/roles without having to override them completely. [<a href="https://redirect.github.com/hashicorp/consul/issues/16288">GH-16288</a>]</li>
<li>cli: added <code>-append-service-identity</code> and <code>-append-node-identity</code> flags to the <code>consul token update</code> command.
These flags allow updates to a token's node identities/service identities without having to override them. [<a href="https://redirect.github.com/hashicorp/consul/issues/16506">GH-16506</a>]</li>
<li>connect: Bump Envoy 1.22.5 to 1.22.7, 1.23.2 to 1.23.4, 1.24.0 to 1.24.2, add 1.25.1, remove 1.21.5 [<a href="https://redirect.github.com/hashicorp/consul/issues/16274">GH-16274</a>]</li>
<li>mesh: Add ServiceResolver RequestTimeout for route timeouts to make request timeouts configurable [<a href="https://redirect.github.com/hashicorp/consul/issues/16495">GH-16495</a>]</li>
<li>ui: support filtering API gateways in the ui and displaying their documentation links [<a href="https://redirect.github.com/hashicorp/consul/issues/16508">GH-16508</a>]</li>
</ul>
<p>DEPRECATIONS:</p>
<ul>
<li>cli: Deprecate the <code>-merge-node-identites</code> and <code>-merge-service-identities</code> flags from the <code>consul token update</code> command in favor of: <code>-append-node-identity</code> and <code>-append-service-identity</code>. [<a href="https://redirect.github.com/hashicorp/consul/issues/16506">GH-16506</a>]</li>
<li>cli: Deprecate the <code>-merge-policies</code> and <code>-merge-roles</code> flags from the <code>consul token update</code> command in favor of: <code>-append-policy-id</code>, <code>-append-policy-name</code>, <code>-append-role-name</code>, and <code>-append-role-id</code>. [<a href="https://redirect.github.com/hashicorp/consul/issues/16288">GH-16288</a>]</li>
</ul>
<p>BUG FIXES:</p>
<ul>
<li>cli: Fixes an issue with <code>consul connect envoy</code> where a log to STDOUT could malform JSON when used with <code>-bootstrap</code>. [<a href="https://redirect.github.com/hashicorp/consul/issues/16530">GH-16530</a>]</li>
<li>cli: Fixes an issue with <code>consul connect envoy</code> where grpc-disabled agents were not error-handled correctly. [<a href="https://redirect.github.com/hashicorp/consul/issues/16530">GH-16530</a>]</li>
<li>cli: ensure acl token read -self works [<a href="https://redirect.github.com/hashicorp/consul/issues/16445">GH-16445</a>]</li>
<li>cli: fix panic read non-existent acl policy [<a href="https://redirect.github.com/hashicorp/consul/issues/16485">GH-16485</a>]</li>
<li>gateways: fix HTTPRoute bug where service weights could be less than or equal to 0 and result in a downstream envoy protocol error [<a href="https://redirect.github.com/hashicorp/consul/issues/16512">GH-16512</a>]</li>
<li>gateways: fix HTTPRoute bug where services with a weight not divisible by 10000 are never registered properly [<a href="https://redirect.github.com/hashicorp/consul/issues/16531">GH-16531</a>]</li>
<li>mesh: Fix resolution of service resolvers with subsets for external upstreams [<a href="https://redirect.github.com/hashicorp/consul/issues/16499">GH-16499</a>]</li>
<li>proxycfg: ensure that an irrecoverable error in proxycfg closes the xds session and triggers a replacement proxycfg watcher [<a href="https://redirect.github.com/hashicorp/consul/issues/16497">GH-16497</a>]</li>
<li>proxycfg: fix a bug where terminating gateways were not cleaning up deleted service resolvers for their referenced services [<a href="https://redirect.github.com/hashicorp/consul/issues/16498">GH-16498</a>]</li>
<li>ui: Fix issue with lists and filters not rendering properly [<a href="https://redirect.github.com/hashicorp/consul/issues/16444">GH-16444</a>]</li>
</ul>
<h2>1.14.5 (March 7, 2023)</h2>
<p>SECURITY:</p>
<ul>
<li>Upgrade to use Go 1.20.1.
This resolves vulnerabilities <a href="https://go.dev/issue/58001">CVE-2022-41724</a> in <code>crypto/tls</code> and <a href="https://go.dev/issue/57855">CVE-2022-41723</a> in <code>net/http</code>. [<a href="https://redirect.github.com/hashicorp/consul/issues/16263">GH-16263</a>]</li>
</ul>
<p>IMPROVEMENTS:</p>
<ul>
<li>container: Upgrade container image to use to Alpine 3.17. [<a href="https://redirect.github.com/hashicorp/consul/issues/16358">GH-16358</a>]</li>
<li>mesh: Add ServiceResolver RequestTimeout for route timeouts to make request timeouts configurable [<a href="https://redirect.github.com/hashicorp/consul/issues/16495">GH-16495</a>]</li>
</ul>
<p>BUG FIXES:</p>
<ul>
<li>mesh: Fix resolution of service resolvers with subsets for external upstreams [<a href="https://redirect.github.com/hashicorp/consul/issues/16499">GH-16499</a>]</li>
<li>peering: Fix bug where services were incorrectly imported as connect-enabled. [<a href="https://redirect.github.com/hashicorp/consul/issues/16339">GH-16339</a>]</li>
<li>peering: Fix issue where mesh gateways would use the wrong address when contacting a remote peer with the same datacenter name. [<a href="https://redirect.github.com/hashicorp/consul/issues/16257">GH-16257</a>]</li>
<li>peering: Fix issue where secondary wan-federated datacenters could not be used as peering acceptors. [<a href="https://redirect.github.com/hashicorp/consul/issues/16230">GH-16230</a>]</li>
<li>proxycfg: fix a bug where terminating gateways were not cleaning up deleted service resolvers for their referenced services [<a href="https://redirect.github.com/hashicorp/consul/issues/16498">GH-16498</a>]</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/hashicorp/consul/commit/469705946311d3062734264b4d2de1b16fa5486f"><code>4697059</code></a> Bump consul/sdk to v0.13.1</li>
<li><a href="https://github.com/hashicorp/consul/commit/caf85ac67a01eaf6c9809e97d1059c37f9c1c4b7"><code>caf85ac</code></a> Backport of Update docs to reflect functionality into release/1.15.x (<a href="https://redirect.github.com/hashicorp/consul/issues/16555">#16555</a>)</li>
<li><a href="https://github.com/hashicorp/consul/commit/dd740f0154204dbf46b30e9605efba52ca91e278"><code>dd740f0</code></a> Backport of Fix flakey tests related to ACL token updates into release/1.15.x...</li>
<li><a href="https://github.com/hashicorp/consul/commit/e66f26b30667eed9b311465d10aac274f54e4028"><code>e66f26b</code></a> Backport of Update the consul-k8s cli docs for the new <code>proxy log</code> subcommand...</li>
<li><a href="https://github.com/hashicorp/consul/commit/494fba26ec4745958110aa44aee50579af7e2669"><code>494fba2</code></a> Backport of Follow-up fixes to consul connect envoy command into release/1.15...</li>
<li><a href="https://github.com/hashicorp/consul/commit/583466b6d5d9922ffcb6240a23c1fd1d29156773"><code>583466b</code></a> Backport of Improve ux around ACL token to help users avoid overwriting node/...</li>
<li><a href="https://github.com/hashicorp/consul/commit/88496d7f8a2686ac709d8eb4387fed1165ab377f"><code>88496d7</code></a> NET-2904 Fixes API Gateway Route Service Weight Division Error (<a href="https://redirect.github.com/hashicorp/consul/issues/16540">#16540</a>)</li>
<li><a href="https://github.com/hashicorp/consul/commit/a95d028b0665bfb2a26b9d806a63b1e55523bc3e"><code>a95d028</code></a> backport of commit 5dca39b8137daf740f18b6d86f1d48081c317815 (<a href="https://redirect.github.com/hashicorp/consul/issues/16536">#16536</a>)</li>
<li><a href="https://github.com/hashicorp/consul/commit/e69ed7a9564725e4952f690f7f0fdc00d1bf742b"><code>e69ed7a</code></a> Backport of Add some basic UI improvements for api-gateway services into rele...</li>
<li><a href="https://github.com/hashicorp/consul/commit/2f8de3c3d1f93137dce2f7fc1162b737454f7c3f"><code>2f8de3c</code></a> Backport of NET-2903 Normalize weight for http routes into release/1.15.x (<a href="https://redirect.github.com/hashicorp/consul/issues/1">#1</a>...</li>
<li>Additional commits viewable in <a href="https://github.com/hashicorp/consul/compare/api/v1.19.1...api/v1.20.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/hashicorp/consul/api&package-manager=go_modules&previous-version=1.19.1&new-version=1.20.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>> **Note**
> Automatic rebases have been disabled on this pull request as it has been open for over 30 days.


codecov-commenter: ## [Codecov](https://codecov.io/gh/go-kratos/kratos/pull/2731?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) Report
> Merging [#2731](https://codecov.io/gh/go-kratos/kratos/pull/2731?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) (e047874) into [main](https://codecov.io/gh/go-kratos/kratos/commit/e9870cb48f7f6a9fc0889881c84cd31c357850df?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) (e9870cb) will **not change** coverage.
> The diff coverage is `n/a`.

> :exclamation: Current head e047874 differs from pull request most recent head 3ad322c. Consider uploading reports for the commit 3ad322c to get more accurate results

:mega: This organization is not using Codecov’s [GitHub App Integration](https://github.com/apps/codecov). We recommend you install it so Codecov can continue to function properly for your repositories. [Learn more](https://about.codecov.io/blog/codecov-is-updating-its-github-integration/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos)

```diff
@@           Coverage Diff           @@
##             main    #2731   +/-   ##
=======================================
  Coverage   84.69%   84.69%           
=======================================
  Files          88       88           
  Lines        3901     3901           
=======================================
  Hits         3304     3304           
  Misses        423      423           
  Partials      174      174           
```



Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos)


Patch:
diff --git a/contrib/opensergo/go.mod b/contrib/opensergo/go.mod
index 9e54df77..27aca4f0 100644
--- a/contrib/opensergo/go.mod
+++ b/contrib/opensergo/go.mod
@@ -5,7 +5,7 @@ go 1.17
 require (
 	github.com/go-kratos/kratos/v2 v2.6.1
 	github.com/opensergo/opensergo-go v0.0.0-20220331070310-e5b01fee4d1c
-	golang.org/x/net v0.8.0
+	golang.org/x/net v0.9.0
 	google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd
 	google.golang.org/grpc v1.50.1
 	google.golang.org/protobuf v1.28.1
@@ -16,8 +16,8 @@ require (
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/google/uuid v1.3.0 // indirect
 	golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
-	golang.org/x/sys v0.6.0 // indirect
-	golang.org/x/text v0.8.0 // indirect
+	golang.org/x/sys v0.7.0 // indirect
+	golang.org/x/text v0.9.0 // indirect
 	gopkg.in/yaml.v3 v3.0.0 // indirect
 )
 
diff --git a/contrib/opensergo/go.sum b/contrib/opensergo/go.sum
index 24fbc62b..e05c56d0 100644
--- a/contrib/opensergo/go.sum
+++ b/contrib/opensergo/go.sum
@@ -108,8 +108,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -138,19 +138,19 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
+golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 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=
diff --git a/contrib/registry/consul/go.mod b/contrib/registry/consul/go.mod
index 09616ae1..8e64a450 100644
--- a/contrib/registry/consul/go.mod
+++ b/contrib/registry/consul/go.mod
@@ -6,7 +6,7 @@ require (
 	github.com/armon/go-metrics v0.3.10 // indirect
 	github.com/go-kratos/kratos/v2 v2.6.1
 	github.com/google/btree v1.0.0 // indirect
-	github.com/hashicorp/consul/api v1.19.1
+	github.com/hashicorp/consul/api v1.20.0
 	github.com/hashicorp/go-hclog v0.14.1 // indirect
 	github.com/hashicorp/go-immutable-radix v1.3.0 // indirect
 	github.com/hashicorp/go-msgpack v0.5.5 // indirect
diff --git a/contrib/registry/consul/go.sum b/contrib/registry/consul/go.sum
index 8c89680f..abd8880a 100644
--- a/contrib/registry/consul/go.sum
+++ b/contrib/registry/consul/go.sum
@@ -91,8 +91,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/hashicorp/consul/api v1.19.1 h1:GLeK1WD4VIRvt4wRhQKHFudztEkRb8pDs+uRiJgNwes=
-github.com/hashicorp/consul/api v1.19.1/go.mod h1:jAt316eYgWGNLJtxkMQrcqRpuDE/kFJdqkEFwRXFv8U=
+github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc=
+github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo=
 github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY=
 github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0=
 github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
diff --git a/metadata/metadata.go b/metadata/metadata.go
index 3dabdbae..8b072146 100644
--- a/metadata/metadata.go
+++ b/metadata/metadata.go
@@ -9,22 +9,37 @@ import (
 // Metadata is our way of representing request headers internally.
 // They're used at the RPC level and translate back and forth
 // from Transport headers.
-type Metadata map[string]string
+type Metadata map[string][]string
 
 // New creates an MD from a given key-values map.
-func New(mds ...map[string]string) Metadata {
+func New(mds ...map[string][]string) Metadata {
 	md := Metadata{}
 	for _, m := range mds {
-		for k, v := range m {
-			md.Set(k, v)
+		for k, vList := range m {
+			for _, v := range vList {
+				md.Add(k, v)
+			}
 		}
 	}
 	return md
 }
 
+// Add adds the key, value pair to the header.
+func (m Metadata) Add(key, value string) {
+	if len(key) == 0 {
+		return
+	}
+
+	m[strings.ToLower(key)] = append(m[strings.ToLower(key)], value)
+}
+
 // Get returns the value associated with the passed key.
 func (m Metadata) Get(key string) string {
-	return m[strings.ToLower(key)]
+	v := m[strings.ToLower(key)]
+	if len(v) == 0 {
+		return ""
+	}
+	return v[0]
 }
 
 // Set stores the key-value pair.
@@ -32,11 +47,11 @@ func (m Metadata) Set(key string, value string) {
 	if key == "" || value == "" {
 		return
 	}
-	m[strings.ToLower(key)] = value
+	m[strings.ToLower(key)] = []string{value}
 }
 
 // Range iterate over element in metadata.
-func (m Metadata) Range(f func(k, v string) bool) {
+func (m Metadata) Range(f func(k string, v []string) bool) {
 	for k, v := range m {
 		if !f(k, v) {
 			break
@@ -44,6 +59,11 @@ func (m Metadata) Range(f func(k, v string) bool) {
 	}
 }
 
+// Values returns a slice of values associated with the passed key.
+func (m Metadata) Values(key string) []string {
+	return m[strings.ToLower(key)]
+}
+
 // Clone returns a deep copy of Metadata
 func (m Metadata) Clone() Metadata {
 	md := make(Metadata, len(m))
diff --git a/middleware/auth/jwt/jwt.go b/middleware/auth/jwt/jwt.go
index 2c65c209..c83d5cdd 100644
--- a/middleware/auth/jwt/jwt.go
+++ b/middleware/auth/jwt/jwt.go
@@ -114,6 +114,9 @@ func Server(keyFunc jwt.Keyfunc, opts ...Option) middleware.Middleware {
 					if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
 						return nil, ErrTokenExpired
 					}
+					if ve.Inner != nil {
+						return nil, ve.Inner
+					}
 					return nil, ErrTokenParseFail
 				}
 				if !tokenInfo.Valid {
diff --git a/middleware/metadata/metadata.go b/middleware/metadata/metadata.go
index da9e43a9..30cb8df9 100644
--- a/middleware/metadata/metadata.go
+++ b/middleware/metadata/metadata.go
@@ -60,7 +60,9 @@ func Server(opts ...Option) middleware.Middleware {
 			header := tr.RequestHeader()
 			for _, k := range header.Keys() {
 				if options.hasPrefix(k) {
-					md.Set(k, header.Get(k))
+					for _, v := range header.Values(k) {
+						md.Add(k, v)
+					}
 				}
 			}
 			ctx = metadata.NewServerContext(ctx, md)
@@ -86,19 +88,25 @@ func Client(opts ...Option) middleware.Middleware {
 
 			header := tr.RequestHeader()
 			// x-md-local-
-			for k, v := range options.md {
-				header.Set(k, v)
+			for k, vList := range options.md {
+				for _, v := range vList {
+					header.Add(k, v)
+				}
 			}
 			if md, ok := metadata.FromClientContext(ctx); ok {
-				for k, v := range md {
-					header.Set(k, v)
+				for k, vList := range md {
+					for _, v := range vList {
+						header.Add(k, v)
+					}
 				}
 			}
 			// x-md-global-
 			if md, ok := metadata.FromServerContext(ctx); ok {
-				for k, v := range md {
+				for k, vList := range md {
 					if options.hasPrefix(k) {
-						header.Set(k, v)
+						for _, v := range vList {
+							header.Add(k, v)
+						}
 					}
 				}
 			}
diff --git a/transport/grpc/transport.go b/transport/grpc/transport.go
index 50ca5c91..56e21a86 100644
--- a/transport/grpc/transport.go
+++ b/transport/grpc/transport.go
@@ -64,6 +64,11 @@ func (mc headerCarrier) Set(key string, value string) {
 	metadata.MD(mc).Set(key, value)
 }
 
+// Add append value to key-values pair.
+func (mc headerCarrier) Add(key string, value string) {
+	metadata.MD(mc).Append(key, value)
+}
+
 // Keys lists the keys stored in this carrier.
 func (mc headerCarrier) Keys() []string {
 	keys := make([]string, 0, len(mc))
@@ -72,3 +77,8 @@ func (mc headerCarrier) Keys() []string {
 	}
 	return keys
 }
+
+// Values returns a slice of values associated with the passed key.
+func (mc headerCarrier) Values(key string) []string {
+	return metadata.MD(mc).Get(key)
+}
diff --git a/transport/http/transport.go b/transport/http/transport.go
index 32f054ed..686baed7 100644
--- a/transport/http/transport.go
+++ b/transport/http/transport.go
@@ -92,6 +92,11 @@ func (hc headerCarrier) Set(key string, value string) {
 	http.Header(hc).Set(key, value)
 }
 
+// Add append value to key-values pair.
+func (hc headerCarrier) Add(key string, value string) {
+	http.Header(hc).Add(key, value)
+}
+
 // Keys lists the keys stored in this carrier.
 func (hc headerCarrier) Keys() []string {
 	keys := make([]string, 0, len(hc))
@@ -100,3 +105,8 @@ func (hc headerCarrier) Keys() []string {
 	}
 	return keys
 }
+
+// Values returns a slice of values associated with the passed key.
+func (hc headerCarrier) Values(key string) []string {
+	return http.Header(hc).Values(key)
+}
diff --git a/transport/transport.go b/transport/transport.go
index 42510df3..c1a5396f 100644
--- a/transport/transport.go
+++ b/transport/transport.go
@@ -27,7 +27,9 @@ type Endpointer interface {
 type Header interface {
 	Get(key string) string
 	Set(key string, value string)
+	Add(key string, value string)
 	Keys() []string
+	Values(key string) []string
 }
 
 // Transporter is transport context value interface.


Test patch:
diff --git a/metadata/metadata_test.go b/metadata/metadata_test.go
index 8653bbe5..9aa1c620 100644
--- a/metadata/metadata_test.go
+++ b/metadata/metadata_test.go
@@ -8,7 +8,7 @@ import (
 
 func TestNew(t *testing.T) {
 	type args struct {
-		mds []map[string]string
+		mds []map[string][]string
 	}
 	tests := []struct {
 		name string
@@ -17,13 +17,13 @@ func TestNew(t *testing.T) {
 	}{
 		{
 			name: "hello",
-			args: args{[]map[string]string{{"hello": "kratos"}, {"hello2": "go-kratos"}}},
-			want: Metadata{"hello": "kratos", "hello2": "go-kratos"},
+			args: args{[]map[string][]string{{"hello": {"kratos"}}, {"hello2": {"go-kratos"}}}},
+			want: Metadata{"hello": {"kratos"}, "hello2": {"go-kratos"}},
 		},
 		{
 			name: "hi",
-			args: args{[]map[string]string{{"hi": "kratos"}, {"hi2": "go-kratos"}}},
-			want: Metadata{"hi": "kratos", "hi2": "go-kratos"},
+			args: args{[]map[string][]string{{"hi": {"kratos"}}, {"hi2": {"go-kratos"}}}},
+			want: Metadata{"hi": {"kratos"}, "hi2": {"go-kratos"}},
 		},
 	}
 	for _, tt := range tests {
@@ -47,13 +47,13 @@ func TestMetadata_Get(t *testing.T) {
 	}{
 		{
 			name: "kratos",
-			m:    Metadata{"kratos": "value", "env": "dev"},
+			m:    Metadata{"kratos": {"value"}, "env": {"dev"}},
 			args: args{key: "kratos"},
 			want: "value",
 		},
 		{
 			name: "env",
-			m:    Metadata{"kratos": "value", "env": "dev"},
+			m:    Metadata{"kratos": {"value"}, "env": {"dev"}},
 			args: args{key: "env"},
 			want: "dev",
 		},
@@ -67,6 +67,38 @@ func TestMetadata_Get(t *testing.T) {
 	}
 }
 
+func TestMetadata_Values(t *testing.T) {
+	type args struct {
+		key string
+	}
+	tests := []struct {
+		name string
+		m    Metadata
+		args args
+		want []string
+	}{
+		{
+			name: "kratos",
+			m:    Metadata{"kratos": {"value", "value2"}, "env": {"dev"}},
+			args: args{key: "kratos"},
+			want: []string{"value", "value2"},
+		},
+		{
+			name: "env",
+			m:    Metadata{"kratos": {"value", "value2"}, "env": {"dev"}},
+			args: args{key: "env"},
+			want: []string{"dev"},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := tt.m.Values(tt.args.key); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("Get() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
 func TestMetadata_Set(t *testing.T) {
 	type args struct {
 		key   string
@@ -82,13 +114,13 @@ func TestMetadata_Set(t *testing.T) {
 			name: "kratos",
 			m:    Metadata{},
 			args: args{key: "hello", value: "kratos"},
-			want: Metadata{"hello": "kratos"},
+			want: Metadata{"hello": {"kratos"}},
 		},
 		{
 			name: "env",
-			m:    Metadata{"hello": "kratos"},
+			m:    Metadata{"hello": {"kratos"}},
 			args: args{key: "env", value: "pro"},
-			want: Metadata{"hello": "kratos", "env": "pro"},
+			want: Metadata{"hello": {"kratos"}, "env": {"pro"}},
 		},
 		{
 			name: "empty",
@@ -107,6 +139,46 @@ func TestMetadata_Set(t *testing.T) {
 	}
 }
 
+func TestMetadata_Add(t *testing.T) {
+	type args struct {
+		key   string
+		value string
+	}
+	tests := []struct {
+		name string
+		m    Metadata
+		args args
+		want Metadata
+	}{
+		{
+			name: "kratos",
+			m:    Metadata{},
+			args: args{key: "hello", value: "kratos"},
+			want: Metadata{"hello": {"kratos"}},
+		},
+		{
+			name: "env",
+			m:    Metadata{"hello": {"kratos"}},
+			args: args{key: "hello", value: "again"},
+			want: Metadata{"hello": {"kratos", "again"}},
+		},
+		{
+			name: "empty",
+			m:    Metadata{},
+			args: args{key: "", value: ""},
+			want: Metadata{},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tt.m.Add(tt.args.key, tt.args.value)
+			if !reflect.DeepEqual(tt.m, tt.want) {
+				t.Errorf("Set() = %v, want %v", tt.m, tt.want)
+			}
+		})
+	}
+}
+
 func TestClientContext(t *testing.T) {
 	type args struct {
 		ctx context.Context
@@ -118,11 +190,11 @@ func TestClientContext(t *testing.T) {
 	}{
 		{
 			name: "kratos",
-			args: args{context.Background(), Metadata{"hello": "kratos", "kratos": "https://go-kratos.dev"}},
+			args: args{context.Background(), Metadata{"hello": {"kratos"}, "kratos": {"https://go-kratos.dev"}}},
 		},
 		{
 			name: "hello",
-			args: args{context.Background(), Metadata{"hello": "kratos", "hello2": "https://go-kratos.dev"}},
+			args: args{context.Background(), Metadata{"hello": {"kratos"}, "hello2": {"https://go-kratos.dev"}}},
 		},
 	}
 	for _, tt := range tests {
@@ -151,11 +223,11 @@ func TestServerContext(t *testing.T) {
 	}{
 		{
 			name: "kratos",
-			args: args{context.Background(), Metadata{"hello": "kratos", "kratos": "https://go-kratos.dev"}},
+			args: args{context.Background(), Metadata{"hello": {"kratos"}, "kratos": {"https://go-kratos.dev"}}},
 		},
 		{
 			name: "hello",
-			args: args{context.Background(), Metadata{"hello": "kratos", "hello2": "https://go-kratos.dev"}},
+			args: args{context.Background(), Metadata{"hello": {"kratos"}, "hello2": {"https://go-kratos.dev"}}},
 		},
 	}
 	for _, tt := range tests {
@@ -186,12 +258,12 @@ func TestAppendToClientContext(t *testing.T) {
 		{
 			name: "kratos",
 			args: args{Metadata{}, []string{"hello", "kratos", "env", "dev"}},
-			want: Metadata{"hello": "kratos", "env": "dev"},
+			want: Metadata{"hello": {"kratos"}, "env": {"dev"}},
 		},
 		{
 			name: "hello",
-			args: args{Metadata{"hi": "https://go-kratos.dev/"}, []string{"hello", "kratos", "env", "dev"}},
-			want: Metadata{"hello": "kratos", "env": "dev", "hi": "https://go-kratos.dev/"},
+			args: args{Metadata{"hi": {"https://go-kratos.dev/"}}, []string{"hello", "kratos", "env", "dev"}},
+			want: Metadata{"hello": {"kratos"}, "env": {"dev"}, "hi": {"https://go-kratos.dev/"}},
 		},
 	}
 	for _, tt := range tests {
@@ -240,13 +312,13 @@ func TestMergeToClientContext(t *testing.T) {
 	}{
 		{
 			name: "kratos",
-			args: args{Metadata{}, Metadata{"hello": "kratos", "env": "dev"}},
-			want: Metadata{"hello": "kratos", "env": "dev"},
+			args: args{Metadata{}, Metadata{"hello": {"kratos"}, "env": {"dev"}}},
+			want: Metadata{"hello": {"kratos"}, "env": {"dev"}},
 		},
 		{
 			name: "hello",
-			args: args{Metadata{"hi": "https://go-kratos.dev/"}, Metadata{"hello": "kratos", "env": "dev"}},
-			want: Metadata{"hello": "kratos", "env": "dev", "hi": "https://go-kratos.dev/"},
+			args: args{Metadata{"hi": {"https://go-kratos.dev/"}}, Metadata{"hello": {"kratos"}, "env": {"dev"}}},
+			want: Metadata{"hello": {"kratos"}, "env": {"dev"}, "hi": {"https://go-kratos.dev/"}},
 		},
 	}
 	for _, tt := range tests {
@@ -265,19 +337,19 @@ func TestMergeToClientContext(t *testing.T) {
 }
 
 func TestMetadata_Range(t *testing.T) {
-	md := Metadata{"kratos": "kratos", "https://go-kratos.dev/": "https://go-kratos.dev/", "go-kratos": "go-kratos"}
+	md := Metadata{"kratos": {"kratos"}, "https://go-kratos.dev/": {"https://go-kratos.dev/"}, "go-kratos": {"go-kratos"}}
 	tmp := Metadata{}
-	md.Range(func(k, v string) bool {
+	md.Range(func(k string, v []string) bool {
 		if k == "https://go-kratos.dev/" || k == "kratos" {
 			tmp[k] = v
 		}
 		return true
 	})
-	if !reflect.DeepEqual(tmp, Metadata{"https://go-kratos.dev/": "https://go-kratos.dev/", "kratos": "kratos"}) {
-		t.Errorf("metadata = %v, want %v", tmp, Metadata{"https://go-kratos.dev/": "https://go-kratos.dev/", "kratos": "kratos"})
+	if !reflect.DeepEqual(tmp, Metadata{"https://go-kratos.dev/": {"https://go-kratos.dev/"}, "kratos": {"kratos"}}) {
+		t.Errorf("metadata = %v, want %v", tmp, Metadata{"https://go-kratos.dev/": {"https://go-kratos.dev/"}, "kratos": {"kratos"}})
 	}
 	tmp = Metadata{}
-	md.Range(func(k, v string) bool {
+	md.Range(func(k string, v []string) bool {
 		return false
 	})
 	if !reflect.DeepEqual(tmp, Metadata{}) {
@@ -293,13 +365,13 @@ func TestMetadata_Clone(t *testing.T) {
 	}{
 		{
 			name: "kratos",
-			m:    Metadata{"kratos": "kratos", "https://go-kratos.dev/": "https://go-kratos.dev/", "go-kratos": "go-kratos"},
-			want: Metadata{"kratos": "kratos", "https://go-kratos.dev/": "https://go-kratos.dev/", "go-kratos": "go-kratos"},
+			m:    Metadata{"kratos": {"kratos"}, "https://go-kratos.dev/": {"https://go-kratos.dev/"}, "go-kratos": {"go-kratos"}},
+			want: Metadata{"kratos": {"kratos"}, "https://go-kratos.dev/": {"https://go-kratos.dev/"}, "go-kratos": {"go-kratos"}},
 		},
 		{
 			name: "go",
-			m:    Metadata{"language": "golang"},
-			want: Metadata{"language": "golang"},
+			m:    Metadata{"language": {"golang"}},
+			want: Metadata{"language": {"golang"}},
 		},
 	}
 	for _, tt := range tests {
@@ -308,7 +380,7 @@ func TestMetadata_Clone(t *testing.T) {
 			if !reflect.DeepEqual(got, tt.want) {
 				t.Errorf("Clone() = %v, want %v", got, tt.want)
 			}
-			got["kratos"] = "go"
+			got["kratos"] = []string{"go"}
 			if reflect.DeepEqual(got, tt.want) {
 				t.Errorf("want got != want got %v want %v", got, tt.want)
 			}
diff --git a/middleware/auth/jwt/jwt_test.go b/middleware/auth/jwt/jwt_test.go
index 711757bc..dc2cc9e0 100644
--- a/middleware/auth/jwt/jwt_test.go
+++ b/middleware/auth/jwt/jwt_test.go
@@ -24,6 +24,8 @@ func (hc headerCarrier) Get(key string) string { return http.Header(hc).Get(key)
 
 func (hc headerCarrier) Set(key string, value string) { http.Header(hc).Set(key, value) }
 
+func (hc headerCarrier) Add(key string, value string) { http.Header(hc).Add(key, value) }
+
 // Keys lists the keys stored in this carrier.
 func (hc headerCarrier) Keys() []string {
 	keys := make([]string, 0, len(hc))
@@ -33,6 +35,11 @@ func (hc headerCarrier) Keys() []string {
 	return keys
 }
 
+// Values returns a slice value associated with the passed key.
+func (hc headerCarrier) Values(key string) []string {
+	return http.Header(hc).Values(key)
+}
+
 func newTokenHeader(headerKey string, token string) *headerCarrier {
 	header := &headerCarrier{}
 	header.Set(headerKey, token)
diff --git a/middleware/metadata/metadata_test.go b/middleware/metadata/metadata_test.go
index b083f9c1..9e9436d7 100644
--- a/middleware/metadata/metadata_test.go
+++ b/middleware/metadata/metadata_test.go
@@ -17,6 +17,8 @@ func (hc headerCarrier) Get(key string) string { return http.Header(hc).Get(key)
 
 func (hc headerCarrier) Set(key string, value string) { http.Header(hc).Set(key, value) }
 
+func (hc headerCarrier) Add(key string, value string) { http.Header(hc).Add(key, value) }
+
 // Keys lists the keys stored in this carrier.
 func (hc headerCarrier) Keys() []string {
 	keys := make([]string, 0, len(hc))
@@ -26,6 +28,11 @@ func (hc headerCarrier) Keys() []string {
 	return keys
 }
 
+// Values returns a slice value associated with the passed key.
+func (hc headerCarrier) Values(key string) []string {
+	return http.Header(hc).Values(key)
+}
+
 type testTransport struct{ header headerCarrier }
 
 func (tr *testTransport) Kind() transport.Kind            { return transport.KindHTTP }
@@ -123,11 +130,11 @@ func TestClient(t *testing.T) {
 
 func TestWithConstants(t *testing.T) {
 	md := metadata.Metadata{
-		constKey: constValue,
+		constKey: {constValue},
 	}
 	options := &options{
 		md: metadata.Metadata{
-			"override": "override",
+			"override": {"override"},
 		},
 	}
 
diff --git a/middleware/selector/selector_test.go b/middleware/selector/selector_test.go
index f12943e4..0835ef84 100644
--- a/middleware/selector/selector_test.go
+++ b/middleware/selector/selector_test.go
@@ -40,15 +40,23 @@ func (tr *Transport) ReplyHeader() transport.Header {
 }
 
 type mockHeader struct {
-	m map[string]string
+	m map[string][]string
 }
 
 func (m *mockHeader) Get(key string) string {
-	return m.m[key]
+	vals := m.m[key]
+	if len(vals) > 0 {
+		return vals[0]
+	}
+	return ""
 }
 
 func (m *mockHeader) Set(key, value string) {
-	m.m[key] = value
+	m.m[key] = []string{value}
+}
+
+func (m *mockHeader) Add(key, value string) {
+	m.m[key] = append(m.m[key], value)
 }
 
 func (m *mockHeader) Keys() []string {
@@ -59,6 +67,10 @@ func (m *mockHeader) Keys() []string {
 	return keys
 }
 
+func (m *mockHeader) Values(key string) []string {
+	return m.m[key]
+}
+
 func TestMatch(t *testing.T) {
 	tests := []struct {
 		name string
@@ -185,28 +197,28 @@ func TestHeaderFunc(t *testing.T) {
 			name: "/hello.Update/world",
 			ctx: transport.NewServerContext(context.Background(), &Transport{
 				operation: "/hello.Update/world",
-				headers:   &mockHeader{map[string]string{"X-Test": "test"}},
+				headers:   &mockHeader{map[string][]string{"X-Test": {"test"}}},
 			}),
 		},
 		{
 			name: "/hi.Create/world",
 			ctx: transport.NewServerContext(context.Background(), &Transport{
 				operation: "/hi.Create/world",
-				headers:   &mockHeader{map[string]string{"X-Test": "test2", "go-kratos": "kratos"}},
+				headers:   &mockHeader{map[string][]string{"X-Test": {"test2"}, "go-kratos": {"kratos"}}},
 			}),
 		},
 		{
 			name: "/test.Name/1234",
 			ctx: transport.NewServerContext(context.Background(), &Transport{
 				operation: "/test.Name/1234",
-				headers:   &mockHeader{map[string]string{"X-Test": "test3"}},
+				headers:   &mockHeader{map[string][]string{"X-Test": {"test3"}}},
 			}),
 		},
 		{
 			name: "/go-kratos.dev/kratos",
 			ctx: transport.NewServerContext(context.Background(), &Transport{
 				operation: "/go-kratos.dev/kratos",
-				headers:   &mockHeader{map[string]string{"X-Test": "test"}},
+				headers:   &mockHeader{map[string][]string{"X-Test": {"test"}}},
 			}),
 		},
 	}
diff --git a/middleware/tracing/tracing_test.go b/middleware/tracing/tracing_test.go
index b95771ab..72740baf 100644
--- a/middleware/tracing/tracing_test.go
+++ b/middleware/tracing/tracing_test.go
@@ -29,6 +29,11 @@ func (hc headerCarrier) Set(key string, value string) {
 	http.Header(hc).Set(key, value)
 }
 
+// Add value to the key-value pair.
+func (hc headerCarrier) Add(key string, value string) {
+	http.Header(hc).Add(key, value)
+}
+
 // Keys lists the keys stored in this carrier.
 func (hc headerCarrier) Keys() []string {
 	keys := make([]string, 0, len(hc))
@@ -38,6 +43,11 @@ func (hc headerCarrier) Keys() []string {
 	return keys
 }
 
+// Values returns a slice value associated with the passed key.
+func (hc headerCarrier) Values(key string) []string {
+	return http.Header(hc).Values(key)
+}
+
 type mockTransport struct {
 	kind      transport.Kind
 	endpoint  string