Menu
Reference
ChunkedWriter
final class ChunkedWriter
The writer a streaming response body receives; see Response::stream().
For an HTTP/1.1 client each write() becomes one chunk of Transfer-Encoding: chunked. For an
HTTP/1.0 client, which has no chunked encoding, bytes go out raw and the close of the connection
ends the body. The server writes the terminating chunk itself when the closure returns.
Example
// Streaming a response body in chunks with Response::stream().
use Http\{Request, Response, Server};
use function Async\{async, spawn};
// Bind a loopback listener on a free port (no fixed port in the example).
$listener = stream_socket_server('tcp://127.0.0.1:0');
stream_set_blocking($listener, false);
$name = stream_socket_get_name($listener, false);
$port = (int) substr($name, strrpos($name, ':') + 1);
/** One request on a fresh connection; returns the raw response. */
function fetch(int $port, string $request, string $body = ''): string
{
$c = fsockopen('127.0.0.1', $port);
fwrite($c, $request . "Connection: close\r\n\r\n" . $body);
$raw = '';
while (($chunk = fread($c, 8192)) !== '' && $chunk !== false) {
$raw .= $chunk;
}
fclose($c);
return $raw;
}
$server = Server::onListener($listener)->acceptWait(0.02);
async(function () use ($server, $port) {
spawn(fn () => $server->serve(function (Request $req): Response {
return (new Response())->type('text/plain')->stream(function (Http\ChunkedWriter $w) {
foreach (['alpha', 'beta', 'gamma'] as $row) {
$w->write($row . "\n");
$w->flush(); // without flush() the writer batches up to 64 KiB
}
});
}));
$raw = fetch($port, "GET /rows HTTP/1.1\r\nHost: localhost\r\n");
[$head, $body] = explode("\r\n\r\n", $raw, 2);
foreach (explode("\r\n", $head) as $line) {
if (!str_starts_with($line, 'Date:') && !str_starts_with($line, 'Server:')) {
echo $line, "\n";
}
}
echo "-- wire body\n", str_replace("\r\n", '\r\n' . "\n", $body);
echo "-- HTTP/1.0 client: no chunking, the close ends the body\n";
$raw = fetch($port, "GET /rows HTTP/1.0\r\n");
[$head, $body] = explode("\r\n\r\n", $raw, 2);
echo strtok($head, "\r\n"), "\n", $body;
$server->stop();
});Output
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: close
-- wire body
6\r\n
alpha
\r\n
5\r\n
beta
\r\n
6\r\n
gamma
\r\n
0\r\n
\r\n
-- HTTP/1.0 client: no chunking, the close ends the body
HTTP/1.0 200 OK
alpha
beta
gammaSee also: Response
Methods
write() | Queues one chunk. |
flush() | Sends everything queued to the client now. |
end() | Writes the terminating zero-length chunk and flushes. |
bytesWritten() | The number of bytes handed to the socket so far, chunk framing included. |
isChunked() | Whether chunked framing is in use (false for an HTTP/1.0 client). |
write()#source
function write(string $s): void
Queues one chunk. An empty string is ignored, because a zero-length chunk would end the body.
Parameters
$sstring- Bytes to send.
flush()#source
function flush(): void
Sends everything queued to the client now. Without it the writer batches output (up to 64 KiB) before writing to the socket.
end()#source
function end(): void
Writes the terminating zero-length chunk and flushes. Idempotent. The server calls it for you after the closure returns, so a handler normally never needs to.
bytesWritten()#source
function bytesWritten(): int
The number of bytes handed to the socket so far, chunk framing included. Bytes still queued are not counted.
Returns
int โ The byte count.
isChunked()#source
function isChunked(): bool
Whether chunked framing is in use (false for an HTTP/1.0 client).
Returns
bool โ true for chunked framing.