|
4 | 4 |
|
5 | 5 | use React\Dns\Resolver\Resolver;
|
6 | 6 | use React\EventLoop\LoopInterface;
|
| 7 | +use RuntimeException; |
7 | 8 | use Rx\Disposable\CallbackDisposable;
|
8 | 9 | use Rx\Observable;
|
9 | 10 | use Rx\ObserverInterface;
|
10 | 11 | use Rx\Scheduler;
|
11 | 12 | use Rx\Websocket\Client as WebsocketClient;
|
12 | 13 | use Rx\Websocket\MessageSubject;
|
| 14 | +use Rx\Websocket\WebsocketErrorException; |
13 | 15 | use Throwable;
|
| 16 | +use function React\Promise\reject; |
| 17 | +use function React\Promise\resolve; |
14 | 18 |
|
15 | 19 | final class AsyncClient
|
16 | 20 | {
|
@@ -88,29 +92,35 @@ public function __construct(Observable $client)
|
88 | 92 | ->startWith($x);
|
89 | 93 | })
|
90 | 94 |
|
91 |
| - // Handle connection level errors |
92 |
| - ->retryWhen(function (Observable $errors) { |
93 |
| - return $errors->flatMap(function (Throwable $throwable) { |
94 |
| - return $this->handleLowLevelError($throwable); |
95 |
| - }); |
96 |
| - }) |
97 |
| - |
98 | 95 | // Decode JSON
|
99 | 96 | ->_ApiClients_jsonDecode()
|
100 | 97 |
|
101 | 98 | // Deal with connection established messages
|
102 |
| - ->map(function (array $message) { |
| 99 | + ->flatMap(function (array $message) { |
103 | 100 | $this->delay = self::DEFAULT_DELAY;
|
104 | 101 |
|
105 | 102 | $event = Event::createFromMessage($message);
|
106 | 103 |
|
| 104 | + if ($event->getEvent() === 'pusher:error') { |
| 105 | + return Observable::fromPromise(reject(new PusherErrorException($event->getData()['message'], $event->getData()['code']))); |
| 106 | + } |
| 107 | + |
107 | 108 | if ($event->getEvent() === 'pusher:connection_established') {
|
108 | 109 | $this->setActivityTimeout($event);
|
109 | 110 | }
|
110 | 111 |
|
111 |
| - return $event; |
| 112 | + return Observable::fromPromise(resolve($event)); |
| 113 | + }) |
| 114 | + |
| 115 | + // Handle connection level errors |
| 116 | + ->retryWhen(function (Observable $errors) { |
| 117 | + return $errors->flatMap(function (Throwable $throwable) { |
| 118 | + return $this->handleLowLevelError($throwable); |
| 119 | + }); |
112 | 120 | })
|
113 |
| - ->share(); |
| 121 | + |
| 122 | + // Share client |
| 123 | + ->share(); |
114 | 124 | }
|
115 | 125 |
|
116 | 126 | /**
|
@@ -206,8 +216,33 @@ public function send(array $message): bool
|
206 | 216 | return true;
|
207 | 217 | }
|
208 | 218 |
|
| 219 | + /** |
| 220 | + * Handle errors as described at https://pusher.com/docs/pusher_protocol#error-codes. |
| 221 | + */ |
209 | 222 | private function handleLowLevelError(Throwable $throwable)
|
210 | 223 | {
|
| 224 | + if (!($throwable instanceof WebsocketErrorException) && !($throwable instanceof RuntimeException) && !($throwable instanceof PusherErrorException)) { |
| 225 | + return Observable::fromPromise(reject($throwable)); |
| 226 | + } |
| 227 | + |
| 228 | + $code = $throwable->getCode(); |
| 229 | + $pusherError = ($throwable instanceof WebsocketErrorException || $throwable instanceof PusherErrorException); |
| 230 | + |
| 231 | + // Errors 4000-4099, don't retry connecting |
| 232 | + if ($pusherError && $code >= 4000 && $code <= 4099) { |
| 233 | + return Observable::fromPromise(reject($throwable)); |
| 234 | + } |
| 235 | + |
| 236 | + // Errors 4100-4199 reconnect after 1 or more seconds, we do it after 1.001 second |
| 237 | + if ($pusherError && $code >= 4100 && $code <= 4199) { |
| 238 | + return Observable::timer(1001); |
| 239 | + } |
| 240 | + |
| 241 | + // Errors 4200-4299 connection closed by Pusher, reconnect immediately, we wait 0.001 second |
| 242 | + if ($pusherError && $code >= 4200 && $code <= 4299) { |
| 243 | + return Observable::timer(1); |
| 244 | + } |
| 245 | + |
211 | 246 | $this->delay *= 2;
|
212 | 247 |
|
213 | 248 | return Observable::timer($this->delay);
|
|
0 commit comments