Author: Christer Edvartsen (cogo@starzinger.net)
Inheritance: extends Symfony\Component\HttpFoundation\Request
Example #1
0
File: Router.php Project: imbo/imbo
 /**
  * Route the current request
  *
  * @param Request $request The current request
  */
 public function route(Request $request)
 {
     $httpMethod = $request->getMethod();
     if ($httpMethod === 'BREW') {
         throw new RuntimeException('I\'m a teapot!', 418);
     }
     if (!isset(self::$supportedHttpMethods[$httpMethod])) {
         throw new RuntimeException('Unsupported HTTP method: ' . $httpMethod, 501);
     }
     $path = $request->getPathInfo();
     $matches = [];
     foreach ($this->routes as $resourceName => $route) {
         if (preg_match($route, $path, $matches)) {
             break;
         }
     }
     // Path matched no route
     if (!$matches) {
         throw new RuntimeException('Not Found', 404);
     }
     // Create and populate a route instance that we want to inject into the request
     $route = new Route();
     $route->setName($resourceName);
     // Inject all matches into the route as parameters
     foreach ($matches as $key => $value) {
         if (is_string($key)) {
             $route->set($key, $value);
         }
     }
     // Store the route in the request
     $request->setRoute($route);
 }
Example #2
0
 /**
  * @covers Imbo\Application::run
  */
 public function testApplicationSetsTrustedProxies()
 {
     $this->expectOutputRegex('|{"version":"' . preg_quote(Version::VERSION, '|') . '",.*}|');
     $this->assertEmpty(Request::getTrustedProxies());
     $this->application->run(array('database' => $this->getMock('Imbo\\Database\\DatabaseInterface'), 'storage' => $this->getMock('Imbo\\Storage\\StorageInterface'), 'eventListenerInitializers' => [], 'eventListeners' => [], 'contentNegotiateImages' => false, 'resources' => [], 'routes' => [], 'auth' => [], 'trustedProxies' => ['10.0.0.77']));
     $this->assertSame(['10.0.0.77'], Request::getTrustedProxies());
 }
Example #3
0
 /**
  * Create an error based on an exception instance
  *
  * @param Exception $exception An Imbo\Exception instance
  * @param Request The current request
  * @return Error
  */
 public static function createFromException(Exception $exception, Request $request)
 {
     $date = new DateTime('now', new DateTimeZone('UTC'));
     $model = new self();
     $model->setHttpCode($exception->getCode())->setErrorMessage($exception->getMessage())->setDate($date)->setImboErrorCode($exception->getImboErrorCode() ?: Exception::ERR_UNSPECIFIED);
     if ($image = $request->getImage()) {
         $model->setImageIdentifier($image->getChecksum());
     } else {
         if ($identifier = $request->getImageIdentifier()) {
             $model->setImageIdentifier($identifier);
         }
     }
     return $model;
 }
Example #4
0
 /**
  * @dataProvider getQueryStrings
  */
 public function testGetRawUriDecodesUri($queryString, $expectedQueryString)
 {
     $request = new Request([], [], [], [], [], ['SERVER_NAME' => 'imbo', 'SERVER_PORT' => 80, 'QUERY_STRING' => $queryString]);
     $uri = $request->getRawUri();
     $this->assertSame($expectedQueryString, substr($uri, strpos($uri, '?') + 1));
 }
Example #5
0
 /**
  * Check if the request is whitelisted
  *
  * This method will whitelist a request only if all the transformations present in the request
  * are listed in the whitelist filter OR if the whitelist filter is empty, and the blacklist
  * filter has enties, but none of the transformations in the request are present in the
  * blacklist.
  *
  * @param Request $request The request instance
  * @return boolean
  */
 private function isWhitelisted(Request $request)
 {
     $filter = $this->params['transformations'];
     if (empty($filter['whitelist']) && empty($filter['blacklist'])) {
         // No filter has been configured
         return false;
     }
     // Fetch transformations from the request
     $transformations = $request->getTransformations();
     if (empty($transformations)) {
         // No transformations are present in the request, no need to check
         return false;
     }
     $whitelist = array_flip($filter['whitelist']);
     $blacklist = array_flip($filter['blacklist']);
     foreach ($transformations as $transformation) {
         if (isset($blacklist[$transformation['name']])) {
             // Transformation is explicitly blacklisted
             return false;
         }
         if (!empty($whitelist) && !isset($whitelist[$transformation['name']])) {
             // We have a whitelist, but the transformation is not listed in it, so we must deny
             // the request
             return false;
         }
     }
     // All transformations in the request are whitelisted
     return true;
 }
