Issues (321)

tests/09HTTPTest.php (1 issue)

1
<?php
2
3
include_once __DIR__ . '/08ServerTest.php';
4
5
/**
6
 * Tests which stress http features of the library.
7
 * Each of these tests iterates over (almost) all the 'Server' tests
8
 */
9
class HTTPTest extends ServerTest
10
{
11
    protected $expectHttp2 = false;
12
13
    protected $unsafeMethods = array(
14
        'testCatchExceptions', 'testCatchErrors', 'testUtf8Method', 'testServerComments',
15
        'testExoticCharsetsRequests', 'testExoticCharsetsRequests2', 'testExoticCharsetsRequests3',
16
        'testWrapInexistentUrl', 'testNegativeDebug', 'testTimeout'
17
    );
18
19
    /**
20
     * Returns all test methods from the base class, except the ones which failed already and the ones which make no sense
21
     * to run with different HTTP options.
22
     *
23
     * @todo (re)introduce skipping of tests which failed when executed individually even if test runs happen as separate processes
24
     * @todo reintroduce skipping of tests within the loop
25
     * @todo testTimeout is actually good to be tested with proxies etc - but it slows down the testsuite a lot!
26
     */
27
    public function getSingleHttpTestMethods()
28
    {
29
        $methods = array();
30
        // as long as we are descendants, get_class_methods will list private/protected methods
31
        foreach(get_class_methods('ServerTest') as $method)
32
        {
33
            if (strpos($method, 'test') === 0 && !in_array($method, $this->unsafeMethods))
34
            {
35
                if (!isset(self::$failed_tests[$method])) {
36
                    $methods[$method] = array($method);
37
                }
38
            }
39
        }
40
41
        return $methods;
42
    }
43
44
    /**
45
     * @dataProvider getSingleHttpTestMethods
46
     * @param string $method
47
     */
48
    public function testDeflate($method)
49
    {
50
        if (!function_exists('gzdeflate'))
51
        {
52
            $this->markTestSkipped('Zlib missing: cannot test deflate functionality');
53
            return;
54
        }
55
56
        $this->client->accepted_compression = array('deflate');
57
        $this->client->request_compression = 'deflate';
58
59
        $this->$method();
60
    }
61
62
    /**
63
     * @dataProvider getSingleHttpTestMethods
64
     * @param string $method
65
     */
66
    public function testGzip($method)
67
    {
68
        if (!function_exists('gzdeflate'))
69
        {
70
            $this->markTestSkipped('Zlib missing: cannot test gzip functionality');
71
            return;
72
        }
73
74
        $this->client->accepted_compression = array('gzip');
75
        $this->client->request_compression = 'gzip';
76
77
        $this->$method();
78
    }
79
80
    public function testKeepAlives()
81
    {
82
        if (!function_exists('curl_init'))
83
        {
84
            $this->markTestSkipped('CURL missing: cannot test http 1.1');
85
            return;
86
        }
87
88
        $this->method = 'http11';
89
        $this->client->method = 'http11';
90
        $this->client->keepalive = true;
91
92
        // to successfully test keepalive, we have to reuse the same client for all tests, we can not recreate one on setup/teardown...
93
        foreach ($this->getSingleHttpTestMethods() as $methods) {
94
            $method = $methods[0];
95
            $this->$method();
96
        }
97
    }
98
99
    /**
100
     * @dataProvider getSingleHttpTestMethods
101
     * @param string $method
102
     */
103
    public function testRedirects($method)
104
    {
105
        if (!function_exists('curl_init'))
106
        {
107
            $this->markTestSkipped('CURL missing: cannot test redirects');
108
            return;
109
        }
110
111
        /// @todo replace with setOption when dropping the BC layer
112
        $this->client->setUseCurl(\PhpXmlRpc\Client::USE_CURL_ALWAYS);
113
        $this->client->setCurlOptions(array(CURLOPT_FOLLOWLOCATION => true, CURLOPT_POSTREDIR => 3));
114
115
        $this->$method();
116
    }
117
118
    public function testAcceptCharset()
119
    {
120
        if (version_compare(PHP_VERSION, '5.6.0', '<'))
121
        {
122
            $this->markTestSkipped('Cannot test accept-charset on php < 5.6');
123
            return;
124
        }
125
        if (!function_exists('mb_list_encodings'))
126
        {
127
            $this->markTestSkipped('mbstring missing: cannot test accept-charset');
128
            return;
129
        }
130
131
        $r = new \PhpXmlRpc\Request('examples.stringecho', array(new \PhpXmlRpc\Value('€')));
132
        //chr(164)
133
134
        \PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'UTF-8';
135
136
        $this->addQueryParams(array('RESPONSE_ENCODING' => 'auto'));
137
        $this->client->accepted_charset_encodings = array(
138
            'utf-1234;q=0.1',
139
            'windows-1252;q=0.8'
140
        );
141
        $v = $this->send($r, 0, true);
142
        $h = $v->httpResponse();
143
        $this->assertEquals('text/xml; charset=Windows-1252', $h['headers']['content-type']);
144
        if ($v) {
145
            $this->assertEquals('€', $v->value()->scalarval());
146
        }
147
    }
148
149
    /**
150
     * @dataProvider getSingleHttpTestMethods
151
     * @param string $method
152
     */
153
    public function testProxy($method)
154
    {
155
        if ($this->args['PROXYSERVER'] == '')
156
        {
157
            $this->markTestSkipped('PROXYSERVER definition missing: cannot test proxy');
158
            return;
159
        }
160
161
        $this->client->setProxy($this->args['PROXYSERVER'], $this->args['PROXYPORT']);
162
163
        $this->$method();
164
    }
165
166
    /**
167
     * @dataProvider getSingleHttpTestMethods
168
     * @param string $method
169
     */
170
    public function testHttp11($method)
171
    {
172
        if (!function_exists('curl_init'))
173
        {
174
            $this->markTestSkipped('CURL missing: cannot test http 1.1');
175
            return;
176
        }
177
178
        $this->method = 'http11'; // not an error the double assignment!
179
        $this->client->method = 'http11';
180
        $this->client->keepalive = false;
181
182
        $this->$method();
183
    }
184
185
    /**
186
     * @dataProvider getSingleHttpTestMethods
187
     * @param string $method
188
     */
189
    public function testHttp10Curl($method)
190
    {
191
        if (!function_exists('curl_init'))
192
        {
193
            $this->markTestSkipped('CURL missing: cannot test http 1.1');
194
            return;
195
        }
196
197
        $this->method = 'http10'; // not an error the double assignment!
198
        $this->client->method = 'http10';
199
        /// @todo replace with setOption when dropping the BC layer
200
        $this->client->keepalive = false;
201
        $this->client->setUseCurl(\PhpXmlRpc\Client::USE_CURL_ALWAYS);
202
203
        $this->$method();
204
    }
205
206
    /**
207
     * @dataProvider getSingleHttpTestMethods
208
     * @param string $method
209
     */
210
    public function testHttp11Gzip($method)
211
    {
212
        if (!function_exists('curl_init'))
213
        {
214
            $this->markTestSkipped('CURL missing: cannot test http 1.1');
215
            return;
216
        }
217
        $this->method = 'http11'; // not an error the double assignment!
218
        $this->client->method = 'http11';
219
        $this->client->keepalive = false;
220
        $this->client->accepted_compression = array('gzip');
221
        $this->client->request_compression = 'gzip';
222
223
        $this->$method();
224
    }
225
226
    /**
227
     * @dataProvider getSingleHttpTestMethods
228
     * @param string $method
229
     */
230
    public function testHttp11Deflate($method)
231
    {
232
        if (!function_exists('curl_init'))
233
        {
234
            $this->markTestSkipped('CURL missing: cannot test http 1.1');
235
            return;
236
        }
237
        $this->method = 'http11'; // not an error the double assignment!
238
        $this->client->method = 'http11';
239
        $this->client->keepalive = false;
240
        $this->client->accepted_compression = array('deflate');
241
        $this->client->request_compression = 'deflate';
242
243
        $this->$method();
244
    }
245
246
    /**
247
     * @dataProvider getSingleHttpTestMethods
248
     * @param string $method
249
     */
250
    public function testHttp11Proxy($method)
251
    {
252
        if (!function_exists('curl_init'))
253
        {
254
            $this->markTestSkipped('CURL missing: cannot test http 1.1 w. proxy');
255
            return;
256
        }
257
        else if ($this->args['PROXYSERVER'] == '')
258
        {
259
            $this->markTestSkipped('PROXYSERVER definition missing: cannot test proxy w. http 1.1');
260
            return;
261
        }
262
263
        $this->method = 'http11'; // not an error the double assignment!
264
        $this->client->method = 'http11';
265
        $this->client->setProxy($this->args['PROXYSERVER'], $this->args['PROXYPORT']);
266
        $this->client->keepalive = false;
267
268
        $this->$method();
269
    }
270
271
    /**
272
     * @dataProvider getSingleHttpTestMethods
273
     * @param string $method
274
     */
275
    public function testHttps($method)
276
    {
277
        if (!function_exists('curl_init'))
278
        {
279
            $this->markTestSkipped('CURL missing: cannot test https functionality');
280
            return;
281
        }
282
        else if ($this->args['HTTPSSERVER'] == '')
283
        {
284
            $this->markTestSkipped('HTTPS SERVER definition missing: cannot test https');
285
            return;
286
        }
287
288
        $this->client->server = $this->args['HTTPSSERVER'];
289
        $this->method = 'https';
290
        $this->client->method = 'https';
291
        $this->client->path = $this->args['HTTPSURI'];
292
        /// @todo replace with setOptions when dropping the BC layer
293
        $this->client->setSSLVerifyPeer(!$this->args['HTTPSIGNOREPEER']);
294
        $this->client->setSSLVerifyHost($this->args['HTTPSVERIFYHOST']);
295
        $this->client->setSSLVersion($this->args['SSLVERSION']);
296
        if (version_compare(PHP_VERSION, '8.0', '>=') && $this->args['SSLVERSION'] == 0)
297
        {
298
            $version = explode('.', PHP_VERSION);
299
            $this->client->setSSLVersion(min(4 + $version[1], 7));
300
        }
301
302
        $this->$method();
303
    }
304
305
    /**
306
     * @dataProvider getSingleHttpTestMethods
307
     * @param string $method
308
     */
309
    public function testHttpsSocket($method)
310
    {
311
        if ($this->args['HTTPSSERVER'] == '')
312
        {
313
            $this->markTestSkipped('HTTPS SERVER definition missing: cannot test https');
314
            return;
315
        }
316
317
        /// @todo investigate: can we make this work?
318
        if (version_compare(PHP_VERSION, '7.2', '<'))
319
        {
320
            if (is_readable('/etc/os-release')) {
321
                $output = file_get_contents('/etc/os-release');
322
                preg_match('/VERSION="?([0-9]+)/', $output, $matches);
323
                $ubuntuVersion = @$matches[1];
324
            } else {
325
                exec('uname -a', $output, $retval);
326
                preg_match('/ubunutu([0-9]+)/', $output[0], $matches);
327
                $ubuntuVersion = @$matches[1];
328
            }
329
            if ($ubuntuVersion >= 20) {
330
                $this->markTestSkipped('HTTPS via Socket known to fail on php less than 7.2 on Ubuntu 20 and higher');
331
                return;
332
            }
333
        }
334
335
        $this->client->server = $this->args['HTTPSSERVER'];
336
        $this->method = 'https';
337
        $this->client->method = 'https';
338
        $this->client->path = $this->args['HTTPSURI'];
339
        /// @todo replace with setOptions when dropping the BC layer
340
        $this->client->setSSLVerifyPeer(!$this->args['HTTPSIGNOREPEER']);
341
        $this->client->setSSLVerifyHost($this->args['HTTPSVERIFYHOST']);
342
        $this->client->setUseCurl(\PhpXmlRpc\Client::USE_CURL_NEVER);
343
        $this->client->setSSLVersion($this->args['SSLVERSION']);
344
345
        if (version_compare(PHP_VERSION, '8.1', '>='))
346
        {
347
            $version = explode('.', PHP_VERSION);
348
            /// @see https://docs.openssl.org/1.1.1/man3/SSL_CTX_set_security_level/#default-callback-behaviour for levels
349
            $this->client->setOption(\PhpXmlRpc\Client::OPT_EXTRA_SOCKET_OPTS,
350
                array('ssl' => array('security_level' => min(2 + $version[1], 5))));
351
            /// @todo we should probably look deeper into the Apache config / ssl version in use to find out why this
352
            ///       does not work well with TLS < 1.2
353
            if ($this->args['SSLVERSION'] == 0) {
354
                $this->client->setSSLVersion(min(5 + $version[1], 7));
355
            }
356
        }
357
        $this->$method();
358
    }
359
360
    /**
361
     * @dataProvider getSingleHttpTestMethods
362
     * @param string $method
363
     */
364
    public function testHttpsProxy($method)
365
    {
366
        if (!function_exists('curl_init'))
367
        {
368
            $this->markTestSkipped('CURL missing: cannot test https w. proxy');
369
            return;
370
        }
371
        else if ($this->args['PROXYSERVER'] == '')
372
        {
373
            $this->markTestSkipped('PROXYSERVER definition missing: cannot test proxy w. https');
374
            return;
375
        }
376
        else if ($this->args['HTTPSSERVER'] == '')
377
        {
378
            $this->markTestSkipped('HTTPS SERVER definition missing: cannot test https w. proxy');
379
            return;
380
        }
381
382
        $this->method = 'https';
383
        $this->client->method = 'https';
384
        $this->client->server = $this->args['HTTPSSERVER'];
385
        $this->client->path = $this->args['HTTPSURI'];
386
        /// @todo replace with setOptions when dropping the BC layer
387
        $this->client->setProxy($this->args['PROXYSERVER'], $this->args['PROXYPORT']);
388
        $this->client->setSSLVerifyPeer(!$this->args['HTTPSIGNOREPEER']);
389
        $this->client->setSSLVerifyHost($this->args['HTTPSVERIFYHOST']);
390
        $this->client->setSSLVersion($this->args['SSLVERSION']);
391
        if (version_compare(PHP_VERSION, '8.0', '>=') && $this->args['SSLVERSION'] == 0)
392
        {
393
            $version = explode('.', PHP_VERSION);
394
            $this->client->setSSLVersion(min(4 + $version[1], 7));
395
        }
396
397
        $this->$method();
398
    }
399
400
    /**
401
     * @dataProvider getSingleHttpTestMethods
402
     * @param string $method
403
     */
404
    public function testHttp2NoTls($method)
405
    {
406
        if (!function_exists('curl_init'))
407
        {
408
            $this->markTestSkipped('CURL missing: cannot test http/2');
409
            return;
410
        } else if (!defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE'))
411
        {
412
            $this->markTestSkipped('CURL http/2 support missing: cannot test http/2');
413
            return;
414
        }
415
416
        $this->method = 'h2c'; // not an error the double assignment!
417
        $this->client->method = 'h2c';
418
        //$this->client->keepalive = false; // q: is this a good idea?
419
420
        $this->expectHttp2 = true;
421
        $this->$method();
422
        $this->expectHttp2 = false;
423
    }
424
425
    /**
426
     * @dataProvider getSingleHttpTestMethods
427
     * @param string $method
428
     */
429
    public function testHttp2tls($method)
430
    {
431
        if (!function_exists('curl_init'))
432
        {
433
            $this->markTestSkipped('CURL missing: cannot test http/2 tls');
434
            return;
435
        } else if ($this->args['HTTPSSERVER'] == '')
436
        {
437
            $this->markTestSkipped('HTTPS SERVER definition missing: cannot test http/2 tls');
438
            return;
439
        } else if (!defined('CURL_HTTP_VERSION_2_0'))
440
        {
441
            $this->markTestSkipped('CURL http/2 support missing: cannot test http/2 tls');
442
            return;
443
        }
444
445
        $this->method = 'h2';
446
        $this->client->method = 'h2';
447
        $this->client->server = $this->args['HTTPSSERVER'];
448
        $this->client->path = $this->args['HTTPSURI'];
449
        /// @todo replace with setOptions when dropping the BC layer
450
        $this->client->setSSLVerifyPeer(!$this->args['HTTPSIGNOREPEER']);
451
        $this->client->setSSLVerifyHost($this->args['HTTPSVERIFYHOST']);
452
        $this->client->setSSLVersion($this->args['SSLVERSION']);
453
454
        $this->expectHttp2 = true;
455
        $this->$method();
456
        $this->expectHttp2 = false;
457
    }
458
459
    /// @todo a better organization of tests could be to move the 4 tests below and all charset-related tests from
460
    ///       class ServerTest to a dedicated class - and make sure we iterate over each of those with different
461
    ///       proxy/auth/compression/etc... settings
462
463
    /**
464
     * @dataProvider getSingleHttpTestMethods
465
     * @param string $method
466
     */
467
    public function testUTF8Responses($method)
468
    {
469
        $this->addQueryParams(array('RESPONSE_ENCODING' => 'UTF-8'));
470
471
        $this->$method();
472
    }
473
474
    /**
475
     * @dataProvider getSingleHttpTestMethods
476
     * @param string $method
477
     */
478
    public function testUTF8Requests($method)
479
    {
480
        $this->client->request_charset_encoding = 'UTF-8';
481
482
        $this->$method();
483
    }
484
485
    /**
486
     * @dataProvider getSingleHttpTestMethods
487
     * @param string $method
488
     */
489
    public function testISOResponses($method)
490
    {
491
        $this->addQueryParams(array('RESPONSE_ENCODING' => 'ISO-8859-1'));
492
493
        $this->$method();
494
    }
495
496
    /**
497
     * @dataProvider getSingleHttpTestMethods
498
     * @param string $method
499
     */
500
    public function testISORequests($method)
501
    {
502
        $this->client->request_charset_encoding = 'ISO-8859-1';
503
504
        $this->$method();
505
    }
506
507
    /**
508
     * @dataProvider getSingleHttpTestMethods
509
     * @param string $method
510
     */
511
    public function testBasicAuth($method)
512
    {
513
        $this->client->setCredentials('test', 'test');
514
        $this->addQueryParams(array('FORCE_AUTH' => 'Basic'));
515
516
        $this->$method();
517
    }
518
519
    /**
520
     * @dataProvider getSingleHttpTestMethods
521
     * @param string $method
522
     */
523
    public function testDigestAuth($method)
524
    {
525
        if (!function_exists('curl_init'))
526
        {
527
            $this->markTestSkipped('CURL missing: cannot test digest auth functionality');
528
            return;
529
        }
530
531
        $this->client->setCredentials('test', 'test', CURLAUTH_DIGEST);
532
        $this->addQueryParams(array('FORCE_AUTH' => 'Digest'));
533
        $this->method = 'http11';
534
        $this->client->method = 'http11';
535
536
        $this->$method();
537
    }
538
539
    /**
540
     * @dataProvider getAvailableUseCurlOptions
541
     */
542
    public function testTimeout($curlOpt)
543
    {
544
        $this->client->setOption(\PhpXmlRpc\Client::OPT_USE_CURL, $curlOpt);
545
546
        // decrease the timeout to avoid slowing down the testsuite too much
547
        $this->timeout = 3;
548
549
        // the server will wait for 1 second before sending back the response - should pass
550
        $m = new xmlrpcmsg('tests.sleep', array(new xmlrpcval(1, 'int')));
551
        // this checks for a non-failed call
552
        $time = microtime(true);
553
        $this->send($m);
554
        $time = microtime(true) - $time;
555
        $this->assertGreaterThan(1.0, $time);
556
        $this->assertLessThan(2.0, $time);
557
558
        // the server will wait for 5 seconds before sending back the response - fail
559
        $m = new xmlrpcmsg('tests.sleep', array(new xmlrpcval(5, 'int')));
560
        $time = microtime(true);
561
        $r = $this->send($m, array(0, PhpXmlRpc\PhpXmlRpc::$xmlrpcerr['http_error'], PhpXmlRpc\PhpXmlRpc::$xmlrpcerr['curl_fail']));
0 ignored issues
show
The assignment to $r is dead and can be removed.
Loading history...
562
        $time = microtime(true) - $time;
563
        $this->assertGreaterThan(2.0, $time);
564
        $this->assertLessThan(4.0, $time);
565
566
        /*
567
        // the server will send back the response one chunk per second, waiting 5 seconds in between chunks
568
        $m = new xmlrpcmsg('examples.addtwo', array(new xmlrpcval(1, 'int'), new xmlrpcval(2, 'int')));
569
        $this->addQueryParams(array('SLOW_LORIS' => 5));
570
        $time = microtime(true);
571
        $this->send($m, array(PhpXmlRpc\PhpXmlRpc::$xmlrpcerr['http_error'], PhpXmlRpc\PhpXmlRpc::$xmlrpcerr['curl_fail']));
572
        $time = microtime(true) - $time;
573
        $this->assertGreaterThan(2.0, $time);
574
        $this->assertLessThan(4.0, $time);
575
        */
576
577
        // pesky case: the server will send back the response one chunk per second, taking 10 seconds in total
578
        $m = new xmlrpcmsg('examples.addtwo', array(new xmlrpcval(1, 'int'), new xmlrpcval(2, 'int')));
579
        $this->addQueryParams(array('SLOW_LORIS' => 1));
580
        $time = microtime(true);
581
        $this->send($m, array(0, PhpXmlRpc\PhpXmlRpc::$xmlrpcerr['http_error'], PhpXmlRpc\PhpXmlRpc::$xmlrpcerr['curl_fail']));
582
        $time = microtime(true) - $time;
583
        $this->assertGreaterThan(2.0, $time);
584
        $this->assertLessThan(4.0, $time);
585
    }
586
587
    /**
588
     * @param \PhpXmlRpc\Response $r
589
     * @return void
590
     */
591
    protected function validateResponse($r)
592
    {
593
        if ($this->expectHttp2) {
594
            $hr = $r->httpResponse();
595
            $this->assertEquals("2", @$hr['protocol_version']);
596
        }
597
    }
598
}
599