1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* soluble-japha / PHPJavaBridge driver client. |
4
|
|
|
* |
5
|
|
|
* Refactored version of phpjababridge's Java.inc file compatible |
6
|
|
|
* with php java bridge 6.2 |
7
|
|
|
* |
8
|
|
|
* |
9
|
|
|
* @credits http://php-java-bridge.sourceforge.net/pjb/ |
10
|
|
|
* |
11
|
|
|
* @see http://github.com/belgattitude/soluble-japha |
12
|
|
|
* |
13
|
|
|
* @author Jost Boekemeier |
14
|
|
|
* @author Vanvelthem Sébastien (refactoring and fixes from original implementation) |
15
|
|
|
* @license MIT |
16
|
|
|
* |
17
|
|
|
* The MIT License (MIT) |
18
|
|
|
* Copyright (c) 2014-2017 Jost Boekemeier |
19
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
20
|
|
|
* of this software and associated documentation files (the "Software"), to deal |
21
|
|
|
* in the Software without restriction, including without limitation the rights |
22
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
23
|
|
|
* copies of the Software, and to permit persons to whom the Software is |
24
|
|
|
* furnished to do so, subject to the following conditions: |
25
|
|
|
* |
26
|
|
|
* The above copyright notice and this permission notice shall be included in |
27
|
|
|
* all copies or substantial portions of the Software. |
28
|
|
|
* |
29
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
30
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
31
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
32
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
33
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
34
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
35
|
|
|
* THE SOFTWARE. |
36
|
|
|
*/ |
37
|
|
|
|
38
|
|
|
namespace Soluble\Japha\Bridge\Driver\Pjb62; |
39
|
|
|
|
40
|
|
|
use Soluble\Japha\Bridge\Exception\ConnectionException; |
41
|
|
|
use Soluble\Japha\Bridge\Http\Cookie; |
42
|
|
|
|
43
|
|
|
class SimpleHttpTunnelHandler extends SimpleHttpHandler |
44
|
|
|
{ |
45
|
|
|
/** |
46
|
|
|
* @var resource |
47
|
|
|
*/ |
48
|
|
|
public $socket; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @var bool |
52
|
|
|
*/ |
53
|
|
|
protected $hasContentLength = false; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* @var bool |
57
|
|
|
*/ |
58
|
|
|
protected $isRedirect; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @param Protocol $protocol |
62
|
|
|
* @param string $ssl |
63
|
|
|
* @param string $host |
64
|
|
|
* @param int $port |
65
|
|
|
* @param string $java_servlet |
66
|
|
|
* @param int $java_recv_size |
67
|
|
|
* @param int $java_send_size |
68
|
|
|
* |
69
|
|
|
* @throws ConnectionException |
70
|
|
|
*/ |
71
|
26 |
|
public function __construct($protocol, $ssl, $host, $port, $java_servlet, $java_recv_size, $java_send_size) |
72
|
|
|
{ |
73
|
26 |
|
parent::__construct($protocol, $ssl, $host, $port, $java_servlet, $java_recv_size, $java_send_size); |
74
|
26 |
|
$this->open(); |
75
|
24 |
|
} |
76
|
|
|
|
77
|
26 |
|
public function createSimpleChannel() |
78
|
|
|
{ |
79
|
26 |
|
$this->channel = new EmptyChannel($this, $this->java_recv_size, $this->java_send_size); |
80
|
26 |
|
} |
81
|
|
|
|
82
|
26 |
|
public function createChannel() |
83
|
|
|
{ |
84
|
26 |
|
$this->createSimpleChannel(); |
85
|
26 |
|
} |
86
|
|
|
|
87
|
1 |
|
public function shutdownBrokenConnection(string $msg = ''): void |
88
|
|
|
{ |
89
|
1 |
|
fclose($this->socket); |
90
|
1 |
|
$this->dieWithBrokenConnection($msg); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* @param resource $socket |
95
|
|
|
* @param int|null $errno |
96
|
|
|
* @param string|null $errstr |
97
|
|
|
* |
98
|
|
|
* @throws ConnectionException |
99
|
|
|
*/ |
100
|
26 |
|
protected function checkSocket($socket, $errno = null, $errstr = null) |
101
|
|
|
{ |
102
|
26 |
|
if (!$socket) { |
103
|
2 |
|
$msg = "Could not connect to the JEE server {$this->ssl}{$this->host}:{$this->port}. Please start it."; |
104
|
2 |
|
if ($errstr !== null || $errno !== null) { |
105
|
2 |
|
$msg .= '(errno:' . $errno . ',' . $errstr . ')'; |
106
|
|
|
} |
107
|
2 |
|
$logger = $this->protocol->getClient()->getLogger(); |
108
|
2 |
|
$logger->critical("[soluble-japha] $msg." . __METHOD__); |
109
|
2 |
|
throw new ConnectionException(__METHOD__ . ' ' . $msg); |
110
|
|
|
} |
111
|
24 |
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* @throws ConnectionException |
115
|
|
|
*/ |
116
|
26 |
|
protected function open() |
117
|
|
|
{ |
118
|
26 |
|
$errno = null; |
119
|
26 |
|
$errstr = null; |
120
|
|
|
|
121
|
26 |
|
$location = $this->ssl . $this->host; |
122
|
26 |
|
$socket = @fsockopen($location, $this->port, $errno, $errstr, 20); |
123
|
26 |
|
$this->checkSocket($socket, $errno, $errstr); |
124
|
24 |
|
stream_set_timeout($socket, -1); |
125
|
24 |
|
$this->socket = $socket; |
126
|
24 |
|
} |
127
|
|
|
|
128
|
|
|
public function fread(int $size): ?string |
129
|
|
|
{ |
130
|
|
|
$length = hexdec(fgets($this->socket, $this->java_recv_size)); |
131
|
|
|
$data = ''; |
132
|
|
|
while ($length > 0) { |
133
|
|
|
$str = fread($this->socket, $length); |
134
|
|
|
if (feof($this->socket) || $str === false) { |
135
|
|
|
return null; |
136
|
|
|
} |
137
|
|
|
$length -= strlen($str); |
138
|
|
|
$data .= $str; |
139
|
|
|
} |
140
|
|
|
fgets($this->socket, 3); |
141
|
|
|
|
142
|
|
|
return $data; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
public function fwrite(string $data): ?int |
146
|
|
|
{ |
147
|
|
|
$len = dechex(strlen($data)); |
148
|
|
|
$written = fwrite($this->socket, "${len}\r\n${data}\r\n"); |
149
|
|
|
if ($written === false) { |
150
|
|
|
return null; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
return $written; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
protected function close(): void |
157
|
|
|
{ |
158
|
|
|
fwrite($this->socket, "0\r\n\r\n"); |
159
|
|
|
fgets($this->socket, $this->java_recv_size); |
160
|
|
|
fgets($this->socket, 3); |
161
|
|
|
fclose($this->socket); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
public function read(int $size): string |
165
|
|
|
{ |
166
|
9 |
|
if (null === $this->headers) { |
167
|
9 |
|
$this->parseHeaders(); |
168
|
|
|
} |
169
|
|
|
|
170
|
9 |
|
if (isset($this->headers['http_error'])) { |
171
|
1 |
|
$str = null; |
|
|
|
|
172
|
1 |
|
if (isset($this->headers['transfer_chunked'])) { |
173
|
|
|
$str = $this->fread($this->java_recv_size); |
174
|
1 |
|
} elseif (isset($this->headers['content_length'])) { |
175
|
1 |
|
$len = $this->headers['content_length']; |
176
|
1 |
|
for ($str = fread($this->socket, $len); strlen($str) < $len; $str .= fread($this->socket, $len - strlen($str))) { |
177
|
|
|
if (feof($this->socket)) { |
178
|
|
|
break; |
179
|
|
|
} |
180
|
|
|
} |
181
|
|
|
} else { |
182
|
|
|
$str = fread($this->socket, $this->java_recv_size); |
183
|
|
|
} |
184
|
1 |
|
$str = ($str === false || $str === null) ? '' : $str; |
185
|
1 |
|
$this->shutdownBrokenConnection($str); |
186
|
|
|
} |
187
|
|
|
|
188
|
8 |
|
$response = $this->fread($this->java_recv_size); |
189
|
8 |
|
if ($response === null) { |
190
|
|
|
$this->shutdownBrokenConnection('Cannot socket read response from SimpleHttpTunnelHandler'); |
191
|
|
|
} |
192
|
|
|
|
193
|
8 |
|
return (string) $response; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
protected function getBodyFor($compat, $data) |
197
|
|
|
{ |
198
|
21 |
|
$len = dechex(2 + strlen($data)); |
199
|
|
|
|
200
|
21 |
|
return "Cache-Control: no-cache\r\nPragma: no-cache\r\nTransfer-Encoding: chunked\r\n\r\n${len}\r\n\177${compat}${data}\r\n"; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
public function write(string $data): ?int |
204
|
|
|
{ |
205
|
21 |
|
$compat = PjbProxyClient::getInstance()->getCompatibilityOption($this->protocol->client); |
206
|
21 |
|
$this->headers = null; |
207
|
21 |
|
$socket = $this->socket; |
208
|
21 |
|
$webapp = $this->getWebApp(); |
209
|
21 |
|
$cookies = Cookie::getCookiesHeaderLine(); |
210
|
21 |
|
$context = $this->getContext(); |
211
|
21 |
|
$res = 'PUT '; |
212
|
21 |
|
$res .= $webapp; |
213
|
21 |
|
$res .= " HTTP/1.1\r\n"; |
214
|
21 |
|
$res .= "Host: {$this->host}:{$this->port}\r\n"; |
215
|
21 |
|
$res .= $context; |
216
|
21 |
|
$res .= $cookies; |
217
|
21 |
|
$res .= $this->getBodyFor($compat, $data); |
218
|
21 |
|
$count = fwrite($socket, $res); |
219
|
21 |
|
if ($count === false) { |
220
|
|
|
$this->shutdownBrokenConnection('Cannot write to socket, broken connection handle'); |
221
|
|
|
} |
222
|
21 |
|
$flushed = fflush($socket); |
223
|
21 |
|
if ($flushed === false) { |
224
|
|
|
$this->shutdownBrokenConnection('Cannot flush to socket, broken connection handle'); |
225
|
|
|
} |
226
|
|
|
|
227
|
21 |
|
return (int) $count; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
protected function parseHeaders() |
231
|
|
|
{ |
232
|
9 |
|
$this->headers = []; |
233
|
|
|
|
234
|
9 |
|
$line = trim(fgets($this->socket, $this->java_recv_size)); |
235
|
9 |
|
$ar = explode(' ', $line); |
236
|
9 |
|
$code = ((int) $ar[1]); |
237
|
9 |
|
if ($code != 200) { |
238
|
1 |
|
$this->headers['http_error'] = $code; |
239
|
|
|
} |
240
|
9 |
|
while ($str = trim(fgets($this->socket, $this->java_recv_size))) { |
241
|
9 |
|
if ($str[0] == 'X') { |
242
|
8 |
|
if (!strncasecmp('X_JAVABRIDGE_REDIRECT', $str, 21)) { |
243
|
8 |
|
$this->headers['redirect'] = trim(substr($str, 22)); |
244
|
8 |
|
} elseif (!strncasecmp('X_JAVABRIDGE_CONTEXT', $str, 20)) { |
245
|
8 |
|
$this->headers['context'] = trim(substr($str, 21)); |
246
|
|
|
} |
247
|
9 |
|
} elseif ($str[0] == 'S') { |
248
|
|
|
if (!strncasecmp('SET-COOKIE', $str, 10)) { |
249
|
|
|
$str = substr($str, 12); |
250
|
|
|
$this->cookies[] = $str; |
251
|
|
|
$ar = explode(';', $str); |
252
|
|
|
$cookie = explode('=', $ar[0]); |
253
|
|
|
$path = ''; |
254
|
|
|
if (isset($ar[1])) { |
255
|
|
|
$p = explode('=', $ar[1]); |
256
|
|
|
} |
257
|
|
|
if (isset($p)) { |
258
|
|
|
$path = $p[1]; |
259
|
|
|
} |
260
|
|
|
$this->doSetCookie($cookie[0], $cookie[1], $path); |
261
|
|
|
} |
262
|
9 |
|
} elseif ($str[0] == 'C') { |
263
|
9 |
|
if (!strncasecmp('CONTENT-LENGTH', $str, 14)) { |
264
|
9 |
|
$this->headers['content_length'] = trim(substr($str, 15)); |
265
|
9 |
|
$this->hasContentLength = true; |
266
|
1 |
|
} elseif (!strncasecmp('CONNECTION', $str, 10) && !strncasecmp('close', trim(substr($str, 11)), 5)) { |
267
|
9 |
|
$this->headers['connection_close'] = true; |
268
|
|
|
} |
269
|
9 |
|
} elseif ($str[0] == 'T') { |
270
|
|
|
if (!strncasecmp('TRANSFER-ENCODING', $str, 17) && !strncasecmp('chunked', trim(substr($str, 18)), 7)) { |
271
|
|
|
$this->headers['transfer_chunked'] = true; |
272
|
|
|
} |
273
|
|
|
} |
274
|
|
|
} |
275
|
9 |
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* @return ChunkedSocketChannel |
279
|
|
|
*/ |
280
|
|
|
protected function getSimpleChannel() |
281
|
|
|
{ |
282
|
|
|
//public function __construct($peer, $host, $recv_size, $send_size) |
|
|
|
|
283
|
|
|
|
284
|
|
|
// Originally found in Pjb - no sense |
285
|
|
|
//return new ChunkedSocketChannel($this->socket, $this->protocol, $this->host); |
|
|
|
|
286
|
|
|
return new ChunkedSocketChannel($this->socket, $this->host, $this->java_recv_size, $this->java_send_size); |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
public function redirect(): void |
290
|
|
|
{ |
291
|
8 |
|
$this->isRedirect = isset($this->headers['redirect']); |
292
|
8 |
|
if ($this->isRedirect) { |
293
|
8 |
|
$channelName = $this->headers['redirect']; |
294
|
|
|
} else { |
295
|
|
|
$channelName = null; |
296
|
|
|
} |
297
|
8 |
|
$context = $this->headers['context']; |
298
|
8 |
|
$len = strlen($context); |
299
|
8 |
|
$len0 = chr(0xFF); |
300
|
8 |
|
$len1 = chr($len & 0xFF); |
301
|
8 |
|
$len >>= 8; |
302
|
8 |
|
$len2 = chr($len & 0xFF); |
303
|
8 |
|
if ($this->isRedirect) { |
304
|
8 |
|
$this->protocol->setSocketHandler(new SocketHandler($this->protocol, $this->getChannel($channelName))); |
305
|
8 |
|
$this->protocol->write("\177${len0}${len1}${len2}${context}"); |
306
|
8 |
|
$this->context = sprintf("X_JAVABRIDGE_CONTEXT: %s\r\n", $context); |
307
|
8 |
|
$this->close(); |
308
|
8 |
|
$this->protocol->handler = $this->protocol->getSocketHandler(); |
309
|
8 |
|
if ($this->protocol->client->sendBuffer !== null) { |
310
|
8 |
|
$written = $this->protocol->handler->write($this->protocol->client->sendBuffer); |
311
|
8 |
|
if ($written === null) { |
312
|
|
|
$this->protocol->handler->shutdownBrokenConnection('Broken local connection handle'); |
313
|
|
|
} |
314
|
8 |
|
$this->protocol->client->sendBuffer = null; |
315
|
8 |
|
$read = $this->protocol->handler->read(1); |
|
|
|
|
316
|
|
|
} |
317
|
|
|
} else { |
318
|
|
|
$this->protocol->setSocketHandler(new SocketHandler($this->protocol, $this->getSimpleChannel())); |
319
|
|
|
$this->protocol->handler = $this->protocol->getSocketHandler(); |
320
|
|
|
} |
321
|
8 |
|
} |
322
|
|
|
} |
323
|
|
|
|
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.
Both the
$myVar
assignment in line 1 and the$higher
assignment in line 2 are dead. The first because$myVar
is never used and the second because$higher
is always overwritten for every possible time line.