3 final class PhabricatorNotificationServerRef
13 const KEY_REFS
= 'notification.refs';
15 public function setType($type) {
20 public function getType() {
24 public function setHost($host) {
29 public function getHost() {
33 public function setPort($port) {
38 public function getPort() {
42 public function setProtocol($protocol) {
43 $this->protocol
= $protocol;
47 public function getProtocol() {
48 return $this->protocol
;
51 public function setPath($path) {
56 public function getPath() {
60 public function setIsDisabled($is_disabled) {
61 $this->isDisabled
= $is_disabled;
65 public function getIsDisabled() {
66 return $this->isDisabled
;
69 public static function getLiveServers() {
70 $cache = PhabricatorCaches
::getRequestCache();
72 $refs = $cache->getKey(self
::KEY_REFS
);
74 $refs = self
::newRefs();
75 $cache->setKey(self
::KEY_REFS
, $refs);
81 public static function newRefs() {
82 $configs = PhabricatorEnv
::getEnvConfig('notification.servers');
85 foreach ($configs as $config) {
87 ->setType($config['type'])
88 ->setHost($config['host'])
89 ->setPort($config['port'])
90 ->setProtocol($config['protocol'])
91 ->setPath(idx($config, 'path'))
92 ->setIsDisabled(idx($config, 'disabled', false));
99 public static function getEnabledServers() {
100 $servers = self
::getLiveServers();
102 foreach ($servers as $key => $server) {
103 if ($server->getIsDisabled()) {
104 unset($servers[$key]);
108 return array_values($servers);
111 public static function getEnabledAdminServers() {
112 $servers = self
::getEnabledServers();
114 foreach ($servers as $key => $server) {
115 if (!$server->isAdminServer()) {
116 unset($servers[$key]);
120 return array_values($servers);
123 public static function getEnabledClientServers($with_protocol) {
124 $servers = self
::getEnabledServers();
126 foreach ($servers as $key => $server) {
127 if ($server->isAdminServer()) {
128 unset($servers[$key]);
132 $protocol = $server->getProtocol();
133 if ($protocol != $with_protocol) {
134 unset($servers[$key]);
139 return array_values($servers);
142 public function isAdminServer() {
143 return ($this->type
== 'admin');
146 public function getURI($to_path = null) {
147 if ($to_path === null ||
!strlen($to_path)) {
150 $to_path = ltrim($to_path, '/');
153 $base_path = $this->getPath();
154 if ($base_path === null ||
!strlen($base_path)) {
157 $base_path = rtrim($base_path, '/');
159 $full_path = $base_path.'/'.$to_path;
161 $uri = id(new PhutilURI('http://'.$this->getHost()))
162 ->setProtocol($this->getProtocol())
163 ->setPort($this->getPort())
164 ->setPath($full_path);
166 $instance = PhabricatorEnv
::getEnvConfig('cluster.instance');
167 if ($instance !== null && strlen($instance)) {
168 $uri->replaceQueryParam('instance', $instance);
174 public function getWebsocketURI($to_path = null) {
175 $instance = PhabricatorEnv
::getEnvConfig('cluster.instance');
176 if ($instance !== null && strlen($instance)) {
177 $to_path = $to_path.'~'.$instance.'/';
180 $uri = $this->getURI($to_path);
182 if ($this->getProtocol() == 'https') {
183 $uri->setProtocol('wss');
185 $uri->setProtocol('ws');
191 public function testClient() {
192 if ($this->isAdminServer()) {
194 pht('Unable to test client on an admin server!'));
197 $server_uri = $this->getURI();
200 id(new HTTPSFuture($server_uri))
203 } catch (HTTPFutureHTTPResponseStatus
$ex) {
204 // This is what we expect when things are working correctly.
205 if ($ex->getStatusCode() == 501) {
212 pht('Got HTTP 200, but expected HTTP 501 (WebSocket Upgrade)!'));
215 public function loadServerStatus() {
216 if (!$this->isAdminServer()) {
219 'Unable to load server status: this is not an admin server!'));
222 $server_uri = $this->getURI('/status/');
224 list($body) = $this->newFuture($server_uri)
227 return phutil_json_decode($body);
230 public function postMessage(array $data) {
231 if (!$this->isAdminServer()) {
233 pht('Unable to post message: this is not an admin server!'));
236 $server_uri = $this->getURI('/');
237 $payload = phutil_json_encode($data);
239 $this->newFuture($server_uri, $payload)
244 private function newFuture($uri, $data = null) {
245 if ($data === null) {
246 $future = new HTTPSFuture($uri);
248 $future = new HTTPSFuture($uri, $data);
251 $future->setTimeout(2);
253 // At one point, a HackerOne researcher reported a "Location:" redirect
254 // attack here (if the attacker can gain control of the notification
255 // server or the configuration).
257 // Although this attack is not particularly concerning, we don't expect
258 // Aphlict to ever issue a "Location:" header, so receiving one indicates
259 // something is wrong and declining to follow the header may make debugging
262 $future->setFollowLocation(false);