2
2
3
3
namespace Rx \Websocket ;
4
4
5
- use Exception ;
6
- use Rx \ Websocket \RFC6455 \Handshake \ClientNegotiator ;
5
+ use GuzzleHttp \ Psr7 \ Uri ;
6
+ use Ratchet \RFC6455 \Handshake \ClientNegotiator ;
7
7
use React \Dns \Resolver \Factory ;
8
8
use React \HttpClient \Request ;
9
9
use React \HttpClient \Response ;
10
10
use Rx \Disposable \CallbackDisposable ;
11
+ use Rx \Observable ;
11
12
use Rx \Observable \AnonymousObservable ;
12
13
use Rx \Observer \CallbackObserver ;
13
14
use Rx \ObserverInterface ;
14
- use Rx \Subject \ Subject ;
15
+ use Rx \SchedulerInterface ;
15
16
16
- class Client extends Subject
17
+ class Client extends Observable
17
18
{
18
19
/** @var string */
19
20
protected $ url ;
@@ -32,40 +33,46 @@ class Client extends Subject
32
33
*/
33
34
public function __construct ($ url , $ useMessageObject = false , array $ subProtocols = [])
34
35
{
35
- $ this ->url = $ url ;
36
+ $ this ->url = $ url ;
36
37
$ this ->useMessageObject = $ useMessageObject ;
37
- $ this ->subProtocols = $ subProtocols ;
38
+ $ this ->subProtocols = $ subProtocols ;
38
39
}
39
40
40
- private function startConnection ( )
41
+ public function subscribe ( ObserverInterface $ clientObserver , $ scheduler = null )
41
42
{
42
43
$ loop = \EventLoop \getLoop ();
43
44
44
45
$ dnsResolverFactory = new Factory ();
46
+
45
47
$ dnsResolver = $ dnsResolverFactory ->createCached ('8.8.8.8 ' , $ loop );
46
48
47
49
$ factory = new \React \HttpClient \Factory ();
48
- $ client = $ factory ->create ($ loop , $ dnsResolver );
50
+ $ client = $ factory ->create ($ loop , $ dnsResolver );
51
+
52
+ $ cNegotiator = new ClientNegotiator ();
49
53
50
- $ cNegotiator = new ClientNegotiator ($ this ->url , $ this ->subProtocols );
54
+ /** @var \GuzzleHttp\Psr7\Request $nRequest */
55
+ $ nRequest = $ cNegotiator ->generateRequest (new Uri ($ this ->url ));
56
+
57
+ if (!empty ($ this ->subProtocols )) {
58
+ $ nRequest = $ nRequest
59
+ ->withoutHeader ('Sec-WebSocket-Protocol ' )
60
+ ->withHeader ('Sec-WebSocket-Protocol ' , $ this ->subProtocols );
61
+ }
51
62
52
- $ headers = $ cNegotiator -> getRequest () ->getHeaders ();
63
+ $ headers = $ nRequest ->getHeaders ();
53
64
54
65
$ flatHeaders = [];
55
66
foreach ($ headers as $ k => $ v ) {
56
67
$ flatHeaders [$ k ] = $ v [0 ];
57
68
}
58
69
59
- $ request = $ client ->request (" GET " , $ this ->url , $ flatHeaders , '1.1 ' );
70
+ $ request = $ client ->request (' GET ' , $ this ->url , $ flatHeaders , '1.1 ' );
60
71
61
- $ request ->on ('response ' , function (Response $ response , Request $ request ) use ($ cNegotiator ) {
72
+ $ request ->on ('response ' , function (Response $ response , Request $ request ) use ($ flatHeaders , $ cNegotiator, $ nRequest , $ clientObserver ) {
62
73
if ($ response ->getCode () !== 101 ) {
63
- throw new \Exception (" Unexpected response code " . $ response ->getCode ());
74
+ throw new \Exception (' Unexpected response code ' . $ response ->getCode ());
64
75
}
65
- // TODO: Should validate response
66
- //$cNegotiator->validateResponse($response);
67
-
68
- $ subprotoHeader = "" ;
69
76
70
77
$ psr7Response = new \GuzzleHttp \Psr7 \Response (
71
78
$ response ->getCode (),
@@ -74,12 +81,17 @@ private function startConnection()
74
81
$ response ->getVersion ()
75
82
);
76
83
77
- if (count ($ psr7Response ->getHeader ('Sec-WebSocket-Protocol ' )) == 1 ) {
78
- $ subprotoHeader = $ psr7Response ->getHeader ('Sec-WebSocket-Protocol ' )[0 ];
84
+ $ psr7Request = new \GuzzleHttp \Psr7 \Request ('GET ' , $ this ->url , $ flatHeaders );
85
+
86
+ if (!$ cNegotiator ->validateResponse ($ psr7Request , $ psr7Response )) {
87
+ throw new \Exception ('Invalid response ' );
79
88
}
80
89
81
- parent ::onNext (new MessageSubject (
82
- new AnonymousObservable (function (ObserverInterface $ observer ) use ($ response ) {
90
+ $ subprotoHeader = $ psr7Response ->getHeader ('Sec-WebSocket-Protocol ' );
91
+
92
+ $ clientObserver ->onNext (new MessageSubject (
93
+ new AnonymousObservable (function (ObserverInterface $ observer , SchedulerInterface $ scheduler ) use ($ response , $ clientObserver ) {
94
+
83
95
$ response ->on ('data ' , function ($ data ) use ($ observer ) {
84
96
$ observer ->onNext ($ data );
85
97
});
@@ -92,18 +104,19 @@ private function startConnection()
92
104
$ observer ->onCompleted ();
93
105
});
94
106
95
- $ response ->on ('end ' , function () use ($ observer ) {
107
+ $ response ->on ('end ' , function () use ($ observer, $ clientObserver ) {
96
108
$ observer ->onCompleted ();
97
109
98
110
// complete the parent observer - we only do 1 connection
99
- parent :: onCompleted ();
111
+ $ clientObserver -> onCompleted ();
100
112
});
101
113
102
114
103
115
return new CallbackDisposable (function () use ($ response ) {
104
116
// commented this out because disposal was causing the other
105
117
// end (the request) to close also - which causes the pending messages
106
118
// to get tossed
119
+ // maybe try with $response->end()?
107
120
//$response->close();
108
121
});
109
122
}),
@@ -121,41 +134,15 @@ function () use ($request) {
121
134
true ,
122
135
$ this ->useMessageObject ,
123
136
$ subprotoHeader ,
124
- $ cNegotiator -> getRequest () ,
137
+ $ nRequest ,
125
138
$ psr7Response
126
139
));
127
140
});
128
141
129
142
$ request ->writeHead ();
130
- }
131
-
132
- public function subscribe (ObserverInterface $ observer , $ scheduler = null )
133
- {
134
- if (!$ this ->isStopped ) {
135
- $ this ->startConnection ();
136
- }
137
-
138
- return parent ::subscribe ($ observer , $ scheduler );
139
- }
140
-
141
- public function send ($ value )
142
- {
143
- $ this ->onNext ($ value );
144
- }
145
-
146
- // Not sure we need this object to be a subject - just being an observer should be good enough I think
147
- public function onNext ($ value )
148
- {
149
-
150
- }
151
-
152
- public function onError (Exception $ exception )
153
- {
154
-
155
- }
156
-
157
- public function onCompleted ()
158
- {
159
143
144
+ return new CallbackDisposable (function () use ($ request ) {
145
+ $ request ->close ();
146
+ });
160
147
}
161
148
}
0 commit comments