4
4
5
5
use React \Dns \Resolver \Resolver ;
6
6
use React \EventLoop \LoopInterface ;
7
+ use React \EventLoop \Timer \TimerInterface ;
7
8
use Rx \Disposable \CallbackDisposable ;
8
9
use Rx \Observable ;
9
10
use Rx \ObserverInterface ;
14
15
15
16
final class AsyncClient
16
17
{
18
+ const NO_ACTIVITY_TIMEOUT = 120 ;
19
+ const NO_PING_RESPONSE_TIMEOUT = 30 ;
20
+
21
+ /**
22
+ * @var LoopInterface
23
+ */
24
+ protected $ loop ;
25
+
26
+ /**
27
+ * @var WebsocketClient
28
+ */
29
+ protected $ lowLevelClient ;
30
+
17
31
/**
18
32
* @var Observable\RefCountObservable
19
33
*/
@@ -34,6 +48,16 @@ final class AsyncClient
34
48
*/
35
49
protected $ delay = 200 ;
36
50
51
+ /**
52
+ * @var TimerInterface
53
+ */
54
+ private $ noActivityTimer ;
55
+
56
+ /**
57
+ * @var TimerInterface
58
+ */
59
+ private $ pingIimeoutTimer ;
60
+
37
61
/**
38
62
* @param LoopInterface $loop
39
63
* @param string $app Application ID
@@ -50,6 +74,7 @@ public static function create(LoopInterface $loop, string $app, Resolver $resolv
50
74
}
51
75
52
76
return new self (
77
+ $ loop ,
53
78
new WebsocketClient (
54
79
ApiSettings::createUrl ($ app ),
55
80
false ,
@@ -63,22 +88,25 @@ public static function create(LoopInterface $loop, string $app, Resolver $resolv
63
88
/**
64
89
* @internal
65
90
*/
66
- public function __construct (WebsocketClient $ client )
91
+ public function __construct (LoopInterface $ loop , WebsocketClient $ client )
67
92
{
93
+ $ this ->loop = $ loop ;
68
94
//Only create one connection and share the most recent among all subscriber
69
- $ this ->client = $ client ->retryWhen (function (Observable $ errors ) {
95
+ $ this ->lowLevelClient = $ client ;
96
+ $ this ->client = $ this ->lowLevelClient ->retryWhen (function (Observable $ errors ) {
97
+ echo __LINE__ , ': ' , time (), PHP_EOL ;
98
+ $ this ->resetActivityTimer ();
70
99
return $ errors ->flatMap (function (Throwable $ throwable ) {
71
100
return $ this ->handleLowLevelError ($ throwable );
72
101
});
73
102
})->shareReplay (1 );
74
103
$ this ->messages = $ this ->client
75
104
->flatMap (function (MessageSubject $ ms ) {
76
- //var_export($ms);
77
105
return $ ms ;
78
106
})
79
107
->_ApiClients_jsonDecode ()
80
108
->map (function (array $ message ) {
81
- //var_export($message );
109
+ $ this -> resetActivityTimer ( );
82
110
return Event::createFromMessage ($ message );
83
111
});
84
112
}
@@ -134,14 +162,42 @@ public function send(array $message)
134
162
$ this ->client
135
163
->take (1 )
136
164
->subscribe (function (MessageSubject $ ms ) use ($ message ) {
165
+ $ this ->resetActivityTimer ();
137
166
$ ms ->send (json_encode ($ message ));
138
167
});
139
168
}
140
169
141
170
private function handleLowLevelError (Throwable $ throwable )
142
171
{
172
+ $ this ->resetActivityTimer ();
143
173
$ this ->delay *= 2 ;
174
+ echo get_class ($ throwable ), PHP_EOL ;
175
+ echo get_class ($ throwable ->getPrevious ()), PHP_EOL ;
176
+ echo get_class ($ throwable ->getPrevious ()->getPrevious ()), PHP_EOL ;
177
+ echo get_class ($ throwable ->getPrevious ()->getPrevious ()->getPrevious ()), PHP_EOL ;
144
178
echo __LINE__ , ': ' , time (), PHP_EOL ;
145
179
return Observable::timer ($ this ->delay );
146
180
}
181
+
182
+ private function resetActivityTimer ()
183
+ {
184
+ echo 'resetActivityTimer ' , PHP_EOL ;
185
+ if ($ this ->noActivityTimer instanceof TimerInterface) {
186
+ $ this ->noActivityTimer ->cancel ();
187
+ }
188
+
189
+ $ this ->noActivityTimer = $ this ->loop ->addTimer (
190
+ self ::NO_ACTIVITY_TIMEOUT ,
191
+ function () {
192
+ echo 'resetActivityTimer:tick ' , PHP_EOL ;
193
+ $ this ->send (['event ' => 'pusher:ping ' ]);
194
+ $ this ->pingIimeoutTimer = $ this ->loop ->addTimer (
195
+ self ::NO_PING_RESPONSE_TIMEOUT ,
196
+ function () {
197
+ $ this ->lowLevelClient ->dispose ();
198
+ }
199
+ );
200
+ }
201
+ );
202
+ }
147
203
}
0 commit comments