diff --git a/src/aphront/AphrontRequest.php b/src/aphront/AphrontRequest.php index 6e167c64e5..3f7036b092 100644 --- a/src/aphront/AphrontRequest.php +++ b/src/aphront/AphrontRequest.php @@ -1,9 +1,9 @@ getDomain(); + $ip = gethostbyname($domain); + if (!$ip) { + throw new Exception( + pht( + 'Unable to resolve domain "%s"!', + $domain)); + } + + if (!PhabricatorEnv::isClusterAddress($ip)) { + throw new Exception( + pht( + 'Refusing to proxy a request to IP address ("%s") which is not '. + 'in the cluster address block (this address was derived by '. + 'resolving the domain "%s").', + $ip, + $domain)); + } + + $uri->setPath($this->getPath()); + $uri->setQueryParams(self::flattenData($_GET)); + + $input = PhabricatorStartup::getRawInput(); + + $future = id(new HTTPSFuture($uri)) + ->addHeader('Host', self::getHost()) + ->addHeader('X-Phabricator-Cluster', true) + ->setMethod($_SERVER['REQUEST_METHOD']) + ->write($input); + + if (isset($_SERVER['PHP_AUTH_USER'])) { + $future->setHTTPBasicAuthCredentials( + $_SERVER['PHP_AUTH_USER'], + new PhutilOpaqueEnvelope(idx($_SERVER, 'PHP_AUTH_PW', ''))); + } + + $headers = array(); + $seen = array(); + + // NOTE: apache_request_headers() might provide a nicer way to do this, + // but isn't available under FCGI until PHP 5.4.0. + foreach ($_SERVER as $key => $value) { + if (preg_match('/^HTTP_/', $key)) { + // Unmangle the header as best we can. + $key = str_replace('_', ' ', $key); + $key = strtolower($key); + $key = ucwords($key); + $key = str_replace(' ', '-', $key); + + $headers[] = array($key, $value); + $seen[$key] = true; + } + } + + // In some situations, this may not be mapped into the HTTP_X constants. + // CONTENT_LENGTH is similarly affected, but we trust cURL to take care + // of that if it matters, since we're handing off a request body. + if (empty($seen['Content-Type'])) { + if (isset($_SERVER['CONTENT_TYPE'])) { + $headers[] = array('Content-Type', $_SERVER['CONTENT_TYPE']); + } + } + + foreach ($headers as $header) { + list($key, $value) = $header; + switch ($key) { + case 'Host': + case 'Authorization': + // Don't forward these headers, we've already handled them elsewhere. + unset($headers[$key]); + break; + default: + break; + } + } + + foreach ($headers as $header) { + list($key, $value) = $header; + $future->addHeader($key, $value); + } + + return $future; + } + + } diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php index 27d256e2a8..2b82bde43d 100644 --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -205,10 +205,8 @@ final class DiffusionServeController extends DiffusionController { } else { switch ($vcs_type) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - $result = $this->serveGitRequest($repository, $viewer); - break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: - $result = $this->serveMercurialRequest($repository, $viewer); + $result = $this->serveVCSRequest($repository, $viewer); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $result = new PhabricatorVCSResponse( @@ -238,6 +236,42 @@ final class DiffusionServeController extends DiffusionController { return $result; } + private function serveVCSRequest( + PhabricatorRepository $repository, + PhabricatorUser $viewer) { + + // If this repository is hosted on a service, we need to proxy the request + // to a host which can serve it. + $is_cluster_request = $this->getRequest()->isProxiedClusterRequest(); + + $uri = $repository->getAlmanacServiceURI( + $viewer, + $is_cluster_request, + array( + 'http', + 'https', + )); + if ($uri) { + $future = $this->getRequest()->newClusterProxyFuture($uri); + return id(new AphrontHTTPProxyResponse()) + ->setHTTPFuture($future); + } + + // Otherwise, we're going to handle the request locally. + + $vcs_type = $repository->getVersionControlSystem(); + switch ($vcs_type) { + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: + $result = $this->serveGitRequest($repository, $viewer); + break; + case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: + $result = $this->serveMercurialRequest($repository, $viewer); + break; + } + + return $result; + } + private function isReadOnlyRequest( PhabricatorRepository $repository) { $request = $this->getRequest();