Completed
Push — master ( 18c02e...701e2d )
by Sébastien
05:57
created

SimpleHttpTunnelHandler::read()   D

Complexity

Conditions 10
Paths 68

Size

Total Lines 31
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 12.1438

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 13
cts 18
cp 0.7221
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 20
nc 68
nop 1
crap 12.1438

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
0 ignored issues
show
Unused Code introduced by
$str is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

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.

Loading history...
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)
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
283
284
        // Originally found in Pjb - no sense
285
        //return new ChunkedSocketChannel($this->socket, $this->protocol, $this->host);
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
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);
0 ignored issues
show
Unused Code introduced by
$read is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

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.

Loading history...
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