1
|
|
|
<?php namespace Comodojo\Daemon\Socket; |
2
|
|
|
|
3
|
|
|
use \Comodojo\RpcClient\Interfaces\Transport as TransportInterface; |
4
|
|
|
use \Comodojo\Httprequest\Httprequest; |
5
|
|
|
use \phpseclib\Crypt\AES; |
6
|
|
|
use \Psr\Log\LoggerInterface; |
7
|
|
|
use \Comodojo\Exception\RpcException; |
8
|
|
|
use \Comodojo\Exception\SocketException; |
9
|
|
|
use \Exception; |
10
|
|
|
|
11
|
|
|
class SocketTransport extends AbstractSocket implements TransportInterface { |
12
|
|
|
|
13
|
|
|
private $aes = null; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* {@inheritdoc} |
17
|
|
|
*/ |
18
|
5 |
|
public function performCall( |
19
|
|
|
LoggerInterface $logger, |
20
|
|
|
$data, |
21
|
|
|
$content_type, |
22
|
|
|
$encrypt=false |
23
|
|
|
) { |
24
|
|
|
|
25
|
|
|
try { |
26
|
|
|
|
27
|
5 |
|
$logger->debug("Connecting to socket"); |
28
|
|
|
|
29
|
5 |
|
$this->connect(); |
30
|
|
|
|
31
|
5 |
|
$logger->debug("Sending RPC data"); |
32
|
|
|
|
33
|
5 |
|
$data = $this->can($data, $encrypt); |
34
|
|
|
|
35
|
5 |
|
$response = $this->send($content_type, $data); |
36
|
|
|
|
37
|
5 |
|
$this->close(); |
38
|
|
|
|
39
|
5 |
|
$logger->debug("Decoding RPC response"); |
40
|
|
|
|
41
|
5 |
|
$return = $this->uncan($response, $encrypt); |
42
|
|
|
|
43
|
|
|
} catch (SocketException $se) { |
44
|
|
|
|
45
|
|
|
$logger->error("Socket Transport error: ".$se->getMessage()); |
46
|
|
|
|
47
|
|
|
throw $se; |
48
|
|
|
|
49
|
|
|
} catch (RpcException $re) { |
50
|
|
|
|
51
|
|
|
$logger->error("RPC Client error: ".$re->getMessage()); |
52
|
|
|
|
53
|
|
|
throw $re; |
54
|
|
|
|
55
|
|
|
} catch (Exception $e) { |
56
|
|
|
|
57
|
|
|
$logger->critical("Generic Client error: ".$e->getMessage()); |
58
|
|
|
|
59
|
|
|
throw $e; |
60
|
|
|
|
61
|
|
|
} |
62
|
|
|
|
63
|
5 |
|
return $return; |
64
|
|
|
|
65
|
|
|
} |
66
|
|
|
|
67
|
5 |
|
public static function create($handler, $read_buffer = null) { |
68
|
|
|
|
69
|
5 |
|
return new SocketTransport($handler, $read_buffer); |
70
|
|
|
|
71
|
|
|
} |
72
|
|
|
|
73
|
5 |
|
public function connect() { |
74
|
|
|
|
75
|
5 |
|
$this->socket = @socket_create( |
76
|
5 |
|
$this->socket_domain, |
77
|
5 |
|
$this->socket_type, |
78
|
5 |
|
$this->socket_protocol |
79
|
|
|
); |
80
|
|
|
|
81
|
5 |
|
if ( $this->socket === false ) { |
82
|
|
|
$error = self::getSocketError(); |
83
|
|
|
throw new SocketException("Socket unavailable: $error"); |
84
|
|
|
} |
85
|
|
|
|
86
|
5 |
|
$connect = @socket_connect( |
87
|
5 |
|
$this->socket, |
88
|
5 |
|
$this->socket_resource, |
89
|
5 |
|
$this->socket_port |
90
|
|
|
); |
91
|
|
|
|
92
|
5 |
View Code Duplication |
if ( $connect === false ) { |
|
|
|
|
93
|
|
|
$error = self::getSocketError($this->socket); |
94
|
|
|
throw new SocketException("Cannot connect to socket: $error"); |
95
|
|
|
} |
96
|
|
|
|
97
|
5 |
|
$greeter = $this->readGreeter(); |
98
|
|
|
|
99
|
5 |
|
if ( $greeter->status != 'connected' ) { |
|
|
|
|
100
|
|
|
throw new SocketException("Socket connect failed: ".$greeter->status); |
|
|
|
|
101
|
|
|
} |
102
|
|
|
|
103
|
5 |
|
if ( $greeter->version != self::VERSION ) { |
|
|
|
|
104
|
|
|
throw new SocketException("Socket connect failed: socket interface version mismatch"); |
105
|
|
|
} |
106
|
|
|
|
107
|
5 |
|
return $this; |
108
|
|
|
|
109
|
|
|
} |
110
|
|
|
|
111
|
5 |
|
public function close() { |
112
|
|
|
|
113
|
5 |
|
return socket_close($this->socket); |
114
|
|
|
|
115
|
|
|
} |
116
|
|
|
|
117
|
5 |
|
protected function send($content_type, $data) { |
118
|
|
|
|
119
|
5 |
|
$sent = $this->write($content_type, $data); |
|
|
|
|
120
|
|
|
|
121
|
|
|
// TODO: manage exceptions! |
122
|
|
|
|
123
|
5 |
|
$received = $this->read(); |
124
|
|
|
|
125
|
5 |
|
if ( $received->status === false ) { |
|
|
|
|
126
|
|
|
throw new Exception($received->message); |
|
|
|
|
127
|
|
|
} |
128
|
|
|
|
129
|
5 |
|
return $received->message; |
|
|
|
|
130
|
|
|
|
131
|
|
|
} |
132
|
|
|
|
133
|
5 |
|
protected function write($content_type, $data) { |
134
|
|
|
|
135
|
5 |
|
$request = new Request(); |
136
|
|
|
|
137
|
5 |
|
$request->content_type = $content_type; |
|
|
|
|
138
|
5 |
|
$request->message = $data; |
|
|
|
|
139
|
|
|
|
140
|
5 |
|
$datagram = $request->serialize()."\r\n"; |
141
|
|
|
|
142
|
5 |
|
return socket_write($this->socket, $datagram, strlen($datagram)); |
143
|
|
|
|
144
|
|
|
} |
145
|
|
|
|
146
|
5 |
|
protected function read() { |
147
|
|
|
|
148
|
5 |
|
$response = new Response(); |
149
|
|
|
|
150
|
5 |
|
$datagram = $this->rawRead(); |
151
|
|
|
|
152
|
5 |
|
if ( is_null($datagram) ) { |
153
|
|
|
$response->status = false; |
|
|
|
|
154
|
|
|
$response->message = "Server has gone away"; |
|
|
|
|
155
|
5 |
|
} else if ( empty($datagram) ) { |
156
|
|
|
$response->status = false; |
|
|
|
|
157
|
|
|
$response->message = "No response received"; |
|
|
|
|
158
|
|
|
} else { |
159
|
5 |
|
$response->unserialize($datagram); |
160
|
|
|
} |
161
|
|
|
|
162
|
5 |
|
return $response; |
163
|
|
|
|
164
|
|
|
} |
165
|
|
|
|
166
|
5 |
|
protected function readGreeter() { |
167
|
|
|
|
168
|
5 |
|
$greeter = new Greeter(); |
169
|
|
|
|
170
|
5 |
|
$datagram = $this->rawRead(); |
171
|
|
|
|
172
|
5 |
|
if ( is_null($datagram) ) { |
173
|
|
|
$greeter->status = 'greeter not received'; |
|
|
|
|
174
|
|
|
return $greeter; |
175
|
|
|
} |
176
|
|
|
|
177
|
5 |
|
if ( $datagram === false ) { |
178
|
|
|
$greeter->status = 'server has gone away'; |
|
|
|
|
179
|
|
|
return $greeter; |
180
|
|
|
} |
181
|
|
|
|
182
|
5 |
|
return $greeter->unserialize($datagram); |
183
|
|
|
|
184
|
|
|
} |
185
|
|
|
|
186
|
5 |
|
protected function rawRead() { |
187
|
|
|
|
188
|
5 |
|
$datagram = ''; |
189
|
|
|
|
190
|
5 |
View Code Duplication |
while (true) { |
|
|
|
|
191
|
5 |
|
$recv = @socket_read($this->socket, $this->read_buffer, PHP_NORMAL_READ); |
192
|
5 |
|
if ( $recv === false ) break; |
193
|
5 |
|
if ( $recv === 0 ) return null; |
194
|
5 |
|
$datagram .= $recv; |
195
|
5 |
|
if(strstr($recv, PHP_EOL)) break; |
196
|
|
|
} |
197
|
|
|
|
198
|
5 |
|
return trim($datagram); |
199
|
|
|
|
200
|
|
|
} |
201
|
|
|
|
202
|
5 |
|
private function can($data, $key) { |
203
|
|
|
|
204
|
5 |
|
if ( !empty($key) && is_string($key) ) { |
205
|
|
|
|
206
|
|
|
$this->aes = new AES(); |
207
|
|
|
|
208
|
|
|
$this->aes->setKey($key); |
209
|
|
|
|
210
|
|
|
$return = 'comodojo_encrypted_request-'.base64_encode( $this->aes->encrypt($data) ); |
211
|
|
|
|
212
|
|
|
} else { |
213
|
|
|
|
214
|
5 |
|
$return = $data; |
215
|
|
|
|
216
|
|
|
} |
217
|
|
|
|
218
|
5 |
|
return $return; |
219
|
|
|
|
220
|
|
|
} |
221
|
|
|
|
222
|
5 |
|
private function uncan($data, $key) { |
223
|
|
|
|
224
|
5 |
|
if ( !empty($key) && is_string($key) ) { |
225
|
|
|
|
226
|
|
|
if ( self::checkEncryptedResponseConsistency($data) === false ) throw new RpcException("Inconsistent encrypted response received"); |
227
|
|
|
|
228
|
|
|
$return = $this->aes->decrypt(base64_decode(substr($data, 28))); |
229
|
|
|
|
230
|
|
|
} else { |
231
|
|
|
|
232
|
5 |
|
$return = $data; |
233
|
|
|
|
234
|
|
|
} |
235
|
|
|
|
236
|
5 |
|
return $return; |
237
|
|
|
|
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Check if an encrypted envelope is consisent or not |
242
|
|
|
* |
243
|
|
|
* @param string $data |
244
|
|
|
* |
245
|
|
|
* @return bool |
246
|
|
|
*/ |
247
|
|
|
private static function checkEncryptedResponseConsistency($data) { |
248
|
|
|
|
249
|
|
|
return substr($data, 0, 27) == 'comodojo_encrypted_response' ? true : false; |
250
|
|
|
|
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
} |
254
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.