SocketClient::createConnection()   A
last analyzed

Complexity

Conditions 5
Paths 6

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5

Importance

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