opensearch-project / opensearch-php

Official PHP Client for OpenSearch
Other
105 stars 58 forks source link

[FEATURE] Allow useing Sigv4 implementation from AsyncAWS #111

Open shyim opened 1 year ago

shyim commented 1 year ago

Is your feature request related to a problem?

Official AWS SDK has a size of 37 MB and AsyncAWS only 1,4 MB

What solution would you like?

Allow to use also the SigV4 logic from AsyncAWS, so the users are not forced to install the big AWS SDK

jonkerw85 commented 1 year ago

@shyim Might be a good idea.

I use the removeUnusedServices documented here https://github.com/aws/aws-sdk-php/tree/master/src/Script/Composer from the official PHP SDK.

shyim commented 1 year ago

Yes.

I would try to make it independent. After Looking into AsyncAWS. The Logic is not so complicated 😅 .

So like Allow the Configuration object from

So it's completely independent. Have currently no time to work on it and my AWS credits left out 😅 . Will try to work on that next year :)

shyim commented 1 year ago
use AsyncAws\Core\Configuration;
use AsyncAws\Core\Credentials\ChainProvider;
use AsyncAws\Core\Request;
use AsyncAws\Core\RequestContext;
use AsyncAws\Core\Signer\SignerV4;
use AsyncAws\Core\Stream\StringStream;
use OpenSearch\ClientBuilder;

class AsyncAwsSigner
{
    /**
     * @var callable
     */
    private $wrappedHandler;

    private \AsyncAws\Core\Credentials\CredentialProvider $credentialProvider;

    public function __construct(private readonly Configuration $configuration, ?callable $wrappedHandler = null)
    {
        $this->credentialProvider = ChainProvider::createDefaultChain();
        $this->wrappedHandler = $wrappedHandler ?? ClientBuilder::defaultHandler();
    }

    public function __invoke(array $request)
    {
        $transformed = $this->transformRequest($request);

        $signer = new SignerV4('aoss', 'eu-central-1');
        $credentials = $this->credentialProvider->getCredentials($this->configuration);

        if ($credentials === null) {
            throw new \RuntimeException('Could not get credentials');
        }

        $signer->sign($transformed, $credentials, new RequestContext());

        $request['headers'] = [];

        foreach ($transformed->getHeaders() as $key => $value) {
            $request['headers'][$key] = [$value];
        }

        return call_user_func($this->wrappedHandler, $request);
    }

    private function transformRequest(array $ringPhpRequest): Request
    {
        // fix for uppercase 'Host' array key in elasticsearch-php 5.3.1 and backward compatible
        // https://github.com/aws/aws-sdk-php/issues/1225
        $hostKey = isset($ringPhpRequest['headers']['Host']) ? 'Host' : 'host';

        // Amazon ES/OS listens on standard ports (443 for HTTPS, 80 for HTTP).
        // Consequently, the port should be stripped from the host header.
        $parsedUrl = parse_url($ringPhpRequest['headers'][$hostKey][0]);
        if (isset($parsedUrl['host'])) {
            $ringPhpRequest['headers'][$hostKey][0] = $parsedUrl['host'];
        }

        parse_str($ringPhpRequest['query_string'] ?? '', $query);

        $headers = [];

        foreach ($ringPhpRequest['headers'] as $key => $value) {
            $headers[$key] = $value[0];
        }

        $url = $ringPhpRequest['scheme'] . '://' . $ringPhpRequest['headers'][$hostKey][0] . $ringPhpRequest['uri'];

        $request = new Request(
            $ringPhpRequest['http_method'],
            $url,
            $query,
            $headers,
            StringStream::create($ringPhpRequest['body'] ?? ''),
        );
        $request->setEndpoint($url);

        return $request;
    }
}

POC partically working