generated from Nodarx/template
Initial service DAV module
This commit is contained in:
+236
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP\Auth;
|
||||
|
||||
use Sabre\HTTP\Request;
|
||||
use Sabre\HTTP\Response;
|
||||
|
||||
class AWSTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var Sabre\HTTP\Response
|
||||
*/
|
||||
private $response;
|
||||
|
||||
/**
|
||||
* @var Sabre\HTTP\Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* @var Sabre\HTTP\Auth\AWS
|
||||
*/
|
||||
private $auth;
|
||||
|
||||
public const REALM = 'SabreDAV unittest';
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->response = new Response();
|
||||
$this->request = new Request('GET', '/');
|
||||
$this->auth = new AWS(self::REALM, $this->request, $this->response);
|
||||
}
|
||||
|
||||
public function testNoHeader()
|
||||
{
|
||||
$this->request->setMethod('GET');
|
||||
$result = $this->auth->init();
|
||||
|
||||
$this->assertFalse($result, 'No AWS Authorization header was supplied, so we should have gotten false');
|
||||
$this->assertEquals(AWS::ERR_NOAWSHEADER, $this->auth->errorCode);
|
||||
}
|
||||
|
||||
public function testInvalidAuthorizationHeader()
|
||||
{
|
||||
$this->request->setMethod('GET');
|
||||
$this->request->setHeader('Authorization', 'Invalid Auth Header');
|
||||
|
||||
$this->assertFalse($this->auth->init(), 'The Invalid AWS authorization header');
|
||||
}
|
||||
|
||||
public function testIncorrectContentMD5()
|
||||
{
|
||||
$accessKey = 'accessKey';
|
||||
$secretKey = 'secretKey';
|
||||
|
||||
$this->request->setMethod('GET');
|
||||
$this->request->setHeaders([
|
||||
'Authorization' => "AWS $accessKey:sig",
|
||||
'Content-MD5' => 'garbage',
|
||||
]);
|
||||
$this->request->setUrl('/');
|
||||
|
||||
$this->auth->init();
|
||||
$result = $this->auth->validate($secretKey);
|
||||
|
||||
$this->assertFalse($result);
|
||||
$this->assertEquals(AWS::ERR_MD5CHECKSUMWRONG, $this->auth->errorCode);
|
||||
}
|
||||
|
||||
public function testNoDate()
|
||||
{
|
||||
$accessKey = 'accessKey';
|
||||
$secretKey = 'secretKey';
|
||||
$content = 'thisisthebody';
|
||||
$contentMD5 = base64_encode(md5($content, true));
|
||||
|
||||
$this->request->setMethod('POST');
|
||||
$this->request->setHeaders([
|
||||
'Authorization' => "AWS $accessKey:sig",
|
||||
'Content-MD5' => $contentMD5,
|
||||
]);
|
||||
$this->request->setUrl('/');
|
||||
$this->request->setBody($content);
|
||||
|
||||
$this->auth->init();
|
||||
$result = $this->auth->validate($secretKey);
|
||||
|
||||
$this->assertFalse($result);
|
||||
$this->assertEquals(AWS::ERR_INVALIDDATEFORMAT, $this->auth->errorCode);
|
||||
}
|
||||
|
||||
public function testFutureDate()
|
||||
{
|
||||
$accessKey = 'accessKey';
|
||||
$secretKey = 'secretKey';
|
||||
$content = 'thisisthebody';
|
||||
$contentMD5 = base64_encode(md5($content, true));
|
||||
|
||||
$date = new \DateTime('@'.(time() + (60 * 20)));
|
||||
$date->setTimeZone(new \DateTimeZone('GMT'));
|
||||
$date = $date->format('D, d M Y H:i:s \\G\\M\\T');
|
||||
|
||||
$this->request->setMethod('POST');
|
||||
$this->request->setHeaders([
|
||||
'Authorization' => "AWS $accessKey:sig",
|
||||
'Content-MD5' => $contentMD5,
|
||||
'Date' => $date,
|
||||
]);
|
||||
|
||||
$this->request->setBody($content);
|
||||
|
||||
$this->auth->init();
|
||||
$result = $this->auth->validate($secretKey);
|
||||
|
||||
$this->assertFalse($result);
|
||||
$this->assertEquals(AWS::ERR_REQUESTTIMESKEWED, $this->auth->errorCode);
|
||||
}
|
||||
|
||||
public function testPastDate()
|
||||
{
|
||||
$accessKey = 'accessKey';
|
||||
$secretKey = 'secretKey';
|
||||
$content = 'thisisthebody';
|
||||
$contentMD5 = base64_encode(md5($content, true));
|
||||
|
||||
$date = new \DateTime('@'.(time() - (60 * 20)));
|
||||
$date->setTimeZone(new \DateTimeZone('GMT'));
|
||||
$date = $date->format('D, d M Y H:i:s \\G\\M\\T');
|
||||
|
||||
$this->request->setMethod('POST');
|
||||
$this->request->setHeaders([
|
||||
'Authorization' => "AWS $accessKey:sig",
|
||||
'Content-MD5' => $contentMD5,
|
||||
'Date' => $date,
|
||||
]);
|
||||
|
||||
$this->request->setBody($content);
|
||||
|
||||
$this->auth->init();
|
||||
$result = $this->auth->validate($secretKey);
|
||||
|
||||
$this->assertFalse($result);
|
||||
$this->assertEquals(AWS::ERR_REQUESTTIMESKEWED, $this->auth->errorCode);
|
||||
}
|
||||
|
||||
public function testIncorrectSignature()
|
||||
{
|
||||
$accessKey = 'accessKey';
|
||||
$secretKey = 'secretKey';
|
||||
$content = 'thisisthebody';
|
||||
|
||||
$contentMD5 = base64_encode(md5($content, true));
|
||||
|
||||
$date = new \DateTime('now');
|
||||
$date->setTimeZone(new \DateTimeZone('GMT'));
|
||||
$date = $date->format('D, d M Y H:i:s \\G\\M\\T');
|
||||
|
||||
$this->request->setUrl('/');
|
||||
$this->request->setMethod('POST');
|
||||
$this->request->setHeaders([
|
||||
'Authorization' => "AWS $accessKey:sig",
|
||||
'Content-MD5' => $contentMD5,
|
||||
'X-amz-date' => $date,
|
||||
]);
|
||||
$this->request->setBody($content);
|
||||
|
||||
$this->auth->init();
|
||||
$result = $this->auth->validate($secretKey);
|
||||
|
||||
$this->assertFalse($result);
|
||||
$this->assertEquals(AWS::ERR_INVALIDSIGNATURE, $this->auth->errorCode);
|
||||
}
|
||||
|
||||
public function testValidRequest()
|
||||
{
|
||||
$accessKey = 'accessKey';
|
||||
$secretKey = 'secretKey';
|
||||
$content = 'thisisthebody';
|
||||
$contentMD5 = base64_encode(md5($content, true));
|
||||
|
||||
$date = new \DateTime('now');
|
||||
$date->setTimeZone(new \DateTimeZone('GMT'));
|
||||
$date = $date->format('D, d M Y H:i:s \\G\\M\\T');
|
||||
|
||||
$sig = base64_encode($this->hmacsha1($secretKey,
|
||||
"POST\n$contentMD5\n\n$date\nx-amz-date:$date\n/evert"
|
||||
));
|
||||
|
||||
$this->request->setUrl('/evert');
|
||||
$this->request->setMethod('POST');
|
||||
$this->request->setHeaders([
|
||||
'Authorization' => "AWS $accessKey:$sig",
|
||||
'Content-MD5' => $contentMD5,
|
||||
'X-amz-date' => $date,
|
||||
]);
|
||||
|
||||
$this->request->setBody($content);
|
||||
|
||||
$this->auth->init();
|
||||
$result = $this->auth->validate($secretKey);
|
||||
|
||||
$this->assertTrue($result, 'Signature did not validate, got errorcode '.$this->auth->errorCode);
|
||||
$this->assertEquals($accessKey, $this->auth->getAccessKey());
|
||||
}
|
||||
|
||||
public function test401()
|
||||
{
|
||||
$this->auth->requireLogin();
|
||||
$test = preg_match('/^AWS$/', $this->response->getHeader('WWW-Authenticate'), $matches);
|
||||
$this->assertTrue(true == $test, 'The WWW-Authenticate response didn\'t match our pattern');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an HMAC-SHA1 signature.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function hmacsha1($key, $message)
|
||||
{
|
||||
$blocksize = 64;
|
||||
if (strlen($key) > $blocksize) {
|
||||
$key = pack('H*', sha1($key));
|
||||
}
|
||||
$key = str_pad($key, $blocksize, chr(0x00));
|
||||
$ipad = str_repeat(chr(0x36), $blocksize);
|
||||
$opad = str_repeat(chr(0x5C), $blocksize);
|
||||
$hmac = pack('H*', sha1(($key ^ $opad).pack('H*', sha1(($key ^ $ipad).$message))));
|
||||
|
||||
return $hmac;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP\Auth;
|
||||
|
||||
use Sabre\HTTP\Request;
|
||||
use Sabre\HTTP\Response;
|
||||
|
||||
class BasicTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testGetCredentials()
|
||||
{
|
||||
$request = new Request('GET', '/', [
|
||||
'Authorization' => 'Basic '.base64_encode('user:pass:bla'),
|
||||
]);
|
||||
|
||||
$basic = new Basic('Dagger', $request, new Response());
|
||||
|
||||
$this->assertEquals([
|
||||
'user',
|
||||
'pass:bla',
|
||||
], $basic->getCredentials());
|
||||
}
|
||||
|
||||
public function testGetInvalidCredentialsColonMissing()
|
||||
{
|
||||
$request = new Request('GET', '/', [
|
||||
'Authorization' => 'Basic '.base64_encode('userpass'),
|
||||
]);
|
||||
|
||||
$basic = new Basic('Dagger', $request, new Response());
|
||||
|
||||
$this->assertNull($basic->getCredentials());
|
||||
}
|
||||
|
||||
public function testGetCredentialsNoHeader()
|
||||
{
|
||||
$request = new Request('GET', '/', []);
|
||||
$basic = new Basic('Dagger', $request, new Response());
|
||||
|
||||
$this->assertNull($basic->getCredentials());
|
||||
}
|
||||
|
||||
public function testGetCredentialsNotBasic()
|
||||
{
|
||||
$request = new Request('GET', '/', [
|
||||
'Authorization' => 'QBasic '.base64_encode('user:pass:bla'),
|
||||
]);
|
||||
$basic = new Basic('Dagger', $request, new Response());
|
||||
|
||||
$this->assertNull($basic->getCredentials());
|
||||
}
|
||||
|
||||
public function testRequireLogin()
|
||||
{
|
||||
$response = new Response();
|
||||
$request = new Request('GET', '/');
|
||||
|
||||
$basic = new Basic('Dagger', $request, $response);
|
||||
|
||||
$basic->requireLogin();
|
||||
|
||||
$this->assertEquals('Basic realm="Dagger", charset="UTF-8"', $response->getHeader('WWW-Authenticate'));
|
||||
$this->assertEquals(401, $response->getStatus());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP\Auth;
|
||||
|
||||
use Sabre\HTTP\Request;
|
||||
use Sabre\HTTP\Response;
|
||||
|
||||
class BearerTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testGetToken()
|
||||
{
|
||||
$request = new Request('GET', '/', [
|
||||
'Authorization' => 'Bearer 12345',
|
||||
]);
|
||||
|
||||
$bearer = new Bearer('Dagger', $request, new Response());
|
||||
|
||||
$this->assertEquals(
|
||||
'12345',
|
||||
$bearer->getToken()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetCredentialsNoHeader()
|
||||
{
|
||||
$request = new Request('GET', '/', []);
|
||||
$bearer = new Bearer('Dagger', $request, new Response());
|
||||
|
||||
$this->assertNull($bearer->getToken());
|
||||
}
|
||||
|
||||
public function testGetCredentialsNotBearer()
|
||||
{
|
||||
$request = new Request('GET', '/', [
|
||||
'Authorization' => 'QBearer 12345',
|
||||
]);
|
||||
$bearer = new Bearer('Dagger', $request, new Response());
|
||||
|
||||
$this->assertNull($bearer->getToken());
|
||||
}
|
||||
|
||||
public function testRequireLogin()
|
||||
{
|
||||
$response = new Response();
|
||||
$request = new Request('GET', '/');
|
||||
$bearer = new Bearer('Dagger', $request, $response);
|
||||
|
||||
$bearer->requireLogin();
|
||||
|
||||
$this->assertEquals('Bearer realm="Dagger"', $response->getHeader('WWW-Authenticate'));
|
||||
$this->assertEquals(401, $response->getStatus());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP\Auth;
|
||||
|
||||
use Sabre\HTTP\Request;
|
||||
use Sabre\HTTP\Response;
|
||||
|
||||
class DigestTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var Sabre\HTTP\Response
|
||||
*/
|
||||
private $response;
|
||||
|
||||
/**
|
||||
* request.
|
||||
*
|
||||
* @var Sabre\HTTP\Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* @var Sabre\HTTP\Auth\Digest
|
||||
*/
|
||||
private $auth;
|
||||
|
||||
public const REALM = 'SabreDAV unittest';
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->response = new Response();
|
||||
$this->request = new Request('GET', '/');
|
||||
$this->auth = new Digest(self::REALM, $this->request, $this->response);
|
||||
}
|
||||
|
||||
public function testDigest()
|
||||
{
|
||||
list($nonce, $opaque) = $this->getServerTokens();
|
||||
|
||||
$username = 'admin';
|
||||
$password = '12345';
|
||||
$nc = '00002';
|
||||
$cnonce = uniqid();
|
||||
|
||||
$digestHash = md5(
|
||||
md5($username.':'.self::REALM.':'.$password).':'.
|
||||
$nonce.':'.
|
||||
$nc.':'.
|
||||
$cnonce.':'.
|
||||
'auth:'.
|
||||
md5('GET:/')
|
||||
);
|
||||
|
||||
$this->request->setMethod('GET');
|
||||
$this->request->setHeader('Authorization', 'Digest username="'.$username.'", realm="'.self::REALM.'", nonce="'.$nonce.'", uri="/", response="'.$digestHash.'", opaque="'.$opaque.'", qop=auth,nc='.$nc.',cnonce="'.$cnonce.'"');
|
||||
|
||||
$this->auth->init();
|
||||
|
||||
$this->assertEquals($username, $this->auth->getUsername());
|
||||
$this->assertEquals(self::REALM, $this->auth->getRealm());
|
||||
$this->assertTrue($this->auth->validateA1(md5($username.':'.self::REALM.':'.$password)), 'Authentication is deemed invalid through validateA1');
|
||||
$this->assertTrue($this->auth->validatePassword($password), 'Authentication is deemed invalid through validatePassword');
|
||||
}
|
||||
|
||||
public function testInvalidDigest()
|
||||
{
|
||||
list($nonce, $opaque) = $this->getServerTokens();
|
||||
|
||||
$username = 'admin';
|
||||
$password = 12345;
|
||||
$nc = '00002';
|
||||
$cnonce = uniqid();
|
||||
|
||||
$digestHash = md5(
|
||||
md5($username.':'.self::REALM.':'.$password).':'.
|
||||
$nonce.':'.
|
||||
$nc.':'.
|
||||
$cnonce.':'.
|
||||
'auth:'.
|
||||
md5('GET:/')
|
||||
);
|
||||
|
||||
$this->request->setMethod('GET');
|
||||
$this->request->setHeader('Authorization', 'Digest username="'.$username.'", realm="'.self::REALM.'", nonce="'.$nonce.'", uri="/", response="'.$digestHash.'", opaque="'.$opaque.'", qop=auth,nc='.$nc.',cnonce="'.$cnonce.'"');
|
||||
|
||||
$this->auth->init();
|
||||
|
||||
$this->assertFalse($this->auth->validateA1(md5($username.':'.self::REALM.':'.($password.'randomness'))), 'Authentication is deemed invalid through validateA1');
|
||||
}
|
||||
|
||||
public function testInvalidDigest2()
|
||||
{
|
||||
$this->request->setMethod('GET');
|
||||
$this->request->setHeader('Authorization', 'basic blablabla');
|
||||
|
||||
$this->auth->init();
|
||||
$this->assertFalse($this->auth->validateA1(md5('user:realm:password')));
|
||||
}
|
||||
|
||||
public function testDigestAuthInt()
|
||||
{
|
||||
$this->auth->setQOP(Digest::QOP_AUTHINT);
|
||||
list($nonce, $opaque) = $this->getServerTokens(Digest::QOP_AUTHINT);
|
||||
|
||||
$username = 'admin';
|
||||
$password = 12345;
|
||||
$nc = '00003';
|
||||
$cnonce = uniqid();
|
||||
|
||||
$digestHash = md5(
|
||||
md5($username.':'.self::REALM.':'.$password).':'.
|
||||
$nonce.':'.
|
||||
$nc.':'.
|
||||
$cnonce.':'.
|
||||
'auth-int:'.
|
||||
md5('POST:/:'.md5('body'))
|
||||
);
|
||||
|
||||
$this->request->setMethod('POST');
|
||||
$this->request->setHeader('Authorization', 'Digest username="'.$username.'", realm="'.self::REALM.'", nonce="'.$nonce.'", uri="/", response="'.$digestHash.'", opaque="'.$opaque.'", qop=auth-int,nc='.$nc.',cnonce="'.$cnonce.'"');
|
||||
$this->request->setBody('body');
|
||||
|
||||
$this->auth->init();
|
||||
|
||||
$this->assertTrue($this->auth->validateA1(md5($username.':'.self::REALM.':'.$password)), 'Authentication is deemed invalid through validateA1');
|
||||
}
|
||||
|
||||
public function testDigestAuthBoth()
|
||||
{
|
||||
$this->auth->setQOP(Digest::QOP_AUTHINT | Digest::QOP_AUTH);
|
||||
list($nonce, $opaque) = $this->getServerTokens(Digest::QOP_AUTHINT | Digest::QOP_AUTH);
|
||||
|
||||
$username = 'admin';
|
||||
$password = 12345;
|
||||
$nc = '00003';
|
||||
$cnonce = uniqid();
|
||||
|
||||
$digestHash = md5(
|
||||
md5($username.':'.self::REALM.':'.$password).':'.
|
||||
$nonce.':'.
|
||||
$nc.':'.
|
||||
$cnonce.':'.
|
||||
'auth-int:'.
|
||||
md5('POST:/:'.md5('body'))
|
||||
);
|
||||
|
||||
$this->request->setMethod('POST');
|
||||
$this->request->setHeader('Authorization', 'Digest username="'.$username.'", realm="'.self::REALM.'", nonce="'.$nonce.'", uri="/", response="'.$digestHash.'", opaque="'.$opaque.'", qop=auth-int,nc='.$nc.',cnonce="'.$cnonce.'"');
|
||||
$this->request->setBody('body');
|
||||
|
||||
$this->auth->init();
|
||||
|
||||
$this->assertTrue($this->auth->validateA1(md5($username.':'.self::REALM.':'.$password)), 'Authentication is deemed invalid through validateA1');
|
||||
}
|
||||
|
||||
private function getServerTokens($qop = Digest::QOP_AUTH)
|
||||
{
|
||||
$this->auth->requireLogin();
|
||||
|
||||
switch ($qop) {
|
||||
case Digest::QOP_AUTH: $qopstr = 'auth';
|
||||
break;
|
||||
case Digest::QOP_AUTHINT: $qopstr = 'auth-int';
|
||||
break;
|
||||
default: $qopstr = 'auth,auth-int';
|
||||
break;
|
||||
}
|
||||
|
||||
$test = preg_match('/Digest realm="'.self::REALM.'",qop="'.$qopstr.'",nonce="([0-9a-f]*)",opaque="([0-9a-f]*)"/',
|
||||
$this->response->getHeader('WWW-Authenticate'), $matches);
|
||||
|
||||
$this->assertTrue(true == $test, 'The WWW-Authenticate response didn\'t match our pattern. We received: '.$this->response->getHeader('WWW-Authenticate'));
|
||||
|
||||
$nonce = $matches[1];
|
||||
$opaque = $matches[2];
|
||||
|
||||
// Reset our environment
|
||||
$this->setUp();
|
||||
$this->auth->setQOP($qop);
|
||||
|
||||
return [$nonce, $opaque];
|
||||
}
|
||||
}
|
||||
+605
@@ -0,0 +1,605 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP;
|
||||
|
||||
class ClientTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testCreateCurlSettingsArrayGET()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$client->addCurlSetting(CURLOPT_POSTREDIR, 0);
|
||||
|
||||
$request = new Request('GET', 'http://example.org/', ['X-Foo' => 'bar']);
|
||||
|
||||
$settings = [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_POSTREDIR => 0,
|
||||
CURLOPT_HTTPHEADER => ['X-Foo: bar'],
|
||||
CURLOPT_NOBODY => false,
|
||||
CURLOPT_URL => 'http://example.org/',
|
||||
CURLOPT_CUSTOMREQUEST => 'GET',
|
||||
CURLOPT_USERAGENT => 'sabre-http/'.Version::VERSION.' (http://sabre.io/)',
|
||||
];
|
||||
|
||||
// FIXME: CURLOPT_PROTOCOLS and CURLOPT_REDIR_PROTOCOLS are currently unsupported by HHVM
|
||||
// at least if this unit test fails in the future we know it is :)
|
||||
if (false === defined('HHVM_VERSION')) {
|
||||
$settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
$settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
}
|
||||
|
||||
$this->assertEquals($settings, $client->createCurlSettingsArray($request));
|
||||
}
|
||||
|
||||
public function testCreateCurlSettingsHTTPHeader(): void
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$header = [
|
||||
'Authorization: Bearer 12345',
|
||||
];
|
||||
$client->addCurlSetting(CURLOPT_POSTREDIR, 0);
|
||||
$client->addCurlSetting(CURLOPT_HTTPHEADER, $header);
|
||||
|
||||
$request = new Request('GET', 'http://example.org/');
|
||||
|
||||
$settings = [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_POSTREDIR => 0,
|
||||
CURLOPT_HTTPHEADER => ['Authorization: Bearer 12345'],
|
||||
CURLOPT_NOBODY => false,
|
||||
CURLOPT_URL => 'http://example.org/',
|
||||
CURLOPT_CUSTOMREQUEST => 'GET',
|
||||
CURLOPT_USERAGENT => 'sabre-http/'.Version::VERSION.' (http://sabre.io/)',
|
||||
CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
|
||||
CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
|
||||
];
|
||||
|
||||
self::assertEquals($settings, $client->createCurlSettingsArray($request));
|
||||
}
|
||||
|
||||
public function testCreateCurlSettingsArrayHEAD()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$request = new Request('HEAD', 'http://example.org/', ['X-Foo' => 'bar']);
|
||||
|
||||
$settings = [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_NOBODY => true,
|
||||
CURLOPT_CUSTOMREQUEST => 'HEAD',
|
||||
CURLOPT_HTTPHEADER => ['X-Foo: bar'],
|
||||
CURLOPT_URL => 'http://example.org/',
|
||||
CURLOPT_USERAGENT => 'sabre-http/'.Version::VERSION.' (http://sabre.io/)',
|
||||
];
|
||||
|
||||
// FIXME: CURLOPT_PROTOCOLS and CURLOPT_REDIR_PROTOCOLS are currently unsupported by HHVM
|
||||
// at least if this unit test fails in the future we know it is :)
|
||||
if (false === defined('HHVM_VERSION')) {
|
||||
$settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
$settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
}
|
||||
|
||||
$this->assertEquals($settings, $client->createCurlSettingsArray($request));
|
||||
}
|
||||
|
||||
public function testCreateCurlSettingsArrayGETAfterHEAD()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$request = new Request('HEAD', 'http://example.org/', ['X-Foo' => 'bar']);
|
||||
|
||||
// Parsing the settings for this method, and discarding the result.
|
||||
// This will cause the client to automatically persist previous
|
||||
// settings and will help us detect problems.
|
||||
$client->createCurlSettingsArray($request);
|
||||
|
||||
// This is the real request.
|
||||
$request = new Request('GET', 'http://example.org/', ['X-Foo' => 'bar']);
|
||||
|
||||
$settings = [
|
||||
CURLOPT_CUSTOMREQUEST => 'GET',
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_HTTPHEADER => ['X-Foo: bar'],
|
||||
CURLOPT_NOBODY => false,
|
||||
CURLOPT_URL => 'http://example.org/',
|
||||
CURLOPT_USERAGENT => 'sabre-http/'.Version::VERSION.' (http://sabre.io/)',
|
||||
];
|
||||
|
||||
// FIXME: CURLOPT_PROTOCOLS and CURLOPT_REDIR_PROTOCOLS are currently unsupported by HHVM
|
||||
// at least if this unit test fails in the future we know it is :)
|
||||
if (false === defined('HHVM_VERSION')) {
|
||||
$settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
$settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
}
|
||||
|
||||
$this->assertEquals($settings, $client->createCurlSettingsArray($request));
|
||||
}
|
||||
|
||||
public function testCreateCurlSettingsArrayPUTStream()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
|
||||
$fileContent = 'booh';
|
||||
$h = fopen('php://memory', 'r+');
|
||||
fwrite($h, $fileContent);
|
||||
$request = new Request('PUT', 'http://example.org/', ['X-Foo' => 'bar'], $h);
|
||||
|
||||
$settings = [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_PUT => true,
|
||||
CURLOPT_INFILE => $h,
|
||||
CURLOPT_INFILESIZE => strlen($fileContent),
|
||||
CURLOPT_NOBODY => false,
|
||||
CURLOPT_CUSTOMREQUEST => 'PUT',
|
||||
CURLOPT_HTTPHEADER => ['X-Foo: bar'],
|
||||
CURLOPT_URL => 'http://example.org/',
|
||||
CURLOPT_USERAGENT => 'sabre-http/'.Version::VERSION.' (http://sabre.io/)',
|
||||
];
|
||||
|
||||
// FIXME: CURLOPT_PROTOCOLS and CURLOPT_REDIR_PROTOCOLS are currently unsupported by HHVM
|
||||
// at least if this unit test fails in the future we know it is :)
|
||||
if (false === defined('HHVM_VERSION')) {
|
||||
$settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
$settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
}
|
||||
|
||||
$this->assertEquals($settings, $client->createCurlSettingsArray($request));
|
||||
}
|
||||
|
||||
public function testCreateCurlSettingsArrayPUTString()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$request = new Request('PUT', 'http://example.org/', ['X-Foo' => 'bar'], 'boo');
|
||||
|
||||
$settings = [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_NOBODY => false,
|
||||
CURLOPT_POSTFIELDS => 'boo',
|
||||
CURLOPT_CUSTOMREQUEST => 'PUT',
|
||||
CURLOPT_HTTPHEADER => ['X-Foo: bar'],
|
||||
CURLOPT_URL => 'http://example.org/',
|
||||
CURLOPT_USERAGENT => 'sabre-http/'.Version::VERSION.' (http://sabre.io/)',
|
||||
];
|
||||
|
||||
// FIXME: CURLOPT_PROTOCOLS and CURLOPT_REDIR_PROTOCOLS are currently unsupported by HHVM
|
||||
// at least if this unit test fails in the future we know it is :)
|
||||
if (false === defined('HHVM_VERSION')) {
|
||||
$settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
$settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
}
|
||||
|
||||
$this->assertEquals($settings, $client->createCurlSettingsArray($request));
|
||||
}
|
||||
|
||||
public function testIssue89MultiplePutInfileGivesWarning()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$tmpFile = tmpfile();
|
||||
$request = new Request('POST', 'http://example.org/', ['X-Foo' => 'bar'], 'body');
|
||||
|
||||
$settings = $client->createCurlSettingsArray($request);
|
||||
$this->assertArrayNotHasKey(CURLOPT_PUT, $settings);
|
||||
$this->assertArrayNotHasKey(CURLOPT_INFILE, $settings);
|
||||
|
||||
$request = new Request('POST', 'http://example.org/', ['X-Foo' => 'bar'], $tmpFile);
|
||||
|
||||
$settings = $client->createCurlSettingsArray($request);
|
||||
$this->assertEquals(true, $settings[CURLOPT_PUT]);
|
||||
$this->assertEquals($tmpFile, $settings[CURLOPT_INFILE]);
|
||||
|
||||
$request = new Request('POST', 'http://example.org/', ['X-Foo' => 'bar'], 'body');
|
||||
|
||||
$settings = $client->createCurlSettingsArray($request);
|
||||
$this->assertArrayNotHasKey(CURLOPT_PUT, $settings);
|
||||
$this->assertArrayNotHasKey(CURLOPT_INFILE, $settings);
|
||||
}
|
||||
|
||||
public function testSend()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$request = new Request('GET', 'http://example.org/');
|
||||
|
||||
$client->on('doRequest', function ($request, &$response) {
|
||||
$response = new Response(200);
|
||||
});
|
||||
|
||||
$response = $client->send($request);
|
||||
|
||||
$this->assertEquals(200, $response->getStatus());
|
||||
}
|
||||
|
||||
protected function getAbsoluteUrl($path)
|
||||
{
|
||||
$baseUrl = getenv('BASEURL');
|
||||
if ($baseUrl) {
|
||||
$path = ltrim($path, '/');
|
||||
|
||||
return "$baseUrl/$path";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @group ci
|
||||
*/
|
||||
public function testSendToGetLargeContent()
|
||||
{
|
||||
$url = $this->getAbsoluteUrl('/large.php');
|
||||
if (!$url) {
|
||||
$this->markTestSkipped('Set an environment value BASEURL to continue');
|
||||
}
|
||||
|
||||
// Allow the peak memory usage limit to be specified externally, if needed.
|
||||
// When running this test in different environments it may be appropriate to set a different limit.
|
||||
$maxPeakMemoryUsageEnvVariable = 'SABRE_HTTP_TEST_GET_LARGE_CONTENT_MAX_PEAK_MEMORY_USAGE';
|
||||
$maxPeakMemoryUsage = \getenv($maxPeakMemoryUsageEnvVariable);
|
||||
if (false === $maxPeakMemoryUsage) {
|
||||
$maxPeakMemoryUsage = 60 * pow(1024, 2);
|
||||
}
|
||||
|
||||
$request = new Request('GET', $url);
|
||||
$client = new Client();
|
||||
$response = $client->send($request);
|
||||
|
||||
$this->assertEquals(200, $response->getStatus());
|
||||
$this->assertLessThan(
|
||||
(int) $maxPeakMemoryUsage,
|
||||
memory_get_peak_usage(),
|
||||
"Hint: you can adjust the max peak memory usage allowed for this test by defining env variable $maxPeakMemoryUsageEnvVariable to be the desired max bytes"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group ci
|
||||
*/
|
||||
public function testSendAsync()
|
||||
{
|
||||
$url = $this->getAbsoluteUrl('/foo');
|
||||
if (!$url) {
|
||||
$this->markTestSkipped('Set an environment value BASEURL to continue');
|
||||
}
|
||||
|
||||
$client = new Client();
|
||||
|
||||
$request = new Request('GET', $url);
|
||||
$client->sendAsync($request, function (ResponseInterface $response) {
|
||||
$this->assertEquals("foo\n", $response->getBody());
|
||||
$this->assertEquals(200, $response->getStatus());
|
||||
$this->assertEquals(4, $response->getHeader('Content-Length'));
|
||||
}, function ($error) use ($request) {
|
||||
$url = $request->getUrl();
|
||||
$this->fail("Failed to GET $url");
|
||||
});
|
||||
|
||||
$client->wait();
|
||||
}
|
||||
|
||||
/**
|
||||
* @group ci
|
||||
*/
|
||||
public function testSendAsynConsecutively()
|
||||
{
|
||||
$url = $this->getAbsoluteUrl('/foo');
|
||||
if (!$url) {
|
||||
$this->markTestSkipped('Set an environment value BASEURL to continue');
|
||||
}
|
||||
|
||||
$client = new Client();
|
||||
|
||||
$request = new Request('GET', $url);
|
||||
$client->sendAsync($request, function (ResponseInterface $response) {
|
||||
$this->assertEquals("foo\n", $response->getBody());
|
||||
$this->assertEquals(200, $response->getStatus());
|
||||
$this->assertEquals(4, $response->getHeader('Content-Length'));
|
||||
}, function ($error) use ($request) {
|
||||
$url = $request->getUrl();
|
||||
$this->fail("Failed to get $url");
|
||||
});
|
||||
|
||||
$url = $this->getAbsoluteUrl('/bar.php');
|
||||
$request = new Request('GET', $url);
|
||||
$client->sendAsync($request, function (ResponseInterface $response) {
|
||||
$this->assertEquals("bar\n", $response->getBody());
|
||||
$this->assertEquals(200, $response->getStatus());
|
||||
$this->assertEquals('Bar', $response->getHeader('X-Test'));
|
||||
}, function ($error) use ($request) {
|
||||
$url = $request->getUrl();
|
||||
$this->fail("Failed to get $url");
|
||||
});
|
||||
|
||||
$client->wait();
|
||||
}
|
||||
|
||||
public function testSendClientError()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$request = new Request('GET', 'http://example.org/');
|
||||
|
||||
$client->on('doRequest', function ($request, &$response) {
|
||||
throw new ClientException('aaah', 1);
|
||||
});
|
||||
$called = false;
|
||||
$client->on('exception', function () use (&$called) {
|
||||
$called = true;
|
||||
});
|
||||
|
||||
try {
|
||||
$client->send($request);
|
||||
$this->fail('send() should have thrown an exception');
|
||||
} catch (ClientException $e) {
|
||||
}
|
||||
$this->assertTrue($called);
|
||||
}
|
||||
|
||||
public function testSendHttpError()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$request = new Request('GET', 'http://example.org/');
|
||||
|
||||
$client->on('doRequest', function ($request, &$response) {
|
||||
$response = new Response(404);
|
||||
});
|
||||
$called = 0;
|
||||
$client->on('error', function () use (&$called) {
|
||||
++$called;
|
||||
});
|
||||
$client->on('error:404', function () use (&$called) {
|
||||
++$called;
|
||||
});
|
||||
|
||||
$client->send($request);
|
||||
$this->assertEquals(2, $called);
|
||||
}
|
||||
|
||||
public function testSendRetry()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$request = new Request('GET', 'http://example.org/');
|
||||
|
||||
$called = 0;
|
||||
$client->on('doRequest', function ($request, &$response) use (&$called) {
|
||||
++$called;
|
||||
if ($called < 3) {
|
||||
$response = new Response(404);
|
||||
} else {
|
||||
$response = new Response(200);
|
||||
}
|
||||
});
|
||||
|
||||
$errorCalled = 0;
|
||||
$client->on('error', function ($request, $response, &$retry, $retryCount) use (&$errorCalled) {
|
||||
++$errorCalled;
|
||||
$retry = true;
|
||||
});
|
||||
|
||||
$response = $client->send($request);
|
||||
$this->assertEquals(3, $called);
|
||||
$this->assertEquals(2, $errorCalled);
|
||||
$this->assertEquals(200, $response->getStatus());
|
||||
}
|
||||
|
||||
public function testHttpErrorException()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$client->setThrowExceptions(true);
|
||||
$request = new Request('GET', 'http://example.org/');
|
||||
|
||||
$client->on('doRequest', function ($request, &$response) {
|
||||
$response = new Response(404);
|
||||
});
|
||||
|
||||
try {
|
||||
$client->send($request);
|
||||
$this->fail('An exception should have been thrown');
|
||||
} catch (ClientHttpException $e) {
|
||||
$this->assertEquals(404, $e->getHttpStatus());
|
||||
$this->assertInstanceOf('Sabre\HTTP\Response', $e->getResponse());
|
||||
}
|
||||
}
|
||||
|
||||
public function testParseCurlResult()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$client->on('curlStuff', function (&$return) {
|
||||
$return = [
|
||||
[
|
||||
'header_size' => 33,
|
||||
'http_code' => 200,
|
||||
],
|
||||
0,
|
||||
'',
|
||||
];
|
||||
});
|
||||
|
||||
$body = "HTTP/1.1 200 OK\r\nHeader1:Val1\r\n\r\nFoo";
|
||||
$result = $client->parseCurlResult($body, 'foobar');
|
||||
|
||||
$this->assertEquals(Client::STATUS_SUCCESS, $result['status']);
|
||||
$this->assertEquals(200, $result['http_code']);
|
||||
$this->assertEquals(200, $result['response']->getStatus());
|
||||
$this->assertEquals(['Header1' => ['Val1']], $result['response']->getHeaders());
|
||||
$this->assertEquals('Foo', $result['response']->getBodyAsString());
|
||||
}
|
||||
|
||||
public function testParseCurlResultEmptyBody()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$client->on('curlStuff', function (&$return) {
|
||||
$return = [
|
||||
[
|
||||
'header_size' => 33,
|
||||
'http_code' => 200,
|
||||
],
|
||||
0,
|
||||
'',
|
||||
];
|
||||
});
|
||||
|
||||
$body = "HTTP/1.1 200 OK\r\nHeader1:Val1\r\n\r\n";
|
||||
$result = $client->parseCurlResult($body, 'foobar');
|
||||
|
||||
$this->assertEquals(Client::STATUS_SUCCESS, $result['status']);
|
||||
$this->assertEquals(200, $result['http_code']);
|
||||
$this->assertEquals(200, $result['response']->getStatus());
|
||||
$this->assertEquals(['Header1' => ['Val1']], $result['response']->getHeaders());
|
||||
$this->assertEquals('', $result['response']->getBodyAsString());
|
||||
}
|
||||
|
||||
public function testParseCurlError()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$client->on('curlStuff', function (&$return) {
|
||||
$return = [
|
||||
[],
|
||||
1,
|
||||
'Curl error',
|
||||
];
|
||||
});
|
||||
|
||||
$body = "HTTP/1.1 200 OK\r\nHeader1:Val1\r\n\r\nFoo";
|
||||
$result = $client->parseCurlResult($body, 'foobar');
|
||||
|
||||
$this->assertEquals(Client::STATUS_CURLERROR, $result['status']);
|
||||
$this->assertEquals(1, $result['curl_errno']);
|
||||
$this->assertEquals('Curl error', $result['curl_errmsg']);
|
||||
}
|
||||
|
||||
public function testDoRequest()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$request = new Request('GET', 'http://example.org/');
|
||||
$client->on('curlExec', function (&$return) {
|
||||
$return = "HTTP/1.1 200 OK\r\nHeader1:Val1\r\n\r\nFoo";
|
||||
});
|
||||
$client->on('curlStuff', function (&$return) {
|
||||
$return = [
|
||||
[
|
||||
'header_size' => 33,
|
||||
'http_code' => 200,
|
||||
],
|
||||
0,
|
||||
'',
|
||||
];
|
||||
});
|
||||
$response = $client->doRequest($request);
|
||||
$this->assertEquals(200, $response->getStatus());
|
||||
$this->assertEquals(['Header1' => ['Val1']], $response->getHeaders());
|
||||
$this->assertEquals('Foo', $response->getBodyAsString());
|
||||
}
|
||||
|
||||
public function testDoRequestCurlError()
|
||||
{
|
||||
$client = new ClientMock();
|
||||
$request = new Request('GET', 'http://example.org/');
|
||||
$client->on('curlExec', function (&$return) {
|
||||
$return = '';
|
||||
});
|
||||
$client->on('curlStuff', function (&$return) {
|
||||
$return = [
|
||||
[],
|
||||
1,
|
||||
'Curl error',
|
||||
];
|
||||
});
|
||||
|
||||
try {
|
||||
$response = $client->doRequest($request);
|
||||
$this->fail('This should have thrown an exception');
|
||||
} catch (ClientException $e) {
|
||||
$this->assertEquals(1, $e->getCode());
|
||||
$this->assertEquals('Curl error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ClientMock extends Client
|
||||
{
|
||||
protected $persistedSettings = [];
|
||||
|
||||
/**
|
||||
* Making this method public.
|
||||
*/
|
||||
public function receiveCurlHeader($curlHandle, $headerLine)
|
||||
{
|
||||
return parent::receiveCurlHeader($curlHandle, $headerLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Making this method public.
|
||||
*/
|
||||
public function createCurlSettingsArray(RequestInterface $request): array
|
||||
{
|
||||
return parent::createCurlSettingsArray($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Making this method public.
|
||||
*/
|
||||
public function parseCurlResult(string $response, $curlHandle): array
|
||||
{
|
||||
return parent::parseCurlResult($response, $curlHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is responsible for performing a single request.
|
||||
*/
|
||||
public function doRequest(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
$response = null;
|
||||
$this->emit('doRequest', [$request, &$response]);
|
||||
|
||||
// If nothing modified $response, we're using the default behavior.
|
||||
if (is_null($response)) {
|
||||
return parent::doRequest($request);
|
||||
} else {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bunch of information about a curl request.
|
||||
*
|
||||
* This method exists so it can easily be overridden and mocked.
|
||||
*
|
||||
* @param resource $curlHandle
|
||||
*/
|
||||
protected function curlStuff($curlHandle): array
|
||||
{
|
||||
$return = null;
|
||||
$this->emit('curlStuff', [&$return]);
|
||||
|
||||
// If nothing modified $return, we're using the default behavior.
|
||||
if (is_null($return)) {
|
||||
return parent::curlStuff($curlHandle);
|
||||
} else {
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls curl_exec.
|
||||
*
|
||||
* This method exists so it can easily be overridden and mocked.
|
||||
*
|
||||
* @param resource $curlHandle
|
||||
*/
|
||||
protected function curlExec($curlHandle): string
|
||||
{
|
||||
$return = null;
|
||||
$this->emit('curlExec', [&$return]);
|
||||
|
||||
// If nothing modified $return, we're using the default behavior.
|
||||
if (is_null($return)) {
|
||||
return parent::curlExec($curlHandle);
|
||||
} else {
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP;
|
||||
|
||||
class FunctionsTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider getHeaderValuesDataOnValues2
|
||||
*/
|
||||
public function testGetHeaderValuesOnValues2($result, $values1, $values2)
|
||||
{
|
||||
$this->assertEquals($result, getHeaderValues($values1, $values2));
|
||||
}
|
||||
|
||||
public function getHeaderValuesDataOnValues2()
|
||||
{
|
||||
return [
|
||||
[
|
||||
['a', 'b'],
|
||||
['a'],
|
||||
['b'],
|
||||
],
|
||||
[
|
||||
['a', 'b', 'c', 'd', 'e'],
|
||||
['a', 'b', 'c'],
|
||||
['d', 'e'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getHeaderValuesData
|
||||
*/
|
||||
public function testGetHeaderValues($input, $output)
|
||||
{
|
||||
$this->assertEquals(
|
||||
$output,
|
||||
getHeaderValues($input)
|
||||
);
|
||||
}
|
||||
|
||||
public function getHeaderValuesData()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'a',
|
||||
['a'],
|
||||
],
|
||||
[
|
||||
'a,b',
|
||||
['a', 'b'],
|
||||
],
|
||||
[
|
||||
'a, b',
|
||||
['a', 'b'],
|
||||
],
|
||||
[
|
||||
['a, b'],
|
||||
['a', 'b'],
|
||||
],
|
||||
[
|
||||
['a, b', 'c', 'd,e'],
|
||||
['a', 'b', 'c', 'd', 'e'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider preferData
|
||||
*/
|
||||
public function testPrefer($input, $output)
|
||||
{
|
||||
$this->assertEquals(
|
||||
$output,
|
||||
parsePrefer($input)
|
||||
);
|
||||
}
|
||||
|
||||
public function preferData()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'foo; bar',
|
||||
['foo' => true],
|
||||
],
|
||||
[
|
||||
'foo; bar=""',
|
||||
['foo' => true],
|
||||
],
|
||||
[
|
||||
'foo=""; bar',
|
||||
['foo' => true],
|
||||
],
|
||||
[
|
||||
'FOO',
|
||||
['foo' => true],
|
||||
],
|
||||
[
|
||||
'respond-async',
|
||||
['respond-async' => true],
|
||||
],
|
||||
[
|
||||
['respond-async, wait=100', 'handling=lenient'],
|
||||
['respond-async' => true, 'wait' => 100, 'handling' => 'lenient'],
|
||||
],
|
||||
[
|
||||
['respond-async, wait=100, handling=lenient'],
|
||||
['respond-async' => true, 'wait' => 100, 'handling' => 'lenient'],
|
||||
],
|
||||
// Old values
|
||||
[
|
||||
'return-asynch, return-representation',
|
||||
['respond-async' => true, 'return' => 'representation'],
|
||||
],
|
||||
[
|
||||
'return-minimal',
|
||||
['return' => 'minimal'],
|
||||
],
|
||||
[
|
||||
'strict',
|
||||
['handling' => 'strict'],
|
||||
],
|
||||
[
|
||||
'lenient',
|
||||
['handling' => 'lenient'],
|
||||
],
|
||||
// Invalid token
|
||||
[
|
||||
['foo=%bar%'],
|
||||
[],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testParseHTTPDate()
|
||||
{
|
||||
$times = [
|
||||
'Wed, 13 Oct 2010 10:26:00 GMT',
|
||||
'Wednesday, 13-Oct-10 10:26:00 GMT',
|
||||
'Wed Oct 13 10:26:00 2010',
|
||||
];
|
||||
|
||||
$expected = 1286965560;
|
||||
|
||||
foreach ($times as $time) {
|
||||
$result = parseDate($time);
|
||||
$this->assertEquals($expected, $result->format('U'));
|
||||
}
|
||||
|
||||
$result = parseDate('Wed Oct 6 10:26:00 2010');
|
||||
$this->assertEquals(1286360760, $result->format('U'));
|
||||
}
|
||||
|
||||
public function testParseHTTPDateFail()
|
||||
{
|
||||
$times = [
|
||||
// random string
|
||||
'NOW',
|
||||
// not-GMT timezone
|
||||
'Wednesday, 13-Oct-10 10:26:00 UTC',
|
||||
// No space before the 6
|
||||
'Wed Oct 6 10:26:00 2010',
|
||||
// Invalid day
|
||||
'Wed Oct 0 10:26:00 2010',
|
||||
'Wed Oct 32 10:26:00 2010',
|
||||
'Wed, 0 Oct 2010 10:26:00 GMT',
|
||||
'Wed, 32 Oct 2010 10:26:00 GMT',
|
||||
'Wednesday, 32-Oct-10 10:26:00 GMT',
|
||||
// Invalid hour
|
||||
'Wed, 13 Oct 2010 24:26:00 GMT',
|
||||
'Wednesday, 13-Oct-10 24:26:00 GMT',
|
||||
'Wed Oct 13 24:26:00 2010',
|
||||
];
|
||||
|
||||
foreach ($times as $time) {
|
||||
$this->assertFalse(parseDate($time), 'We used the string: '.$time);
|
||||
}
|
||||
}
|
||||
|
||||
public function testTimezones()
|
||||
{
|
||||
$default = date_default_timezone_get();
|
||||
date_default_timezone_set('Europe/Amsterdam');
|
||||
|
||||
$this->testParseHTTPDate();
|
||||
|
||||
date_default_timezone_set($default);
|
||||
}
|
||||
|
||||
public function testToHTTPDate()
|
||||
{
|
||||
$dt = new \DateTime('2011-12-10 12:00:00 +0200');
|
||||
|
||||
$this->assertEquals(
|
||||
'Sat, 10 Dec 2011 10:00:00 GMT',
|
||||
toDate($dt)
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseMimeTypeOnInvalidMimeType()
|
||||
{
|
||||
if (false === \getenv('EXECUTE_INVALID_MIME_TYPE_TEST')) {
|
||||
$this->markTestSkipped('Test skipped because parseMimeType with an invalid mime type will exit in 5.x');
|
||||
}
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Not a valid mime-type: invalid_mime_type');
|
||||
|
||||
parseMimeType('invalid_mime_type');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP;
|
||||
|
||||
class MessageDecoratorTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
protected $inner;
|
||||
protected $outer;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->inner = new Request('GET', '/');
|
||||
$this->outer = new RequestDecorator($this->inner);
|
||||
}
|
||||
|
||||
public function testBody()
|
||||
{
|
||||
$this->outer->setBody('foo');
|
||||
$this->assertEquals('foo', stream_get_contents($this->inner->getBodyAsStream()));
|
||||
$this->assertEquals('foo', stream_get_contents($this->outer->getBodyAsStream()));
|
||||
$this->assertEquals('foo', $this->inner->getBodyAsString());
|
||||
$this->assertEquals('foo', $this->outer->getBodyAsString());
|
||||
$this->assertEquals('foo', $this->inner->getBody());
|
||||
$this->assertEquals('foo', $this->outer->getBody());
|
||||
}
|
||||
|
||||
public function testHeaders()
|
||||
{
|
||||
$this->outer->setHeaders([
|
||||
'a' => 'b',
|
||||
]);
|
||||
|
||||
$this->assertEquals(['a' => ['b']], $this->inner->getHeaders());
|
||||
$this->assertEquals(['a' => ['b']], $this->outer->getHeaders());
|
||||
|
||||
$this->outer->setHeaders([
|
||||
'c' => 'd',
|
||||
]);
|
||||
|
||||
$this->assertEquals(['a' => ['b'], 'c' => ['d']], $this->inner->getHeaders());
|
||||
$this->assertEquals(['a' => ['b'], 'c' => ['d']], $this->outer->getHeaders());
|
||||
|
||||
$this->outer->addHeaders([
|
||||
'e' => 'f',
|
||||
]);
|
||||
|
||||
$this->assertEquals(['a' => ['b'], 'c' => ['d'], 'e' => ['f']], $this->inner->getHeaders());
|
||||
$this->assertEquals(['a' => ['b'], 'c' => ['d'], 'e' => ['f']], $this->outer->getHeaders());
|
||||
}
|
||||
|
||||
public function testHeader()
|
||||
{
|
||||
$this->assertFalse($this->outer->hasHeader('a'));
|
||||
$this->assertFalse($this->inner->hasHeader('a'));
|
||||
$this->outer->setHeader('a', 'c');
|
||||
$this->assertTrue($this->outer->hasHeader('a'));
|
||||
$this->assertTrue($this->inner->hasHeader('a'));
|
||||
|
||||
$this->assertEquals('c', $this->inner->getHeader('A'));
|
||||
$this->assertEquals('c', $this->outer->getHeader('A'));
|
||||
|
||||
$this->outer->addHeader('A', 'd');
|
||||
|
||||
$this->assertEquals(
|
||||
['c', 'd'],
|
||||
$this->inner->getHeaderAsArray('A')
|
||||
);
|
||||
$this->assertEquals(
|
||||
['c', 'd'],
|
||||
$this->outer->getHeaderAsArray('A')
|
||||
);
|
||||
|
||||
$success = $this->outer->removeHeader('a');
|
||||
|
||||
$this->assertTrue($success);
|
||||
$this->assertNull($this->inner->getHeader('A'));
|
||||
$this->assertNull($this->outer->getHeader('A'));
|
||||
|
||||
$this->assertFalse($this->outer->removeHeader('i-dont-exist'));
|
||||
}
|
||||
|
||||
public function testHttpVersion()
|
||||
{
|
||||
$this->outer->setHttpVersion('1.0');
|
||||
|
||||
$this->assertEquals('1.0', $this->inner->getHttpVersion());
|
||||
$this->assertEquals('1.0', $this->outer->getHttpVersion());
|
||||
}
|
||||
}
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP;
|
||||
|
||||
class MessageTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testConstruct()
|
||||
{
|
||||
$message = new MessageMock();
|
||||
$this->assertInstanceOf('Sabre\HTTP\Message', $message);
|
||||
}
|
||||
|
||||
public function testStreamBody()
|
||||
{
|
||||
$body = 'foo';
|
||||
$h = fopen('php://memory', 'r+');
|
||||
fwrite($h, $body);
|
||||
rewind($h);
|
||||
|
||||
$message = new MessageMock();
|
||||
$message->setBody($h);
|
||||
|
||||
$this->assertEquals($body, $message->getBodyAsString());
|
||||
rewind($h);
|
||||
$this->assertEquals($body, stream_get_contents($message->getBodyAsStream()));
|
||||
rewind($h);
|
||||
$this->assertEquals($body, stream_get_contents($message->getBody()));
|
||||
}
|
||||
|
||||
public function testStringBody()
|
||||
{
|
||||
$body = 'foo';
|
||||
|
||||
$message = new MessageMock();
|
||||
$message->setBody($body);
|
||||
|
||||
$this->assertEquals($body, $message->getBodyAsString());
|
||||
$this->assertEquals($body, stream_get_contents($message->getBodyAsStream()));
|
||||
$this->assertEquals($body, $message->getBody());
|
||||
}
|
||||
|
||||
public function testCallbackBodyAsString()
|
||||
{
|
||||
$body = $this->createCallback('foo');
|
||||
|
||||
$message = new MessageMock();
|
||||
$message->setBody($body);
|
||||
|
||||
$string = $message->getBodyAsString();
|
||||
|
||||
$this->assertSame('foo', $string);
|
||||
}
|
||||
|
||||
public function testCallbackBodyAsStream()
|
||||
{
|
||||
$body = $this->createCallback('foo');
|
||||
|
||||
$message = new MessageMock();
|
||||
$message->setBody($body);
|
||||
|
||||
$stream = $message->getBodyAsStream();
|
||||
|
||||
$this->assertSame('foo', stream_get_contents($stream));
|
||||
}
|
||||
|
||||
public function testGetBodyWhenCallback()
|
||||
{
|
||||
$callback = $this->createCallback('foo');
|
||||
|
||||
$message = new MessageMock();
|
||||
$message->setBody($callback);
|
||||
|
||||
$this->assertSame($callback, $message->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* It's possible that streams contains more data than the Content-Length.
|
||||
*
|
||||
* The request object should make sure to never emit more than
|
||||
* Content-Length, if Content-Length is set.
|
||||
*
|
||||
* This is in particular useful when responding to range requests with
|
||||
* streams that represent files on the filesystem, as it's possible to just
|
||||
* seek the stream to a certain point, set the content-length and let the
|
||||
* request object do the rest.
|
||||
*/
|
||||
public function testLongStreamToStringBody()
|
||||
{
|
||||
$body = fopen('php://memory', 'r+');
|
||||
fwrite($body, 'abcdefg');
|
||||
fseek($body, 2);
|
||||
|
||||
$message = new MessageMock();
|
||||
$message->setBody($body);
|
||||
$message->setHeader('Content-Length', '4');
|
||||
|
||||
$this->assertEquals(
|
||||
'cdef',
|
||||
$message->getBodyAsString()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some clients include a content-length header, but the header is empty.
|
||||
* This is definitely broken behavior, but we should support it.
|
||||
*/
|
||||
public function testEmptyContentLengthHeader()
|
||||
{
|
||||
$body = fopen('php://memory', 'r+');
|
||||
fwrite($body, 'abcdefg');
|
||||
fseek($body, 2);
|
||||
|
||||
$message = new MessageMock();
|
||||
$message->setBody($body);
|
||||
$message->setHeader('Content-Length', '');
|
||||
|
||||
$this->assertEquals(
|
||||
'cdefg',
|
||||
$message->getBodyAsString()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetEmptyBodyStream()
|
||||
{
|
||||
$message = new MessageMock();
|
||||
$body = $message->getBodyAsStream();
|
||||
|
||||
$this->assertEquals('', stream_get_contents($body));
|
||||
}
|
||||
|
||||
public function testGetEmptyBodyString()
|
||||
{
|
||||
$message = new MessageMock();
|
||||
$body = $message->getBodyAsString();
|
||||
|
||||
$this->assertEquals('', $body);
|
||||
}
|
||||
|
||||
public function testHeaders()
|
||||
{
|
||||
$message = new MessageMock();
|
||||
$message->setHeader('X-Foo', 'bar');
|
||||
|
||||
// Testing caselessness
|
||||
$this->assertEquals('bar', $message->getHeader('X-Foo'));
|
||||
$this->assertEquals('bar', $message->getHeader('x-fOO'));
|
||||
|
||||
$this->assertTrue(
|
||||
$message->removeHeader('X-FOO')
|
||||
);
|
||||
$this->assertNull($message->getHeader('X-Foo'));
|
||||
$this->assertFalse(
|
||||
$message->removeHeader('X-FOO')
|
||||
);
|
||||
}
|
||||
|
||||
public function testSetHeaders()
|
||||
{
|
||||
$message = new MessageMock();
|
||||
|
||||
$headers = [
|
||||
'X-Foo' => ['1'],
|
||||
'X-Bar' => ['2'],
|
||||
];
|
||||
|
||||
$message->setHeaders($headers);
|
||||
$this->assertEquals($headers, $message->getHeaders());
|
||||
|
||||
$message->setHeaders([
|
||||
'X-Foo' => ['3', '4'],
|
||||
'X-Bar' => '5',
|
||||
]);
|
||||
|
||||
$expected = [
|
||||
'X-Foo' => ['3', '4'],
|
||||
'X-Bar' => ['5'],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $message->getHeaders());
|
||||
}
|
||||
|
||||
public function testAddHeaders()
|
||||
{
|
||||
$message = new MessageMock();
|
||||
|
||||
$headers = [
|
||||
'X-Foo' => ['1'],
|
||||
'X-Bar' => ['2'],
|
||||
];
|
||||
|
||||
$message->addHeaders($headers);
|
||||
$this->assertEquals($headers, $message->getHeaders());
|
||||
|
||||
$message->addHeaders([
|
||||
'X-Foo' => ['3', '4'],
|
||||
'X-Bar' => '5',
|
||||
]);
|
||||
|
||||
$expected = [
|
||||
'X-Foo' => ['1', '3', '4'],
|
||||
'X-Bar' => ['2', '5'],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $message->getHeaders());
|
||||
}
|
||||
|
||||
public function testSendBody()
|
||||
{
|
||||
$message = new MessageMock();
|
||||
|
||||
// String
|
||||
$message->setBody('foo');
|
||||
|
||||
// Stream
|
||||
$h = fopen('php://memory', 'r+');
|
||||
fwrite($h, 'bar');
|
||||
rewind($h);
|
||||
$message->setBody($h);
|
||||
|
||||
$body = $message->getBody();
|
||||
rewind($body);
|
||||
|
||||
$this->assertEquals('bar', stream_get_contents($body));
|
||||
}
|
||||
|
||||
public function testMultipleHeaders()
|
||||
{
|
||||
$message = new MessageMock();
|
||||
$message->setHeader('a', '1');
|
||||
$message->addHeader('A', '2');
|
||||
|
||||
$this->assertEquals(
|
||||
'1,2',
|
||||
$message->getHeader('A')
|
||||
);
|
||||
$this->assertEquals(
|
||||
'1,2',
|
||||
$message->getHeader('a')
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
['1', '2'],
|
||||
$message->getHeaderAsArray('a')
|
||||
);
|
||||
$this->assertEquals(
|
||||
['1', '2'],
|
||||
$message->getHeaderAsArray('A')
|
||||
);
|
||||
$this->assertEquals(
|
||||
[],
|
||||
$message->getHeaderAsArray('B')
|
||||
);
|
||||
}
|
||||
|
||||
public function testHasHeaders()
|
||||
{
|
||||
$message = new MessageMock();
|
||||
|
||||
$this->assertFalse($message->hasHeader('X-Foo'));
|
||||
$message->setHeader('X-Foo', 'Bar');
|
||||
$this->assertTrue($message->hasHeader('X-Foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
*
|
||||
* @return \Closure Returns a callback printing $content to php://output stream
|
||||
*/
|
||||
private function createCallback($content)
|
||||
{
|
||||
return function () use ($content) {
|
||||
echo $content;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class MessageMock extends Message
|
||||
{
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP;
|
||||
|
||||
class NegotiateTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider negotiateData
|
||||
*/
|
||||
public function testNegotiate($acceptHeader, $available, $expected)
|
||||
{
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
negotiateContentType($acceptHeader, $available)
|
||||
);
|
||||
}
|
||||
|
||||
public function negotiateData()
|
||||
{
|
||||
return [
|
||||
[ // simple
|
||||
'application/xml',
|
||||
['application/xml'],
|
||||
'application/xml',
|
||||
],
|
||||
[ // no header
|
||||
null,
|
||||
['application/xml'],
|
||||
'application/xml',
|
||||
],
|
||||
[ // 2 options
|
||||
'application/json',
|
||||
['application/xml', 'application/json'],
|
||||
'application/json',
|
||||
],
|
||||
[ // 2 choices
|
||||
'application/json, application/xml',
|
||||
['application/xml'],
|
||||
'application/xml',
|
||||
],
|
||||
[ // quality
|
||||
'application/xml;q=0.2, application/json',
|
||||
['application/xml', 'application/json'],
|
||||
'application/json',
|
||||
],
|
||||
[ // wildcard
|
||||
'image/jpeg, image/png, */*',
|
||||
['application/xml', 'application/json'],
|
||||
'application/xml',
|
||||
],
|
||||
[ // wildcard + quality
|
||||
'image/jpeg, image/png; q=0.5, */*',
|
||||
['application/xml', 'application/json', 'image/png'],
|
||||
'application/xml',
|
||||
],
|
||||
[ // no match
|
||||
'image/jpeg',
|
||||
['application/xml'],
|
||||
null,
|
||||
],
|
||||
[ // This is used in sabre/dav
|
||||
'text/vcard; version=4.0',
|
||||
[
|
||||
// Most often used mime-type. Version 3
|
||||
'text/x-vcard',
|
||||
// The correct standard mime-type. Defaults to version 3 as
|
||||
// well.
|
||||
'text/vcard',
|
||||
// vCard 4
|
||||
'text/vcard; version=4.0',
|
||||
// vCard 3
|
||||
'text/vcard; version=3.0',
|
||||
// jCard
|
||||
'application/vcard+json',
|
||||
],
|
||||
'text/vcard; version=4.0',
|
||||
],
|
||||
[ // rfc7231 example 1
|
||||
'audio/*; q=0.2, audio/basic',
|
||||
[
|
||||
'audio/pcm',
|
||||
'audio/basic',
|
||||
],
|
||||
'audio/basic',
|
||||
],
|
||||
[ // Lower quality after
|
||||
'audio/pcm; q=0.2, audio/basic; q=0.1',
|
||||
[
|
||||
'audio/pcm',
|
||||
'audio/basic',
|
||||
],
|
||||
'audio/pcm',
|
||||
],
|
||||
[ // Random parameter, should be ignored
|
||||
'audio/pcm; hello; q=0.2, audio/basic; q=0.1',
|
||||
[
|
||||
'audio/pcm',
|
||||
'audio/basic',
|
||||
],
|
||||
'audio/pcm',
|
||||
],
|
||||
[ // No whitespace after type, should pick the one that is the most specific.
|
||||
'text/vcard;version=3.0, text/vcard',
|
||||
[
|
||||
'text/vcard',
|
||||
'text/vcard; version=3.0',
|
||||
],
|
||||
'text/vcard; version=3.0',
|
||||
],
|
||||
[ // Same as last one, but order is different
|
||||
'text/vcard, text/vcard;version=3.0',
|
||||
[
|
||||
'text/vcard; version=3.0',
|
||||
'text/vcard',
|
||||
],
|
||||
'text/vcard; version=3.0',
|
||||
],
|
||||
[ // Charset should be ignored here.
|
||||
'text/vcard; charset=utf-8; version=3.0, text/vcard',
|
||||
[
|
||||
'text/vcard',
|
||||
'text/vcard; version=3.0',
|
||||
],
|
||||
'text/vcard; version=3.0',
|
||||
],
|
||||
[ // Undefined offset issue.
|
||||
'text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2',
|
||||
['application/xml', 'application/json', 'image/png'],
|
||||
'application/xml',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP;
|
||||
|
||||
class RequestDecoratorTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
protected $inner;
|
||||
protected $outer;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->inner = new Request('GET', '/');
|
||||
$this->outer = new RequestDecorator($this->inner);
|
||||
}
|
||||
|
||||
public function testMethod()
|
||||
{
|
||||
$this->outer->setMethod('FOO');
|
||||
$this->assertEquals('FOO', $this->inner->getMethod());
|
||||
$this->assertEquals('FOO', $this->outer->getMethod());
|
||||
}
|
||||
|
||||
public function testUrl()
|
||||
{
|
||||
$this->outer->setUrl('/foo');
|
||||
$this->assertEquals('/foo', $this->inner->getUrl());
|
||||
$this->assertEquals('/foo', $this->outer->getUrl());
|
||||
}
|
||||
|
||||
public function testAbsoluteUrl()
|
||||
{
|
||||
$this->outer->setAbsoluteUrl('http://example.org/foo');
|
||||
$this->assertEquals('http://example.org/foo', $this->inner->getAbsoluteUrl());
|
||||
$this->assertEquals('http://example.org/foo', $this->outer->getAbsoluteUrl());
|
||||
}
|
||||
|
||||
public function testBaseUrl()
|
||||
{
|
||||
$this->outer->setBaseUrl('/foo');
|
||||
$this->assertEquals('/foo', $this->inner->getBaseUrl());
|
||||
$this->assertEquals('/foo', $this->outer->getBaseUrl());
|
||||
}
|
||||
|
||||
public function testPath()
|
||||
{
|
||||
$this->outer->setBaseUrl('/foo');
|
||||
$this->outer->setUrl('/foo/bar');
|
||||
$this->assertEquals('bar', $this->inner->getPath());
|
||||
$this->assertEquals('bar', $this->outer->getPath());
|
||||
}
|
||||
|
||||
public function testQueryParams()
|
||||
{
|
||||
$this->outer->setUrl('/foo?a=b&c=d&e');
|
||||
$expected = [
|
||||
'a' => 'b',
|
||||
'c' => 'd',
|
||||
'e' => null,
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $this->inner->getQueryParameters());
|
||||
$this->assertEquals($expected, $this->outer->getQueryParameters());
|
||||
}
|
||||
|
||||
public function testPostData()
|
||||
{
|
||||
$postData = [
|
||||
'a' => 'b',
|
||||
'c' => 'd',
|
||||
'e' => null,
|
||||
];
|
||||
|
||||
$this->outer->setPostData($postData);
|
||||
$this->assertEquals($postData, $this->inner->getPostData());
|
||||
$this->assertEquals($postData, $this->outer->getPostData());
|
||||
}
|
||||
|
||||
public function testServerData()
|
||||
{
|
||||
$serverData = [
|
||||
'HTTPS' => 'On',
|
||||
];
|
||||
|
||||
$this->outer->setRawServerData($serverData);
|
||||
$this->assertEquals('On', $this->inner->getRawServerValue('HTTPS'));
|
||||
$this->assertEquals('On', $this->outer->getRawServerValue('HTTPS'));
|
||||
|
||||
$this->assertNull($this->inner->getRawServerValue('FOO'));
|
||||
$this->assertNull($this->outer->getRawServerValue('FOO'));
|
||||
}
|
||||
|
||||
public function testToString()
|
||||
{
|
||||
$this->inner->setMethod('POST');
|
||||
$this->inner->setUrl('/foo/bar/');
|
||||
$this->inner->setBody('foo');
|
||||
$this->inner->setHeader('foo', 'bar');
|
||||
|
||||
$this->assertEquals((string) $this->inner, (string) $this->outer);
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP;
|
||||
|
||||
class RequestTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testConstruct()
|
||||
{
|
||||
$request = new Request('GET', '/foo', [
|
||||
'User-Agent' => 'Evert',
|
||||
]);
|
||||
$this->assertEquals('GET', $request->getMethod());
|
||||
$this->assertEquals('/foo', $request->getUrl());
|
||||
$this->assertEquals([
|
||||
'User-Agent' => ['Evert'],
|
||||
], $request->getHeaders());
|
||||
}
|
||||
|
||||
public function testGetQueryParameters()
|
||||
{
|
||||
$request = new Request('GET', '/foo?a=b&c&d=e');
|
||||
$this->assertEquals([
|
||||
'a' => 'b',
|
||||
'c' => null,
|
||||
'd' => 'e',
|
||||
], $request->getQueryParameters());
|
||||
}
|
||||
|
||||
public function testGetQueryParametersNoData()
|
||||
{
|
||||
$request = new Request('GET', '/foo');
|
||||
$this->assertEquals([], $request->getQueryParameters());
|
||||
}
|
||||
|
||||
/**
|
||||
* @backupGlobals
|
||||
*/
|
||||
public function testCreateFromPHPRequest()
|
||||
{
|
||||
$_SERVER['REQUEST_URI'] = '/';
|
||||
$_SERVER['REQUEST_METHOD'] = 'PUT';
|
||||
|
||||
$request = Sapi::getRequest();
|
||||
$this->assertEquals('PUT', $request->getMethod());
|
||||
}
|
||||
|
||||
public function testGetAbsoluteUrl()
|
||||
{
|
||||
$r = new Request('GET', '/foo', [
|
||||
'Host' => 'sabredav.org',
|
||||
]);
|
||||
|
||||
$this->assertEquals('http://sabredav.org/foo', $r->getAbsoluteUrl());
|
||||
|
||||
$s = [
|
||||
'HTTP_HOST' => 'sabredav.org',
|
||||
'REQUEST_URI' => '/foo',
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'HTTPS' => 'on',
|
||||
];
|
||||
|
||||
$r = Sapi::createFromServerArray($s);
|
||||
|
||||
$this->assertEquals('https://sabredav.org/foo', $r->getAbsoluteUrl());
|
||||
}
|
||||
|
||||
public function testGetPostData()
|
||||
{
|
||||
$post = [
|
||||
'bla' => 'foo',
|
||||
];
|
||||
$r = new Request('POST', '/');
|
||||
$r->setPostData($post);
|
||||
$this->assertEquals($post, $r->getPostData());
|
||||
}
|
||||
|
||||
public function testGetPath()
|
||||
{
|
||||
$request = new Request('GET', '/foo/bar/');
|
||||
$request->setBaseUrl('/foo');
|
||||
$request->setUrl('/foo/bar/');
|
||||
|
||||
$this->assertEquals('bar', $request->getPath());
|
||||
}
|
||||
|
||||
public function testGetPathStrippedQuery()
|
||||
{
|
||||
$request = new Request('GET', '/foo/bar?a=B');
|
||||
$request->setBaseUrl('/foo');
|
||||
|
||||
$this->assertEquals('bar', $request->getPath());
|
||||
}
|
||||
|
||||
public function testGetPathMissingSlash()
|
||||
{
|
||||
$request = new Request('GET', '/foo');
|
||||
$request->setBaseUrl('/foo/');
|
||||
|
||||
$this->assertEquals('', $request->getPath());
|
||||
}
|
||||
|
||||
public function testGetPathOutsideBaseUrl()
|
||||
{
|
||||
$this->expectException('LogicException');
|
||||
$request = new Request('GET', '/bar/');
|
||||
$request->setBaseUrl('/foo/');
|
||||
|
||||
$request->getPath();
|
||||
}
|
||||
|
||||
public function testToString()
|
||||
{
|
||||
$request = new Request('PUT', '/foo/bar', ['Content-Type' => 'text/xml']);
|
||||
$request->setBody('foo');
|
||||
|
||||
$expected = "PUT /foo/bar HTTP/1.1\r\n"
|
||||
."Content-Type: text/xml\r\n"
|
||||
."\r\n"
|
||||
.'foo';
|
||||
$this->assertEquals($expected, (string) $request);
|
||||
}
|
||||
|
||||
public function testToStringAuthorization()
|
||||
{
|
||||
$request = new Request('PUT', '/foo/bar', ['Content-Type' => 'text/xml', 'Authorization' => 'Basic foobar']);
|
||||
$request->setBody('foo');
|
||||
|
||||
$expected = "PUT /foo/bar HTTP/1.1\r\n"
|
||||
."Content-Type: text/xml\r\n"
|
||||
."Authorization: Basic REDACTED\r\n"
|
||||
."\r\n"
|
||||
.'foo';
|
||||
$this->assertEquals($expected, (string) $request);
|
||||
}
|
||||
|
||||
public function testAbsoluteUrlHttp(): void
|
||||
{
|
||||
$request = new Request('GET', 'http://example.com/foo/bar?a=b&c=d');
|
||||
self::assertEquals('http://example.com/foo/bar?a=b&c=d', $request->getAbsoluteUrl());
|
||||
}
|
||||
|
||||
public function testAbsoluteUrlHttpHostPrevalence(): void
|
||||
{
|
||||
$request = new Request('GET', 'http://example.com/foo/bar?a=b&c=d', [
|
||||
'Host' => 'example.org',
|
||||
]);
|
||||
self::assertEquals('http://example.com/foo/bar?a=b&c=d', $request->getAbsoluteUrl());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP;
|
||||
|
||||
class ResponseDecoratorTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
protected $inner;
|
||||
protected $outer;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->inner = new Response();
|
||||
$this->outer = new ResponseDecorator($this->inner);
|
||||
}
|
||||
|
||||
public function testStatus()
|
||||
{
|
||||
$this->outer->setStatus(201);
|
||||
$this->assertEquals(201, $this->inner->getStatus());
|
||||
$this->assertEquals(201, $this->outer->getStatus());
|
||||
$this->assertEquals('Created', $this->inner->getStatusText());
|
||||
$this->assertEquals('Created', $this->outer->getStatusText());
|
||||
}
|
||||
|
||||
public function testToString()
|
||||
{
|
||||
$this->inner->setStatus(201);
|
||||
$this->inner->setBody('foo');
|
||||
$this->inner->setHeader('foo', 'bar');
|
||||
|
||||
$this->assertEquals((string) $this->inner, (string) $this->outer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP;
|
||||
|
||||
class ResponseTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testConstruct()
|
||||
{
|
||||
$response = new Response(200, ['Content-Type' => 'text/xml']);
|
||||
$this->assertEquals(200, $response->getStatus());
|
||||
$this->assertEquals('OK', $response->getStatusText());
|
||||
}
|
||||
|
||||
public function testSetStatus()
|
||||
{
|
||||
$response = new Response();
|
||||
$response->setStatus('402 Where\'s my money?');
|
||||
$this->assertEquals(402, $response->getStatus());
|
||||
$this->assertEquals('Where\'s my money?', $response->getStatusText());
|
||||
}
|
||||
|
||||
public function testInvalidStatus()
|
||||
{
|
||||
$this->expectException('InvalidArgumentException');
|
||||
$response = new Response(1000);
|
||||
}
|
||||
|
||||
public function testToString()
|
||||
{
|
||||
$response = new Response(200, ['Content-Type' => 'text/xml']);
|
||||
$response->setBody('foo');
|
||||
|
||||
$expected = "HTTP/1.1 200 OK\r\n"
|
||||
."Content-Type: text/xml\r\n"
|
||||
."\r\n"
|
||||
.'foo';
|
||||
$this->assertEquals($expected, (string) $response);
|
||||
}
|
||||
}
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP;
|
||||
|
||||
class SapiTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testConstructFromServerArray()
|
||||
{
|
||||
$request = Sapi::createFromServerArray([
|
||||
'REQUEST_URI' => '/foo',
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'HTTP_USER_AGENT' => 'Evert',
|
||||
'CONTENT_TYPE' => 'text/xml',
|
||||
'CONTENT_LENGTH' => '400',
|
||||
'SERVER_PROTOCOL' => 'HTTP/1.0',
|
||||
]);
|
||||
|
||||
$this->assertEquals('GET', $request->getMethod());
|
||||
$this->assertEquals('/foo', $request->getUrl());
|
||||
$this->assertEquals([
|
||||
'User-Agent' => ['Evert'],
|
||||
'Content-Type' => ['text/xml'],
|
||||
'Content-Length' => ['400'],
|
||||
], $request->getHeaders());
|
||||
|
||||
$this->assertEquals('1.0', $request->getHttpVersion());
|
||||
|
||||
$this->assertEquals('400', $request->getRawServerValue('CONTENT_LENGTH'));
|
||||
$this->assertNull($request->getRawServerValue('FOO'));
|
||||
}
|
||||
|
||||
public function testConstructFromServerArrayOnNullUrl()
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('The _SERVER array must have a REQUEST_URI key');
|
||||
|
||||
$request = Sapi::createFromServerArray([
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'HTTP_USER_AGENT' => 'Evert',
|
||||
'CONTENT_TYPE' => 'text/xml',
|
||||
'CONTENT_LENGTH' => '400',
|
||||
'SERVER_PROTOCOL' => 'HTTP/1.0',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testConstructFromServerArrayOnNullMethod()
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('The _SERVER array must have a REQUEST_METHOD key');
|
||||
|
||||
$request = Sapi::createFromServerArray([
|
||||
'REQUEST_URI' => '/foo',
|
||||
'HTTP_USER_AGENT' => 'Evert',
|
||||
'CONTENT_TYPE' => 'text/xml',
|
||||
'CONTENT_LENGTH' => '400',
|
||||
'SERVER_PROTOCOL' => 'HTTP/1.0',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testConstructPHPAuth()
|
||||
{
|
||||
$request = Sapi::createFromServerArray([
|
||||
'REQUEST_URI' => '/foo',
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'PHP_AUTH_USER' => 'user',
|
||||
'PHP_AUTH_PW' => 'pass',
|
||||
]);
|
||||
|
||||
$this->assertEquals('GET', $request->getMethod());
|
||||
$this->assertEquals('/foo', $request->getUrl());
|
||||
$this->assertEquals([
|
||||
'Authorization' => ['Basic '.base64_encode('user:pass')],
|
||||
], $request->getHeaders());
|
||||
}
|
||||
|
||||
public function testConstructPHPAuthDigest()
|
||||
{
|
||||
$request = Sapi::createFromServerArray([
|
||||
'REQUEST_URI' => '/foo',
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'PHP_AUTH_DIGEST' => 'blabla',
|
||||
]);
|
||||
|
||||
$this->assertEquals('GET', $request->getMethod());
|
||||
$this->assertEquals('/foo', $request->getUrl());
|
||||
$this->assertEquals([
|
||||
'Authorization' => ['Digest blabla'],
|
||||
], $request->getHeaders());
|
||||
}
|
||||
|
||||
public function testConstructRedirectAuth()
|
||||
{
|
||||
$request = Sapi::createFromServerArray([
|
||||
'REQUEST_URI' => '/foo',
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'REDIRECT_HTTP_AUTHORIZATION' => 'Basic bla',
|
||||
]);
|
||||
|
||||
$this->assertEquals('GET', $request->getMethod());
|
||||
$this->assertEquals('/foo', $request->getUrl());
|
||||
$this->assertEquals([
|
||||
'Authorization' => ['Basic bla'],
|
||||
], $request->getHeaders());
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*
|
||||
* Unfortunately we have no way of testing if the HTTP response code got
|
||||
* changed.
|
||||
*/
|
||||
public function testSend()
|
||||
{
|
||||
if (!function_exists('xdebug_get_headers')) {
|
||||
$this->markTestSkipped('XDebug needs to be installed for this test to run');
|
||||
}
|
||||
|
||||
$response = new Response(204, ['Content-Type' => 'text/xml;charset=UTF-8']);
|
||||
|
||||
// Second Content-Type header. Normally this doesn't make sense.
|
||||
$response->addHeader('Content-Type', 'application/xml');
|
||||
$response->setBody('foo');
|
||||
|
||||
ob_start();
|
||||
|
||||
Sapi::sendResponse($response);
|
||||
$headers = xdebug_get_headers();
|
||||
|
||||
$result = ob_get_clean();
|
||||
header_remove();
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
'Content-Type: text/xml;charset=UTF-8',
|
||||
'Content-Type: application/xml',
|
||||
],
|
||||
$headers
|
||||
);
|
||||
|
||||
$this->assertEquals('foo', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*
|
||||
* @depends testSend
|
||||
*/
|
||||
public function testSendLimitedByContentLengthString()
|
||||
{
|
||||
$response = new Response(200);
|
||||
|
||||
$response->addHeader('Content-Length', 19);
|
||||
$response->setBody('Send this sentence. Ignore this one.');
|
||||
|
||||
ob_start();
|
||||
|
||||
Sapi::sendResponse($response);
|
||||
|
||||
$result = ob_get_clean();
|
||||
header_remove();
|
||||
|
||||
$this->assertEquals('Send this sentence.', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether http2 is recognized.
|
||||
*/
|
||||
public function testRecognizeHttp2()
|
||||
{
|
||||
$request = Sapi::createFromServerArray([
|
||||
'SERVER_PROTOCOL' => 'HTTP/2.0',
|
||||
'REQUEST_URI' => 'bla',
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
]);
|
||||
|
||||
$this->assertEquals('2.0', $request->getHttpVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*
|
||||
* @depends testSend
|
||||
*/
|
||||
public function testSendLimitedByContentLengthStream()
|
||||
{
|
||||
$response = new Response(200, ['Content-Length' => 19]);
|
||||
|
||||
$body = fopen('php://memory', 'w');
|
||||
fwrite($body, 'Ignore this. Send this sentence. Ignore this too.');
|
||||
rewind($body);
|
||||
fread($body, 13);
|
||||
$response->setBody($body);
|
||||
|
||||
ob_start();
|
||||
|
||||
Sapi::sendResponse($response);
|
||||
|
||||
$result = ob_get_clean();
|
||||
header_remove();
|
||||
|
||||
$this->assertEquals('Send this sentence.', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*
|
||||
* @depends testSend
|
||||
*
|
||||
* @dataProvider sendContentRangeStreamData
|
||||
*/
|
||||
public function testSendContentRangeStream($ignoreAtStart, $sendText, $multiplier, $ignoreAtEnd, $contentLength)
|
||||
{
|
||||
$partial = str_repeat($sendText, $multiplier);
|
||||
$ignoreAtStartLength = strlen($ignoreAtStart);
|
||||
$ignoreAtEndLength = strlen($ignoreAtEnd);
|
||||
$body = fopen('php://memory', 'w');
|
||||
if (!$contentLength) {
|
||||
$contentLength = strlen($partial);
|
||||
}
|
||||
fwrite($body, $ignoreAtStart);
|
||||
fwrite($body, $partial);
|
||||
if ($ignoreAtEndLength > 0) {
|
||||
fwrite($body, $ignoreAtEnd);
|
||||
}
|
||||
rewind($body);
|
||||
if ($ignoreAtStartLength > 0) {
|
||||
fread($body, $ignoreAtStartLength);
|
||||
}
|
||||
$response = new Response(200, [
|
||||
'Content-Length' => $contentLength,
|
||||
'Content-Range' => sprintf('bytes %d-%d/%d', $ignoreAtStartLength, $ignoreAtStartLength + strlen($partial) - 1, $ignoreAtStartLength + strlen($partial) + $ignoreAtEndLength),
|
||||
]);
|
||||
$response->setBody($body);
|
||||
|
||||
ob_start();
|
||||
|
||||
Sapi::sendResponse($response);
|
||||
|
||||
$result = ob_get_clean();
|
||||
header_remove();
|
||||
|
||||
$this->assertEquals($partial, $result);
|
||||
}
|
||||
|
||||
public function sendContentRangeStreamData()
|
||||
{
|
||||
return [
|
||||
['Ignore this. ', 'Send this.', 10, ' Ignore this at end.'],
|
||||
['Ignore this. ', 'Send this.', 1000, ' Ignore this at end.'],
|
||||
['Ignore this. ', 'S', 4096, ' Ignore this at end.'],
|
||||
['I', 'S', 4094, 'E'],
|
||||
['', 'Send this.', 10, ' Ignore this at end.'],
|
||||
['', 'Send this.', 1000, ' Ignore this at end.'],
|
||||
['', 'S', 4096, ' Ignore this at end.'],
|
||||
['', 'S', 4094, 'En'],
|
||||
['Ignore this. ', 'Send this.', 10, ''],
|
||||
['Ignore this. ', 'Send this.', 1000, ''],
|
||||
['Ignore this. ', 'S', 4096, ''],
|
||||
['Ig', 'S', 4094, ''],
|
||||
|
||||
// Provide contentLength greater than the bytes remaining in the stream.
|
||||
['Ignore this. ', 'Send this.', 10, '', 101],
|
||||
['Ignore this. ', 'Send this.', 1000, '', 10001],
|
||||
['Ignore this. ', 'S', 4096, '', 5000000],
|
||||
['I', 'S', 4094, '', 8095],
|
||||
// Provide contentLength equal to the bytes remaining in the stream.
|
||||
['', 'Send this.', 10, '', 100],
|
||||
['Ignore this. ', 'Send this.', 1000, '', 10000],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*
|
||||
* @depends testSend
|
||||
*/
|
||||
public function testSendWorksWithCallbackAsBody()
|
||||
{
|
||||
$response = new Response(200, [], function () {
|
||||
$fd = fopen('php://output', 'r+');
|
||||
fwrite($fd, 'foo');
|
||||
fclose($fd);
|
||||
});
|
||||
|
||||
ob_start();
|
||||
|
||||
Sapi::sendResponse($response);
|
||||
|
||||
$result = ob_get_clean();
|
||||
|
||||
$this->assertEquals('foo', $result);
|
||||
}
|
||||
|
||||
public function testSendConnectionAborted(): void
|
||||
{
|
||||
$baseUrl = getenv('BASEURL');
|
||||
if (!$baseUrl) {
|
||||
$this->markTestSkipped('Set an environment value BASEURL to continue');
|
||||
}
|
||||
|
||||
$url = rtrim($baseUrl, '/').'/connection_aborted.php';
|
||||
$chunk_size = 4 * 1024 * 1024;
|
||||
$fetch_size = 6 * 1024 * 1024;
|
||||
|
||||
$stream = fopen($url, 'r');
|
||||
$size = 0;
|
||||
|
||||
while ($size <= $fetch_size) {
|
||||
$temp = fread($stream, 8192);
|
||||
if (false === $temp) {
|
||||
break;
|
||||
}
|
||||
$size += strlen($temp);
|
||||
}
|
||||
|
||||
fclose($stream);
|
||||
|
||||
sleep(5);
|
||||
|
||||
$bytes_read = file_get_contents(sys_get_temp_dir().'/dummy_stream_read_counter');
|
||||
$this->assertEquals($chunk_size * 2, $bytes_read);
|
||||
$this->assertGreaterThanOrEqual($fetch_size, $bytes_read);
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP;
|
||||
|
||||
class URLUtilTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testEncodePath()
|
||||
{
|
||||
$str = '';
|
||||
for ($i = 0; $i < 128; ++$i) {
|
||||
$str .= chr($i);
|
||||
}
|
||||
|
||||
$newStr = encodePath($str);
|
||||
|
||||
$this->assertEquals(
|
||||
'%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f'.
|
||||
'%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f'.
|
||||
'%20%21%22%23%24%25%26%27()%2a%2b%2c-./'.
|
||||
'0123456789:%3b%3c%3d%3e%3f'.
|
||||
'@ABCDEFGHIJKLMNO'.
|
||||
'PQRSTUVWXYZ%5b%5c%5d%5e_'.
|
||||
'%60abcdefghijklmno'.
|
||||
'pqrstuvwxyz%7b%7c%7d~%7f',
|
||||
$newStr);
|
||||
|
||||
$this->assertEquals($str, decodePath($newStr));
|
||||
}
|
||||
|
||||
public function testEncodePathSegment()
|
||||
{
|
||||
$str = '';
|
||||
for ($i = 0; $i < 128; ++$i) {
|
||||
$str .= chr($i);
|
||||
}
|
||||
|
||||
$newStr = encodePathSegment($str);
|
||||
|
||||
// Note: almost exactly the same as the last test, with the
|
||||
// exception of the encoding of / (ascii code 2f)
|
||||
$this->assertEquals(
|
||||
'%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f'.
|
||||
'%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f'.
|
||||
'%20%21%22%23%24%25%26%27()%2a%2b%2c-.%2f'.
|
||||
'0123456789:%3b%3c%3d%3e%3f'.
|
||||
'@ABCDEFGHIJKLMNO'.
|
||||
'PQRSTUVWXYZ%5b%5c%5d%5e_'.
|
||||
'%60abcdefghijklmno'.
|
||||
'pqrstuvwxyz%7b%7c%7d~%7f',
|
||||
$newStr);
|
||||
|
||||
$this->assertEquals($str, decodePathSegment($newStr));
|
||||
}
|
||||
|
||||
public function testDecode()
|
||||
{
|
||||
$str = 'Hello%20Test+Test2.txt';
|
||||
$newStr = decodePath($str);
|
||||
$this->assertEquals('Hello Test+Test2.txt', $newStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testDecode
|
||||
*/
|
||||
public function testDecodeUmlaut()
|
||||
{
|
||||
$str = 'Hello%C3%BC.txt';
|
||||
$newStr = decodePath($str);
|
||||
$this->assertEquals("Hello\xC3\xBC.txt", $newStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testDecode
|
||||
*/
|
||||
public function testDecodeSlavicWords()
|
||||
{
|
||||
$words = [
|
||||
'Ostroměr',
|
||||
'Šventaragis',
|
||||
'Świętopełk',
|
||||
'Dušan',
|
||||
'Živko',
|
||||
];
|
||||
foreach ($words as $word) {
|
||||
$str = rawurlencode($word);
|
||||
$newStr = decodePath($str);
|
||||
$this->assertEquals($word, $newStr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testDecodeUmlaut
|
||||
*/
|
||||
public function testDecodeUmlautLatin1()
|
||||
{
|
||||
$str = 'Hello%FC.txt';
|
||||
$newStr = decodePath($str);
|
||||
$this->assertEquals("Hello\xC3\xBC.txt", $newStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* This testcase was sent by a bug reporter.
|
||||
*
|
||||
* @depends testDecode
|
||||
*/
|
||||
public function testDecodeAccentsWindows7()
|
||||
{
|
||||
$str = '/webdav/%C3%A0fo%C3%B3';
|
||||
$newStr = decodePath($str);
|
||||
$this->assertEquals(strtolower($str), encodePath($newStr));
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
date_default_timezone_set('UTC');
|
||||
|
||||
ini_set('error_reporting', (string) (E_ALL | E_STRICT | E_DEPRECATED));
|
||||
|
||||
// Composer autoloader
|
||||
if (file_exists(__DIR__.'/../vendor/autoload.php')) {
|
||||
include __DIR__.'/../vendor/autoload.php';
|
||||
} else {
|
||||
// We may be running as a dependency inside some other repo.
|
||||
// So we are probably in vendor/sabre/http/tests of that repo.
|
||||
// Go up 3 levels to find autoload.php
|
||||
include __DIR__.'/../../../autoload.php';
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<phpunit
|
||||
colors="true"
|
||||
beStrictAboutCoversAnnotation="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTestsThatDoNotTestAnything="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
bootstrap="bootstrap.php"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Sabre_HTTP">
|
||||
<directory>HTTP/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">../lib/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<php>
|
||||
<env name="BASEURL" value="http://localhost:8000"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
header('X-Test: Bar');
|
||||
?>
|
||||
bar
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Sabre\HTTP;
|
||||
|
||||
include '../bootstrap.php';
|
||||
|
||||
class DummyStream
|
||||
{
|
||||
private $position;
|
||||
|
||||
public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool
|
||||
{
|
||||
$this->position = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function stream_read(int $count): string
|
||||
{
|
||||
$this->position += $count;
|
||||
|
||||
return random_bytes($count);
|
||||
}
|
||||
|
||||
public function stream_tell(): int
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function stream_eof(): bool
|
||||
{
|
||||
return $this->position > 25 * 1024 * 1024;
|
||||
}
|
||||
|
||||
public function stream_close(): void
|
||||
{
|
||||
file_put_contents(sys_get_temp_dir().'/dummy_stream_read_counter', $this->position);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The DummyStream wrapper has two functions:
|
||||
* - Provide dummy data.
|
||||
* - Count how many bytes have been read.
|
||||
*/
|
||||
stream_wrapper_register('dummy', DummyStream::class);
|
||||
|
||||
/*
|
||||
* Overwrite default connection handling.
|
||||
* The default behaviour is however for your script to be aborted when the remote client disconnects.
|
||||
*
|
||||
* Nextcloud/ownCloud set ignore_user_abort(true) on purpose to work around
|
||||
* some edge cases where the default behavior would end a script too early.
|
||||
*
|
||||
* https://github.com/owncloud/core/issues/22370
|
||||
* https://github.com/owncloud/core/pull/26775
|
||||
*/
|
||||
ignore_user_abort(true);
|
||||
|
||||
$body = fopen('dummy://hello', 'r');
|
||||
|
||||
$response = new HTTP\Response();
|
||||
$response->setStatus(200);
|
||||
$response->addHeader('Content-Length', 25 * 1024 * 1024);
|
||||
$response->setBody($body);
|
||||
|
||||
HTTP\Sapi::sendResponse($response);
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
foo
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
$data = str_repeat('x', 32 * 1024 * 1024);
|
||||
header('Content-Length: '.strlen($data));
|
||||
header('Content-Type: text/plain');
|
||||
|
||||
echo $data;
|
||||
Reference in New Issue
Block a user