public function listen($request, $response) {
//		$this->debug('Host::listen: path='.$request->getPath().'?'.http_build_query($request->getQuery()));
		$this->hits++;

		$path = '/'.ltrim($request->getPath(), '/');

		$endpoint = NULL;

		if(isset($this->routes[$path])) {
			// Direct lookup
			$endpoint = $this->routes[$path];
		} else {
			$endpointKey = NULL;

			if(isset($this->lookupCache[$path])) {
				$endpointKey = $this->lookupCache[$path];
				// The item was found in the lookupCache, so we move it back to the end of the array (to avoid unshifting it too soon) - LRU algorithm
				if($this->lookupCacheCount == 1000) {
					unset($this->lookupCache[$path]);
					$this->lookupCache[$path] = $endpointKey;
				}
				$endpoint = $this->searchRoutes[$endpointKey][1];
			} else {
				foreach($this->searchRoutes as $key => $route) {
					if(fnmatch($route[0], $path)) {
						$endpointKey = $key;
						break;
					}
				}

				if($endpointKey !== NULL) {
					// LRU algorithm
					$this->lookupCache[$path] = $endpointKey; // Added to the end of the array
					$this->lookupCacheCount++;
					while($this->lookupCacheCount-- > 1000)
						array_shift($this->lookupCache); // Remove something from the beginning of the array

					$endpoint = $this->searchRoutes[$endpointKey][1];
				}
			}
		}

		if($endpoint) {
			// We identified an endpoint
			return $endpoint->listen($request, $response);
		} else {
			// We failed to find an endpoint, so we redirect to /errors/404
			// This is not cached, because we don't want anybody to be able to fill the lookupCache with garbage
			return $this->respondError(404, $request, $response);
		}
	}