Commit 94e7878a authored by Gareth Evans's avatar Gareth Evans Committed by epriestley
Browse files

Route internal conduit calls if other hosts available

Summary:
Ref T2785

Looks for hosts in `conduit.servers` config and if any exist route any conduit calls through any one of the hosts.

Test Plan:
Make some curl calls to public methods (`conduit.ping`), watch the access log for two requests. Make some calls from the UI that require authentication, watch the access log a bit more.

Also ran the unit tests.

Reviewers: epriestley

Reviewed By: epriestley

CC: aran, Korvin

Maniphest Tasks: T2785

Differential Revision: https://secure.phabricator.com/D5970
parent a1a46656
......@@ -830,6 +830,7 @@ phutil_register_library_map(array(
'PhabricatorChatLogQuery' => 'applications/chatlog/PhabricatorChatLogQuery.php',
'PhabricatorConduitAPIController' => 'applications/conduit/controller/PhabricatorConduitAPIController.php',
'PhabricatorConduitCertificateToken' => 'applications/conduit/storage/PhabricatorConduitCertificateToken.php',
'PhabricatorConduitConfigOptions' => 'applications/conduit/config/PhabricatorConduitConfigOptions.php',
'PhabricatorConduitConnectionLog' => 'applications/conduit/storage/PhabricatorConduitConnectionLog.php',
'PhabricatorConduitConsoleController' => 'applications/conduit/controller/PhabricatorConduitConsoleController.php',
'PhabricatorConduitController' => 'applications/conduit/controller/PhabricatorConduitController.php',
......@@ -2601,6 +2602,7 @@ phutil_register_library_map(array(
'PhabricatorChatLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
'PhabricatorConduitCertificateToken' => 'PhabricatorConduitDAO',
'PhabricatorConduitConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO',
'PhabricatorConduitConsoleController' => 'PhabricatorConduitController',
'PhabricatorConduitController' => 'PhabricatorController',
......
......@@ -113,18 +113,33 @@ abstract class AphrontApplicationConfiguration {
}
}
$path = $request->getPath();
$host = $request->getHost();
$base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
$prod_uri = PhabricatorEnv::getEnvConfig('phabricator.production-uri');
$file_uri = PhabricatorEnv::getEnvConfig('security.alternate-file-domain');
$path = $request->getPath();
$host = $request->getHost();
$base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
$prod_uri = PhabricatorEnv::getEnvConfig('phabricator.production-uri');
$file_uri = PhabricatorEnv::getEnvConfig(
'security.alternate-file-domain');
$conduit_uris = PhabricatorEnv::getEnvConfig('conduit.servers');
$uris = array_merge(
array(
$base_uri,
$prod_uri,
$file_uri,
),
$conduit_uris);
$host_match = false;
foreach ($uris as $uri) {
if ($host === id(new PhutilURI($uri))->getDomain()) {
$host_match = true;
break;
}
}
// NOTE: If the base URI isn't defined yet, don't activate alternate
// domains.
if ($base_uri &&
$host != id(new PhutilURI($base_uri))->getDomain() &&
$host != id(new PhutilURI($prod_uri))->getDomain() &&
$host != id(new PhutilURI($file_uri))->getDomain()) {
if ($base_uri && !$host_match) {
try {
$blog = id(new PhameBlogQuery())
......
......@@ -13,10 +13,14 @@ final class ConduitCall {
private $method;
private $request;
private $user;
private $servers;
private $forceLocal;
public function __construct($method, array $params) {
$this->method = $method;
$this->handler = $this->buildMethodHandler($method);
$this->method = $method;
$this->handler = $this->buildMethodHandler($method);
$this->servers = PhabricatorEnv::getEnvConfig('conduit.servers');
$this->forceLocal = false;
$invalid_params = array_diff_key(
$params,
......@@ -27,6 +31,16 @@ final class ConduitCall {
implode("', '", array_keys($invalid_params)) . "'.");
}
if ($this->servers) {
$current_host = AphrontRequest::getHTTPHeader('HOST');
foreach ($this->servers as $server) {
if ($current_host === id(new PhutilURI($server))->getDomain()) {
$this->forceLocal = true;
break;
}
}
}
$this->request = new ConduitAPIRequest($params);
}
......@@ -39,6 +53,15 @@ final class ConduitCall {
return $this->user;
}
public function setForceLocal($force_local) {
$this->forceLocal = $force_local;
return $this;
}
public function shouldForceLocal() {
return $this->forceLocal;
}
public function shouldRequireAuthentication() {
return $this->handler->shouldRequireAuthentication();
}
......@@ -64,7 +87,35 @@ final class ConduitCall {
$this->request->setUser($this->getUser());
}
return $this->handler->executeMethod($this->request);
if (!$this->shouldForceLocal() && $this->servers) {
$server = $this->pickRandomServer($this->servers);
$client = new ConduitClient($server);
$params = $this->request->getAllParameters();
$params["__conduit__"]["isProxied"] = true;
if ($this->handler->shouldRequireAuthentication()) {
$client->callMethodSynchronous(
'conduit.connect',
array(
'client' => 'PhabricatorConduit',
'clientVersion' => '1.0',
'user' => $this->getUser()->getUserName(),
'certificate' => $this->getUser()->getConduitCertificate(),
'__conduit__' => $params["__conduit__"],
));
}
return $client->callMethodSynchronous(
$this->method,
$params);
} else {
return $this->handler->executeMethod($this->request);
}
}
protected function pickRandomServer($servers) {
return $servers[array_rand($servers)];
}
protected function buildMethodHandler($method) {
......
......@@ -4,13 +4,15 @@ final class ConduitCallTestCase extends PhabricatorTestCase {
public function testConduitPing() {
$call = new ConduitCall('conduit.ping', array());
$call->setForceLocal(true);
$result = $call->execute();
$this->assertEqual(false, empty($result));
}
public function testConduitAuth() {
$call = new ConduitCall('user.whoami', array());
$call = new ConduitCall('user.whoami', array(), true);
$call->setForceLocal(true);
$caught = null;
try {
......
<?php
final class PhabricatorConduitConfigOptions
extends PhabricatorApplicationConfigOptions {
public function getName() {
return pht("Conduit");
}
public function getDescription() {
return pht("Configure conduit.");
}
public function getOptions() {
return array(
$this->newOption("conduit.servers", "list<string>", array())
->setLocked(true)
->setSummary(pht("Servers that conduit can connect to."))
->setDescription(
pht(
"Set an array of servers where conduit can connect to. This is ".
"an advanced option. Don't touch this unless you know what you ".
"are doing."))
->addExample(
'["http://phabricator2.example.com/", '.
'"http://phabricator3.example.com/]"',
pht('Valid Setting')),
);
}
}
......@@ -35,7 +35,8 @@ final class PhabricatorConduitAPIController
$metadata = idx($params, '__conduit__', array());
unset($params['__conduit__']);
$call = new ConduitCall($method, $params);
$call = new ConduitCall(
$method, $params, idx($metadata, 'isProxied', false));
$result = null;
......
......@@ -98,6 +98,7 @@ final class ConduitAPI_differential_finishpostponedlinters_Method
'diff_id' => $diff_id,
'name' => 'arc:lint',
'data' => json_encode($messages)));
$call->setForceLocal(true);
$call->setUser($request->getUser());
$call->execute();
$call = new ConduitCall(
......@@ -106,6 +107,7 @@ final class ConduitAPI_differential_finishpostponedlinters_Method
'diff_id' => $diff_id,
'name' => 'arc:lint-postponed',
'data' => json_encode($postponed_linters)));
$call->setForceLocal(true);
$call->setUser($request->getUser());
$call->execute();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment