GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#28)
by
unknown
01:26
created

VarnishSocket::write()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 3
nop 1
1
<?php
2
3
/**
4
 * Based on the Varnish Admin Socket class used in the Terpentine extension for Magento.
5
 * @link https://github.com/nexcess/magento-turpentine
6
 *
7
 * This was in turn based on Tim Whitlock's VarnishAdminSocket.php from php-varnish
8
 * @link https://github.com/timwhitlock/php-varnish
9
 *
10
 * Pieces from both resources above were used to fit our needs.
11
 */
12
13
/**
14
 * Nexcess.net Turpentine Extension for Magento
15
 * Copyright (C) 2012  Nexcess.net L.L.C.
16
 *
17
 * This program is free software; you can redistribute it and/or modify
18
 * it under the terms of the GNU General Public License as published by
19
 * the Free Software Foundation; either version 2 of the License, or
20
 * (at your option) any later version.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
 * GNU General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU General Public License along
28
 * with this program; if not, write to the Free Software Foundation, Inc.,
29
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30
 */
31
32
/**
33
 * Copyright (c) 2010 Tim Whitlock.
34
 *
35
 * Permission is hereby granted, free of charge, to any person obtaining a copy
36
 * of this software and associated documentation files (the "Software"), to deal
37
 * in the Software without restriction, including without limitation the rights
38
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
39
 * copies of the Software, and to permit persons to whom the Software is
40
 * furnished to do so, subject to the following conditions:
41
 *
42
 * The above copyright notice and this permission notice shall be included in
43
 * all copies or substantial portions of the Software.
44
 *
45
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
51
 * THE SOFTWARE.
52
 */
