Path: blob/master/src/applications/notification/client/PhabricatorNotificationServerRef.php
12256 views
<?php12final class PhabricatorNotificationServerRef3extends Phobject {45private $type;6private $host;7private $port;8private $protocol;9private $path;10private $isDisabled;1112const KEY_REFS = 'notification.refs';1314public function setType($type) {15$this->type = $type;16return $this;17}1819public function getType() {20return $this->type;21}2223public function setHost($host) {24$this->host = $host;25return $this;26}2728public function getHost() {29return $this->host;30}3132public function setPort($port) {33$this->port = $port;34return $this;35}3637public function getPort() {38return $this->port;39}4041public function setProtocol($protocol) {42$this->protocol = $protocol;43return $this;44}4546public function getProtocol() {47return $this->protocol;48}4950public function setPath($path) {51$this->path = $path;52return $this;53}5455public function getPath() {56return $this->path;57}5859public function setIsDisabled($is_disabled) {60$this->isDisabled = $is_disabled;61return $this;62}6364public function getIsDisabled() {65return $this->isDisabled;66}6768public static function getLiveServers() {69$cache = PhabricatorCaches::getRequestCache();7071$refs = $cache->getKey(self::KEY_REFS);72if (!$refs) {73$refs = self::newRefs();74$cache->setKey(self::KEY_REFS, $refs);75}7677return $refs;78}7980public static function newRefs() {81$configs = PhabricatorEnv::getEnvConfig('notification.servers');8283$refs = array();84foreach ($configs as $config) {85$ref = id(new self())86->setType($config['type'])87->setHost($config['host'])88->setPort($config['port'])89->setProtocol($config['protocol'])90->setPath(idx($config, 'path'))91->setIsDisabled(idx($config, 'disabled', false));92$refs[] = $ref;93}9495return $refs;96}9798public static function getEnabledServers() {99$servers = self::getLiveServers();100101foreach ($servers as $key => $server) {102if ($server->getIsDisabled()) {103unset($servers[$key]);104}105}106107return array_values($servers);108}109110public static function getEnabledAdminServers() {111$servers = self::getEnabledServers();112113foreach ($servers as $key => $server) {114if (!$server->isAdminServer()) {115unset($servers[$key]);116}117}118119return array_values($servers);120}121122public static function getEnabledClientServers($with_protocol) {123$servers = self::getEnabledServers();124125foreach ($servers as $key => $server) {126if ($server->isAdminServer()) {127unset($servers[$key]);128continue;129}130131$protocol = $server->getProtocol();132if ($protocol != $with_protocol) {133unset($servers[$key]);134continue;135}136}137138return array_values($servers);139}140141public function isAdminServer() {142return ($this->type == 'admin');143}144145public function getURI($to_path = null) {146if ($to_path === null || !strlen($to_path)) {147$to_path = '';148} else {149$to_path = ltrim($to_path, '/');150}151152$base_path = $this->getPath();153if ($base_path === null || !strlen($base_path)) {154$base_path = '';155} else {156$base_path = rtrim($base_path, '/');157}158$full_path = $base_path.'/'.$to_path;159160$uri = id(new PhutilURI('http://'.$this->getHost()))161->setProtocol($this->getProtocol())162->setPort($this->getPort())163->setPath($full_path);164165$instance = PhabricatorEnv::getEnvConfig('cluster.instance');166if ($instance !== null && strlen($instance)) {167$uri->replaceQueryParam('instance', $instance);168}169170return $uri;171}172173public function getWebsocketURI($to_path = null) {174$instance = PhabricatorEnv::getEnvConfig('cluster.instance');175if ($instance !== null && strlen($instance)) {176$to_path = $to_path.'~'.$instance.'/';177}178179$uri = $this->getURI($to_path);180181if ($this->getProtocol() == 'https') {182$uri->setProtocol('wss');183} else {184$uri->setProtocol('ws');185}186187return $uri;188}189190public function testClient() {191if ($this->isAdminServer()) {192throw new Exception(193pht('Unable to test client on an admin server!'));194}195196$server_uri = $this->getURI();197198try {199id(new HTTPSFuture($server_uri))200->setTimeout(2)201->resolvex();202} catch (HTTPFutureHTTPResponseStatus $ex) {203// This is what we expect when things are working correctly.204if ($ex->getStatusCode() == 501) {205return true;206}207throw $ex;208}209210throw new Exception(211pht('Got HTTP 200, but expected HTTP 501 (WebSocket Upgrade)!'));212}213214public function loadServerStatus() {215if (!$this->isAdminServer()) {216throw new Exception(217pht(218'Unable to load server status: this is not an admin server!'));219}220221$server_uri = $this->getURI('/status/');222223list($body) = $this->newFuture($server_uri)224->resolvex();225226return phutil_json_decode($body);227}228229public function postMessage(array $data) {230if (!$this->isAdminServer()) {231throw new Exception(232pht('Unable to post message: this is not an admin server!'));233}234235$server_uri = $this->getURI('/');236$payload = phutil_json_encode($data);237238$this->newFuture($server_uri, $payload)239->setMethod('POST')240->resolvex();241}242243private function newFuture($uri, $data = null) {244if ($data === null) {245$future = new HTTPSFuture($uri);246} else {247$future = new HTTPSFuture($uri, $data);248}249250$future->setTimeout(2);251252// At one point, a HackerOne researcher reported a "Location:" redirect253// attack here (if the attacker can gain control of the notification254// server or the configuration).255256// Although this attack is not particularly concerning, we don't expect257// Aphlict to ever issue a "Location:" header, so receiving one indicates258// something is wrong and declining to follow the header may make debugging259// easier.260261$future->setFollowLocation(false);262263return $future;264}265266}267268269