diff --git a/src/ManticoreSearch/Client.php b/src/ManticoreSearch/Client.php old mode 100755 new mode 100644 index 059d281..957e6c2 --- a/src/ManticoreSearch/Client.php +++ b/src/ManticoreSearch/Client.php @@ -51,6 +51,9 @@ class Client { /** @var ?string $authToken */ protected ?string $authToken = null; + /** @var ?string $delegatedUser */ + protected ?string $delegatedUser = null; + /** @var string $buddyVersion */ protected string $buddyVersion; @@ -90,6 +93,21 @@ function () { $this->clientMap = new Map; } + /** + * Ensure request-scoped cloned clients do not share mutable internals. + * @return void + */ + public function __clone() { + $this->connectionPool = new ConnectionPool( + function () { + $client = new HttpClient($this->host, $this->port); + $client->set(['timeout' => -1]); + return $client; + } + ); + $this->clientMap = new Map; + } + /** * Set server URL of Manticore searchd to send requests to * @param string $url it supports http:// prefixed and not @@ -121,6 +139,30 @@ public function getServerUrl(): string { return static::URL_PREFIX . $this->host . ':' . $this->port; } + /** + * Set delegated user context for outgoing daemon requests. + * + * Context is coroutine-scoped when running in coroutine mode. + * In sync mode it falls back to the instance-local value. + * + * @param ?string $user + * @return static + */ + public function setDelegatedUser(?string $user): static { + $normalizedUser = $user ? trim($user) : null; + // Empty string or null should be null + $this->delegatedUser = $normalizedUser ?: null; + return $this; + } + + /** + * Clear delegated user context from current request scope. + * @return static + */ + public function clearDelegatedUser(): static { + return $this->setDelegatedUser(null); + } + /** * Send the request where request represents the SQL query to be send * @param string $request @@ -134,7 +176,7 @@ public function sendRequest( string $request, ?string $path = null, bool $disableAgentHeader = false, - string $requestMethod = 'POST' + string $requestMethod = 'POST', ): Response { $t = microtime(true); if ($request === '') { @@ -168,6 +210,15 @@ public function sendRequest( 'Content-Type' => $contentTypeHeader, 'User-Agent' => $userAgentHeader, ]; + + $delegatedUser = $this->resolveDelegatedUser(); + $delegatedUserLog = $delegatedUser ?? ''; + Buddy::debug("delegated user for daemon request on /{$path}: {$delegatedUserLog}"); + + if ($delegatedUser !== null) { + $headers['X-Manticore-User'] = $delegatedUser; + } + // Add authorization header if we have token if (isset($this->authToken)) { $headers['Authorization'] = "Bearer {$this->authToken}"; @@ -254,9 +305,24 @@ public function sendRequestToUrl( bool $disableAgentHeader = false ): Response { $client = $this->getClientForUrl($url); + + $delegatedUser = $this->resolveDelegatedUser(); + if ($delegatedUser === null) { + $client->clearDelegatedUser(); + } else { + $client->setDelegatedUser($delegatedUser); + } + return $client->sendRequest($request, $path, $disableAgentHeader); } + /** + * @return ?string + */ + protected function resolveDelegatedUser(): ?string { + return $this->delegatedUser; + } + /** * @param string $url * @return Client