53
54
namespace Spatie\Varnish;
55
56
class VarnishSocket
57
{
58
    /**
59
     * Varnish return codes (from vcli.h)
60
     * https://github.com/varnishcache/varnish-cache/blob/master/include/vcli.h#L42.
61
     */
62
    const VARN_SYNTAX = 100;
63
    const VARN_UNKNOWN = 101;
64
    const VARN_UNIMPL = 102;
65
    const VARN_TOOFEW = 104;
66
    const VARN_TOOMANY = 105;
67
    const VARN_PARAM = 106;
68
    const VARN_AUTH = 107;
69
    const VARN_OK = 200;
70
    const VARN_TRUNCATED = 201;
71
    const VARN_CANT = 300;
72
    const VARN_COMMS = 400;
73
    const VARN_CLOSE = 500;
74
75
    /**
76
     * The socket used to connect to Varnish and a timeout in seconds.
77
     */
78
    protected $varnishSocket = null;
79
    protected $socketTimeout = 10;
80
81
    /**
82
     * Limits for reading and writing to and from the socket.
83
     */
84
    const READ_CHUNK_SIZE = 1024;
85
    const MAX_WRITE_SIZE = 16 * 1024;
86
87
    /**
88
     * Connect to the Varnish socket and authenticate when needed.
89
     * @param string $host
90
     * @param int $port
91
     * @param string $secret
92
     *
93
     * @return bool
94
     *
95
     * @throws \Exception
96
     */
97
    public function connect($host, $port, $secret = '')
98
    {
99
        // Open socket connection
100
        $this->varnishSocket = fsockopen(
101
            $host, $port,
102
            $errno, $errstr,
103
            $this->socketTimeout
104
        );
105
106
        if (! is_resource($this->varnishSocket)) {
107
            throw new \Exception(sprintf(
108
                'Failed to connect to Varnish on %s:%d, error %d: %s',
109
                $host, $port, $errno, $errstr
110
            ));
111
        }
112
113
        // Set stream options
114
        stream_set_blocking($this->varnishSocket, true);
115
        stream_set_timeout($this->varnishSocket, $this->socketTimeout);
116
117
        // Read first data from socket
118
        $data = $this->read();
119
120
        // Authenticate using secret if authentication is required
121
        // https://varnish-cache.org/docs/trunk/reference/varnish-cli.html#authentication-with-s
122
        if ($data['code'] === self::VARN_AUTH) {
123
            $challenge = substr($data['content'], 0, 32)."\n";
124
125
            // Generate the authentication token based on the challenge and secret
126
            $token = hash('sha256',
127
                sprintf('%s%s%s',
128
                    $challenge,
129
                    $secret,
130
                    $challenge
131
                ));
132
133
            // Authenticate using token
134
            $data = $this->command(sprintf('auth %s', $token));
135
        }
136
137
        if ($data['code'] !== self::VARN_OK) {
138
            throw new \Exception(sprintf(
139
                'Varnish admin authentication failed: %s',
140
                $data['content']
141
            ));
142
        }
143
144
        return $this->isConnected();
145
    }
146
147
    /**
148
     * Check if we're connected to Varnish socket.
149
     *
150
     * @return bool
151
     */
152
    public function isConnected()
153
    {
154
        return ! is_null($this->varnishSocket);
155
    }
156
157
    /**
158
     * @return array|bool|string
159
     *
160
     * @throws \Exception
161
     */
162
    private function read()
163
    {
164
        $code = null;
165
        $len = -1;
166
167
        while (! feof($this->varnishSocket)) {
168
            // Read data from socket and check for timeout
169
            $response = fgets($this->varnishSocket, self::READ_CHUNK_SIZE);
170
            if (empty($response)) {
171
                $meta = stream_get_meta_data($this->varnishSocket);
172
                if ($meta['timed_out']) {
173
                    throw new \Exception(
174
                        'Varnish socket connection timed out'
175
                    );
176
                }
177
            }
178
179
            // Varnish will output a code and a content length, followed by the actual content
180
            if (preg_match('~^(\d{3}) (\d+)~', $response, $match)) {
181
                $code = (int) $match[1];
182
                $len = (int) $match[2];
183
                break;
184
            }
185
        }
186
187
        // Failed to get code from socket
188
        if ($code === null) {
189
            throw new \Exception(
190
                'Failed to read response code from Varnish socket'
191
            );
192
        } else {
193
            $response = [
194
                'code' => $code,
195
                'content' => '',
196
            ];
197
198
            // Read content with length $len
199
            while (! feof($this->varnishSocket) &&
200
                strlen($response['content']) < $len) {
201
                $response['content'] .= fgets($this->varnishSocket, self::READ_CHUNK_SIZE);
202
            }
203
204
            return $response;
205
        }
206
    }
207
208
    /**
209
     * Write data to the socket input stream.
210
     *
211
     * @param string $data
212
     *
213
     * @return VarnishSocket
214
     *
215
     * @throws \Exception
216
     */
217
    private function write($data)
218
    {
219
        if (strlen($data) >= self::MAX_WRITE_SIZE) {
220
            throw new \Exception(sprintf(
221
                'Data to write to Varnish socket is too large (max %d chars)',
222
                self::MAX_WRITE_SIZE
223
            ));
224
        }
225
226
        // Write data to socket
227
        $bytes = fwrite($this->varnishSocket, $data);
228
        if ($bytes !== strlen($data)) {
229
            throw new \Exception('Failed to write to Varnish socket');
230
        }
231
232
        return $this;
233
    }
234
235
    /**
236
     * Write a command to the socket with a trailing line break and get response straight away.
237
     *
238
     * @param string $cmd
239
     * @param int $ok
240
     *
241
     * @return string
242
     *
243
     * @throws \Exception
244
     */
245
    public function command($cmd, $ok = self::VARN_OK)
246
    {
247
        $response = $this->write($cmd."\n")->read();
248
        if ($response['code'] !== $ok) {
249
            throw new \Exception(
250
                sprintf(
251
                    "Command '%s' responded %d: '%s'",
252
                    $cmd, $response['code'], $response['content']
253
                ),
254
                $response['code']
255
            );
256
        }
257
258
        return $response;
259
    }
260
261
    /**
262
     * Brutal close, doesn't send quit command to varnishadm.
263
     *
264
     * @return void
265
     */
266
    public function close()
267
    {
268
        is_resource($this->varnishSocket) and fclose($this->varnishSocket);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
269
        $this->varnishSocket = null;
270
    }
271
272
    /**
273
     * Graceful close, sends quit command.
274
     *
275
     * @return void
276
     *
277
     * @throws \Exception
278
     */
279
    public function quit()
280
    {
281
        try {
282
            $this->command('quit', self::VARN_CLOSE);
283
        } finally {
284
            $this->close();
285
        }
286
    }
287
}
288