Passed
Push — master ( 01f538...95b550 )
by Igor
04:19 queued 59s
created

SocketClient::receiveResponse()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
cc 2
eloc 5
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 11
ccs 5
cts 6
cp 0.8333
crap 2.0185
rs 10
1
<?php
2
/*
3
 * This file is part of JSON RPC Client.
4
 *
5
 * (c) Igor Lazarev <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Strider2038\JsonRpcClient\Transport\Socket;
12
13
use Strider2038\JsonRpcClient\Configuration\ConnectionOptions;
14
use Strider2038\JsonRpcClient\Exception\ConnectionFailedException;
15
use Strider2038\JsonRpcClient\Exception\ConnectionLostException;
16
use Strider2038\JsonRpcClient\Exception\RemoteProcedureCallFailedException;
17
18
/**
19
 * @author Igor Lazarev <[email protected]>
20
 */
21
class SocketClient
22
{
23
    /**
24
     * @var SocketConnector
25
     */
26
    private $connector;
27
28
    /**
29
     * @var SocketConnection|null
30
     */
31
    private $connection;
32
33
    /**
34
     * Connection in URL format.
35
     *
36
     * @var string
37
     */
38
    private $url;
39
40
    /**
41
     * @var ConnectionOptions
42
     */
43
    private $options;
44
45
    /**
46
     * Request timeout in microseconds.
47
     *
48
     * @var int
49
     */
50
    private $requestTimeoutUs;
51
52 20
    public function __construct(
53
        SocketConnector $connector,
54
        string $url,
55
        ConnectionOptions $options,
56
        int $requestTimeoutUs
57
    ) {
58 20
        $this->connector = $connector;
59 20
        $this->url = $url;
60 20
        $this->options = $options;
61 20
        $this->requestTimeoutUs = $requestTimeoutUs;
62 20
    }
63
64
    /**
65
     * Sends request under socket client and returns response.
66
     *
67
     * @throws ConnectionFailedException          when connection to server cannot be established
68
     * @throws ConnectionLostException            if connection to server was lost and request cannot be sent
69
     * @throws RemoteProcedureCallFailedException if request to server was sent and response cannot be received.
70
     *                                            Be aware that request may be successfully processed by server,
71
     *                                            so resending the request may lead to inconsistent state
72
     *                                            of the server data.
73
     */
74 7
    public function send(string $request): string
75
    {
76 7
        $connection = $this->getConnection();
77 7
        if ($connection->isClosed()) {
78 1
            throw new ConnectionLostException($this->url, 'closed by server');
79
        }
80
81 6
        $connection->sendRequest($request);
82
83 6
        return $connection->receiveResponse();
84
    }
85
86
    /**
87
     * Forces new connection. Can be used to reconnect.
88
     *
89
     * @throws ConnectionFailedException
90
     */
91 9
    public function connect(): void
92
    {
93 9
        unset($this->connection);
94
95 9
        $this->connection = $this->createConnection();
96 1
    }
97
98
    /**
99
     * @throws ConnectionFailedException
100
     */
101 7
    private function getConnection(): SocketConnection
102
    {
103 7
        if (null === $this->connection) {
104 7
            $this->connection = $this->createConnection();
105
        }
106
107 7
        return $this->connection;
108
    }
109
110
    /**
111
     * @throws ConnectionFailedException
112
     */
113 16
    private function createConnection(): SocketConnection
114
    {
115 16
        $connection = null;
116
117 16
        $maxAttempts = $this->options->getMaxAttempts();
118 16
        $timeout = $this->options->getAttemptTimeoutUs();
119 16
        $multiplier = $this->options->getTimeoutMultiplier();
120
121 16
        $attempt = 0;
122
123 16
        while (true) {
124
            try {
125 16
                $connection = $this->connector->open($this->url, $timeout, $this->requestTimeoutUs);
126
127 8
                break;
128 8
            } catch (ConnectionFailedException $exception) {
129 8
                $attempt++;
130
131 8
                if ($attempt >= $maxAttempts) {
132 8
                    break;
133
                }
134
135 6
                $this->connector->wait($timeout);
136 6
                $timeout = (int) ($timeout * $multiplier);
137
            }
138
        }
139
140 16
        if (null === $connection) {
141 8
            throw new ConnectionFailedException($this->url, 'failed by timeout');
142
        }
143
144 8
        return $connection;
145
    }
146
}
147