Unverified Commit c94a88e4 authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #688 from capnproto/harris/http-chunked-body-fixes

Chunked body fixes in kj-http
parents 50fb6e51 79a3ff0d
......@@ -376,7 +376,7 @@ private:
// Write the first piece.
auto promise = output.write(writeBuffer.begin(), writeBuffer.size());
// Write full pieces as a singcle gather-write.
// Write full pieces as a single gather-write.
if (i > 0) {
auto more = morePieces.slice(0, i);
promise = promise.then([&output,more]() { return output.write(more); });
......
......@@ -903,6 +903,86 @@ KJ_TEST("HttpClient canceled write") {
KJ_EXPECT(text == "POST / HTTP/1.1\r\nContent-Length: 4096\r\n\r\n", text);
}
KJ_TEST("HttpClient chunked body gather-write") {
kj::EventLoop eventLoop;
kj::WaitScope waitScope(eventLoop);
auto pipe = kj::newTwoWayPipe();
auto serverPromise = pipe.ends[1]->readAllText();
{
HttpHeaderTable table;
auto client = newHttpClient(table, *pipe.ends[0]);
auto req = client->request(HttpMethod::POST, "/", HttpHeaders(table));
kj::ArrayPtr<const byte> bodyParts[] = {
{ 'f','o','o' }, { ' ' }, { 'b','a','r' }, { ' ' }, { 'b','a','z' }
};
req.body->write(kj::arrayPtr(bodyParts, kj::size(bodyParts))).wait(waitScope);
req.body = nullptr;
// Wait for a response so the client has a chance to end the request body with a 0-chunk.
kj::StringPtr responseText = "HTTP/1.1 204 No Content\r\n\r\n";
pipe.ends[1]->write(responseText.begin(), responseText.size()).wait(waitScope);
auto response = req.response.wait(waitScope);
}
pipe.ends[0]->shutdownWrite();
auto text = serverPromise.wait(waitScope);
KJ_EXPECT(text == "POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
"b\r\nfoo bar baz\r\n0\r\n\r\n", text);
}
KJ_TEST("HttpClient chunked body pump from fixed length stream") {
class FixedBodyStream final: public kj::AsyncInputStream {
Promise<size_t> tryRead(void* buffer, size_t minBytes, size_t maxBytes) override {
auto n = kj::min(body.size(), maxBytes);
n = kj::max(n, minBytes);
n = kj::min(n, body.size());
memcpy(buffer, body.begin(), n);
body = body.slice(n);
return n;
}
Maybe<uint64_t> tryGetLength() override { return body.size(); }
kj::StringPtr body = "foo bar baz";
};
kj::EventLoop eventLoop;
kj::WaitScope waitScope(eventLoop);
auto pipe = kj::newTwoWayPipe();
auto serverPromise = pipe.ends[1]->readAllText();
{
HttpHeaderTable table;
auto client = newHttpClient(table, *pipe.ends[0]);
auto req = client->request(HttpMethod::POST, "/", HttpHeaders(table));
FixedBodyStream bodyStream;
bodyStream.pumpTo(*req.body).wait(waitScope);
req.body = nullptr;
// Wait for a response so the client has a chance to end the request body with a 0-chunk.
kj::StringPtr responseText = "HTTP/1.1 204 No Content\r\n\r\n";
pipe.ends[1]->write(responseText.begin(), responseText.size()).wait(waitScope);
auto response = req.response.wait(waitScope);
}
pipe.ends[0]->shutdownWrite();
auto text = serverPromise.wait(waitScope);
KJ_EXPECT(text == "POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
"b\r\nfoo bar baz\r\n0\r\n\r\n", text);
}
KJ_TEST("HttpServer requests") {
HttpResponseTestCase RESPONSE = {
"HTTP/1.1 200 OK\r\n"
......
......@@ -1879,8 +1879,8 @@ public:
if (size == 0) return kj::READY_NOW; // can't encode zero-size chunk since it indicates EOF.
auto header = kj::str(size, "\r\n");
auto partsBuilder = kj::heapArrayBuilder<ArrayPtr<const byte>>(pieces.size());
auto header = kj::str(kj::hex(size), "\r\n");
auto partsBuilder = kj::heapArrayBuilder<ArrayPtr<const byte>>(pieces.size() + 2);
partsBuilder.add(header.asBytes());
for (auto& piece: pieces) {
partsBuilder.add(piece);
......@@ -1897,7 +1897,7 @@ public:
// Hey, we know exactly how large the input is, so we can write just one chunk.
uint64_t length = kj::min(amount, *l);
inner.writeBodyData(kj::str(length, "\r\n"));
inner.writeBodyData(kj::str(kj::hex(length), "\r\n"));
return inner.pumpBodyFrom(input, length)
.then([this,length](uint64_t actual) {
if (actual < length) {
......
......@@ -667,7 +667,7 @@ struct HttpServerSettings {
// completes, we'll let the connection stay open to handle more requests.
};
class HttpServer: private kj::TaskSet::ErrorHandler {
class HttpServer final: private kj::TaskSet::ErrorHandler {
// Class which listens for requests on ports or connections and sends them to an HttpService.
public:
......
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