caseyamcl / guzzle_retry_middleware

Middleware for Guzzle v6/7+ that automatically retries HTTP requests on 429, 503 responses.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Change some headers on each retry to reflect date and time and request signature

stoufa06 opened this issue · comments

Detailed description

I have used this package to resolve a problem of some failed requests but i got other errors when when retrying to send same request to garmin api server using League\OAuth1 package.
Make it clear if the issue is a bug, an enhancement or just a question.
This is probably not an issue for most of users but it is very important to have a way to change request headers when retying for many seconds if the api requires timestamp and signature to be up to date for authentication.

Received error [{
  "message" : "Invalid nonce and timestamp",
  "error" : "The request has not been applied because it lacks valid authentication credentials for the target resource."
}

The oauth_nonce and oauth_timestamp and oauth_signature headers needs to be updated for each request

    /**
     * Get the base protocol parameters for an OAuth request.
     * Each request builds on these parameters.
     *
     * @see OAuth 1.0 RFC 5849 Section 3.1
     */
    protected function baseProtocolParameters(): array
    {
        $dateTime = new DateTime('now', new DateTimeZone('UTC'));

        return [
            'oauth_consumer_key' => $this->clientCredentials->getIdentifier(),
            'oauth_nonce' => $this->nonce(),
            'oauth_signature_method' => $this->signature->method(),
            'oauth_timestamp' => $dateTime->format('U'),
            'oauth_version' => '1.0',
        ];
    }
    /**
     * Generate the OAuth protocol header for requests other than temporary
     * credentials, based on the URI, method, given credentials & body query
     * string.
     * 
     * @param string $method
     * @param string $uri
     * @param CredentialsInterface $credentials
     * @param array $bodyParameters
     * @return string
     */
    protected function protocolHeader(string $method, string $uri, CredentialsInterface $credentials, array $bodyParameters = array()): string
    {
        $parameters = array_merge(
            $this->baseProtocolParameters(),
            $this->additionalProtocolParameters(),
            array(
                'oauth_token' => $credentials->getIdentifier(),

            ),
            $bodyParameters
        );
        $this->signature->setCredentials($credentials);

        $parameters['oauth_signature'] = $this->signature->sign(
            $uri,
            array_merge($parameters, $bodyParameters),
            $method
        );

        return $this->normalizeProtocolParameters($parameters);
    }

Hi @stoufa06, I think this is a great use-case for the on-retry callback.

$listener = function($attemptNumber, $delay, &$request, &$options, $response) use (/* whatever */) {
    $request = $request->withHeader('oauth_nonce', '[NONCE_VALUE]')
        ->withHeader('oauth_signature', '[SIGNATURE_VALUE]')
        ->withHeader('oauth_timestamp', [TIMESTAMP_HERE]');
}

$client = new \GuzzleHttp\Client([
    'on_retry_callback' => $listener
    // other options as desired...
]);

// Send your requests here!