Example #6
0
 /**
  * Generate a cache key
  *
  * @param Request $request The current request instance
  * @return string Returns a string that can be used as a cache key for the current image
  */
 private function getCacheKey(Request $request)
 {
     $user = $request->getUser();
     $imageIdentifier = $request->getImageIdentifier();
     $accept = $request->headers->get('Accept', '*/*');
     $accept = array_filter(explode(',', $accept), function (&$value) {
         // Trim whitespace
         $value = trim($value);
         // Remove optional params
         $pos = strpos($value, ';');
         if ($pos !== false) {
             $value = substr($value, 0, $pos);
         }
         // Keep values starting with "*/" or "image/"
         return $value[0] === '*' && $value[1] === '/' || substr($value, 0, 6) === 'image/';
     });
     // Sort the remaining values
     sort($accept);
     $accept = implode(',', $accept);
     $extension = $request->getExtension();
     $transformations = $request->query->get('t');
     if (!empty($transformations)) {
         $transformations = implode('&', $transformations);
     }
     return md5($user . $imageIdentifier . $accept . $extension . $transformations);
 }
Example #7
0
 /**
  * Run the application
  */
 public function run(array $config)
 {
     // Request and response objects
     $request = Request::createFromGlobals();
     Request::setTrustedProxies($config['trustedProxies']);
     $response = new Response();
     $response->setPublic();
     $response->headers->set('X-Imbo-Version', Version::VERSION);
     // Database and storage adapters
     $database = $config['database'];
     if (is_callable($database) && !$database instanceof DatabaseInterface) {
         $database = $database();
     }
     if (!$database instanceof DatabaseInterface) {
         throw new InvalidArgumentException('Invalid database adapter', 500);
     }
     $storage = $config['storage'];
     if (is_callable($storage) && !$storage instanceof StorageInterface) {
         $storage = $storage();
     }
     if (!$storage instanceof StorageInterface) {
         throw new InvalidArgumentException('Invalid storage adapter', 500);
     }
     // User lookup adapters
     $userLookup = $config['auth'];
     // Construct an ArrayStorage instance if the auth details is an array
     if (is_array($userLookup)) {
         $userLookup = new Auth\ArrayStorage($userLookup);
     }
     // Make sure the "auth" part of the configuration is an instance of the user lookup
     // interface
     if (!$userLookup instanceof Auth\UserLookupInterface) {
         throw new InvalidArgumentException('Invalid auth configuration', 500);
     }
     // Create a router based on the routes in the configuration and internal routes
     $router = new Router($config['routes']);
     // Create the event manager and the event template
     $eventManager = new EventManager();
     $event = new Event();
     $event->setArguments(array('request' => $request, 'response' => $response, 'database' => $database, 'storage' => $storage, 'userLookup' => $userLookup, 'config' => $config, 'manager' => $eventManager));
     $eventManager->setEventTemplate($event);
     // A date formatter helper
     $dateFormatter = new Helpers\DateFormatter();
     // Response formatters
     $formatters = array('json' => new Formatter\JSON($dateFormatter), 'xml' => new Formatter\XML($dateFormatter));
     $contentNegotiation = new Http\ContentNegotiation();
     // Collect event listener data
     $eventListeners = array('Imbo\\Resource\\Index', 'Imbo\\Resource\\Status', 'Imbo\\Resource\\Stats', 'Imbo\\Resource\\GlobalShortUrl', 'Imbo\\Resource\\ShortUrls', 'Imbo\\Resource\\ShortUrl', 'Imbo\\Resource\\User', 'Imbo\\Resource\\Images', 'Imbo\\Resource\\Image', 'Imbo\\Resource\\Metadata', 'Imbo\\Http\\Response\\ResponseFormatter' => array('formatters' => $formatters, 'contentNegotiation' => $contentNegotiation), 'Imbo\\EventListener\\DatabaseOperations', 'Imbo\\EventListener\\StorageOperations', 'Imbo\\Image\\ImagePreparation', 'Imbo\\EventListener\\ImageTransformer', 'Imbo\\EventListener\\ResponseSender', 'Imbo\\EventListener\\ResponseETag');
     foreach ($eventListeners as $listener => $params) {
         if (is_string($params)) {
             $listener = $params;
             $params = array();
         }
         $eventManager->addEventHandler($listener, $listener, $params)->addCallbacks($listener, $listener::getSubscribedEvents());
     }
     // Event listener initializers
     foreach ($config['eventListenerInitializers'] as $name => $initializer) {
         if (!$initializer) {
             // The initializer has been disabled via config
             continue;
         }
         if (is_string($initializer)) {
             // The initializer has been specified as a string, representing a class name. Create
             // an instance
             $initializer = new $initializer();
         }
         if (!$initializer instanceof InitializerInterface) {
             throw new InvalidArgumentException('Invalid event listener initializer: ' . $name, 500);
         }
         $eventManager->addInitializer($initializer);
     }
     // Listeners from configuration
     foreach ($config['eventListeners'] as $name => $definition) {
         if (!$definition) {
             // This occurs when a user disables a default event listener
             continue;
         }
         if (is_string($definition)) {
             // Class name
             $eventManager->addEventHandler($name, $definition)->addCallbacks($name, $definition::getSubscribedEvents());
             continue;
         }
         if (is_callable($definition) && !$definition instanceof ListenerInterface) {
             // Callable piece of code which is not an implementation of the listener interface
             $definition = $definition();
         }
         if ($definition instanceof ListenerInterface) {
             $eventManager->addEventHandler($name, $definition)->addCallbacks($name, $definition::getSubscribedEvents());
             continue;
         }
         if (is_array($definition) && !empty($definition['listener'])) {
             $listener = $definition['listener'];
             $params = is_string($listener) && isset($definition['params']) ? $definition['params'] : array();
             $publicKeys = isset($definition['publicKeys']) ? $definition['publicKeys'] : array();
             if (is_callable($listener) && !$listener instanceof ListenerInterface) {
                 $listener = $listener();
             }
             if (!is_string($listener) && !$listener instanceof ListenerInterface) {
                 throw new InvalidArgumentException('Invalid event listener definition', 500);
             }
             $eventManager->addEventHandler($name, $listener, $params)->addCallbacks($name, $listener::getSubscribedEvents(), $publicKeys);
         } else {
             if (is_array($definition) && !empty($definition['callback']) && !empty($definition['events'])) {
                 $priority = 0;
                 $events = array();
                 $publicKeys = array();
                 if (isset($definition['priority'])) {
                     $priority = (int) $definition['priority'];
                 }
                 if (isset($definition['publicKeys'])) {
                     $publicKeys = $definition['publicKeys'];
                 }
                 foreach ($definition['events'] as $event => $p) {
                     if (is_int($event)) {
                         $event = $p;
                         $p = $priority;
                     }
                     $events[$event] = $p;
                 }
                 $eventManager->addEventHandler($name, $definition['callback'])->addCallbacks($name, $events, $publicKeys);
             } else {
                 throw new InvalidArgumentException('Invalid event listener definition', 500);
             }
         }
     }
     // Custom resources
     foreach ($config['resources'] as $name => $resource) {
         if (is_callable($resource)) {
             $resource = $resource();
         }
         $eventManager->addEventHandler($name, $resource)->addCallbacks($name, $resource::getSubscribedEvents());
     }
     try {
         // Route the request
         $router->route($request);
         $eventManager->trigger('route.match');
         // Create the resource
         $routeName = (string) $request->getRoute();
         if (isset($config['resources'][$routeName])) {
             $resource = $config['resources'][$routeName];
             if (is_callable($resource)) {
                 $resource = $resource();
             }
             if (is_string($resource)) {
                 $resource = new $resource();
             }
             if (!$resource instanceof ResourceInterface) {
                 throw new InvalidArgumentException('Invalid resource class for route: ' . $routeName, 500);
             }
         } else {
             $className = 'Imbo\\Resource\\' . ucfirst($routeName);
             $resource = new $className();
         }
         // Inform the user agent of which methods are allowed against this resource
         $response->headers->set('Allow', $resource->getAllowedMethods(), false);
         if ($publicKey = $request->getPublicKey()) {
             // Ensure that the public key actually exists
             if (!$userLookup->publicKeyExists($publicKey)) {
                 $e = new RuntimeException('Public key not found', 404);
                 $e->setImboErrorCode(Exception::AUTH_UNKNOWN_PUBLIC_KEY);
                 throw $e;
             }
         }
         $methodName = strtolower($request->getMethod());
         // Generate the event name based on the accessed resource and the HTTP method
         $eventName = $routeName . '.' . $methodName;
         if (!$eventManager->hasListenersForEvent($eventName)) {
             throw new RuntimeException('Method not allowed', 405);
         }
         $eventManager->trigger($eventName)->trigger('response.negotiate');
     } catch (Exception $exception) {
         $negotiated = false;
         $error = Error::createFromException($exception, $request);
         $response->setError($error);
         // If the error is not from the previous attempt at doing content negotiation, force
         // another round since the model has changed into an error model.
         if ($exception->getCode() !== 406) {
             try {
                 $eventManager->trigger('response.negotiate');
                 $negotiated = true;
             } catch (Exception $exception) {
                 // The client does not accept any of the content types. Generate a new error
                 $error = Error::createFromException($exception, $request);
                 $response->setError($error);
             }
         }
         // Try to negotiate in a non-strict manner if the response format still has not been
         // chosen
         if (!$negotiated) {
             $eventManager->trigger('response.negotiate', array('noStrict' => true));
         }
     }
     // Send the response
     $eventManager->trigger('response.send');
 }
 /**
  * Construct a message body with the data we need
  *
  * @param string $eventName Event that was triggered
  * @param Request $request Request that triggered this event
  * @param Response $response Response for this request
  * @param Imbo\EventManager\EventInterface $eventName Current event
  * @return array
  */
 public function constructMessageBody($eventName, Request $request, Response $response, EventInterface $event)
 {
     if ($response->getModel() instanceof ErrorModel) {
         trigger_error($response->getModel()->getErrorMessage());
         return;
     }
     // Construct the basics
     $message = ['eventName' => $eventName, 'request' => ['date' => date('c'), 'user' => $request->getUser(), 'publicKey' => $request->getPublicKey(), 'extension' => $request->getExtension(), 'url' => $request->getRawUri(), 'clientIp' => $request->getClientIp()], 'response' => ['statusCode' => $response->getStatusCode()]];
     // Include any JSON request body in the message
     if ($request->getContentType() === 'application/json') {
         $message['request']['body'] = $request->getContent();
     }
     // See if we've got an image identifier for this request
     $imageIdentifier = $request->getImageIdentifier();
     // The imageIdentifier was not part of the URL, see if we have it in the response model
     if (!$imageIdentifier) {
         $responseData = $response->getModel()->getData();
         if (isset($responseData['imageIdentifier'])) {
             $imageIdentifier = $responseData['imageIdentifier'];
         } else {
             return $message;
         }
     }
     // Get image information
     $image = $this->getImageData($event, $imageIdentifier);
     // Construct an array of all the image information we have
     $message['image'] = ['identifier' => $imageIdentifier, 'user' => $image->getUser() ?: $request->getUser(), 'size' => $image->getFilesize(), 'extension' => $image->getExtension(), 'mime' => $image->getMimeType(), 'added' => $image->getAddedDate()->format(DateTime::ATOM), 'updated' => $image->getUpdatedDate()->format(DateTime::ATOM), 'width' => $image->getWidth(), 'height' => $image->getHeight(), 'metadata' => $image->getMetadata()];
     return $message;
 }