Fixed flushing of empty responses.
This commit is contained in:
parent
18851ac786
commit
22e8fa9b70
|
@ -56,7 +56,13 @@ unittest {
|
||||||
HttpRequestHandler handler = HttpRequestHandler.of(
|
HttpRequestHandler handler = HttpRequestHandler.of(
|
||||||
(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
||||||
response.status = HttpStatus.OK;
|
response.status = HttpStatus.OK;
|
||||||
response.writeBodyString("Testing");
|
|
||||||
});
|
});
|
||||||
testHttp1Transport(new TaskPoolHttp1Transport(handler));
|
testHttp1Transport(new TaskPoolHttp1Transport(handler));
|
||||||
|
|
||||||
|
HttpRequestHandler handler2 = HttpRequestHandler.of(
|
||||||
|
(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
||||||
|
response.status = HttpStatus.OK;
|
||||||
|
response.writeBodyString("Testing");
|
||||||
|
});
|
||||||
|
testHttp1Transport(new TaskPoolHttp1Transport(handler2));
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ version(unittest) {
|
||||||
|
|
||||||
string httpResponseContent = cast(string) buffer[0 .. totalBytesReceived];
|
string httpResponseContent = cast(string) buffer[0 .. totalBytesReceived];
|
||||||
string[] parts = httpResponseContent.split("\r\n\r\n");
|
string[] parts = httpResponseContent.split("\r\n\r\n");
|
||||||
assert(parts.length > 0, "HTTP 1.1 response is missing required status and headers section.");
|
assert(parts.length > 0, "HTTP 1.1 response is missing required status and headers section:\n\n" ~ httpResponseContent);
|
||||||
string[] headerLines = parts[0].split("\r\n");
|
string[] headerLines = parts[0].split("\r\n");
|
||||||
assert(headerLines.length > 0, "HTTP 1.1 response is missing required status line.");
|
assert(headerLines.length > 0, "HTTP 1.1 response is missing required status line.");
|
||||||
string statusLine = headerLines[0];
|
string statusLine = headerLines[0];
|
||||||
|
@ -131,13 +131,22 @@ void handleClient(Socket clientSocket, HttpRequestHandler requestHandler) {
|
||||||
scope ServerHttpRequest request = result.request;
|
scope ServerHttpRequest request = result.request;
|
||||||
scope ServerHttpResponse response;
|
scope ServerHttpResponse response;
|
||||||
SocketOutputStream* outputStream = new SocketOutputStream(clientSocket);
|
SocketOutputStream* outputStream = new SocketOutputStream(clientSocket);
|
||||||
response.outputStream = outputStreamObjectFor(HttpResponseOutputStream!(SocketOutputStream*)(
|
HttpResponseOutputStream!(SocketOutputStream*) responseOutputStream
|
||||||
outputStream,
|
= HttpResponseOutputStream!(SocketOutputStream*)(
|
||||||
&response
|
outputStream,
|
||||||
));
|
&response
|
||||||
|
);
|
||||||
|
response.outputStream = outputStreamObjectFor(&responseOutputStream);
|
||||||
try {
|
try {
|
||||||
requestHandler.handle(request, response);
|
requestHandler.handle(request, response);
|
||||||
debugF!"%s %s -> %d %s"(request.method, request.url, response.status.code, response.status.text);
|
debugF!"%s %s -> %d %s"(request.method, request.url, response.status.code, response.status.text);
|
||||||
|
// If the response's headers aren't flushed yet, write them now.
|
||||||
|
if (!responseOutputStream.areHeadersFlushed()) {
|
||||||
|
auto writeResult = responseOutputStream.writeHeaders();
|
||||||
|
if (writeResult.hasError) {
|
||||||
|
errorF!"Failed to write response headers: %s"(writeResult.error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
error("Exception thrown while handling request.", e);
|
error("Exception thrown while handling request.", e);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
|
|
@ -23,6 +23,15 @@ struct HttpResponseOutputStream(S) if (isByteOutputStream!S) {
|
||||||
this.response = response;
|
this.response = response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the HTTP response headers have been flushed to the
|
||||||
|
* underlying output stream.
|
||||||
|
* Returns: `true` if the headers have been flushed, `false` otherwise.
|
||||||
|
*/
|
||||||
|
bool areHeadersFlushed() const {
|
||||||
|
return headersFlushed;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the given data to the stream. If the referenced HTTP response's
|
* Writes the given data to the stream. If the referenced HTTP response's
|
||||||
* status and headers haven't yet been written, they will be written first.
|
* status and headers haven't yet been written, they will be written first.
|
||||||
|
@ -37,7 +46,6 @@ struct HttpResponseOutputStream(S) if (isByteOutputStream!S) {
|
||||||
auto result = writeHeaders();
|
auto result = writeHeaders();
|
||||||
if (result.hasError) return result;
|
if (result.hasError) return result;
|
||||||
bytesWritten += result.count;
|
bytesWritten += result.count;
|
||||||
headersFlushed = true;
|
|
||||||
}
|
}
|
||||||
auto result = outputStream.writeToStream(buffer);
|
auto result = outputStream.writeToStream(buffer);
|
||||||
if (result.hasError) return result;
|
if (result.hasError) return result;
|
||||||
|
@ -50,6 +58,10 @@ struct HttpResponseOutputStream(S) if (isByteOutputStream!S) {
|
||||||
* Returns: The stream result of writing.
|
* Returns: The stream result of writing.
|
||||||
*/
|
*/
|
||||||
StreamResult writeHeaders() {
|
StreamResult writeHeaders() {
|
||||||
|
if (headersFlushed) {
|
||||||
|
return StreamResult(0); // No need to write again.
|
||||||
|
}
|
||||||
|
headersFlushed = true;
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
char[6] statusCodeBuffer; // Normal HTTP codes are 3 digits, but this leaves room for extensions.
|
char[6] statusCodeBuffer; // Normal HTTP codes are 3 digits, but this leaves room for extensions.
|
||||||
writeUIntToBuffer(response.status.code, statusCodeBuffer, idx);
|
writeUIntToBuffer(response.status.code, statusCodeBuffer, idx);
|
||||||
|
@ -111,13 +123,15 @@ unittest {
|
||||||
resp.status = HttpStatus.OK;
|
resp.status = HttpStatus.OK;
|
||||||
resp.headers.add("Content-Type", "text/plain");
|
resp.headers.add("Content-Type", "text/plain");
|
||||||
auto httpOut = HttpResponseOutputStream!(ArrayOutputStream!ubyte*)(&os, &resp);
|
auto httpOut = HttpResponseOutputStream!(ArrayOutputStream!ubyte*)(&os, &resp);
|
||||||
resp.outputStream = outputStreamObjectFor(httpOut);
|
resp.outputStream = outputStreamObjectFor(&httpOut);
|
||||||
|
assert(httpOut.areHeadersFlushed() == false);
|
||||||
StreamResult r = resp.outputStream.writeToStream(cast(ubyte[]) "Hello world!");
|
StreamResult r = resp.outputStream.writeToStream(cast(ubyte[]) "Hello world!");
|
||||||
const expectedOutput =
|
const expectedOutput =
|
||||||
"HTTP/1.1 200 OK\r\n" ~
|
"HTTP/1.1 200 OK\r\n" ~
|
||||||
"Content-Type: text/plain\r\n" ~
|
"Content-Type: text/plain\r\n" ~
|
||||||
"\r\n" ~
|
"\r\n" ~
|
||||||
"Hello world!";
|
"Hello world!";
|
||||||
|
assert(httpOut.areHeadersFlushed() == true);
|
||||||
assert(os.toArray() == expectedOutput);
|
assert(os.toArray() == expectedOutput);
|
||||||
assert(r.hasCount);
|
assert(r.hasCount);
|
||||||
assert(r.count == os.toArray().length);
|
assert(r.count == os.toArray().length);
|
||||||
|
|
Loading…
Reference in New Issue