Example #1
0
 /**
  * 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);
 }