/** * Will call for the measurement protocol endpoint * * @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 * * @return null */ public function call(RequestInterface $request, ResponseInterface $response, RequestContextInterface $requestContext) { // merge default and configured parameters into our list $parameters = array_merge($this->defaultParameters, $this->parameters); // we want the request to be like it came from the same host, so we will reuse part of it $parameters['ua'] = $request->getHeader(HttpProtocol::HEADER_USER_AGENT); $parameters['uip'] = $requestContext->getServerVar(ServerVars::REMOTE_ADDR); // the client will be a random UUID, at least if we do not get a matching cookie if ($request->hasHeader(HttpProtocol::HEADER_COOKIE)) { // the user is known to us $cookie = $request->getHeader(HttpProtocol::HEADER_COOKIE); $matches = array(); preg_match('/_ga=GA[0-9]\\.[0-9]\\.(.+)/', $cookie, $matches); if (isset($matches[1])) { $parameters['cid'] = $matches[1]; // remove the cookie to avoid additional calls $response->removeCookie('_ga'); // filter the parameters for a known cookie $parameters = $this->filterParameters($parameters, self::COOKIE_PRESENT); } } // if there is no known client id we will set one randomly if (!isset($parameters['cid'])) { $uuid4 = Uuid::uuid4(); $parameters['cid'] = $uuid4->toString(); // filter the parameters for usage without cookie $parameters = $this->filterParameters($parameters, self::COOKIE_NOT_PRESENT); } // make the actual call $this->sendToService($parameters); }
/** * Simply returns the first application, assuming we only have one. If no application * has been deployed, an exception will be thrown. * * @param \AppserverIo\Server\Interfaces\RequestContextInterface $requestContext Context of the current request * * @return null|\AppserverIo\Psr\Application\ApplicationInterface * @throws \AppserverIo\Appserver\ServletEngine\BadRequestException Is thrown if no application is available */ public function findRequestedApplication(RequestContextInterface $requestContext) { // return the first application (we only have one) foreach ($this->applications as $application) { return $application; } // if we did not find anything we should throw a bad request exception throw new BadRequestException(sprintf('Can\'t find application for URL %s%s', $requestContext->getServerVar(ServerVars::HTTP_HOST), $requestContext->getServerVar(ServerVars::X_REQUEST_URI)), 404); }
/** * Will try to find the application based on the context path taken from the requested filename. * Will return the found application on success and throw an exception if nothing could be found * * @param \AppserverIo\Server\Interfaces\RequestContextInterface $requestContext Context of the current request * * @return null|\AppserverIo\Psr\Application\ApplicationInterface * * @throws \AppserverIo\Appserver\ServletEngine\BadRequestException */ protected function findRequestedApplication(RequestContextInterface $requestContext) { // prepare the request URL we want to match $webappsDir = $this->getServerContext()->getServerConfig()->getDocumentRoot(); $relativeRequestPath = strstr($requestContext->getServerVar(ServerVars::DOCUMENT_ROOT) . $requestContext->getServerVar(ServerVars::X_REQUEST_URI), $webappsDir); $proposedAppName = strstr(str_replace($webappsDir . '/', '', $relativeRequestPath), '/', true); // try to match a registered application with the passed request foreach ($this->applications as $application) { if ($application->getName() === $proposedAppName) { return $application; } } // if we did not find anything we should throw a bad request exception throw new BadRequestException(sprintf('Can\'t find application for URL %s%s', $requestContext->getServerVar(ServerVars::HTTP_HOST), $requestContext->getServerVar(ServerVars::X_REQUEST_URI)), 404); }
/** * Inits the connection handler by given context and params * * @param \AppserverIo\Server\Interfaces\ServerContextInterface $serverContext The server's context * @param array $params The params for connection handler * * @return void */ public function init(ServerContextInterface $serverContext, array $params = null) { // set server context $this->serverContext = $serverContext; // set params $this->errorsPageTemplate = $params["errorsPageTemplate"]; // init http request object $httpRequest = new HttpRequest(); // init http response object $httpResponse = new HttpResponse(); // set default response headers $httpResponse->setDefaultHeaders(array(Protocol::HEADER_SERVER => $this->getServerConfig()->getSoftware(), Protocol::HEADER_CONNECTION => Protocol::HEADER_CONNECTION_VALUE_CLOSE)); // setup http parser $this->parser = new HttpRequestParser($httpRequest, $httpResponse); $this->parser->injectQueryParser(new HttpQueryParser()); $this->parser->injectPart(new HttpPart()); // setup request context // get request context type $requestContextType = $this->getServerConfig()->getRequestContextType(); /** * @var \AppserverIo\Server\Interfaces\RequestContextInterface $requestContext */ // instantiate and init request context $this->requestContext = new $requestContextType(); $this->requestContext->init($this->getServerConfig()); }
/** * Inits the connection handler by given context and params * * @param \AppserverIo\Server\Interfaces\ServerContextInterface $serverContext The server's context * @param array $params The params for connection handler * * @return void */ public function init(ServerContextInterface $serverContext, array $params = null) { $this->serverContext = $serverContext; // init DNS request object $dnsRequest = new DnsRequest(); // init DNS response object $dnsResponse = new DnsResponse(); // setup DNS parser $this->parser = new DnsRequestParser($dnsRequest, $dnsResponse); // get request context type $requestContextType = $this->getServerConfig()->getRequestContextType(); /** * @var \AppserverIo\Server\Interfaces\RequestContextInterface $requestContext */ // instantiate and init request context $this->requestContext = new $requestContextType(); $this->requestContext->init($this->getServerConfig()); }
/** * 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 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) { // get server context to local ref $serverContext = $this->getServerContext(); // check if response post is is comming if (ModuleHooks::RESPONSE_POST === $hook) { $this->checkShouldDisconnect(); return; } // if wrong hook is coming do nothing if (ModuleHooks::REQUEST_POST !== $hook) { return; } try { // init upstreamname and transport $upstreamName = null; $transport = 'tcp'; // check if we've configured module variables if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_FILE_HANDLER_VARIABLES)) { // load the volatile file handler variables and set connection data $fileHandlerVariables = $requestContext->getModuleVar(ModuleVars::VOLATILE_FILE_HANDLER_VARIABLES); // check if upstream is set for proxy function if (isset($fileHandlerVariables['upstream'])) { $upstreamName = $fileHandlerVariables['upstream']; } if (isset($fileHandlerVariables['transport'])) { $transport = $fileHandlerVariables['transport']; } } // if there was no upstream defined if (is_null($upstreamName)) { throw new ModuleException('No upstream configured for proxy filehandler'); } // get upstream instance by configured upstream name $upstream = $serverContext->getUpstream($upstreamName); // find next proxy server by given upstream type $remoteAddr = $requestContext->getServerVar(ServerVars::REMOTE_ADDR); $proxyServer = $upstream->findServer(md5($remoteAddr)); // build proxy socket address for connection $proxySocketAddress = sprintf('%s://%s:%s', $transport, $proxyServer->getAddress(), $proxyServer->getPort()); // check if should reconnect $this->checkShouldDisconnect(); // check if proxy connection object was initialised but connection resource is not ready if ($this->connection && $this->connection->getStatus() === false) { // unset connection if corrupt $this->connection = null; } // check if connection should be established if ($this->connection === null) { // create and connect to defined backend $this->connection = StreamSocket::getClientInstance($proxySocketAddress); // set proxy connection resource as stream source for body stream directly // that avoids huge memory consumtion when transferring big files via proxy connections $response->setBodyStream($this->connection->getConnectionResource()); } // get connection to local var $connection = $this->connection; // build up raw request start line $rawRequestString = sprintf('%s %s %s' . "\r\n", $request->getMethod(), $request->getUri(), HttpProtocol::VERSION_1_1); // populate request headers $headers = $request->getHeaders(); foreach ($headers as $headerName => $headerValue) { // @todo: make keep-alive available for proxy connections if ($headerName === HttpProtocol::HEADER_CONNECTION) { $headerValue = HttpProtocol::HEADER_CONNECTION_VALUE_CLOSE; } $rawRequestString .= $headerName . HttpProtocol::HEADER_SEPARATOR . $headerValue . "\r\n"; } // get current protocol $reqProto = $requestContext->getServerVar(ServerVars::REQUEST_SCHEME); // add proxy depending headers $rawRequestString .= HttpProtocol::HEADER_X_FORWARD_FOR . HttpProtocol::HEADER_SEPARATOR . $remoteAddr . "\r\n"; $rawRequestString .= HttpProtocol::HEADER_X_FORWARDED_PROTO . HttpProtocol::HEADER_SEPARATOR . $reqProto . "\r\n"; $rawRequestString .= "\r\n"; // write headers to proxy connection $connection->write($rawRequestString); // copy raw request body stream to proxy connection $connection->copyStream($request->getBodyStream()); // read status line from proxy connection $statusLine = $connection->readLine(1024, 5); // parse start line list(, $responseStatusCode) = explode(' ', $statusLine); // map everything from proxy response to our response object $response->setStatusCode($responseStatusCode); $line = ''; $messageHeaders = ''; while (!in_array($line, array("\r\n", "\n"))) { // read next line $line = $connection->readLine(); // enhance headers $messageHeaders .= $line; } // remove ending CRLF's before parsing $messageHeaders = trim($messageHeaders); // check if headers are empty if (strlen($messageHeaders) === 0) { throw new HttpException('Missing headers'); } // delimit headers by CRLF $headerLines = explode("\r\n", $messageHeaders); // iterate all headers foreach ($headerLines as $headerLine) { // extract header info $extractedHeaderInfo = explode(HttpProtocol::HEADER_SEPARATOR, trim($headerLine)); if (!$extractedHeaderInfo || $extractedHeaderInfo[0] === $headerLine) { throw new HttpException('Wrong header format'); } // split name and value list($headerName, $headerValue) = $extractedHeaderInfo; // check header name for server // @todo: make this configurable if ($headerName === HttpProtocol::HEADER_SERVER) { continue; } // add header $response->addHeader(trim($headerName), trim($headerValue)); } // set flag false by default $this->shouldDisconnect = false; // check if connection should be closed as given in connection header if ($response->getHeader(HttpProtocol::HEADER_CONNECTION) === HttpProtocol::HEADER_CONNECTION_VALUE_CLOSE) { $this->shouldDisconnect = true; } } catch (\AppserverIo\Psr\Socket\SocketReadException $e) { // close and unset connection and try to process the request again to // not let a white page get delivered to the client $this->shouldDisconnect = true; return $this->process($request, $response, $requestContext, $hook); } catch (\AppserverIo\Psr\Socket\SocketReadTimeoutException $e) { // close and unset connection and try to process the request again to // not let a white page get delivered to the client $this->shouldDisconnect = true; return $this->process($request, $response, $requestContext, $hook); } // set response to be dispatched at this point $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); } }
/** * Will call for the measurement protocol endpoint * * @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 * * @return null */ public function call(RequestInterface $request, ResponseInterface $response, RequestContextInterface $requestContext) { // merge default and configured parameters into our list $parameters = array_merge($this->defaultParameters, $this->parameters); // we want the request to be like it came from the same host, so we will reuse part of it $parameters['ua'] = $request->getHeader(HttpProtocol::HEADER_USER_AGENT); $parameters['uip'] = $requestContext->getServerVar(ServerVars::REMOTE_ADDR); // the client will be a random UUID, at least if we do not get a matching cookie if ($request->hasHeader(HttpProtocol::HEADER_COOKIE)) { $cookie = $request->getHeader(HttpProtocol::HEADER_COOKIE); $matches = array(); preg_match('/_ga=GA[0-9]\\.[0-9]\\.(.+)/', $cookie, $matches); if (isset($matches[1])) { $parameters['cid'] = $matches[1]; } } if (!isset($parameters['cid'])) { $uuid4 = Uuid::uuid4(); $parameters['cid'] = $uuid4->toString(); } // make the actual call $this->sendToService($parameters); }
/** * Process servlet request. * * @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) { // if false hook is coming do nothing if (ModuleHooks::REQUEST_POST !== $hook) { return; } // check if we are the handler that has to process this request if ($requestContext->getServerVar(ServerVars::SERVER_HANDLER) !== $this->getModuleName()) { return; } // load the application associated with this request $application = $this->findRequestedApplication($requestContext); // check if the application has already been connected if ($application->isConnected() === false) { throw new \Exception(sprintf('Application %s has not connected yet', $application->getName()), 503); } // create a copy of the valve instances $valves = $this->valves; $handlers = $this->handlers; // create a new request instance from the HTTP request $servletRequest = new Request(); $servletRequest->injectHandlers($handlers); $servletRequest->injectHttpRequest($request); $servletRequest->injectServerVars($requestContext->getServerVars()); $servletRequest->init(); // initialize the request handler instance $requestHandler = new RequestHandler(); $requestHandler->injectValves($valves); $requestHandler->injectApplication($application); $requestHandler->injectRequest($servletRequest); $requestHandler->start(PTHREADS_INHERIT_NONE | PTHREADS_INHERIT_CONSTANTS); $requestHandler->join(); // copy values to the HTTP response $requestHandler->copyToHttpResponse($response); // 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 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 $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 rewrite maps definitions $rewriteMaps = $this->rewriteMaps; // check if there are some volatile rewrite map definitions so add them if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_REWRITE_MAPS)) { $volatileRewriteMaps = $requestContext->getModuleVar(ModuleVars::VOLATILE_REWRITE_MAPS); // merge rewrite maps $rewriteMaps = array_merge($volatileRewriteMaps, $this->rewriteMaps); } // check protocol to be either http or https when secure is going on $protocol = 'http://'; if ($requestContext->getServerVar(ServerVars::HTTPS) === ServerVars::VALUE_HTTPS_ON) { $protocol = 'https://'; } // get clean request path without query string etc... $requestPath = parse_url($requestContext->getServerVar(ServerVars::X_REQUEST_URI), PHP_URL_PATH); // init all rewrite mappers by types and do look up foreach ($rewriteMaps as $rewriteMapType => $rewriteMapParams) { // Include the requested hostname as a param, some mappers might need it $rewriteMapParams['headerHost'] = $request->getHeader(Protocol::HEADER_HOST); // Same for the protocol $rewriteMapParams['protocol'] = $protocol; // Get ourselves a rewriteMapper of the right type $rewriteMapper = new $rewriteMapType($rewriteMapParams); // lookup by request path if ($targetUrl = $rewriteMapper->lookup($requestPath)) { // set enhance uri to response $response->addHeader(Protocol::HEADER_LOCATION, $targetUrl); // send redirect status $response->setStatusCode(301); // add header to be sure that is was us $response->addHeader('X-Rewritten-By', __CLASS__); // set response state to be dispatched after this without calling other modules process $response->setState(HttpResponseStates::DISPATCH); // We found something, stop the loop break; } } 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 boolean * @throws \AppserverIo\Server\Exceptions\ModuleException */ public function process(RequestInterface $request, ResponseInterface $response, RequestContextInterface $requestContext, $hook) { try { // if false hook is coming do nothing if (ModuleHooks::RESPONSE_POST !== $hook) { return; } // get default analytics definitions $analytics = $this->analytics; // check if there are some volatile access definitions so use them and override global accesses if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_ANALYTICS)) { // reset by volatile accesses $analytics = array_merge($analytics, $requestContext->getModuleVar(ModuleVars::VOLATILE_ANALYTICS)); } // check all analytics and check if the uri matches foreach ($analytics as $analytic) { // run through our connectors if the if the URI matches $matches = array(); if (preg_match('/' . $analytic['uri'] . '/', $requestContext->getServerVar(ServerVars::X_REQUEST_URI), $matches)) { // we only need the matching parts of the URI unset($matches[0]); // prepare the matches for later usage $backreferenceKeys = array(); foreach ($matches as $key => $match) { $backreferenceKeys[] = '$' . $key; } // iterate over all connectors and call their services foreach ($analytic['connectors'] as $connector) { // iterate all params and fill in the regex backreferences foreach ($connector['params'] as $key => $param) { // if the param might contain backreferences we will replace them if (strpos($param, '$') !== false) { $connector['params'][$key] = str_replace($backreferenceKeys, $matches, $param); } } // make a new connector instance, initialize it and make the call to its service $connectorClass = str_replace('\\\\', '\\', $connector['type']); if (class_exists($connectorClass)) { // create the connector an make the call through it $connectorInstance = new $connectorClass($this->serverContext); $connectorInstance->init($connector['params']); $connectorInstance->call($request, $response, $requestContext); } } } } } catch (\Exception $e) { // Re-throw as a ModuleException throw new ModuleException($e); } }
/** * 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 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 declare 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::RESPONSE_PRE !== $hook) { return; } // check if content type header exists if not stop processing if (!$response->hasHeader(Protocol::HEADER_CONTENT_TYPE)) { return; } // check if no accept encoding headers are sent if (!$request->hasHeader(Protocol::HEADER_ACCEPT_ENCODING)) { return; } // check if response was encoded before and exit than if ($response->hasHeader(Protocol::HEADER_CONTENT_ENCODING)) { return; } // do not deflate on proxy requests because proxy servers are responsible for sending correct responses if ($requestContext->getServerVar(ServerVars::SERVER_HANDLER) === 'proxy') { // stop processing return; } // check if request accepts deflate if (strpos($request->getHeader(Protocol::HEADER_ACCEPT_ENCODING), 'deflate') !== false) { // get stream meta data $streamMetaData = stream_get_meta_data($response->getBodyStream()); /** * Currently it's not possible to apply zlib.deflate filter on memory (php://memory) or * temp (php://temp) streams due to a bug in that zlib library., * * So for now we'll check if stream type is not MEMORY in case of static files and add * deflate filter just for static files served via core module. * * @link https://bugs.php.net/bug.php?id=48725 */ if ($streamMetaData['stream_type'] !== 'MEMORY' && $this->isRelevantMimeType($response->getHeader(Protocol::HEADER_CONTENT_TYPE))) { // apply encoding filter to response body stream stream_filter_append($response->getBodyStream(), 'zlib.deflate', STREAM_FILTER_READ); // rewind current body stream @rewind($response->getBodyStream()); // copy body stream to make use of filter in read mode $deflateBodyStream = fopen('php://memory', 'w+b'); // copy stream with appended filter to new deflate body stream stream_copy_to_stream($response->getBodyStream(), $deflateBodyStream); // reset body stream on response $response->setBodyStream($deflateBodyStream); // set encoding header info $response->addHeader(Protocol::HEADER_CONTENT_ENCODING, 'deflate'); } } }
/** * 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 $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 document root $documentRoot = $requestContext->getServerVar(ServerVars::DOCUMENT_ROOT); // get url $url = parse_url($requestContext->getServerVar(ServerVars::X_REQUEST_URI), PHP_URL_PATH); // get query string with asterisk $queryString = strstr($requestContext->getServerVar(ServerVars::X_REQUEST_URI), '?'); // get read path to requested uri $realPath = $documentRoot . $url; // check if it's a dir if (is_dir($realPath) || $url === '/') { // check if uri has trailing slash if (substr($url, -1) !== '/') { // set enhance uri with trailing slash to response $response->addHeader(Protocol::HEADER_LOCATION, $url . '/' . $queryString); // send redirect status $response->setStatusCode(301); // set response state to be dispatched after this without calling other modules process $response->setState(HttpResponseStates::DISPATCH); } else { // check directory index definitions foreach ($this->getDirectoryIndex() as $index) { // check if defined index files are found in directory if (is_file($realPath . $index)) { // reset uri with indexed filename $requestContext->setServerVar(ServerVars::X_REQUEST_URI, $url . $index . $queryString); // break out if index file was found return true; } } } } return true; }
/** * 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 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) { // if false hook is coming do nothing if (ModuleHooks::REQUEST_POST !== $hook) { return; } // set req and res object internally $this->request = $request; $this->response = $response; // get server context to local var $serverContext = $this->getServerContext(); // Get the authentications locally so we do not mess with inter-request configuration $authenticationSets = array(); // check if there are some volatile rewrite map definitions so add them if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_AUTHENTICATIONS)) { $authenticationSets[] = $requestContext->getModuleVar(ModuleVars::VOLATILE_AUTHENTICATIONS); } // get the global authentications last, as volatile authentications are prefered here as more specific configurations can lessen security $authenticationSets[] = $this->authentications; // get system logger $systemLogger = $serverContext->getLogger(LoggerUtils::SYSTEM); // check authentication information if something matches foreach ($authenticationSets as $authenticationSet) { foreach ($authenticationSet as $uriPattern => $data) { // check if pattern matches uri if (preg_match('/' . $uriPattern . '/', $requestContext->getServerVar(ServerVars::X_REQUEST_URI))) { // set type Instance to local ref $typeInstance = $this->getAuthenticationInstance($uriPattern, $data); // check if auth header is not set in coming request headers if (!$request->hasHeader(Protocol::HEADER_AUTHORIZATION)) { // send header for challenge authentication against client $response->addHeader(Protocol::HEADER_WWW_AUTHENTICATE, $typeInstance->getAuthenticateHeader()); // throw exception for auth required throw new ModuleException(null, 401); } // init type instance by request $typeInstance->init($request->getHeader(Protocol::HEADER_AUTHORIZATION), $request->getMethod()); try { // check if auth works if ($typeInstance->authenticate()) { // set server vars $requestContext->setServerVar(ServerVars::REMOTE_USER, $typeInstance->getUsername()); // break out because everything is fine at this point break; } } catch (\Exception $e) { // log exception as warning to not end up with a 500 response which is not wanted here $systemLogger->warning($e->getMessage()); } // send header for challenge authentication against client $response->addHeader(Protocol::HEADER_WWW_AUTHENTICATE, $typeInstance->getAuthenticateHeader()); // throw exception for auth required throw new ModuleException(null, 401); } } } }
/** * Will prepare a response for a redirect. * This includes setting the new target, the appropriate status code and dispatching it to break the * module chain * * @param \AppserverIo\Server\Interfaces\RequestContextInterface $requestContext The request's context * @param \AppserverIo\Psr\HttpMessage\ResponseInterface $response The response instance to be prepared * * @return void */ protected function prepareRedirect($requestContext, ResponseInterface $response) { // if we got a specific status code we have to filter it and apply it if possible $statusCode = 301; $proposedStatusCode = $this->sortedFlags[RuleFlags::REDIRECT]; if (is_numeric($proposedStatusCode) && $proposedStatusCode >= 300 && $proposedStatusCode < 400) { $statusCode = $proposedStatusCode; } // there might be work to be done depending on whether or not we got a complete URL if ($this->type === 'relative') { $newTarget = $requestContext->getServerVar(ServerVars::REQUEST_SCHEME); $newTarget .= '://'; $newTarget .= $requestContext->getServerVar(ServerVars::HTTP_HOST); $this->target = $newTarget . $this->getTarget(); } // set enhance uri to response $response->addHeader(Protocol::HEADER_LOCATION, $this->target); // send redirect status $response->setStatusCode($statusCode); // set response state to be dispatched after this without calling other modules process $response->setState(HttpResponseStates::DISPATCH); }
/** * Process servlet request. * * @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) { // if false hook is coming do nothing if (ModuleHooks::REQUEST_POST !== $hook) { return; } // check if we are the handler that has to process this request if ($requestContext->getServerVar(ServerVars::SERVER_HANDLER) !== $this->getModuleName()) { return; } // load the application associated with this request $application = $this->findRequestedApplication($requestContext); $application->registerClassLoaders(); // check if the application has already been connected if ($application->isConnected() === false) { throw new \Exception(sprintf('Application %s has not connected yet', $application->getName()), 503); } // create a copy of the valve instances $valves = $this->valves; $handlers = $this->handlers; // create a new request instance from the HTTP request $servletRequest = new Request(); $servletRequest->injectHandlers($handlers); $servletRequest->injectHttpRequest($request); $servletRequest->injectServerVars($requestContext->getServerVars()); $servletRequest->init(); // initialize servlet response $servletResponse = new Response(); $servletResponse->init(); // load the session and the authentication manager $sessionManager = $application->search(SessionManagerInterface::IDENTIFIER); $authenticationManager = $application->search(AuthenticationManagerInterface::IDENTIFIER); // inject the sapplication and servlet response $servletRequest->injectContext($application); $servletRequest->injectResponse($servletResponse); $servletRequest->injectSessionManager($sessionManager); $servletRequest->injectAuthenticationManager($authenticationManager); // prepare the request instance $servletRequest->prepare(); // initialize static request and application context RequestHandler::$requestContext = $servletRequest; RequestHandler::$applicationContext = $application; // process the valves foreach ($valves as $valve) { $valve->invoke($servletRequest, $servletResponse); if ($servletRequest->isDispatched() === true) { break; } } // copy response values to the HTTP response $response->setState($servletResponse->getState()); $response->setVersion($servletResponse->getVersion()); $response->setStatusCode($servletResponse->getStatusCode()); $response->setStatusReasonPhrase($servletResponse->getStatusReasonPhrase()); // copy the body content to the HTTP response $response->appendBodyStream($servletResponse->getBodyStream()); // copy headers to the HTTP response foreach ($servletResponse->getHeaders() as $headerName => $headerValue) { $response->addHeader($headerName, $headerValue); } // copy cookies to the HTTP response $response->setCookies($servletResponse->getCookies()); // append the servlet engine's signature $response->addHeader(Protocol::HEADER_X_POWERED_BY, get_class($this), true); // 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 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) { // 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; $virtualHosts = $this->getServerContext()->getServerConfig()->getVirtualHosts(); $serverName = $requestContext->getServerVar(ServerVars::SERVER_NAME); // check if current host matches any virtual host configuration if (isset($virtualHosts[$serverName])) { // read out params $params = $virtualHosts[$serverName]['params']; // iterate over all params and try to set as server var via mapping foreach ($params as $paramName => $paramValue) { // check if server var mapping exists if (isset($this->paramServerVarsMap[$paramName])) { // check if documentRoot is changed if ($this->paramServerVarsMap[$paramName] === ServerVars::DOCUMENT_ROOT) { // check if relative path is given and make is absolute by using cwd as prefix if (substr($paramValue, 0, 1) !== "/") { $paramValue = getcwd() . DIRECTORY_SEPARATOR . $paramValue; } } // set server var $requestContext->setServerVar($this->paramServerVarsMap[$paramName], $paramValue); } } // Add the headers we have (if any) to the configuration's headers pool if (!empty($virtualHosts[$serverName]['headers'])) { // Set the rewrites we encountered as a temporary module var $requestContext->setModuleVar(ModuleVars::VOLATILE_HEADERS, $virtualHosts[$serverName]['headers']); } // Add the rewrites we have (if any) to the configuration's rewrite pool if (!empty($virtualHosts[$serverName]['rewrites'])) { // Set the rewrites we encountered as a temporary module var $requestContext->setModuleVar(ModuleVars::VOLATILE_REWRITES, $virtualHosts[$serverName]['rewrites']); } // Add the environment vars we have (if any) to the configuration's environment variable pool if (!empty($virtualHosts[$serverName]['environmentVariables'])) { // Set the environment variables we encountered as a temporary module var $requestContext->setModuleVar(ModuleVars::VOLATILE_ENVIRONMENT_VARIABLES, $virtualHosts[$serverName]['environmentVariables']); } // Add the accesses (if any) to the configuration's access pool if (!empty($virtualHosts[$serverName]['accesses'])) { // Set the environment variables we encountered as a temporary module var $requestContext->setModuleVar(ModuleVars::VOLATILE_ACCESSES, $virtualHosts[$serverName]['accesses']); } // add the analytics (if any) to the configuration's analytics pool if (!empty($virtualHosts[$serverName]['analytics'])) { // set the analytics we encountered as a temporary module var $requestContext->setModuleVar(ModuleVars::VOLATILE_ANALYTICS, $virtualHosts[$serverName]['analytics']); } // Add the locations we have (if any) to the configuration's location pool if (!empty($virtualHosts[$serverName]['locations'])) { // Set the locations we encountered as a temporary module var $requestContext->setModuleVar(ModuleVars::VOLATILE_LOCATIONS, $virtualHosts[$serverName]['locations']); } // Add the rewriteMaps we have (if any) to the configuration's rewriteMaps pool if (!empty($virtualHosts[$serverName]['rewriteMaps'])) { // Set the rewriteMaps we encountered as a temporary module var $requestContext->setModuleVar(ModuleVars::VOLATILE_REWRITE_MAPS, $virtualHosts[$serverName]['rewriteMaps']); } // Add the authentications we have (if any) to the configuration's authentications pool if (!empty($virtualHosts[$serverName]['authentications'])) { // Set the authentications we encountered as a temporary module var $requestContext->setModuleVar(ModuleVars::VOLATILE_AUTHENTICATIONS, $virtualHosts[$serverName]['authentications']); } } }
/** * 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) { // if false hook is comming do nothing if (ModuleHooks::RESPONSE_PRE !== $hook) { return; } // set req and res object internally $this->request = $request; $this->response = $response; // get server context ref to local func $serverContext = $this->getServerContext(); // init volatile headers array $volatileHeaders = array(); // apply possible volatile headers if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_HEADERS)) { $volatileHeaders = $requestContext->getModuleVar(ModuleVars::VOLATILE_HEADERS); } // apply server headers first and then volatile headers $this->applyHeaders(array_merge_recursive($serverContext->getServerConfig()->getHeaders(), $volatileHeaders)); // signal good processing state return true; }
/** * 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 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) { // if false hook is coming do nothing if (ModuleHooks::REQUEST_POST !== $hook) { return; } // set req and res object internally $this->request = $request; $this->response = $response; // get server context to local var $serverContext = $this->getServerContext(); // Get the authentications locally so we do not mess with inter-request configuration $authenticationSets = array(); // check if there are some volatile rewrite map definitions so add them if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_AUTHENTICATIONS)) { $authenticationSets[] = $requestContext->getModuleVar(ModuleVars::VOLATILE_AUTHENTICATIONS); } // get the global authentications last, as volatile authentications are prefered here as more specific configurations can lessen security $authenticationSets[] = $this->authentications; // get system logger $systemLogger = $serverContext->getLogger(LoggerUtils::SYSTEM); // check authentication information if something matches foreach ($authenticationSets as $authenticationSet) { foreach ($authenticationSet as $uriPattern => $data) { // check if pattern matches uri if (preg_match('/' . $uriPattern . '/', $requestContext->getServerVar(ServerVars::X_REQUEST_URI))) { try { // append the document root to the authentication params $data['documentRoot'] = $requestContext->getServerVar(ServerVars::DOCUMENT_ROOT); // create a local type instance, initialize and authenticate the request $typeInstance = $this->getAuthenticationInstance($uriPattern, $data); $typeInstance->init($request, $response); $typeInstance->authenticate($response); // set authenticated username as a server var $requestContext->setServerVar(ServerVars::REMOTE_USER, $typeInstance->getUsername()); // break out because everything is fine at this point break; } catch (\Exception $e) { // log exception as warning to not end up with a 500 response which is not wanted here $systemLogger->warning($e->getMessage()); } // throw exception for auth required throw new ModuleException(null, 401); } } } }
/** * 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 $response \AppserverIo\Psr\HttpMessage\ResponseInterface */ // if false hook is coming do nothing if (ModuleHooks::REQUEST_POST !== $hook) { return; } // We have to throw a ModuleException on failure, so surround the body with a try...catch block try { // set request context as member property for further usage $this->requestContext = $requestContext; // Reset the $serverBackreferences array to avoid mixups of different requests $this->serverBackreferences = array(); // Resolve all used backreferences which are NOT linked to the query string. // We will resolve query string related backreferences separately as we are not able to cache them // as easily as, say, the URI // We also have to resolve all the changes rules in front of us made, so build up the backreferences // IN the loop. // TODO switch to backreference request not prefill as it might be faster $this->fillContextBackreferences(); $this->fillHeaderBackreferences($request); $this->fillSslEnvironmentBackreferences(); // Get the environment variables as the array they are within the config. // We have to also collect any volative rules which might be set on request base. // We might not even get anything, so prepare our rules accordingly $volatileEnvironmentVariables = array(); if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_ENVIRONMENT_VARIABLES)) { $volatileEnvironmentVariables = $requestContext->getModuleVar(ModuleVars::VOLATILE_ENVIRONMENT_VARIABLES); } // Build up the complete ruleset, volatile rules up front $variables = array_merge($volatileEnvironmentVariables, $this->configuredVariables); // Only act if we got something if (is_array($variables)) { // Convert the rules to our internally used objects foreach ($variables as $variable) { // Make that condition handling only if there even are conditions if (!empty($variable['condition'])) { // Get the operand $condition = $variable['condition'] . $this->getDefaultOperand(); if (strpos($condition, '@') !== false) { // Get the pieces of the condition $conditionPieces = array(); preg_match_all('`(.*?)@(\\$[0-9a-zA-Z_\\-]+)`', $condition, $conditionPieces); // Check the condition and continue for the next variable if we do not match if (!isset($this->serverBackreferences[$conditionPieces[2][0]])) { continue; } // Do we have a match? Get the potential backreferences $conditionBackreferences = array(); if (preg_match('`' . $conditionPieces[1][0] . '`', $this->serverBackreferences[$conditionPieces[2][0]], $conditionBackreferences) !== 1) { continue; } } } // We have to split up the definition string, if we do not find a equal character we have to fail if (!strpos($variable['definition'], '=')) { throw new ModuleException('Invalid definition ' . $variable['definition'] . 'missing "=".'); } // Get the variable name and its value from the definition string $varName = $this->filterVariableName(strstr($variable['definition'], '=', true)); $value = substr(strstr($variable['definition'], '='), 1); // We also have to resolve backreferences for the value part of the definition, as people might want // to pass OS environment vars to the server vars if (strpos($value, '$') !== false) { // Get the possible backreference (might as well be something else) and resolve it if needed // TODO tell them if we do not find a backreference to resolve, might be a problem $possibleBackreferences = array(); preg_match('`\\$.+?`', $value, $possibleBackreferences); foreach ($possibleBackreferences as $possibleBackreference) { if ($backrefValue = getenv($possibleBackreference)) { // Do we have a backreference which is a server or env var? $value = str_replace($possibleBackreference, $backrefValue, $value); } elseif (isset($conditionBackreferences[(int) substr($possibleBackreference, 1)])) { // We got no backreference from any of the server or env vars, so maybe we got // something from the preg_match $value = str_replace($possibleBackreference, $conditionBackreferences[(int) substr($possibleBackreference, 1)], $value); } } } // If the value is "null" we will unset the variable if ($value === 'null') { // Unset the variable and continue with the next environment variable if ($requestContext->hasEnvVar($varName)) { $requestContext->unsetEnvVar($varName); } continue; } // Take action according to the needed definition $requestContext->setEnvVar($varName, $value); } } } catch (\Exception $e) { // Re-throw as a ModuleException throw new ModuleException($e); } }
/** * Process servlet request. * * @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) { try { // if false hook is coming do nothing if (ModuleHooks::REQUEST_POST !== $hook) { return; } // check if we are the handler that has to process this request if ($requestContext->getServerVar(ServerVars::SERVER_HANDLER) !== $this->getModuleName()) { return; } // initialize servlet session, request + response $servletRequest = new Request(); $servletRequest->injectHttpRequest($request); $servletRequest->injectServerVars($requestContext->getServerVars()); // initialize the parts foreach ($request->getParts() as $part) { $servletRequest->addPart(Part::fromHttpRequest($part)); } // set the body content if we can find one if ($request->getHeader(HttpProtocol::HEADER_CONTENT_LENGTH) > 0) { $servletRequest->setBodyStream($request->getBodyContent()); } // prepare the servlet request $this->prepareServletRequest($servletRequest); // initialize the servlet response with the Http response values $servletResponse = new Response(); $servletRequest->injectResponse($servletResponse); // load the application associated with this request $application = $this->findRequestedApplication($requestContext); // prepare and set the applications context path $servletRequest->setContextPath($contextPath = '/' . $application->getName()); $servletRequest->setServletPath(str_replace($contextPath, '', $servletRequest->getServletPath())); // prepare the base modifier which allows our apps to provide a base URL $webappsDir = $this->getServerContext()->getServerConfig()->getDocumentRoot(); $relativeRequestPath = strstr($servletRequest->getServerVar(ServerVars::DOCUMENT_ROOT), $webappsDir); $proposedBaseModifier = str_replace($webappsDir, '', $relativeRequestPath); if (strpos($proposedBaseModifier, $contextPath) === 0) { $servletRequest->setBaseModifier(''); } else { $servletRequest->setBaseModifier($contextPath); } // initialize the request handler instance $dispatched = false; $applicationName = $application->getName(); while ($dispatched === false) { if ($this->requestHandlers[$applicationName][$i = rand(0, 9)]->isWaiting()) { $this->requestHandlers[$applicationName][$i]->handleRequest($servletRequest, $servletResponse); $dispatched = true; break; } } // copy the values from the servlet response back to the HTTP response $response->setStatusCode($servletResponse->getStatusCode()); $response->setStatusReasonPhrase($servletResponse->getStatusReasonPhrase()); $response->setVersion($servletResponse->getVersion()); $response->setState($servletResponse->getState()); // append the content to the body stream $response->appendBodyStream($servletResponse->getBodyStream()); // transform the servlet headers back into HTTP headers $headers = array(); foreach ($servletResponse->getHeaders() as $name => $header) { $headers[$name] = $header; } // set the headers as array (because we don't know if we have to use the append flag) $response->setHeaders($headers); // copy the servlet response cookies back to the HTTP response foreach ($servletResponse->getCookies() as $cookie) { $response->addCookie(unserialize($cookie)); } // set response state to be dispatched after this without calling other modules process $response->setState(HttpResponseStates::DISPATCH); } catch (ModuleException $me) { throw $me; } catch (\Exception $e) { throw new ModuleException($e, 500); } }
/** * 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 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) { /** * @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; } // load the locations $locations = $this->locations; // check if there are some volatile location definitions so use them and override global locations if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_LOCATIONS)) { $locations = $requestContext->getModuleVar(ModuleVars::VOLATILE_LOCATIONS); } // query whether we've locations configured or not if (sizeof($locations) === 0) { return; } // initialize the array for the handlers $handlers = array(); // initialize the array for the headers $headers = array(); // load the actual request URI without query string $uriWithoutQueryString = $requestContext->getServerVar(ServerVars::X_REQUEST_URI); // process the all locations found for this request foreach ($locations as $location) { // query whether the location matches the acutal request URI if (preg_match('/' . $location['condition'] . '/', $uriWithoutQueryString)) { // query whether the location has file handlers configured for the actual URI if (isset($location['params'])) { // iterate over all params and try to set as server var via mapping foreach ($location['params'] as $paramName => $paramValue) { // check if server var mapping exists if (isset($this->paramServerVarsMap[$paramName])) { // check if documentRoot is changed if ($this->paramServerVarsMap[$paramName] === ServerVars::DOCUMENT_ROOT) { // check if relative path is given and make is absolute by using cwd as prefix if (substr($paramValue, 0, 1) !== "/") { $paramValue = getcwd() . DIRECTORY_SEPARATOR . $paramValue; } } // set server var $requestContext->setServerVar($this->paramServerVarsMap[$paramName], $paramValue); } } } // query whether the location has file handlers configured for the actual URI if (isset($location['handlers'])) { $handlers = array_merge($handlers, $location['handlers']); } // merge headers information to volatile headers if exists if (isset($location['headers']) && is_array($location['headers'])) { $volatileHeaders = array(); if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_HEADERS)) { $volatileHeaders = $requestContext->getModuleVar(ModuleVars::VOLATILE_HEADERS); } $headers = array_merge_recursive($volatileHeaders, $location['headers']); } } } // add the handlers we have (if any) if (sizeof($handlers) !== 0) { $requestContext->setModuleVar(ModuleVars::VOLATILE_HANDLERS, $handlers); } // add the headers we have (if any) if (sizeof($headers) !== 0) { $requestContext->setModuleVar(ModuleVars::VOLATILE_HEADERS, $headers); } }
/** * 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 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 declare the right ones afterwards... /** * @var $request \AppserverIo\Psr\HttpMessage\RequestInterface */ /** * @var $request \AppserverIo\Psr\HttpMessage\ResponseInterface */ // if false hook is coming do nothing if (ModuleHooks::REQUEST_POST !== $hook) { return; } // set member ref for request context $this->requestContext = $requestContext; // We have to throw a ModuleException on failure, so surround the body with a try...catch block try { $requestUrl = $requestContext->getServerVar(ServerVars::HTTP_HOST) . $requestContext->getServerVar(ServerVars::X_REQUEST_URI); if (!isset($this->rules[$requestUrl])) { // Reset the $serverBackreferences array to avoid mixups of different requests $this->serverBackreferences = array(); // Resolve all used backreferences which are NOT linked to the query string. // We will resolve query string related backreferences separately as we are not able to cache them // as easily as, say, the URI // We also have to resolve all the changes rules in front of us made, so build up the backreferences // IN the loop. $this->fillContextBackreferences(); $this->fillHeaderBackreferences($request); // Get the rules as the array they are within the config. // We have to also collect any volatile rules which might be set on request base. // We might not even get anything, so prepare our rules accordingly $volatileRewrites = array(); if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_REWRITES)) { $volatileRewrites = $requestContext->getModuleVar(ModuleVars::VOLATILE_REWRITES); } // Build up the complete ruleset, volatile rules up front $rules = array_merge($volatileRewrites, $this->configuredRules); $this->rules[$requestUrl] = array(); // Only act if we got something if (is_array($rules)) { // Convert the rules to our internally used objects foreach ($rules as $rule) { // Add the rule as a Rule object $rule = new Rule($rule['condition'], $rule['target'], $rule['flag']); $rule->resolve($this->serverBackreferences); $this->rules[$requestUrl][] = $rule; } } } // Iterate over all rules, resolve vars and apply the rule (if needed) foreach ($this->rules[$requestUrl] as $rule) { // Check if the rule matches, and if, apply the rule if ($rule->matches()) { // Apply the rule. If apply() returns false this means this was the last rule to process if ($rule->apply($requestContext, $response, $this->serverBackreferences) === false) { break; } } } } catch (\Exception $e) { // Re-throw as a ModuleException throw new ModuleException($e); } }
/** * 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 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) { /** * @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; } // make request context available for usage in template $this->requestContext = $requestContext; // query whether the auto index module is available if ($this->getRequestContext()->hasServerVar(ServerVars::SERVER_AUTO_INDEX) === false) { return; } // query whether the auto index module is available and enabled if ($this->getRequestContext()->getServerVar(ServerVars::SERVER_AUTO_INDEX) === ServerVars::VALUE_AUTO_INDEX_OFF) { return; } // stop processing if file handler will not be core in case that location module // has changed the server handler to be proxy, fastcgi or what ever. if ($requestContext->getServerVar(ServerVars::SERVER_HANDLER) !== 'core') { return; } // now load the URL without path information and query string $url = $this->getUrl(); // query whether the URL ends with a slash if ($url[strlen($url) - 1] !== '/') { return; } // query whether an existing path is requested if (is_dir($realPath = $this->getRealPath()) === false) { return; } // load the auto index template if available $autoIndexTemplatePath = $this->getRequestContext()->getServerVar(ServerVars::SERVER_AUTO_INDEX_TEMPLATE_PATH); // query whether a template is configured and available if ($autoIndexTemplatePath && is_file($autoIndexTemplatePath)) { // render errors page ob_start(); require $autoIndexTemplatePath; $autoIndexPage = ob_get_clean(); } else { // initialize the directory listing content $directoryListing = '<tr><th>Name</th><th>Last Modified</th><th>Size</th></tr>'; // query whether if we've parent directory or not if ($this->hasParent($realPath)) { $directoryListing .= sprintf('<tr><td colspan="3"><a href="%s">Parent Directory</a></td></tr>', $this->getParentLink()); } // append the found files + directories to the directory listing foreach ($this->getDirectoryContent($realPath) as $directory) { // append the file or directory to the directory listing $directoryListing .= sprintf('<tr><td><a href="%s">%s</a></td><td>%s</td><td>%d</td></tr>', $this->getLink($directory), $this->getName($directory), $this->getDate($directory), $this->getFilesize($directory)); } // concatenate the elements of the auto index page $autoIndexPage = sprintf('<!DOCTYPE html><html><head><title>Index of %s</title></head><body><h1>Index of %s</h1><table>%s</table></body></html>', $this->getUri(), $this->getUri(), $directoryListing); } // append errors page to response body $response->appendBodyStream($autoIndexPage); // set the Content-Type to text/html $response->addHeader(Protocol::HEADER_CONTENT_TYPE, 'text/html'); // 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 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 declare the right ones afterwards... /** * @var $request \AppserverIo\Psr\HttpMessage\RequestInterface */ /** * @var $response \AppserverIo\Psr\HttpMessage\ResponseInterface */ // if false hook is coming do nothing if (ModuleHooks::REQUEST_POST !== $hook) { return; } // check if core module should still handle this request // maybe later on this can be overwritten by another core module for some reasons if ($requestContext->getServerVar(ServerVars::SERVER_HANDLER) !== self::MODULE_NAME) { // stop processing return; } // populates request context for possible script calling based on file handler configurations $this->populateRequestContext($requestContext); // check if file handler is not core module anymore if ($requestContext->getServerVar(ServerVars::SERVER_HANDLER) !== self::MODULE_NAME) { // stop processing return; } // if existing file should be served if ($requestContext->hasServerVar(ServerVars::SCRIPT_FILENAME)) { $scriptFilename = $requestContext->getServerVar(ServerVars::SCRIPT_FILENAME); // get file info $fileInfo = new \SplFileInfo($scriptFilename); // build etag $eTag = sprintf('"%x-%x-%x"', $fileInfo->getInode(), $fileInfo->getSize(), (double) str_pad($fileInfo->getMTime(), 16, '0')); // set last modified header $response->addHeader(Protocol::HEADER_LAST_MODIFIED, gmdate(DATE_RFC822, $fileInfo->getMTime())); // set etag header $response->addHeader(Protocol::HEADER_ETAG, $eTag); // set correct mimetype header $response->addHeader(Protocol::HEADER_CONTENT_TYPE, MimeTypes::getMimeTypeByExtension($fileInfo->getExtension())); // caching checks if ($request->hasHeader(Protocol::HEADER_IF_NONE_MATCH) && $request->getHeader(Protocol::HEADER_IF_NONE_MATCH) === $eTag) { // set not modified status without content $response->setStatusCode(304); } else { // serve file by set body stream to file descriptor stream $response->setBodyStream(fopen($scriptFilename, "r")); } // set response state to be dispatched after this without calling other modules process $response->setState(HttpResponseStates::DISPATCH); // if we got here its maybe a directory index surfing request if $validDir is same as uri // todo: implement directory index view and surfing } else { // for now we will throw a 404 as well here for non existing index files in directory throw new ModuleException(sprintf("The requested URL %s was not found on this server.", parse_url($requestContext->getServerVar(ServerVars::X_REQUEST_URI), PHP_URL_PATH)), 404); } }
/** * 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; }
/** * Creates and returns a new FastCGI client instance. * * @param \AppserverIo\Server\Interfaces\RequestContextInterface $requestContext A requests context instance * @param \React\EventLoop\LoopInterface $loop The event loop instance * * @return \Crunch\FastCGI\Connection The FastCGI connection instance */ protected function getFastCgiClient(RequestContextInterface $requestContext, LoopInterface $loop) { // initialize default host/port/DNS server $host = FcgiModule::DEFAULT_FAST_CGI_IP; $port = FcgiModule::DEFAULT_FAST_CGI_PORT; $dnsServer = FcgiModule::DEFAULT_DNS_SERVER; // set the connection data to be used for the Fast-CGI connection $fileHandlerVariables = array(); // check if we've configured module variables if ($requestContext->hasModuleVar(ModuleVars::VOLATILE_FILE_HANDLER_VARIABLES)) { // load the volatile file handler variables and set connection data $fileHandlerVariables = $requestContext->getModuleVar(ModuleVars::VOLATILE_FILE_HANDLER_VARIABLES); if (isset($fileHandlerVariables[FcgiModule::PARAM_HOST])) { $host = $fileHandlerVariables[FcgiModule::PARAM_HOST]; } if (isset($fileHandlerVariables[FcgiModule::PARAM_PORT])) { $port = $fileHandlerVariables[FcgiModule::PARAM_PORT]; } if (isset($fileHandlerVariables[FcgiModule::PARAM_DNS_SERVER])) { $dnsServer = $fileHandlerVariables[FcgiModule::PARAM_DNS_SERVER]; } } // initialize the socket connector with the DNS resolver $dnsResolverFactory = new DnsResolverFactory(); $dns = $dnsResolverFactory->createCached($dnsServer, $loop); // initialize the FastCGI factory with the connector $connector = new SocketConnector($loop, $dns); $factory = new FcgiClientFactory($loop, $connector); // initialize the FastCGI client with the FastCGI server IP and port return $factory->createClient($host, $port); }