Skip to content

Commit

Permalink
Fix server to write warnings when not being able to send HTTP headers
Browse files Browse the repository at this point in the history
  • Loading branch information
thekid committed Jan 5, 2025
1 parent 65eb268 commit 430c8dd
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 3 deletions.
2 changes: 2 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ Web change log

## ?.?.? / ????-??-??

* Fixed server to write warnings when not being able to send HTTP headers
(@thekid)
* Added logic to prevent double-closing file when serving partial content
(@thekid)

Expand Down
7 changes: 4 additions & 3 deletions src/main/php/xp/web/srv/Output.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use peer\SocketException;
use web\io\{Buffered, WriteChunks, Output as Base};

/** @test web.unittest.server.OutputTest */
class Output extends Base {
private $socket, $version;

Expand Down Expand Up @@ -36,13 +37,13 @@ public function stream() {
* @return void
*/
public function begin($status, $message, $headers) {
$this->socket->write(sprintf("HTTP/%s %d %s\r\n", $this->version, $status, $message));
$this->write(sprintf("HTTP/%s %d %s\r\n", $this->version, $status, $message));
foreach ($headers as $name => $header) {
foreach ($header as $value) {
$this->socket->write($name.': '.$value."\r\n");
$this->write($name.': '.$value."\r\n");
}
}
$this->socket->write("\r\n");
$this->write("\r\n");
}

/**
Expand Down
93 changes: 93 additions & 0 deletions src/test/php/web/unittest/server/OutputTest.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php namespace web\unittest\server;

use peer\{Socket, SocketException};
use test\{Assert, Test, Values};
use web\io\{Buffered, WriteChunks};
use xp\web\srv\{Output, CannotWrite};

class OutputTest {

/**
* Returns a socket which can be written to and read from
*
* @return peer.Socket
*/
private function socket() {
return new class() extends Socket {
private $bytes= '';

public function __construct() { }

public function readBinary($maxLen= 4096) {
$chunk= substr($this->bytes, 0, $maxLen);
$this->bytes= substr($this->bytes, $maxLen);
return (string)$chunk;
}

public function write($bytes) {
$this->bytes.= $bytes;
}
};
}

/**
* Returns a socket which raises an error when being written to
*
* @param string $error
* @return peer.Socket
*/
private function throws($error) {
return new class($error) extends Socket {
private $error;

public function __construct($error) { $this->error= $error; }

public function write($bytes) {
throw new SocketException($this->error);
}
};
}

#[Test]
public function can_create() {
new Output($this->socket());
}

#[Test]
public function chunked_stream_for_http_1_1() {
Assert::instance(WriteChunks::class, (new Output($this->socket(), '1.1'))->stream());
}

#[Test]
public function buffered_stream_for_http_1_0() {
Assert::instance(Buffered::class, (new Output($this->socket(), '1.0'))->stream());
}

#[Test, Values(['1.0', '1.1'])]
public function begin($version) {
$socket= $this->socket();
(new Output($socket, $version))->begin(200, 'OK', ['Content-Length' => [0]]);
Assert::equals("HTTP/{$version} 200 OK\r\nContent-Length: 0\r\n\r\n", $socket->readBinary());
}

#[Test]
public function write() {
$socket= $this->socket();
(new Output($socket))->write('Test');
Assert::equals('Test', $socket->readBinary());
}

#[Test]
public function begin_error() {
Assert::throws(CannotWrite::class, function() {
(new Output($this->throws('Write failed')))->begin(200, 'OK', []);
});
}

#[Test]
public function write_error() {
Assert::throws(CannotWrite::class, function() {
(new Output($this->throws('Write failed')))->write('Test');
});
}
}

0 comments on commit 430c8dd

Please sign in to comment.