Commit e010fee4 authored by Kenton Varda's avatar Kenton Varda

Don't log/throw exceptions if the service returned successfully without filling the response body.

Theory is that maybe it did this intentionally, e.g. because it really just wanted to disconnect and it already dealt with any errors. The client should notice the stream ending prematurely so it should be possible for the developer to see the problem from the client side.
parent 3fa95beb
......@@ -2131,6 +2131,52 @@ KJ_TEST("HttpServer threw exception after starting response") {
"foo", text);
}
class PartialResponseNoThrowService final: public HttpService {
// HttpService that sends a partial response then returns without throwing.
public:
kj::Promise<void> request(
HttpMethod method, kj::StringPtr url, const HttpHeaders& headers,
kj::AsyncInputStream& requestBody, Response& response) override {
return requestBody.readAllBytes()
.then([this,&response](kj::Array<byte>&&) -> kj::Promise<void> {
HttpHeaders headers(table);
auto body = response.send(200, "OK", headers, 32);
auto promise = body->write("foo", 3);
return promise.attach(kj::mv(body));
});
}
private:
kj::Maybe<kj::Exception> exception;
HttpHeaderTable table;
};
KJ_TEST("HttpServer failed to write complete response but didn't throw") {
auto PIPELINE_TESTS = pipelineTestCases();
kj::EventLoop eventLoop;
kj::WaitScope waitScope(eventLoop);
kj::TimerImpl timer(kj::origin<kj::TimePoint>());
auto pipe = kj::newTwoWayPipe();
HttpHeaderTable table;
PartialResponseNoThrowService service;
HttpServer server(timer, table, service);
auto listenTask = server.listenHttp(kj::mv(pipe.ends[0]));
// Do one request.
pipe.ends[1]->write(PIPELINE_TESTS[0].request.raw.begin(), PIPELINE_TESTS[0].request.raw.size())
.wait(waitScope);
auto text = pipe.ends[1]->readAllText().wait(waitScope);
KJ_EXPECT(text ==
"HTTP/1.1 200 OK\r\n"
"Content-Length: 32\r\n"
"\r\n"
"foo", text);
}
class SimpleInputStream final: public kj::AsyncInputStream {
// An InputStream that returns bytes out of a static string.
......
......@@ -1614,6 +1614,10 @@ public:
return !writeInProgress && inBody;
}
bool isBroken() {
return broken;
}
void writeHeaders(String content) {
// Writes some header content and begins a new entity body.
......@@ -4175,6 +4179,15 @@ public:
"ERROR: The HttpService did not generate a response."));
}
if (httpOutput.isBroken()) {
// We started a response but didn't finish it. But HttpService returns success? Perhaps
// it decided that it doesn't want to finish this response. We'll have to disconnect
// here. If the response body is not complete (e.g. Content-Length not reached), the
// client should notice. We don't want to log an error because this condition might be
// intentional on the service's part.
return false;
}
return httpOutput.flush().then(kj::mvCapture(body,
[this](kj::Own<kj::AsyncInputStream> body) -> kj::Promise<bool> {
if (httpInput.canReuse()) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment