restclient.module in RESTClient 7.2
Defines a standard REST interface to RESTful services
File
restclient.moduleView source
<?php
/**
* @file
* Defines a standard REST interface to RESTful services
*/
// List of possible libraries to use with the RESTClient
// Default is 'curl', but you can also use 'drupal' as an option to use
// drupal_http_request instead (some features are unavailable when 'drupal'
// is set)
define('RESTCLIENT_LIBRARY_CURL', 'curl');
define('RESTCLIENT_LIBRARY_DRUPAL', 'drupal');
define('RESTCLIENT_FILES_SYSTEM', 'file');
// Response code classes
define('RESTCLIENT_RESPONSE_INFORMATIONAL', '1');
define('RESTCLIENT_RESPONSE_SUCCESS', '2');
define('RESTCLIENT_RESPONSE_REDIRECTION', '3');
define('RESTCLIENT_RESPONSE_CLIENT_ERROR', '4');
define('RESTCLIENT_RESPONSE_SERVER_ERROR', '5');
// Store the desired library to use with the RESTClient functions
define('RESTCLIENT_ACTIVE_LIBRARY', variable_get('restclient_active_library', RESTCLIENT_LIBRARY_DRUPAL));
/**
* Implements hook_permission().
*/
function restclient_permission() {
return array(
'administer restclient' => array(
'title' => t('Administer REST Client'),
'description' => t('Perform administration tasks for REST Client.'),
),
);
}
/**
* Implements hook_flush_caches().
*/
function restclient_flush_caches() {
return array(
'cache_restclient',
);
}
/**
* Implements hook_menu().
*/
function restclient_menu() {
$items = array();
// Administrative links
$items['admin/config/services/restclient'] = array(
'description' => 'Configure a location to make REST calls',
'title' => 'REST Client',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'restclient_admin_settings',
),
'access arguments' => array(
'administer restclient',
),
'file' => 'restclient.admin.inc',
);
return $items;
}
/**
* Analyze the response code from a request
*
* @param object $response [reference]
* Response object
* @return string
* Returns the class of response code, FALSE otherwise.
*/
function restclient_response_code(&$response) {
// Classify the response code
if (isset($response->code)) {
if (intval($response->code) >= 100) {
switch (intval(floor(intval($response->code) / 100))) {
case 1:
return RESTCLIENT_RESPONSE_INFORMATIONAL;
case 2:
return RESTCLIENT_RESPONSE_SUCCESS;
case 3:
return RESTCLIENT_RESPONSE_REDIRECTION;
case 4:
return RESTCLIENT_RESPONSE_CLIENT_ERROR;
case 5:
return RESTCLIENT_RESPONSE_SERVER_ERROR;
default:
return FALSE;
}
}
}
return FALSE;
}
/**
* Implements hook_wsconfig_connector_info().
*/
function restclient_wsconfig_connector_info() {
return array(
'restclient' => array(
'name' => 'REST Client',
'class' => 'restclient_wsconnector',
),
);
}
/**
* Make a GET request
*
* @param string resource_path
* The path to the REST method.
*
* You can include wildcards in the string
* which will be filled in by the $parameters array.
* Ex: /Courses/%session
*
* @param array $variables
* An array of variables with the following keys:
*
* endpoint [optional] : URL or hostname to the REST root. Specify a value to override the default
* configuration
*
* parameters [optional] : Key/value pairs of parameters to inject into the resource path to replace dynamic values
* Ex: array('%session' => 20111)
*
* query [optional] : Key/value pairs of query string parameters
* Ex: array('personid' => 2896263)
*
* headers [optional] : Key/value pairs of extra header data to include in the request
*
* retry [optional] : Number of retries. Defaults to 3
*
* timeout [optional] : Timeout in seconds. Defaults to 30
*
* reset [optional] : Boolean flag to reset the cache. Defaults to FALSE.
*
* authentication [optional] : Array ...
* - oauth2_client: Authenticate using OAuth. Contains an array of values to pass through so oauth2_client creates the client. May contain just ['name'] to look up an existing client by name. See oauth2_client documentation.
* - oauth_format [optional]: The format for the Authorization request header to accommodate different server implementations. The default format is 'Bearer :token' where :token is replaced with the OAuth token.
*
* @return object
* Returns an object containing the response data, FALSE otherwise.
*/
function restclient_get($resource_path, $variables = array()) {
return _restclient_request($resource_path, $variables, 'GET');
}
/**
* Make a POST request
*
* @param string resource_path
* The path to the REST method.
*
* You can include wildcards in the string
* which will be filled in by the $parameters array.
* Ex: /Courses/%session
*
* @param array $variables
* An array of variables with the following keys:
*
* endpoint [optional] : URL or hostname to the REST root. Specify a value to override the default
* configuration
*
* parameters [optional] : Key/value pairs of parameters to inject into the resource path
* Ex: array('%session' => 20111)
*
* headers [optional] : Key/value pairs of extra header data to include in the request
*
* data [optional] : Key/value pairs of data to include in the request body of the request
*
* body [optional] : Raw body data to include in the post body. This will be sent as is.
*
* multipart [optional] : Whether to treat your data as a multipart request
* If TRUE, your data should be formatted as follows
* 'data' => array('fid' => $fid, 'some text', 'some more text')
*
* The $fid value is a Drupal file id. A key of 'file' is required to denote
* file data so the API can treat it as a file accordingly.
*
* retry [optional] : Number of retries. Defaults to 3
*
* timeout [optional] : Timeout in seconds. Defaults to 30
*
* reset [optional] : Boolean flag to reset the cache. Defaults to FALSE.
*
* authentication [optional] : Array ...
* - oauth2_client: Authenticate using OAuth. Contains an array of values to pass through so oauth2_client creates the client. May contain just ['name'] to look up an existing client by name. See oauth2_client documentation.
* - oauth_format [optional]: The format for the Authorization request header to accommodate different server implementations. The default format is 'Bearer :token' where :token is replaced with the OAuth token.
*
* @return object
* Returns an object containing the response data, FALSE otherwise.
*
* @see http://en.wikipedia.org/wiki/POST_%28HTTP%29
* @see http://api.drupal.org/api/drupal/includes--common.inc/function/drupal_http_request
*/
function restclient_post($resource_path, $variables = array()) {
return _restclient_request_with_body($resource_path, $variables, 'POST');
}
/**
* Make a PUT request
*
* @param string resource_path
* The path to the REST method.
*
* You can include wildcards in the string
* which will be filled in by the $parameters array.
* Ex: /Courses/%session
*
* @param array $variables
* An array of variables with the following keys:
*
* endpoint [optional] : URL or hostname to the REST root. Specify a value to override the default
* configuration
*
* parameters [optional] : Key/value pairs of parameters to inject into the resource path
* Ex: array('%session' => 20111)
*
* headers [optional] : Key/value pairs of extra header data to include in the request
*
* data [optional] : Key/value pairs of data to include in the request body of the request
*
* body [optional] : Raw body data to include in the post body. This will be sent as is.
*
* multipart [optional] : Whether to treat your data as a multipart request
* If TRUE, your data should be formatted as follows
* 'data' => array('fid' => $fid, 'some text', 'some more text')
*
* The $fid value is a Drupal file id. A key of 'file' is required to denote
* file data so the API can treat it as a file accordingly.
*
* retry [optional] : Number of retries. Defaults to 3
*
* timeout [optional] : Timeout in seconds. Defaults to 30
*
* reset [optional] : Boolean flag to reset the cache. Defaults to FALSE.
*
* authentication [optional] : Array ...
* - oauth2_client: Authenticate using OAuth. Contains an array of values to pass through so oauth2_client creates the client. May contain just ['name'] to look up an existing client by name. See oauth2_client documentation.
* - oauth_format [optional]: The format for the Authorization request header to accommodate different server implementations. The default format is 'Bearer :token' where :token is replaced with the OAuth token.
*
* @return object
* Returns an object containing the response data, FALSE otherwise.
*
* @see http://en.wikipedia.org/wiki/POST_%28HTTP%29
* @see http://api.drupal.org/api/drupal/includes--common.inc/function/drupal_http_request
*/
function restclient_put($resource_path, $variables = array()) {
return _restclient_request_with_body($resource_path, $variables, 'PUT');
}
/**
* Make a DELETE request
*
* @param string resource_path
* The path to the REST method.
*
* You can include wildcards in the string
* which will be filled in by the $parameters array.
* Ex: /Courses/%session
*
* @param array $variables
* An array of variables with the following keys:
*
* endpoint [optional] : URL or hostname to the REST root. Specify a value to override the default
* configuration
*
* parameters [optional] : Key/value pairs of parameters to inject into the resource path to replace dynamic values
* Ex: array('%session' => 20111)
*
* query [optional] : Key/value pairs of query string parameters
* Ex: array('personid' => 2896263)
*
* headers [optional] : Key/value pairs of extra header data to include in the request
*
* retry [optional] : Number of retries. Defaults to 3
*
* timeout [optional] : Timeout in seconds. Defaults to 30
*
* reset [optional] : Boolean flag to reset the cache. Defaults to FALSE.
*
* authentication [optional] : Array ...
* - oauth2_client: Authenticate using OAuth. Contains an array of values to pass through so oauth2_client creates the client. May contain just ['name'] to look up an existing client by name. See oauth2_client documentation.
* - oauth_format [optional]: The format for the Authorization request header to accommodate different server implementations. The default format is 'Bearer :token' where :token is replaced with the OAuth token.
*
* @return object
* Returns an object containing the response data, FALSE otherwise.
*/
function restclient_delete($resource_path, $variables = array()) {
return _restclient_request($resource_path, $variables, 'DELETE');
}
/**
* Basic request with no body data.
*
* Compatible with GET, DELETE, OPTION, and TRACE requests.
*/
function _restclient_request(&$resource_path, &$variables = array(), $type = 'GET') {
// List of cacheable requests
$cacheable = array(
'GET',
'OPTION',
);
// Map variables
_restclient_map_variables($variables);
// Cache reset
$reset = $variables['reset'];
// Set the method
$variables['method'] = $type;
// Prepare the URL parameters
if (!empty($variables['parameters'])) {
_restclient_prepare_url_parameters($resource_path, $variables);
}
// Build the URL
$url = _restclient_build_resource_path($resource_path, $variables);
// We only cache certain requests
if (in_array($type, $cacheable)) {
// Generate the cache id
$cid = _restclient_generate_cid($type, $url, $variables['headers']);
// Check the cache
if (!$reset) {
$data = restclient_cache_get($variables, $cid);
if (!empty($data) and REQUEST_TIME < $data->expire) {
// Debug output
if (variable_get("restclient_debug", FALSE)) {
// Temporarily add the response to the variables for display
$variables['from_cache'] = TRUE;
$variables['response_from_cache'] = $data->data;
$variables['resource_path'] = $resource_path;
_restclient_debug($variables);
// Cleanup
unset($variables['from_cache']);
unset($variables['response_from_cache']);
unset($variables['resource_path']);
}
return $data->data;
}
}
}
// Authentication - done after checking the cache
if (!_restclient_prepare_authentication($variables)) {
// Already logged.
return FALSE;
}
// Make the HTTP request
switch (RESTCLIENT_ACTIVE_LIBRARY) {
case RESTCLIENT_FILES_SYSTEM:
module_load_include('inc', 'restclient', 'restclient.filemode');
$response = restclient_filemode_fetch_response($url, $variables);
break;
case RESTCLIENT_LIBRARY_CURL:
$response = chr_curl_http_request($url, $variables);
break;
case RESTCLIENT_LIBRARY_DRUPAL:
default:
$response = drupal_http_request($url, $variables);
}
// Debug output
if (variable_get("restclient_debug", FALSE)) {
// Temporarily add the response to the variables for display
$variables['from_cache'] = FALSE;
$variables['response'] = $response;
$variables['resource_path'] = $resource_path;
_restclient_debug($variables);
// Cleanup
unset($variables['from_cache']);
unset($variables['response']);
unset($variables['resource_path']);
}
if (variable_get("restclient_testing", FALSE)) {
if (RESTCLIENT_ACTIVE_LIBRARY != RESTCLIENT_FILES_SYSTEM) {
module_load_include('inc', 'restclient', 'restclient.filemode');
restclient_filemode_save_response($response, $url, $variables);
}
}
// Check the response
if (!isset($response->error)) {
if (in_array($type, $cacheable)) {
// Add default overrides if not set
if (!isset($variables['cache_default_time'])) {
$variables['cache_default_time'] = 0;
}
if (!isset($variables['cache_default_override'])) {
$variables['cache_default_override'] = FALSE;
}
restclient_cache_set($variables, $cid, $response);
}
// Log if the response code is Redirection
if (RESTCLIENT_RESPONSE_REDIRECTION == restclient_response_code($response)) {
_restclient_watchdog($type, $response->code, 'Redirection', $url, NULL, WATCHDOG_INFO);
}
// No error occured, return the response
return $response;
}
// Log the error
_restclient_watchdog($type, $response->code, $response->error, $url);
// Handle authentication (oauth, hybridauth) related errors. The request
// may have failed due to an expired token, in which case we can re-authenticate
// and retry this request.
$retry = _restclient_authentication_request_error($response, $variables);
if ($retry and empty($variables['auth_retry'])) {
$variables['auth_retry'] = TRUE;
return _restclient_request($resource_path, $variables, $type);
}
// Try returning a stale cache entry.
$stale = restclient_cache_get($variables, $cid, TRUE);
if ($stale) {
return $stale->data;
}
// If error handling is set, return the response anyway, otherwise return FALSE
if (isset($variables['error_handling']) and $variables['error_handling']) {
return $response;
}
return FALSE;
}
/**
* Requests with body data.
*
* Compatible with POST and PUT
*/
function _restclient_request_with_body(&$resource_path, &$variables = array(), $type = 'POST') {
// Map variables
_restclient_map_variables($variables);
// Prepare any URL parameters
if (!empty($variables['parameters'])) {
_restclient_prepare_url_parameters($resource_path, $variables);
}
// Set the method
$variables['method'] = $type;
// Append raw body data
if (!empty($variables['body'])) {
$variables['data'] = $variables['body'];
}
else {
// Prepare any data variables
_restclient_prepare_post_data($variables);
}
// Build the URL
$url = _restclient_build_resource_path($resource_path, $variables);
// Authentication
if (!_restclient_prepare_authentication($variables)) {
// Already logged.
return FALSE;
}
switch (RESTCLIENT_ACTIVE_LIBRARY) {
case RESTCLIENT_LIBRARY_CURL:
$response = chr_curl_http_request($url, $variables);
break;
case RESTCLIENT_LIBRARY_DRUPAL:
default:
$response = drupal_http_request($url, $variables);
}
// Debug output
if (variable_get("restclient_debug", FALSE)) {
// Temporarily add the response to the variables for display
$variables['response'] = $response;
$variables['resource_path'] = $resource_path;
_restclient_debug($variables);
// Cleanup
unset($variables['response']);
unset($variables['resource_path']);
}
// Log if the response code is Redirection
if (RESTCLIENT_RESPONSE_REDIRECTION == restclient_response_code($response)) {
_restclient_watchdog($type, $response->code, 'Redirection', $url, NULL, WATCHDOG_INFO);
}
if (!isset($response->error) || (isset($response->errno) and $response->errno == 0)) {
// No error occured, return the response
return $response;
}
// Log the error
_restclient_watchdog($type, $response->code, $response->error, $url, $response);
// Handle authentication (oauth, hybridauth) related errors. The request
// may have failed due to an expired token, in which case we can re-authenticate
// and retry this request.
$retry = _restclient_authentication_request_error($response, $variables);
if ($retry and empty($variables['auth_retry'])) {
$variables['auth_retry'] = TRUE;
return _restclient_request($resource_path, $variables, $type);
}
// If error handling is set, return the response anyway, otherwise return FALSE
if (isset($variables['error_handling']) and $variables['error_handling']) {
return $response;
}
return FALSE;
}
/**
* Encodes and appends any URL parameters to the resource path
*
* @param string $resource_path [reference]
* Resource path
* @param array $parameters [reference]
* Array of URL parameters
*
*/
function _restclient_prepare_url_parameters(&$resource_path, &$variables) {
// URL Encode all parameters
foreach ($variables['parameters'] as $key => $param) {
$variables['parameters'][$key] = urlencode($param);
}
// Add the parameters to the resource path
if (!empty($variables['parameters'])) {
$resource_path = strtr($resource_path, $variables['parameters']);
}
}
/**
* Prepare put body data
*
* @see _restclient_prepare_post_data().
*/
function _restclient_prepare_put_data(&$variables) {
_restclient_prepare_post_data($variables);
}
/**
* Encodes and sanitizes and body data
*
* @param array $variables [reference]
* $variables['headers']
* Current set of headers
* $variables['data']
* Data to include in the POST request
* $variables['multipart'] [optional]
* Treat the data as a multipart request
*/
function _restclient_prepare_post_data(&$variables) {
if (!isset($variables['data'])) {
$variables['data'] = array();
}
// Detect content type
if (empty($variables['headers']['Content-Type'])) {
$variables['headers']['Content-Type'] = 'application/x-www-form-urlencoded';
}
else {
// @todo detect which content type is set in the headers
// Encode based on that. If we don't know what it is, default to 'Content-Type' => 'application/x-www-form-urlencoded'
// If the body is preset, leave the text as is.
}
// Check the library in use. If curl is set, let curl handle the post
// data automatically. Otherwise, parse the data for drupal_http_request
$body = '';
if (RESTCLIENT_LIBRARY_DRUPAL == RESTCLIENT_ACTIVE_LIBRARY) {
if (!empty($variables['multipart']) && FALSE !== $variables['multipart']) {
// Build the boundary
$boundary = uniqid();
// Set the new header data
$variables['headers']['Content-Type'] = "multipart/form-data; boundary={$boundary}";
// Build the multipart data
_restclient_multipart_encode($variables['data'], $boundary);
}
else {
// Build the body string
foreach ($variables['data'] as $param => $value) {
$body .= urlencode($param) . '=' . urlencode($value) . '&';
}
// Set the value of data to the new string
$variables['data'] = $body;
}
}
elseif (RESTCLIENT_LIBRARY_CURL == RESTCLIENT_ACTIVE_LIBRARY) {
// Build the body string
foreach ($variables['data'] as $param => $value) {
$body .= urlencode($param) . '=' . urlencode($value) . '&';
}
// Check for a file upload and add the '@' sign accordingly
// @see http://php.net/function.curl-setopt.php
if (isset($variables['data']['fid'])) {
$file = file_load($variables['data']['fid']);
// @todo check the user's access to this file
if (isset($file->fid) && isset($file->status) && $file->status == 1) {
// Load the file
$file_content = file_get_contents(drupal_realpath($file->uri));
$variables['data']['fid'] = '@' . drupal_realpath($file->uri);
}
}
// Set the value of data to the new string
$variables['data'] = $body;
}
}
/**
* Build the multipart encoded header data
*
* @param array $data [reference]
* Array of parameters to encode. Also the return data.
* @param string $boundary [optional]
* Boundary string
*
* If not value is specified, a string will be generated
*/
function _restclient_multipart_encode(&$data, $boundary = NULL) {
// Copy the current parameters into a new variables
// We use the $data reference to store the result. We use a reference to
// avoid creating copies of large variables in memory. The file encode
// function loads a complete file into memory as a string. We have to be
// careful to avoid hitting memory limits.
$params = $data;
// Reset the data container
$data = '';
// Check the boundary text
if (is_null($boundary)) {
$boundary = uniqid("", TRUE);
}
foreach ($params as $key => $value) {
// Check the type of data to be encoded
if ($key == 'fid') {
// @todo add support for an array of fids
$file_encode = _restclient_multipart_encode_file($value);
// Check the encoding
if (FALSE !== $file_encode) {
// Build the data
$data .= "--{$boundary}\r\n";
$data .= $file_encode;
$data .= "\r\n--{$boundary}--";
// Clean up some memory
unset($file_encode);
}
}
else {
$text_encode = _restclient_multipart_encode_text($key, $value);
// Check the encoding
if (FALSE !== $text_encode) {
// Build the data
$data .= "--{$boundary}\r\n";
$data .= $text_encode;
// Clean up some memory
unset($text_encode);
}
}
}
// Check that the data isn't empty and mark the last boundary
if (!empty($data)) {
$data .= "--{$boundary}--";
}
}
/**
* Multipart encode the given text
*
* @param string $name
* Name of the text
* @param string $text
* Text string
* @return string
* Returns the header string.
*/
function _restclient_multipart_encode_text($name, $text) {
return "Content-Disposition: form-data; name=\"{$name}\"\r\n\r\n{$text}\r\n";
}
/**
* Debug function
*/
function _restclient_debug($values = array()) {
// Check if debug is turned on.
if (!variable_get("restclient_debug", FALSE)) {
return;
}
// Output to watchdog.
$values_string = 'Debug: <pre>' . print_r($values, TRUE) . '</pre>';
watchdog('restclient', $values_string, NULL, WATCHDOG_DEBUG);
// Output to devel dpm or debug.
if (module_exists('devel')) {
dpm($values);
}
else {
debug($values, 'RESTClient Debug', TRUE);
}
// @todo add debug output to use with SimpleTest
}
/**
* Generate the cache id for a given request
*
* @param array $variables
* Request variables
* @return string|boolean
* Returns a unique string to be used as a cache id, FALSE otherwise.
*/
function _restclient_generate_cid($method, $url, $headers) {
$cid = '';
if (empty($headers)) {
$cid = $method . ':' . $url;
}
else {
$cid = $method . ':' . $url . ':' . serialize($headers);
}
// Do not use drupal_strlen() here.
if (strlen($cid) > 255) {
$cid = $method . ':' . hash('sha512', $cid);
}
return $cid;
}
/**
* Map the variable values and defaults
*
* @param array $variables [reference]
*/
function _restclient_map_variables(&$variables) {
// Map variables
// Endpoint
if (!empty($variables['endpoint'])) {
// Do nothing
}
else {
$variables['endpoint'] = variable_get('restclient_hostname', 'http://localhost:80/rest');
}
// Retry
if (isset($variables['retry']) && is_numeric($variables['retry']) && intval($variables['retry']) >= 0) {
// Do nothing
}
else {
$variables['retry'] = 3;
}
// Timeout
if (isset($variables['timeout']) && is_numeric($variables['timeout']) && intval($variables['timeout']) >= 0) {
// Do nothing
}
else {
$variables['timeout'] = 30;
}
// Cache reset
$variables['reset'] = isset($variables['reset']) ? $variables['reset'] : FALSE;
// Headers
if (isset($variables['headers']) and is_array($variables['headers'])) {
// Do nothing
}
else {
$variables['headers'] = array();
}
// Additional headers
$additional_headers = variable_get('restclient_additional_headers', FALSE);
if ($additional_headers and is_array($additional_headers)) {
$variables['headers'] = array_merge($additional_headers, $variables['headers']);
}
}
/**
* Build the URL to connect to
*
* @param string $resource_path
* Base path
* @param array $variables
* Configuration variables
* @return string
* Returns a full URL
*/
function _restclient_build_resource_path(&$resource_path, &$variables) {
if (!empty($resource_path)) {
$url = $variables['endpoint'] . '/' . $resource_path;
}
else {
$url = $variables['endpoint'];
}
// Set the options to be used by url()
$options = array(
'query' => isset($variables['query']) ? $variables['query'] : '',
// 'fragment' => $variables['fragment'], @todo add fragment support
'absolute' => TRUE,
'alias' => TRUE,
'external' => TRUE,
);
// @todo find a way to skip hook_url_outbound or migrate url() function
// to internal function
$url = url($url, $options);
return $url;
}
/**
* Prepare authentication for the request, if needed.
*
* @param array $variables [reference]
* Array of URL variables
* ['authentication']['type'] indicates which type of authentication
* See _restclient_prepare_authentication_oauth2_client for additional settings when type is 'oauth2_client'.
*
* @return boolean
* TRUE if authentication is ready or not needed, FALSE if there is an error.
*/
function _restclient_prepare_authentication(&$variables) {
// If authentication is not requested then return success.
if (!isset($variables['authentication'])) {
return TRUE;
}
// Prepare according to the authentication method.
$return_value = FALSE;
$authentication_type = '<unspecified>';
if (isset($variables['authentication']['type'])) {
$authentication_type = $variables['authentication']['type'];
}
switch ($authentication_type) {
case 'oauth2_client':
$return_value = _restclient_prepare_authentication_oauth2_client($variables);
break;
case 'hybridauth':
$return_value = _restclient_prepare_authentication_hybridauth($variables);
break;
default:
// The authentication method is not supported.
watchdog('restclient', 'Authentication method "%type" is not supported.', array(
'%type' => $authentication_type,
), WATCHDOG_ERROR);
$return_value = FALSE;
break;
}
return $return_value;
}
/**
* Prepare authentication using oauth2 client.
*
* @param array $variables [reference]
* Array of URL variables
* - Incoming ['authentication']['oauth2_client'] - Array of values to pass through so oauth2_client creates the client. May contain just ['name'] to look up an existing client by name. See oauth2_client documentation.
* - Incoming ['authentication']['oauth_format'] - Optional format for the Authorization request header to accommodate different server implementations. The default format is 'Bearer :token' where :token is replaced with the OAuth token.
* - Outgoing ['headers']['Authorization'] - authorization header containing the oauth token
*
* @return boolean
* TRUE if authentication is ready or not needed, FALSE if there is an error.
*/
function _restclient_prepare_authentication_oauth2_client(&$variables) {
$error_message = '';
// Check if restclient has oauth2_client turned off.
if (!variable_get('restclient_oauth2_client', FALSE)) {
$error_message = 'OAuth2 client authentication is required but restclient has oauth2_client turned off.';
}
else {
if (!module_exists('oauth2_client')) {
$error_message = 'OAuth2 client authentication is required but oauth2_client module is not enabled.';
}
}
// Check if oauth2_client is specified.
if (!isset($variables['authentication']['oauth2_client'])) {
$error_message = "Authorization parameters for ['oauth2_client'] not found.";
}
if (!empty($error_message)) {
watchdog('restclient', $error_message, NULL, WATCHDOG_ERROR);
return FALSE;
}
// Load the client and get the access token.
try {
// Method 1 - Look up the client by name in oauth2_client.
if (isset($variables['authentication']['oauth2_client']['name'])) {
$oauth2_client = oauth2_client_load($variables['authentication']['oauth2_client']['name']);
}
else {
// Method 2 - Pass the array through so oauth2_client creates the client.
$client_id = 'default_client_id';
if (isset($variables['authentication']['oauth2_client']['client_id'])) {
$client_id = $variables['authentication']['oauth2_client']['client_id'];
}
$oauth2_client = new OAuth2\Client($variables['authentication']['oauth2_client'], $client_id);
}
$oauth_token = $oauth2_client
->getAccessToken();
if (empty($oauth_token)) {
$error_message = 'Retrieved OAuth2 token is empty';
}
} catch (Exception $e) {
$error_message = 'Exception retrieving OAuth2 token: ' . $e
->getMessage();
}
if (!empty($error_message)) {
watchdog('restclient', $error_message, NULL, WATCHDOG_ERROR);
return FALSE;
}
// Use the oauth token to prepare the authorization header according to
// the format specified in $variables['authentication']['oauth_format'].
$oauth_format = 'Bearer :token';
// Default
if (isset($variables['authentication']['oauth_format'])) {
$oauth_format = $variables['authentication']['oauth_format'];
}
$variables['headers']['Authorization'] = str_replace(':token', $oauth_token, $oauth_format);
return TRUE;
}
/**
* Prepare authentication using hybridauth.
*
* @param array $variables [reference]
* Array of URL variables
* - Incoming ['authentication']['hybridauth_client'] - Array of values to pass through so hybridauth creates the client. May contain just ['name'] to look up an existing client by name. See hybridauth documentation.
* - Incoming ['authentication']['oauth_format'] - Optional format for the Authorization request header to accommodate different server implementations. The default format is 'Bearer :token' where :token is replaced with the OAuth token.
* - Outgoing ['headers']['Authorization'] - authorization header containing the oauth token
*
* @return boolean
* TRUE if authentication is ready or not needed, FALSE if there is an error.
*/
function _restclient_prepare_authentication_hybridauth(&$variables) {
$error_message = '';
// Check if restclient has hybridauth turned off.
if (!variable_get('restclient_hybridauth', FALSE)) {
$error_message = 'HybridAuth authentication is required but restclient has hybridauth turned off.';
}
else {
if (!module_exists('hybridauth')) {
$error_message = 'HybridAuth authentication is required but hybridauth module is not enabled.';
}
}
// Check if hybridauth is specified.
if (!isset($variables['authentication']['hybridauth'])) {
$error_message = "Authorization parameters for ['hybridauth'] not found.";
}
if (!empty($error_message)) {
watchdog('restclient', $error_message, NULL, WATCHDOG_ERROR);
return FALSE;
}
$hybridauth_instance = hybridauth_get_instance();
$session_data = $hybridauth_instance
->storage()
->getSessionData();
global $user;
if (!empty($hybridauth_instance) and !empty($session_data) and $user->uid != 1) {
// Get the HybridAuth client ID
$hybridauth_client_id = $variables['authentication']['hybridauth']['client_id'];
$hybridauth_adapter = $hybridauth_instance
->getAdapter($hybridauth_client_id);
try {
if (!$hybridauth_adapter
->isUserConnected()) {
$hybridauth_instance
->authenticate($hybridauth_adapter->id);
}
$hybridauth_tokens = $hybridauth_adapter
->getAccessToken();
$oauth_token = $hybridauth_tokens['access_token'];
} catch (Exception $e) {
// Something went wrong.
watchdog('restclient', 'An exception occurred during HybridAuth processing: @e', array(
'@e' => $e
->getMessage(),
), WATCHDOG_ERROR);
}
}
// At this point, no point in continuing if the token is empty.
if (empty($oauth_token)) {
return FALSE;
}
// Use the oauth token to prepare the authorization header according to
// the format specified in $variables['authentication']['oauth_format'].
$oauth_format = 'Bearer :token';
// Default
if (isset($variables['authentication']['oauth_format'])) {
$oauth_format = $variables['authentication']['oauth_format'];
}
$variables['headers']['Authorization'] = str_replace(':token', $oauth_token, $oauth_format);
return TRUE;
}
/**
* Handle authentication-related request errors, and indicate
* if the request can be retried. This can be the case for situations
* where the token had expired: we re-authenticate and indicate that
* the request can be re-attempted.
*/
function _restclient_authentication_request_error($response, $variables) {
// If authentication is not specified for some reason, return FALSE;
if (!isset($variables['authentication'])) {
return FALSE;
}
$authentication_type = NULL;
if (isset($variables['authentication']['type'])) {
$authentication_type = $variables['authentication']['type'];
}
$return_value = FALSE;
switch ($authentication_type) {
case 'hybridauth':
$return_value = _restclient_authentication_hybridauth_request_error($response, $variables);
break;
default:
// The authentication method is not supported or was not specified. Do nothing.
break;
}
return $return_value;
return FALSE;
}
function _restclient_authentication_hybridauth_request_error($response, $variables) {
if ($response->code == '401') {
if ($response->headers['content-type'] == 'application/json') {
$data = drupal_json_decode($response->data);
if (!empty($data['error']) and ($data['error'] == 'expired_token' or $data['error'] = 'invalid_token')) {
$hybridauth_instance = hybridauth_get_instance();
$hybridauth_client_id = $variables['authentication']['hybridauth']['client_id'];
$hybridauth_adapter = $hybridauth_instance
->getAdapter($hybridauth_client_id);
$hybridauth_adapter
->logout();
$hybridauth_instance
->authenticate($hybridauth_client_id);
return TRUE;
}
}
}
return FALSE;
}
function _restclient_watchdog($method, $code, $error, $url, $extra = array(), $log_severity = WATCHDOG_ERROR) {
// Check if logging is turned off - on by default
if (!variable_get('restclient_watchdog', TRUE)) {
return;
}
$debug = "";
if (!empty($extra)) {
$debug .= "\n" . print_r($extra, TRUE);
}
// @todo add support for devel watchdog functions
watchdog('restclient', 'Response for @method request is @code (@message): @url @debug', array(
'@method' => $method,
'@code' => $code,
'@message' => $error,
'@url' => $url,
'@debug' => $debug,
), $log_severity);
}
/**
* hook to define the list of elements to include in in the file signature.
*
* @return array
* Return an array of elements to be used for the file signature.
*/
function restclient_filemode_signature_alter() {
return array(
'headers',
'method',
);
}
function restclient_cache_set($variables, $cid, $data) {
$expires = 1;
if (!empty($data->headers['expires']) and !$variables['cache_default_override']) {
$expires = strtotime($data->headers['expires']);
if (!is_int($expires)) {
$expires = 1;
}
}
if ($variables['cache_default_override']) {
$expires = time() + $variables['cache_default_time'];
}
if (isset($variables['stale_cache']) and $variables['stale_cache']) {
$data->stale_cache = $expires;
$expires = CACHE_PERMANENT;
}
return cache_set($cid, $data, 'cache_restclient', $expires);
}
function restclient_cache_get($variables, $cid, $stale = FALSE) {
$cache = cache_get($cid, 'cache_restclient');
// If we do want stale records, but stale cache has been disabled, return FALSE.
if ($cache and isset($cache->data->stale_cache) and $stale and !isset($variables['stale_cache'])) {
return FALSE;
}
if (isset($cache->data->stale_cache)) {
$cache->expire = $cache->data->stale_cache;
}
return $cache;
}
Functions
Name | Description |
---|---|
restclient_cache_get | |
restclient_cache_set | |
restclient_delete | Make a DELETE request |
restclient_filemode_signature_alter | hook to define the list of elements to include in in the file signature. |
restclient_flush_caches | Implements hook_flush_caches(). |
restclient_get | Make a GET request |
restclient_menu | Implements hook_menu(). |
restclient_permission | Implements hook_permission(). |
restclient_post | Make a POST request |
restclient_put | Make a PUT request |
restclient_response_code | Analyze the response code from a request |
restclient_wsconfig_connector_info | Implements hook_wsconfig_connector_info(). |
_restclient_authentication_hybridauth_request_error | |
_restclient_authentication_request_error | Handle authentication-related request errors, and indicate if the request can be retried. This can be the case for situations where the token had expired: we re-authenticate and indicate that the request can be re-attempted. |
_restclient_build_resource_path | Build the URL to connect to |
_restclient_debug | Debug function |
_restclient_generate_cid | Generate the cache id for a given request |
_restclient_map_variables | Map the variable values and defaults |
_restclient_multipart_encode | Build the multipart encoded header data |
_restclient_multipart_encode_text | Multipart encode the given text |
_restclient_prepare_authentication | Prepare authentication for the request, if needed. |
_restclient_prepare_authentication_hybridauth | Prepare authentication using hybridauth. |
_restclient_prepare_authentication_oauth2_client | Prepare authentication using oauth2 client. |
_restclient_prepare_post_data | Encodes and sanitizes and body data |
_restclient_prepare_put_data | Prepare put body data |
_restclient_prepare_url_parameters | Encodes and appends any URL parameters to the resource path |
_restclient_request | Basic request with no body data. |
_restclient_request_with_body | Requests with body data. |
_restclient_watchdog |