/** * Prepares and returns the array with the FastCGI environment variables. * * @param \AppserverIo\Psr\HttpMessage\RequestInterface $request A request object * @param \AppserverIo\Server\Interfaces\RequestContextInterface $requestContext A requests context instance * * @return array The array with the prepared FastCGI environment variables */ protected function prepareEnvironment(RequestInterface $request, RequestContextInterface $requestContext) { // prepare the Fast-CGI environment variables $environment = array(ServerVars::GATEWAY_INTERFACE => 'FastCGI/1.0', ServerVars::REQUEST_METHOD => $requestContext->getServerVar(ServerVars::REQUEST_METHOD), ServerVars::SCRIPT_FILENAME => $requestContext->getServerVar(ServerVars::SCRIPT_FILENAME), ServerVars::QUERY_STRING => $requestContext->getServerVar(ServerVars::QUERY_STRING), ServerVars::SCRIPT_NAME => $requestContext->getServerVar(ServerVars::SCRIPT_NAME), ServerVars::REQUEST_URI => $requestContext->getServerVar(ServerVars::REQUEST_URI), ServerVars::DOCUMENT_ROOT => $requestContext->getServerVar(ServerVars::DOCUMENT_ROOT), ServerVars::SERVER_PROTOCOL => $requestContext->getServerVar(ServerVars::SERVER_PROTOCOL), ServerVars::HTTPS => $requestContext->getServerVar(ServerVars::HTTPS), ServerVars::SERVER_SOFTWARE => $requestContext->getServerVar(ServerVars::SERVER_SOFTWARE), ServerVars::REMOTE_ADDR => $requestContext->getServerVar(ServerVars::REMOTE_ADDR), ServerVars::REMOTE_PORT => $requestContext->getServerVar(ServerVars::REMOTE_PORT), ServerVars::SERVER_ADDR => $requestContext->getServerVar(ServerVars::SERVER_ADDR), ServerVars::SERVER_PORT => $requestContext->getServerVar(ServerVars::SERVER_PORT), ServerVars::SERVER_NAME => $requestContext->getServerVar(ServerVars::SERVER_NAME)); // if we found a redirect status, add it to the environment variables if ($requestContext->hasServerVar(ServerVars::REDIRECT_STATUS)) { $environment[ServerVars::REDIRECT_STATUS] = $requestContext->getServerVar(ServerVars::REDIRECT_STATUS); } // if we found a redirect URL, add it to the environment variables if ($requestContext->hasServerVar(ServerVars::REDIRECT_URL)) { $environment[ServerVars::REDIRECT_URL] = $requestContext->getServerVar(ServerVars::REDIRECT_URL); } // if we found a redirect URI, add it to the environment variables if ($requestContext->hasServerVar(ServerVars::REDIRECT_URI)) { $environment[ServerVars::REDIRECT_URI] = $requestContext->getServerVar(ServerVars::REDIRECT_URI); } // if we found a Content-Type header, add it to the environment variables if ($request->hasHeader(Protocol::HEADER_CONTENT_TYPE)) { $environment['CONTENT_TYPE'] = $request->getHeader(Protocol::HEADER_CONTENT_TYPE); } // if we found a Content-Length header, add it to the environment variables if ($request->hasHeader(Protocol::HEADER_CONTENT_LENGTH)) { $environment['CONTENT_LENGTH'] = $request->getHeader(Protocol::HEADER_CONTENT_LENGTH); } // create an HTTP_ environment variable for each header foreach ($request->getHeaders() as $key => $value) { $environment['HTTP_' . str_replace('-', '_', strtoupper($key))] = $value; } // create an HTTP_ environment variable for each server environment variable foreach ($requestContext->getEnvVars() as $key => $value) { $environment[$key] = $value; } // return the prepared environment return $environment; }
/** * Expands request context on given request constellation (uri) based on file handler configuration * * @param \AppserverIo\Server\Interfaces\RequestContextInterface $requestContext The request context instance * * @return void */ public function populateRequestContext(RequestContextInterface $requestContext) { // get local refs $serverContext = $this->getServerContext(); // get document root $documentRoot = $requestContext->getServerVar(ServerVars::DOCUMENT_ROOT); // load the default handlers $handlers = $serverContext->getServerConfig()->getHandlers(); // check if there are some volatile location definitions so use them and merge with global locations if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_HANDLERS)) { $handlers = array_merge($handlers, $requestContext->getModuleVar(ModuleVars::VOLATILE_HANDLERS)); } // get uri without querystring // Just make sure that you check for the existence of the query string first, as it might not be set $uriWithoutQueryString = parse_url($requestContext->getServerVar(ServerVars::X_REQUEST_URI), PHP_URL_PATH); // check if uri without query string is just "/" if ($uriWithoutQueryString === '/' && $requestContext->hasServerVar(ServerVars::SERVER_WELCOME_PAGE_TEMPLATE_PATH)) { // in this case we will set welcome page template to be errors template if ($welcomePageTemplate = $requestContext->getServerVar(ServerVars::SERVER_WELCOME_PAGE_TEMPLATE_PATH)) { $requestContext->setServerVar(ServerVars::SERVER_ERRORS_PAGE_TEMPLATE_PATH, $welcomePageTemplate); } } // split all path parts got from uri without query string $pathParts = explode('/', $uriWithoutQueryString); // init vars for path parsing $possibleValidPathExtension = ''; $possibleValidPath = ''; $pathInfo = ''; $validDir = null; $scriptName = null; $scriptFilename = null; // note: only if file extension hits a filehandle info it will be possible to set path info etc... // iterate through all dirs beginning at 1 because 0 is always empty in this case for ($i = 1; $i < count($pathParts); ++$i) { // check if no script name was found yet if (!$scriptName) { // append valid path $possibleValidPath .= DIRECTORY_SEPARATOR . $pathParts[$i]; // get possible extension $possibleValidPathExtension = pathinfo($possibleValidPath, PATHINFO_EXTENSION); // check if dir does not exists if (!is_dir($documentRoot . $possibleValidPath)) { // check if its not a existing file if (!is_file($documentRoot . $possibleValidPath)) { // check if file handler is defined for that virtual file if (isset($handlers['.' . $possibleValidPathExtension])) { // set script name for further processing as script aspect $scriptName = $possibleValidPath; } } else { // set script name $scriptName = $possibleValidPath; // set script filename $scriptFilename = $documentRoot . $scriptName; } } else { // save valid dir for indexed surfing later on $validDir = $possibleValidPath; } } else { // else build up path info $pathInfo .= DIRECTORY_SEPARATOR . $pathParts[$i]; } } // set special server var for requested file $requestContext->setServerVar(ServerVars::REQUEST_FILENAME, $documentRoot . $possibleValidPath); // set specific script name server var if exists if ($scriptName) { $requestContext->setServerVar(ServerVars::SCRIPT_NAME, $scriptName); } // check if requested file is on filesystem and set it to be valid script filename if ($scriptFilename) { $requestContext->setServerVar(ServerVars::SCRIPT_FILENAME, $scriptFilename); } // if path info is set put it into server vars if (strlen($pathInfo) > 0) { // set path info vars $requestContext->setServerVar(ServerVars::PATH_INFO, $pathInfo); $requestContext->setServerVar(ServerVars::PATH_TRANSLATED, $documentRoot . $pathInfo); } // first check if wildcard file handler was registered if (isset($handlers['.*'])) { // set wildcard filehandler which will overload all specific filehandlers at this point $possibleValidPathExtension = '*'; } // check if file handler is defined for that script and expand request context if (isset($handlers['.' . $possibleValidPathExtension])) { // set the file handler to use for modules being able to react on this setting $requestContext->setServerVar(ServerVars::SERVER_HANDLER, $handlers['.' . $possibleValidPathExtension]['name']); // if file handler params are given, set them as module var if (isset($handlers['.' . $possibleValidPathExtension]['params'])) { $requestContext->setModuleVar(ModuleVars::VOLATILE_FILE_HANDLER_VARIABLES, $handlers['.' . $possibleValidPathExtension]['params']); } } }
/** * Initiates the module * * @param \AppserverIo\Server\Interfaces\RequestContextInterface $requestContext The request's context * @param \AppserverIo\Psr\HttpMessage\ResponseInterface $response The response instance * @param array $serverBackreferences Server backreferences * * @throws \InvalidArgumentException * * @return boolean */ public function apply(RequestContextInterface $requestContext, ResponseInterface $response, array $serverBackreferences) { // First of all we have to resolve the target string with the backreferences of the matching condition // Separate the keys from the values so we can use them in str_replace // And also mix in the server's backreferences for good measure $this->matchingBackreferences = array_merge($this->matchingBackreferences, $serverBackreferences); $backreferenceHolders = array_keys($this->matchingBackreferences); $backreferenceValues = array_values($this->matchingBackreferences); // If we got a target map (flag "M") we have to resolve the target string we have to use first // The following checks will be treated as an additional condition if (array_key_exists(RuleFlags::MAP, $this->sortedFlags) && !empty($this->sortedFlags[RuleFlags::MAP])) { // Get our map key for better readability $mapKey = str_replace(array_keys($this->matchingBackreferences), $this->matchingBackreferences, $this->sortedFlags[RuleFlags::MAP]); // Still here? That sounds good. Get the needed target string now if (isset($this->target[$mapKey])) { $this->target = $this->target[$mapKey]; } else { // Empty string, we will do nothing with this rule $this->target = ''; // Also clear any L-flag we might find, we could not find what we are looking for so we should not end if (array_key_exists(RuleFlags::LAST, $this->sortedFlags)) { unset($this->sortedFlags[RuleFlags::LAST]); } } } // Back to our rule... // If the target string is empty we do not have to do anything if (!empty($this->target)) { // Just make sure that you check for the existence of the query string first, as it might not be set $queryFreeRequestUri = $requestContext->getServerVar(ServerVars::X_REQUEST_URI); if ($requestContext->hasServerVar(ServerVars::QUERY_STRING)) { $queryFreeRequestUri = str_replace('?' . $requestContext->getServerVar(ServerVars::QUERY_STRING), '', $queryFreeRequestUri); // Set the "redirect" query string as a backup as we might change the original $requestContext->setServerVar('REDIRECT_QUERY_STRING', $requestContext->getServerVar(ServerVars::QUERY_STRING)); } $requestContext->setServerVar('REDIRECT_URL', $queryFreeRequestUri); // Substitute the backreferences in our operation $this->target = str_replace($backreferenceHolders, $backreferenceValues, $this->target); // We have to find out what type of rule we got here if (is_readable($this->target)) { // We have an absolute file path! $this->type = 'absolute'; // Set the REQUEST_FILENAME path $requestContext->setServerVar(ServerVars::REQUEST_FILENAME, $this->target); } elseif (filter_var($this->target, FILTER_VALIDATE_URL) !== false) { // We have a complete URL! $this->type = 'url'; } else { // Last but not least we might have gotten a relative path (most likely) // Build up the REQUEST_FILENAME from DOCUMENT_ROOT and X_REQUEST_URI (without the query string) $this->type = 'relative'; // Setting the X_REQUEST_URI for internal communication // Requested uri always has to begin with a slash $this->target = '/' . ltrim($this->target, '/'); $requestContext->setServerVar(ServerVars::X_REQUEST_URI, $this->target); // Only change the query string if we have one in our target string if (strpos($this->target, '?') !== false) { $requestContext->setServerVar(ServerVars::QUERY_STRING, substr(strstr($this->target, '?'), 1)); } } // do we have to make a redirect? // if so we have to have to set the status code accordingly and dispatch the response if (array_key_exists(RuleFlags::REDIRECT, $this->sortedFlags)) { $this->prepareRedirect($requestContext, $response); } // Lets tell them that we successfully made a redirect $requestContext->setServerVar(ServerVars::REDIRECT_STATUS, '200'); } // If we got the "LAST"-flag we have to end here, so return false if (array_key_exists(RuleFlags::LAST, $this->sortedFlags)) { return false; } // Still here? That sounds good return true; }
/** * Implement's module logic for given hook * * @param \AppserverIo\Psr\HttpMessage\RequestInterface $request A request object * @param \AppserverIo\Psr\HttpMessage\ResponseInterface $response A response object * @param \AppserverIo\Server\Interfaces\RequestContextInterface $requestContext A requests context instance * @param int $hook The current hook to process logic for * * @return bool * @throws \AppserverIo\Server\Exceptions\ModuleException */ public function process(RequestInterface $request, ResponseInterface $response, RequestContextInterface $requestContext, $hook) { // In php an interface is, by definition, a fixed contract. It is immutable. // So we have to declair the right ones afterwards... /** * @var $request \AppserverIo\Psr\HttpMessage\RequestInterface */ /** * @var $request \AppserverIo\Psr\HttpMessage\ResponseInterface */ // check if shutdown hook is comming if (ModuleHooks::SHUTDOWN === $hook) { return $this->shutdown($request, $response); } // if wrong hook is comming do nothing if (ModuleHooks::REQUEST_POST !== $hook) { return; } // set request context as member ref $this->requestContext = $requestContext; // set req and res internally $this->request = $request; $this->response = $response; // check if server handler sais php modules should react on this request as file handler if ($requestContext->getServerVar(ServerVars::SERVER_HANDLER) === self::MODULE_NAME) { // check if file does not exist if (!$requestContext->hasServerVar(ServerVars::SCRIPT_FILENAME)) { // send 404 $response->setStatusCode(404); throw new ModuleException(null, 404); } // init script filename var $scriptFilename = $requestContext->getServerVar(ServerVars::SCRIPT_FILENAME); /** * Check if script name exists on filesystem * This is necessary because of seq faults if a non existing file will be required. */ if (!file_exists($scriptFilename)) { // send 404 $response->setStatusCode(404); throw new ModuleException(null, 404); } /** * todo: fill up those server vars in future when mod auth is present * * PHP_AUTH_DIGEST * PHP_AUTH_USER * PHP_AUTH_PW */ // prepare modules specific server vars $this->prepareServerVars(); // initialize the globals $_SERVER, $_REQUEST, $_POST, $_GET, $_COOKIE, $_FILES and set the headers $this->initGlobals(); // start new php process $process = new ProcessThread($scriptFilename, $this->globals, $this->uploadedFiles); // start process $process->start(PTHREADS_INHERIT_ALL | PTHREADS_ALLOW_HEADERS); // wait for process to finish $process->join(); // check if process fatal error occurred so throw module exception because the modules process class // is not responsible for set correct headers and messages for error's in module context if ($lastError = $process->getLastError()) { // check if last error was a fatal one if ($lastError['type'] === E_ERROR || $lastError['type'] === E_USER_ERROR) { // check if output buffer was set by the application executed by the php process // so do not override content by exception stack trace if (strlen($errorMessage = $process->getOutputBuffer()) === 0) { $errorMessage = 'PHP Fatal error: ' . $lastError['message'] . ' in ' . $lastError['file'] . ' on line ' . $lastError['line']; } // set internal server error code with error message to exception throw new ModuleException($errorMessage, 500); } } // prepare response $this->prepareResponse($process); // store the file's contents in the response $response->appendBodyStream($process->getOutputBuffer()); // set response state to be dispatched after this without calling other modules process $response->setState(HttpResponseStates::DISPATCH); } }
/** * Implements module logic for given hook * * @param \AppserverIo\Psr\HttpMessage\RequestInterface $request A request object * @param \AppserverIo\Psr\HttpMessage\ResponseInterface $response A response object * @param \AppserverIo\Server\Interfaces\RequestContextInterface $requestContext A requests context instance * @param integer $hook The current hook to process logic for * * @return boolean * @throws \AppserverIo\Server\Exceptions\ModuleException */ public function process(RequestInterface $request, ResponseInterface $response, RequestContextInterface $requestContext, $hook) { // In php an interface is, by definition, a fixed contract. It is immutable. // So we have to declair the right ones afterwards... /** * @var $request \AppserverIo\Psr\HttpMessage\RequestInterface */ /** * @var $response \AppserverIo\Psr\HttpMessage\ResponseInterface */ // if false hook is comming do nothing if (ModuleHooks::REQUEST_POST !== $hook) { return; } // set req and res object internally $this->request = $request; $this->response = $response; // get default access definitions $accesses = $this->accesses; // check if there are some volatile access definitions so use them and override global accesses if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_ACCESSES)) { // reset by volatile accesses $accesses = $requestContext->getModuleVar(ModuleVars::VOLATILE_ACCESSES); } // generally everything is not allowed $allowed = false; if (isset($accesses['allow'])) { // check allow accesses information if something matches foreach ($accesses['allow'] as $accessData) { // we are optimistic an initial say data will match $matchAllow = true; // check if accessData matches server vars foreach ($accessData as $serverVar => $varPattern) { // check if server var exists if ($requestContext->hasServerVar($serverVar)) { // check if pattern matches if (!preg_match('/' . $varPattern . '/', $requestContext->getServerVar($serverVar))) { $matchAllow = false; // break here if anything not matches break; } } } if ($matchAllow) { // set allowed flag true $allowed = true; // break here cause' we found an allowed access break; } } } if (isset($accesses['deny'])) { // check deny accesses information if something matches foreach ($accesses['deny'] as $accessData) { // initial nothing denies the request $matchDeny = false; // check if accessData matches server vars foreach ($accessData as $serverVar => $varPattern) { // check if server var exists if ($requestContext->hasServerVar($serverVar)) { // check if pattern matches if (preg_match('/' . $varPattern . '/', $requestContext->getServerVar($serverVar))) { $matchDeny = true; // break here if anything matches break; } } } if ($matchDeny) { // set allowed flag false $allowed = false; // break here cause' we found an allowed access break; } } } // check if it's finally not allowed if (!$allowed) { throw new ModuleException('This request is forbidden', 403); } }