Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function __construct(string $sdkKey, array $options = [])
$this->_curl = $options['curl'];
}

$this->_eventHeaders = Util::eventHeaders($sdkKey, $options['application_info'] ?? null);
$this->_eventHeaders = Util::eventHeaders($sdkKey, $options);
$this->_connectTimeout = $options['connect_timeout'];
$this->_isWindows = PHP_OS_FAMILY == 'Windows';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function __construct(string $sdkKey, array $options = [])
$this->_eventsUri = \LaunchDarkly\Impl\Util::adjustBaseUri($baseUri);

$this->_requestOptions = [
'headers' => Util::eventHeaders($this->_sdkKey, $options['application_info'] ?? null),
'headers' => Util::eventHeaders($this->_sdkKey, $options),
'timeout' => $options['timeout'],
'connect_timeout' => $options['connect_timeout']
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function __construct(string $baseUri, string $sdkKey, array $options)
}

$defaults = [
'headers' => Util::defaultHeaders($sdkKey, $options['application_info'] ?? null),
'headers' => Util::defaultHeaders($sdkKey, $options),
'timeout' => $options['timeout'],
'connect_timeout' => $options['connect_timeout'],
'handler' => $stack,
Expand Down
19 changes: 14 additions & 5 deletions src/LaunchDarkly/Impl/Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ public static function makeNullLogger(): LoggerInterface
* made to LaunchDarkly servers.
*
* @param string $sdkKey
* @param ApplicationInfo|null $applicationInfo
* @params array<string, mixed> $options
* @return array<string, string>
*/
public static function defaultHeaders(string $sdkKey, $applicationInfo): array
public static function defaultHeaders(string $sdkKey, array $options): array
{
$headers = [
'Content-Type' => 'application/json',
Expand All @@ -104,13 +104,22 @@ public static function defaultHeaders(string $sdkKey, $applicationInfo): array
'User-Agent' => 'PHPClient/' . LDClient::VERSION,
];

$applicationInfo = $options['application_info'] ?? null;
if ($applicationInfo instanceof ApplicationInfo) {
$headerValue = (string) $applicationInfo;
if ($headerValue) {
$headers['X-LaunchDarkly-Tags'] = $headerValue;
}
}

if (!empty($options['wrapper_name'])) {
$headers['X-LaunchDarkly-Wrapper'] = $options['wrapper_name'];

if (!empty($options['wrapper_version'])) {
$headers['X-LaunchDarkly-Wrapper'] .= '/' . $options['wrapper_version'];
}
}

return $headers;
}

Expand All @@ -119,12 +128,12 @@ public static function defaultHeaders(string $sdkKey, $applicationInfo): array
* made to the LaunchDarkly Events API.
*
* @param string $sdkKey
* @param ApplicationInfo|null $applicationInfo
* @param array<string, mixed> $options
* @return array
*/
public static function eventHeaders(string $sdkKey, $applicationInfo): array
public static function eventHeaders(string $sdkKey, array $options): array
{
$headers = Util::defaultHeaders($sdkKey, $applicationInfo);
$headers = Util::defaultHeaders($sdkKey, $options);
$headers['X-LaunchDarkly-Event-Schema'] = EventPublisher::CURRENT_SCHEMA_VERSION;
// Only the presence of this header is important. We encode a string
// value of 'true' to ensure it isn't dropped along the way.
Expand Down
2 changes: 2 additions & 0 deletions src/LaunchDarkly/LDClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ class LDClient
* with this configuration active will have attributes with these names removed. You can also set private attributes on a
* - `application_info`: An optional {@see \LaunchDarkly\Types\ApplicationInfo} instance.
* per-user basis in the LDContext builder.
* - `wrapper_name`: For use by wrapper libraries to set an identifying name for the wrapper being used. This will be sent in User-Agent headers during requests to the LaunchDarkly servers to allow recording metrics on the usage of these wrapper libraries.
* - `wrapper_version`: For use by wrapper libraries to report the version of the library in use. If `wrapper_name` is not set, this field will be ignored. Otherwise the version string will be included in the User-Agent headers along with the `wrapper_name` during requests to the LaunchDarkly servers.
* - Other options may be available depending on any features you are using from the `LaunchDarkly\Integrations` namespace.
*
* @return LDClient
Expand Down
70 changes: 70 additions & 0 deletions tests/Impl/Integrations/GuzzleFeatureRequesterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,74 @@ public function testSendsCorrectHeaders(): void
$this->assertEquals('PHPClient/' . LDClient::VERSION, $headers['User-Agent']);
$this->assertEquals('application-id/my-id application-version/my-version', $headers['X-LaunchDarkly-Tags']);
}

public function wrapperProvider(): array
{
return [
[null, null, null],
['my-wrapper', null, 'my-wrapper'],
['my-wrapper', '1.0.0', 'my-wrapper/1.0.0'],
[null, '1.0.0', null],
];
}

/**
* @dataProvider wrapperProvider
*/
public function testSendsCorrectWrapperNameHeaders(?string $wrapper_name, ?string $wrapper_version, ?string $expected_header): void
{
/** @var LoggerInterface **/
$logger = $this->getMockBuilder(LoggerInterface::class)->getMock();

$config = [
'logger' => $logger,
'timeout' => 3,
'connect_timeout' => 3,
];

if ($wrapper_name) {
$config['wrapper_name'] = $wrapper_name;
}
if ($wrapper_version) {
$config['wrapper_version'] = $wrapper_version;
}

$requester = new GuzzleFeatureRequester('http://localhost:8080', 'sdk-key', $config);
$requester->getFeature("flag-key");

$requests = [];
$client = new Client();

// Provide time for the curl to execute
$start = time();
while (time() - $start < 5) {
$response = $client->request('GET', 'http://localhost:8080/__admin/requests');
$body = json_decode($response->getBody()->getContents(), true);
$requests = $body['requests'];

if ($requests) {
break;
}
usleep(100);
}

if (!$requests) {
$this->fail("Unable to connect to endpoint within specified timeout");
}

$this->assertCount(1, $requests);

$request = $requests[0]['request'];

// Validate that we hit the right endpoint
$this->assertEquals('/sdk/flags/flag-key', $request['url']);

// And validate that we provided all the correct headers
$headers = $request['headers'];
if ($expected_header) {
$this->assertEquals($expected_header, $headers['X-LaunchDarkly-Wrapper']);
} else {
$this->assertNotContains('X-LaunchDarkly-Wrapper', $headers);
}
}
}