1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-22 05:20:56 +01:00

Make serving repositories work with alternate URIs

Summary: Ref T4245. Consolidates the URI parsing/rewriting logic so that repositories can be served from either `/diffusion/XYZ/` or `/diffusion/123/`, over both HTTP and SSH.

Test Plan:
  - Pulled a Git repository by ID and callsign over HTTP and SSH.
  - Pulled a Mercurial repository by ID and callsign over HTTP and SSH.
  - Pulled a Subversion repository by ID and callsign over SSH (no HTTP support for SVN).

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T4245

Differential Revision: https://secure.phabricator.com/D15302
This commit is contained in:
epriestley 2016-02-18 04:38:11 -08:00
parent c2b8dd28d8
commit 74a79aa634
4 changed files with 74 additions and 22 deletions

View file

@ -262,17 +262,23 @@ final class DiffusionServeController extends DiffusionController {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$result = new PhabricatorVCSResponse( $result = new PhabricatorVCSResponse(
500, 500,
pht('This is not a Git repository.')); pht(
'This repository ("%s") is not a Git repository.',
$repository->getDisplayName()));
break; break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$result = new PhabricatorVCSResponse( $result = new PhabricatorVCSResponse(
500, 500,
pht('This is not a Mercurial repository.')); pht(
'This repository ("%s") is not a Mercurial repository.',
$repository->getDisplayName()));
break; break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$result = new PhabricatorVCSResponse( $result = new PhabricatorVCSResponse(
500, 500,
pht('This is not a Subversion repository.')); pht(
'This repository ("%s") is not a Subversion repository.',
$repository->getDisplayName()));
break; break;
default: default:
$result = new PhabricatorVCSResponse( $result = new PhabricatorVCSResponse(
@ -480,7 +486,9 @@ final class DiffusionServeController extends DiffusionController {
private function getRequestDirectoryPath(PhabricatorRepository $repository) { private function getRequestDirectoryPath(PhabricatorRepository $repository) {
$request = $this->getRequest(); $request = $this->getRequest();
$request_path = $request->getRequestURI()->getPath(); $request_path = $request->getRequestURI()->getPath();
$base_path = preg_replace('@^/diffusion/[A-Z]+@', '', $request_path);
$info = PhabricatorRepository::parseRepositoryServicePath($request_path);
$base_path = $info['path'];
// For Git repositories, strip an optional directory component if it // For Git repositories, strip an optional directory component if it
// isn't the name of a known Git resource. This allows users to clone // isn't the name of a known Git resource. This allows users to clone

View file

@ -6,6 +6,7 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
private $repository; private $repository;
private $hasWriteAccess; private $hasWriteAccess;
private $proxyURI; private $proxyURI;
private $baseRequestPath;
public function getRepository() { public function getRepository() {
if (!$this->repository) { if (!$this->repository) {
@ -45,6 +46,10 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
abstract protected function identifyRepository(); abstract protected function identifyRepository();
abstract protected function executeRepositoryOperations(); abstract protected function executeRepositoryOperations();
protected function getBaseRequestPath() {
return $this->baseRequestPath;
}
protected function writeError($message) { protected function writeError($message) {
$this->getErrorChannel()->write($message); $this->getErrorChannel()->write($message);
return $this; return $this;
@ -149,25 +154,29 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
protected function loadRepositoryWithPath($path) { protected function loadRepositoryWithPath($path) {
$viewer = $this->getUser(); $viewer = $this->getUser();
$regex = '@^/?diffusion/(?P<callsign>[A-Z]+)(?:/|\z)@'; $info = PhabricatorRepository::parseRepositoryServicePath($path);
$matches = null; if ($info === null) {
if (!preg_match($regex, $path, $matches)) {
throw new Exception( throw new Exception(
pht( pht(
'Unrecognized repository path "%s". Expected a path like "%s".', 'Unrecognized repository path "%s". Expected a path like "%s" '.
'or "%s".',
$path, $path,
'/diffusion/X/')); '/diffusion/X/',
'/diffusion/123/'));
} }
$callsign = $matches[1]; $identifier = $info['identifier'];
$base = $info['base'];
$this->baseRequestPath = $base;
$repository = id(new PhabricatorRepositoryQuery()) $repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer) ->setViewer($viewer)
->withCallsigns(array($callsign)) ->withIdentifiers(array($identifier))
->executeOne(); ->executeOne();
if (!$repository) { if (!$repository) {
throw new Exception( throw new Exception(
pht('No repository "%s" exists!', $callsign)); pht('No repository "%s" exists!', $identifier));
} }
switch ($repository->getServeOverSSH()) { switch ($repository->getServeOverSSH()) {
@ -179,7 +188,9 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
case PhabricatorRepository::SERVE_OFF: case PhabricatorRepository::SERVE_OFF:
default: default:
throw new Exception( throw new Exception(
pht('This repository is not available over SSH.')); pht(
'This repository ("%s") is not available over SSH.',
$repository->getDisplayName()));
} }
return $repository; return $repository;

View file

@ -377,14 +377,12 @@ final class DiffusionSubversionServeSSHWorkflow
$repository = $this->getRepository(); $repository = $this->getRepository();
$path = $this->getPathFromSubversionURI($uri_string); $path = $this->getPathFromSubversionURI($uri_string);
$path = preg_replace( $external_base = $this->getBaseRequestPath();
'(^/diffusion/[A-Z]+)',
rtrim($repository->getLocalPath(), '/'),
$path);
if (preg_match('(^/diffusion/[A-Z]+/\z)', $path)) { // Replace "/diffusion/X" in the request with the repository local path,
$path = rtrim($path, '/'); // so "/diffusion/X/master/" becomes "/path/to/repository/X/master/".
} $local_path = rtrim($repository->getLocalPath(), '/');
$path = $local_path.substr($path, strlen($external_base));
// NOTE: We are intentionally NOT removing username information from the // NOTE: We are intentionally NOT removing username information from the
// URI. Subversion retains it over the course of the request and considers // URI. Subversion retains it over the course of the request and considers
@ -398,7 +396,7 @@ final class DiffusionSubversionServeSSHWorkflow
if ($this->externalBaseURI === null) { if ($this->externalBaseURI === null) {
$pre = (string)id(clone $uri)->setPath(''); $pre = (string)id(clone $uri)->setPath('');
$external_path = '/diffusion/'.$repository->getCallsign(); $external_path = $external_base;
$external_path = $this->normalizeSVNPath($external_path); $external_path = $this->normalizeSVNPath($external_path);
$this->externalBaseURI = $pre.$external_path; $this->externalBaseURI = $pre.$external_path;

View file

@ -687,6 +687,41 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
return "/r{$callsign}{$identifier}"; return "/r{$callsign}{$identifier}";
} }
public static function parseRepositoryServicePath($request_path) {
// NOTE: In Mercurial over SSH, the path will begin without a leading "/",
// so we're matching it optionally.
$patterns = array(
'(^'.
'(?P<base>/?diffusion/(?P<identifier>[A-Z]+|[0-9]\d*))'.
'(?P<path>(?:/.*)?)'.
'\z)',
);
$identifier = null;
foreach ($patterns as $pattern) {
$matches = null;
if (!preg_match($pattern, $request_path, $matches)) {
continue;
}
$identifier = $matches['identifier'];
$base = $matches['base'];
$path = $matches['path'];
break;
}
if ($identifier === null) {
return null;
}
return array(
'identifier' => $identifier,
'base' => $base,
'path' => $path,
);
}
public function getCanonicalPath($request_path) { public function getCanonicalPath($request_path) {
$standard_pattern = $standard_pattern =
'(^'. '(^'.