/**
  * Helper function to get a formatter and apply a method.
  *
  * @param string $method
  *   A valid method to call on the FormatterInterface object.
  * @param array $data
  *   The array of data to process.
  * @param string $formatter_name
  *   The name of the formatter for the current resource. Leave it NULL to use
  *   the Accept headers.
  *
  * @return string
  *   The processed output.
  */
 protected function processData($method, array $data, $formatter_name = NULL)
 {
     if ($resource = $this->resource) {
         $request = $resource->getRequest();
     } else {
         $request = restful()->getRequest();
     }
     $accept = $request->getHeaders()->get('accept')->getValueString();
     $formatter = $this->negotiateFormatter($accept, $formatter_name);
     $output = ResourceManager::executeCallback(array($formatter, $method), array($data, $formatter_name));
     // The content type header is modified after the massaging if there is
     // an error code. Therefore we need to set the content type header after
     // formatting the output.
     $content_type = $formatter->getContentTypeHeader();
     $response_headers = restful()->getResponse()->getHeaders();
     $response_headers->add(HttpHeader::create('Content-Type', $content_type));
     return $output;
 }
 /**
  * Sets an HTTP header.
  *
  * @param string $name
  *   The header name.
  * @param string $value
  *   The header value.
  */
 protected function setHttpHeader($name, $value)
 {
     $this->getRequest()->getHeaders()->add(HttpHeader::create($name, $value));
 }
 /**
  * {@inheritdoc}
  */
 public function setDate(\DateTime $date)
 {
     $date->setTimezone(new \DateTimeZone('UTC'));
     $this->headers->add(HttpHeader::create('Date', $date->format('D, d M Y H:i:s') . ' GMT'));
 }
 /**
  * Adds the Allowed-Origin headers.
  *
  * @param string $path
  *   The requested path.
  */
 protected function preflight($path)
 {
     $plugin_definition = $this->getPluginDefinition();
     $header_bag = restful()->getResponse()->getHeaders();
     // Populate the Accept header.
     $accepted_formats = array();
     $formatter_manager = restful()->getFormatterManager();
     if (empty($plugin_definition['formatter'])) {
         foreach ($formatter_manager->getPlugins() as $formatter) {
             /** @var $formatter \Drupal\restful\Plugin\formatter\FormatterInterface */
             $header_bag->append(HttpHeader::create('Accept', $formatter->getContentTypeHeader()));
         }
     } else {
         try {
             $accepted_format = $formatter_manager->getPlugin($plugin_definition['formatter'])->getContentTypeHeader();
             $header_bag->add(HttpHeader::create('Accept', $accepted_format));
         } catch (PluginNotFoundException $e) {
             throw new NotImplementedException($e->getMessage());
         }
     }
     $allowed_origin = empty($plugin_definition['allowOrigin']) ? variable_get('restful_allowed_origin', NULL) : $plugin_definition['allowOrigin'];
     // Always add the allow origin if configured.
     if ($allowed_origin) {
         $header_bag->add(HttpHeader::create('Access-Control-Allow-Origin', check_plain($allowed_origin)));
         // @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials
         $accepts_credentials = $allowed_origin == '*' ? 'false' : 'true';
         $header_bag->add(HttpHeader::create('Access-Control-Allow-Credentials', $accepts_credentials));
     }
     // Make sure the Access-Control-Allow-Methods is populated.
     $allowed_methods = array();
     foreach ($this->getControllers() as $pattern => $controllers) {
         // Find the controllers for the provided path.
         if ($pattern == $path || $pattern && preg_match('/' . $pattern . '/', $path)) {
             foreach ($controllers as $method => $controller) {
                 if (is_array($controller)) {
                     // If there is a custom access method for this endpoint check it.
                     if (!empty($selected_controller['access callback']) && !ResourceManager::executeCallback(array($this, $selected_controller['access callback']), array($path))) {
                         // There is no access for this method.
                         continue;
                     }
                 }
                 $allowed_methods[] = $method;
             }
             $header_bag->add(HttpHeader::create('Access-Control-Allow-Methods', implode(',', $allowed_methods)));
             break;
         }
     }
 }
 /**
  * Parses the header names and values from globals.
  *
  * @return HttpHeaderBag
  *   The headers.
  */
 protected static function parseHeadersFromGlobals()
 {
     $bag = new HttpHeaderBag();
     $headers = array();
     if (function_exists('apache_request_headers')) {
         $headers = apache_request_headers();
     } else {
         foreach ($_SERVER as $key => $value) {
             if (strpos($key, 'HTTP_') === 0) {
                 // Generate the plausible header name based on the $name.
                 // Converts 'HTTP_X_FORWARDED_FOR' to 'X-Forwarded-For'
                 $name = substr($key, 5);
                 $parts = explode('_', $name);
                 $parts = array_map('strtolower', $parts);
                 $parts = array_map('ucfirst', $parts);
                 $name = implode('-', $parts);
                 $headers[$name] = $value;
             }
         }
     }
     // Iterate over the headers and bag them.
     foreach ($headers as $name => $value) {
         $bag->add(HttpHeader::create($name, $value));
     }
     return $bag;
 }
 /**
  * {@inheritdoc}
  */
 public function view($path)
 {
     // TODO: This is duplicating the code from Resource::view
     $ids = explode(static::IDS_SEPARATOR, $path);
     // REST requires a canonical URL for every resource.
     $canonical_path = $this->getDataProvider()->canonicalPath($path);
     $this->getRequest()->getHeaders()->add(HttpHeader::create('Link', $this->versionedUrl($canonical_path, array(), FALSE) . '; rel="canonical"'));
     // If there is only one ID then use 'view'. Else, use 'viewMultiple'. The
     // difference between the two is that 'view' allows access denied
     // exceptions.
     if (count($ids) == 1) {
         return array($this->getDataProvider()->view($ids[0]));
     } else {
         return $this->getDataProvider()->viewMultiple($ids);
     }
 }
 /**
  * Checks if the current request has reached the rate limit.
  *
  * If the user has reached the limit this method will throw an exception. If
  * not, the hits counter will be updated for subsequent calls. Since the
  * request can match multiple events, the access is only granted if all events
  * are cleared.
  *
  * @param RequestInterface $request
  *   The request array.
  *
  * @throws FloodException if the rate limit has been reached for the
  * current request.
  */
 public function checkRateLimit(RequestInterface $request)
 {
     $now = new \DateTime();
     $now->setTimestamp(REQUEST_TIME);
     // Check all rate limits configured for this handler.
     foreach ($this->plugins as $instance_id => $plugin) {
         // If the limit is unlimited then skip everything.
         /* @var RateLimit $plugin */
         $limit = $plugin->getLimit($this->account);
         $period = $plugin->getPeriod();
         if ($limit == static::UNLIMITED_RATE_LIMIT) {
             // User has unlimited access to the resources.
             continue;
         }
         // If the current request matches the configured event then check if the
         // limit has been reached.
         if (!$plugin->isRequestedEvent($request)) {
             continue;
         }
         if (!($rate_limit_entity = $plugin->loadRateLimitEntity($this->account))) {
             // If there is no entity, then create one.
             // We don't need to save it since it will be saved upon hit.
             $rate_limit_entity = entity_create('rate_limit', array('timestamp' => REQUEST_TIME, 'expiration' => $now->add($period)->format('U'), 'hits' => 0, 'event' => $plugin->getPluginId(), 'identifier' => $plugin->generateIdentifier($this->account)));
         }
         // When the new rate limit period starts.
         $new_period = new \DateTime();
         $new_period->setTimestamp($rate_limit_entity->expiration);
         if ($rate_limit_entity->isExpired()) {
             // If the rate limit has expired renew the timestamps and assume 0
             // hits.
             $rate_limit_entity->timestamp = REQUEST_TIME;
             $rate_limit_entity->expiration = $now->add($period)->format('U');
             $rate_limit_entity->hits = 0;
             if ($limit == 0) {
                 $exception = new FloodException('Rate limit reached');
                 $exception->setHeader('Retry-After', $new_period->format(\DateTime::RFC822));
                 throw $exception;
             }
         } else {
             if ($rate_limit_entity->hits >= $limit) {
                 $exception = new FloodException('Rate limit reached');
                 $exception->setHeader('Retry-After', $new_period->format(\DateTime::RFC822));
                 throw $exception;
             }
         }
         // Save a new hit after generating the exception to mitigate DoS attacks.
         $rate_limit_entity->hit();
         // Add the limit headers to the response.
         $remaining = $limit == static::UNLIMITED_RATE_LIMIT ? 'unlimited' : $limit - ($rate_limit_entity->hits + 1);
         $response = restful()->getResponse();
         $headers = $response->getHeaders();
         $headers->append(HttpHeader::create('X-Rate-Limit-Limit', $limit));
         $headers->append(HttpHeader::create('X-Rate-Limit-Remaining', $remaining));
         $time_remaining = $rate_limit_entity->expiration - REQUEST_TIME;
         $headers->append(HttpHeader::create('X-Rate-Limit-Reset', $time_remaining));
     }
 }