Repository navigation
Cover the whole Consul CE HTTP API - #70
Merged
Merged
Conversation
Add the missing /v1/agent endpoints, whitelist documented options, and fix join() and forceLeave() which used GET while Consul only accepts PUT.
Add connect(), nodeServices() and gatewayServices(), and whitelist the documented options.
Add connect() and ingress(), and whitelist the documented options.
Allow blocking queries (index, wait) on get() and check-and-set (cas) on delete().
Allow blocking queries and consistency modes on info(), node() and all().
Consul expects multi-valued parameters to be repeated (?tag=a&tag=b), but Symfony HttpClient encodes arrays PHP-style (?tag[0]=a&tag[1]=b).
nicolas-grekas
added a commit
to symfony/symfony
that referenced
this pull request
Sep 28, 2026
… `body` options (lyrixx) This PR was merged into the 8.2 branch. Discussion ---------- [HttpClient] Allow repeating fields in the `query` and `body` options | Q | A | ------------- | --- | Branch? | 8.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | - | License | MIT Many HTTP APIs expect multi-valued query parameters to be sent as repeated keys (`?name=a&name=b`), not PHP-style (`?name[0]=a&name[1]=b`). It is the default serialization of arrays in OpenAPI 3 (`style: form`, `explode: true`), and what Go's `net/url`, Python's `requests` and many others produce. Today, the `query` option can't express this, and neither can array bodies: arrays always go through `http_build_query()`. The only way is to build the URL by hand, which defeats the purpose of the option (and of the default `query` of scoped clients). This has been discussed twice already: #33488 and #53489 (`flatten_query_string`). Both were closed because adding an *option* to switch the encoding is not the right approach. In #53489, `@nicolas`-grekas suggested another way ([comment](#53489 (comment))): > What we need to solve is the representation of arbitrary query strings as arrays that we can serialize to query strings. [...] > ```php > $query = [ > ['foo', 'bar'], > ['foo', 'baz'], > ]; > ``` > Because the root array passes array_is_list, we could decide to serialize it using something else than http_build_query. > We'd need to define something natural that's safe from mistakes as much as possible. E.g I would forbid nested arrays in this representation. This PR implements that idea, writing each pair as a single-entry array, the convention `FormDataPart` already uses for repeated form fields. No new option, no change in the contracts: ```php $client->request('GET', 'http://127.0.0.1:8500/v1/connect/intentions/match', [ 'query' => [['by' => 'destination'], ['name' => 'web'], ['name' => 'api']], ]); // GET /v1/connect/intentions/match?by=destination&name=web&name=api $client->request('POST', $url, [ 'body' => [['tag' => 'a'], ['tag' => 'b'], ['file' => fopen('a.pdf', 'r')], ['file' => fopen('b.pdf', 'r')]], ]); ``` I need this for the [Consul PHP SDK](https://github.com/FriendsOfPHP/consul-php-sdk), which is built on top of HttpClient. The [Consul HTTP API](https://developer.hashicorp.com/consul/api-docs) uses repeated keys, e.g. `name` on [`/v1/connect/intentions/match`](https://developer.hashicorp.com/consul/api-docs/connect/intentions#list-matching-intentions) ("You can repeat this parameter for batching multiple matches"), or `node-meta` on the health endpoints. The SDK currently has to take multi-valued parameters out of the `query` option and append them to the URL by hand ([code](FriendsOfPHP/consul-php-sdk#70)). How it works: * When `query` or `body` is a list whose first item is an array, each item must be an array with a single `name => value` entry, encoded with the same escaping as today; string keys can't be mixed in the same array (`['by' => 'x', ['name' => 'web']]` keeps the associative meaning); * Names can be repeated; brackets must be explicit (`['tag[]' => 'a']` gives `tag[]=a`); * Values must be scalars, `\Stringable`, `\BackedEnum` or `null`, plus streams in bodies (to upload several files under the same name); anything else, including nested arrays, throws an `InvalidArgumentException`; * A `null` value removes the parameter from the URL, like with the associative form, and is skipped in bodies; * A name passed in the option replaces all the occurrences of that name in the URL, and default `query` options still don't override the request ones (it works with `ScopingHttpClient` too); in the URL, values that share a name are grouped where the name first appears (`[['a' => 1], ['b' => 2], ['a' => 3]]` gives `a=1&a=3&b=2`), while bodies keep the order of the list. The associative form is unchanged. What changes is lists whose first item is an array: `[['a' => 'b']]` used to be encoded as `0[a]=b`, and shapes like `[[1, 2, 3]]` now throw. Since `parse_str()` and `$request->query->all()` can produce such lists (`?0[a]=b`), forwarded input should be filtered with an allow-list, not by removing keys. Commits ------- 34e5866 [HttpClient] Allow repeating fields in the `query` and `body` options
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds all the Consul Community Edition (v2.0.x) HTTP API endpoints to the SDK. It can be reviewed commit by commit.
New services
ACL,Config,Connect(CA + intentions),Coordinate,DiscoveryChain,Event,Operator(raft, keyring, autopilot, usage),Peering(+ exported / imported services),PreparedQuery,SnapshotandStatus.Existing services
Agent,CatalogandHealthnow cover all their endpointsfilter, ...) inAgent,Catalog,Health,KVandSessionAgent::join()andAgent::forceLeave(): they sentGET, Consul only acceptsPUT(405)No BC break: nothing is removed nor renamed, only new optional arguments.
Client
CONSUL_HTTP_TOKENenvironment variable (like the Consul CLI); an explicitX-Consul-Tokenheader wins['tag' => ['v1', 'primary']]is sent as?tag=v1&tag=primary(Symfony HttpClient would send?tag[0]=v1&tag[1]=primary, which Consul does not understand)dcoption is allowed on all endpoints, except the ones local to the agent, the datacenter listings, and the catalog (de)registration (where it goes in the payload). A test checks every method forwards it.Out of scope
ns,partition, operator area / license / segment / audit / utilization, OIDC...)POST /v1/connect/intentions)/v1/agent/monitor,/v1/agent/metrics/stream)/v1/internal/*Tests & tooling
default_policy = allow,rootmanagement token)MockHttpClient