Skip to content

Commit 34f1920

Browse files
authored
Merge pull request #105 from clue-labs/write-chunk-size
Add optional $writeChunkSize parameter for max number of bytes to write
2 parents 81be9c2 + 87b4a13 commit 34f1920

File tree

3 files changed

+129
-2
lines changed

3 files changed

+129
-2
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,21 @@ This value SHOULD NOT be changed unless you know what you're doing.
941941
$stream = new WritableResourceStream(STDOUT, $loop, 8192);
942942
```
943943

944+
This class takes an optional `int|null $writeChunkSize` parameter that controls
945+
this maximum buffer size in bytes to write at once to the stream.
946+
You can use a `null` value here in order to apply its default value.
947+
This value SHOULD NOT be changed unless you know what you're doing.
948+
This can be a positive number which means that up to X bytes will be written
949+
at once to the underlying stream resource. Note that the actual number
950+
of bytes written may be lower if the stream resource has less than X bytes
951+
currently available.
952+
This can be `-1` which means "write everything available" to the
953+
underlying stream resource.
954+
955+
```php
956+
$stream = new WritableResourceStream(STDOUT, $loop, null, 8192);
957+
```
958+
944959
See also [`write()`](#write) for more details.
945960

946961
### DuplexResourceStream

src/WritableResourceStream.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ final class WritableResourceStream extends EventEmitter implements WritableStrea
1010
private $stream;
1111
private $loop;
1212
private $softLimit;
13+
private $writeChunkSize;
1314

1415
private $listening = false;
1516
private $writable = true;
1617
private $closed = false;
1718
private $data = '';
1819

19-
public function __construct($stream, LoopInterface $loop, $writeBufferSoftLimit = null)
20+
public function __construct($stream, LoopInterface $loop, $writeBufferSoftLimit = null, $writeChunkSize = null)
2021
{
2122
if (!is_resource($stream) || get_resource_type($stream) !== "stream") {
2223
throw new \InvalidArgumentException('First parameter must be a valid stream resource');
@@ -36,6 +37,7 @@ public function __construct($stream, LoopInterface $loop, $writeBufferSoftLimit
3637
$this->stream = $stream;
3738
$this->loop = $loop;
3839
$this->softLimit = ($writeBufferSoftLimit === null) ? 65536 : (int)$writeBufferSoftLimit;
40+
$this->writeChunkSize = ($writeChunkSize === null) ? -1 : (int)$writeChunkSize;
3941
}
4042

4143
public function isWritable()
@@ -107,7 +109,11 @@ public function handleWrite()
107109
);
108110
});
109111

110-
$sent = fwrite($this->stream, $this->data);
112+
if ($this->writeChunkSize === -1) {
113+
$sent = fwrite($this->stream, $this->data);
114+
} else {
115+
$sent = fwrite($this->stream, $this->data, $this->writeChunkSize);
116+
}
111117

112118
restore_error_handler();
113119

tests/FunctionalInternetTest.php

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
namespace React\Tests\Stream;
4+
5+
use React\Stream\DuplexResourceStream;
6+
use React\EventLoop\Factory;
7+
use React\Stream\WritableResourceStream;
8+
9+
/**
10+
* @group internet
11+
*/
12+
class FunctionalInternetTest extends TestCase
13+
{
14+
public function testUploadKilobytePlain()
15+
{
16+
$size = 1000;
17+
$stream = stream_socket_client('tcp://httpbin.org:80');
18+
19+
$loop = Factory::create();
20+
$stream = new DuplexResourceStream($stream, $loop);
21+
22+
$buffer = '';
23+
$stream->on('data', function ($chunk) use (&$buffer) {
24+
$buffer .= $chunk;
25+
});
26+
27+
$stream->on('error', $this->expectCallableNever());
28+
29+
$stream->write("POST /post HTTP/1.0\r\nHost: httpbin.org\r\nContent-Length: $size\r\n\r\n" . str_repeat('.', $size));
30+
31+
$loop->run();
32+
33+
$this->assertNotEquals('', $buffer);
34+
}
35+
36+
public function testUploadBiggerBlockPlain()
37+
{
38+
$size = 1000 * 30;
39+
$stream = stream_socket_client('tcp://httpbin.org:80');
40+
41+
$loop = Factory::create();
42+
$stream = new DuplexResourceStream($stream, $loop);
43+
44+
$buffer = '';
45+
$stream->on('data', function ($chunk) use (&$buffer) {
46+
$buffer .= $chunk;
47+
});
48+
49+
$stream->on('error', $this->expectCallableNever());
50+
51+
$stream->write("POST /post HTTP/1.0\r\nHost: httpbin.org\r\nContent-Length: $size\r\n\r\n" . str_repeat('.', $size));
52+
53+
$loop->run();
54+
55+
$this->assertNotEquals('', $buffer);
56+
}
57+
58+
public function testUploadKilobyteSecure()
59+
{
60+
$size = 1000;
61+
$stream = stream_socket_client('tls://httpbin.org:443');
62+
63+
$loop = Factory::create();
64+
$stream = new DuplexResourceStream($stream, $loop);
65+
66+
$buffer = '';
67+
$stream->on('data', function ($chunk) use (&$buffer) {
68+
$buffer .= $chunk;
69+
});
70+
71+
$stream->on('error', $this->expectCallableNever());
72+
73+
$stream->write("POST /post HTTP/1.0\r\nHost: httpbin.org\r\nContent-Length: $size\r\n\r\n" . str_repeat('.', $size));
74+
75+
$loop->run();
76+
77+
$this->assertNotEquals('', $buffer);
78+
}
79+
80+
public function testUploadBiggerBlockSecureRequiresSmallerChunkSize()
81+
{
82+
$size = 1000 * 30000;
83+
$stream = stream_socket_client('tls://httpbin.org:443');
84+
85+
$loop = Factory::create();
86+
$stream = new DuplexResourceStream(
87+
$stream,
88+
$loop,
89+
null,
90+
new WritableResourceStream($stream, $loop, null, 8192)
91+
);
92+
93+
$buffer = '';
94+
$stream->on('data', function ($chunk) use (&$buffer) {
95+
$buffer .= $chunk;
96+
});
97+
98+
$stream->on('error', $this->expectCallableNever());
99+
100+
$stream->write("POST /post HTTP/1.0\r\nHost: httpbin.org\r\nContent-Length: $size\r\n\r\n" . str_repeat('.', $size));
101+
102+
$loop->run();
103+
104+
$this->assertNotEquals('', $buffer);
105+
}
106+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy