Skip to content

Commit

Permalink
Merge pull request #209 from phil-davis/patches-for-5.1.7
Browse files Browse the repository at this point in the history
Patches for 5.1.7
  • Loading branch information
phil-davis authored Jun 26, 2023
2 parents 9976ac3 + 10f57ab commit b6fa04f
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 1 deletion.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
ChangeLog
=========

5.1.7 (2023-06-26)
------------------

* #98 and #176 Add more tests (@peter279k)
* #207 fix: handle client disconnect properly with ignore_user_abort true (@kesselb)

5.1.6 (2022-07-15)
------------------

Expand Down
6 changes: 6 additions & 0 deletions lib/Sapi.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ public static function sendResponse(ResponseInterface $response)
if ($copied <= 0) {
break;
}
// Abort on client disconnect.
// With ignore_user_abort(true), the script is not aborted on client disconnect.
// To avoid reading the entire stream and dismissing the data afterward, check between the chunks if the client is still there.
if (1 === ignore_user_abort() && 1 === connection_aborted()) {
break;
}
$left -= $copied;
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion lib/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ class Version
/**
* Full version number.
*/
const VERSION = '5.1.6';
const VERSION = '5.1.7';
}
8 changes: 8 additions & 0 deletions tests/HTTP/Auth/AWSTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ public function testNoHeader()
$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';
Expand Down
32 changes: 32 additions & 0 deletions tests/HTTP/FunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,30 @@

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
*/
Expand Down Expand Up @@ -174,4 +198,12 @@ public function testToHTTPDate()
toDate($dt)
);
}

public function testParseMimeTypeOnInvalidMimeType()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Not a valid mime-type: invalid_mime_type');

parseMimeType('invalid_mime_type');
}
}
59 changes: 59 additions & 0 deletions tests/HTTP/SapiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,34 @@ public function testConstructFromServerArray()
$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([
Expand Down Expand Up @@ -259,4 +287,35 @@ public function testSendWorksWithCallbackAsBody()

$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);
}
}
69 changes: 69 additions & 0 deletions tests/www/connection_aborted.php
Original file line number Diff line number Diff line change
@@ -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);

0 comments on commit b6fa04f

Please sign in to comment.