Page MenuHomePhabricator

D16090.id38715.diff
No OneTemporary

D16090.id38715.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -211,6 +211,7 @@
'PhutilGitURI' => 'parser/PhutilGitURI.php',
'PhutilGitURITestCase' => 'parser/__tests__/PhutilGitURITestCase.php',
'PhutilGoogleAuthAdapter' => 'auth/PhutilGoogleAuthAdapter.php',
+ 'PhutilHTTPEngineExtension' => 'future/http/PhutilHTTPEngineExtension.php',
'PhutilHangForeverDaemon' => 'daemon/torture/PhutilHangForeverDaemon.php',
'PhutilHashingIterator' => 'utils/PhutilHashingIterator.php',
'PhutilHashingIteratorTestCase' => 'utils/__tests__/PhutilHashingIteratorTestCase.php',
@@ -770,6 +771,7 @@
'PhutilGitURI' => 'Phobject',
'PhutilGitURITestCase' => 'PhutilTestCase',
'PhutilGoogleAuthAdapter' => 'PhutilOAuthAuthAdapter',
+ 'PhutilHTTPEngineExtension' => 'Phobject',
'PhutilHangForeverDaemon' => 'PhutilTortureTestDaemon',
'PhutilHashingIterator' => array(
'PhutilProxyIterator',
diff --git a/src/future/http/HTTPSFuture.php b/src/future/http/HTTPSFuture.php
--- a/src/future/http/HTTPSFuture.php
+++ b/src/future/http/HTTPSFuture.php
@@ -9,7 +9,6 @@
private static $results = array();
private static $pool = array();
private static $globalCABundle;
- private static $blindTrustDomains = array();
private $handle;
private $profilerCallID;
@@ -118,17 +117,6 @@
}
/**
- * Set a list of domains to blindly trust. Certificates for these domains
- * will not be validated.
- *
- * @param list<string> List of domain names to trust blindly.
- * @return void
- */
- public static function setBlindlyTrustDomains(array $domains) {
- self::$blindTrustDomains = array_fuse($domains);
- }
-
- /**
* Load contents of remote URI. Behaves pretty much like
* `@file_get_contents($uri)` but doesn't require `allow_url_fopen`.
*
@@ -187,11 +175,15 @@
$domain = id(new PhutilURI($uri))->getDomain();
if (!$this->handle) {
+ $uri_object = new PhutilURI($uri);
+ $proxy = PhutilHTTPEngineExtension::buildHTTPProxyURI($uri_object);
+
$profiler = PhutilServiceProfiler::getInstance();
$this->profilerCallID = $profiler->beginServiceCall(
array(
'type' => 'http',
'uri' => $uri,
+ 'proxy' => (string)$proxy,
));
if (!self::$multi) {
@@ -347,15 +339,26 @@
curl_setopt($curl, CURLOPT_CAINFO, $this->getCABundle());
}
- $domain = id(new PhutilURI($uri))->getDomain();
- if (!empty(self::$blindTrustDomains[$domain])) {
- // Disable peer verification for domains that we blindly trust.
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
- } else {
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
+ $verify_peer = 1;
+ $verify_host = 2;
+
+ $extensions = PhutilHTTPEngineExtension::getAllExtensions();
+ foreach ($extensions as $extension) {
+ if ($extension->shouldTrustAnySSLAuthorityForURI($uri_object)) {
+ $verify_peer = 0;
+ }
+ if ($extension->shouldTrustAnySSLHostnameForURI($uri_object)) {
+ $verify_host = 0;
+ }
}
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $verify_peer);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $verify_host);
curl_setopt($curl, CURLOPT_SSLVERSION, 0);
+
+ if ($proxy) {
+ curl_setopt($curl, CURLOPT_PROXY, (string)$proxy);
+ }
} else {
$curl = $this->handle;
diff --git a/src/future/http/PhutilHTTPEngineExtension.php b/src/future/http/PhutilHTTPEngineExtension.php
new file mode 100644
--- /dev/null
+++ b/src/future/http/PhutilHTTPEngineExtension.php
@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * Extend HTTP behavior by injecting proxies or compromising SSL.
+ *
+ * @task info Extension Information
+ * @task config Configuring HTTP Behavior
+ */
+abstract class PhutilHTTPEngineExtension
+ extends Phobject {
+
+
+ /**
+ * Human-readable name of this HTTP extension.
+ *
+ * @return string Human readable name.
+ * @task info
+ */
+ abstract public function getExtensionName();
+
+
+ /**
+ * Return `true` to disable SSL authority verification for a URI.
+ *
+ * By default, certificates must be signed by a recognized authority and
+ * requests fail if the host provides an invalid certificate. You can
+ * override this behavior to accept any certificate (including self-signed
+ * certificates). Doing this compromises the security of SSL.
+ *
+ * @return bool Return `true` to disable authority verification.
+ * @task config
+ */
+ public function shouldTrustAnySSLAuthorityForURI(PhutilURI $uri) {
+ return false;
+ }
+
+
+ /**
+ * Return `true` to disable SSL hostname verification for a URI.
+ *
+ * By default, certificates should be signed for the correct hostname,
+ * and requests fail if the host provides a certificate for a different
+ * hostname. You can override this behavior to accept any certificate,
+ * including certificates for a different host. Doing this compromises
+ * the security of SSL.
+ *
+ * @return bool Return `true` to disable hostname verification.
+ * @task config
+ */
+ public function shouldTrustAnySSLHostnameForURI(PhutilURI $uri) {
+ return false;
+ }
+
+
+ /**
+ * Return a @{class:PhutilURI} to proxy requests.
+ *
+ * If some or all outbound HTTP requests must be proxied, you can return
+ * the URI for a proxy to use from this method.
+ *
+ * @return null|PhutilURI Proxy URI.
+ * @task config
+ */
+ public function getHTTPProxyURI(PhutilURI $uri) {
+ return null;
+ }
+
+
+ final public function getExtensionKey() {
+ return $this->getPhobjectClassConstant('EXTENSIONKEY');
+ }
+
+ final public static function getAllExtensions() {
+ return id(new PhutilClassMapQuery())
+ ->setAncestorClass(__CLASS__)
+ ->setUniqueMethod('getExtensionKey')
+ ->execute();
+ }
+
+ final public static function getExtension($key) {
+ $extensions = self::getAllExtensions();
+ return idx($extensions, $key);
+ }
+
+ final public static function requireExtension($key) {
+ $extension = self::getExtension($key);
+
+ if (!$extension) {
+ throw new Exception(
+ pht(
+ 'No HTTP engine extension exists with extension key "%s".',
+ $key));
+ }
+
+ return $extension;
+ }
+
+ final public static function buildHTTPProxyURI(PhutilURI $uri) {
+ $proxy = null;
+ $via = null;
+
+ $extensions = self::getAllExtensions();
+ foreach ($extensions as $extension) {
+ $extension_proxy = $extension->getHTTPProxyURI($uri);
+
+ if ($extension_proxy === null) {
+ continue;
+ }
+
+ if (!($extension_proxy instanceof PhutilURI)) {
+ throw new Exception(
+ pht(
+ 'HTTP extension "%s" (of class "%s") returned an invalid '.
+ 'result from "%s": expected null, or an object of class "%s".',
+ $extension->getExtensionName(),
+ get_class($extension),
+ 'getHTTPProxyURI()',
+ 'PhutilURI'));
+ }
+
+ if ($proxy) {
+ throw new Exception(
+ pht(
+ 'Two different HTTP extensions ("%s" of class "%s" and "%s" of '.
+ 'class "%s") both provided a proxy URI for URI "%s". No more '.
+ 'than one extension may provide a proxy for any URI.',
+ $extension->getExtensionName(),
+ get_class($extension),
+ $via->getExtensionName(),
+ get_class($via),
+ (string)$uri));
+ }
+
+ $proxy = $extension_proxy;
+ $via = $extension;
+ }
+
+ return $proxy;
+ }
+
+}
diff --git a/src/serviceprofiler/PhutilServiceProfiler.php b/src/serviceprofiler/PhutilServiceProfiler.php
--- a/src/serviceprofiler/PhutilServiceProfiler.php
+++ b/src/serviceprofiler/PhutilServiceProfiler.php
@@ -109,7 +109,20 @@
}
break;
case 'http':
- $desc = phutil_censor_credentials($data['uri']);
+ if (isset($data['proxy'])) {
+ $proxy = phutil_censor_credentials($data['proxy']);
+ } else {
+ $proxy = null;
+ }
+
+ $uri = phutil_censor_credentials($data['uri']);
+
+ if (strlen($proxy)) {
+ $desc = "{$proxy} >> {$uri}";
+ } else {
+ $desc = $uri;
+ }
+
break;
case 'lint':
$desc = $data['linter'];

File Metadata

Mime Type
text/plain
Expires
Tue, Apr 1, 4:04 AM (18 h, 52 m ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7227748
Default Alt Text
D16090.id38715.diff (8 KB)

Event Timeline