From b265e43ec9148f03936afb7b4385edce0e705387 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 2 Jul 2018 19:58:42 +0200 Subject: [PATCH] Add randomly chunked HTTP parsing stress test, fix bugs --- src/HttpParser.h | 43 +++++++++++++++++++++++++++++--- src/Tests.h | 64 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 98 insertions(+), 9 deletions(-) diff --git a/src/HttpParser.h b/src/HttpParser.h index 16ca6f2..8249dd7 100644 --- a/src/HttpParser.h +++ b/src/HttpParser.h @@ -58,6 +58,10 @@ struct HttpRequest { headers->valueLength = std::max(0, headers->valueLength - 9); } + if (cursor == nullptr) { + return 0; + } + return cursor - data; } @@ -98,7 +102,7 @@ struct HttpRequest { }; class HttpParser { -private: +public://private: std::string fallback; int remainingStreamingBytes = 0; @@ -134,6 +138,8 @@ public: int emittable = std::min(remainingStreamingBytes, length); dataHandler(user, std::string_view(data, emittable)); remainingStreamingBytes -= emittable; + + data += emittable; // denna var buggen? length -= emittable; ret += emittable; @@ -177,9 +183,39 @@ public: fallback.reserve(maxCopyDistance + 32); // padding should be same as libus fallback.append(data, maxCopyDistance); + // helst ska denna inte emitta någon data alls, vi gör det efteråt! if (int consumed = fenceAndConsumePostPadded(fallback.data(), fallback.length(), user, &req, requestHandler, dataHandler); consumed) { + + // I guess? + fallback.clear(); + data += consumed - had; length -= consumed - had; + + // ska vi inte tömma fallback här? + + + // exakt samma if-sats som ovan! + if (remainingStreamingBytes) { + // at this point we reset the timeout timer, we are streaming and we got a chunk + + if (remainingStreamingBytes >= length) { + dataHandler(user, std::string_view(data, length)); + remainingStreamingBytes -= length; + // no change to the socket here! we read all data in the buffer, return + return; + } else { + dataHandler(user, std::string_view(data, remainingStreamingBytes)); + + data += remainingStreamingBytes; + length -= remainingStreamingBytes; + + remainingStreamingBytes = 0; + + // okay we are done with that, let's parse some more + } + } + } else { if (fallback.length() == MAX_FALLBACK_SIZE) { // here we failed to parse any header in the 4kb we were given! @@ -191,6 +227,7 @@ public: } int consumed = fenceAndConsumePostPadded(data, length, user, &req, requestHandler, dataHandler); + data += consumed; length -= consumed; @@ -198,8 +235,8 @@ public: if (length < MAX_FALLBACK_SIZE) { fallback.append(data, length); } else { - // invalid http! - std::cout << "invalid http! fuck off!" << std::endl; + std::cout << "tail is invalid http!" << std::endl; + exit(-1); } } diff --git a/src/Tests.h b/src/Tests.h index 16ebfe0..eaf956c 100644 --- a/src/Tests.h +++ b/src/Tests.h @@ -5,20 +5,72 @@ #include +// todo: random test of chunked http parsing of randomly generated requests void testHttpParserPerformance() { - char data[] = "GET /hello.htm HTTP/1.1\r\n" + char headers[] = "GET /hello.htm HTTP/1.1\r\n" "User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)\r\n" "Host: www.tutorialspoint.com\r\n" "Accept-Language: en-us\r\n" "Accept-Encoding: gzip, deflate\r\n" - "Connection: Keep-Alive\r\n\r\n "; + "Connection: Keep-Alive\r\n" + "Content-length: 1048576\r\n\r\n"; + const int requestLength = sizeof(headers) - 1 + 1048576; + char *request = (char *) malloc(requestLength + 32); + memset(request, 0, requestLength); + memcpy(request, headers, sizeof(headers) - 1); + + char *data = (char *) malloc(requestLength * 10); + int length = requestLength * 10; + //int currentOffset = 0; + + + int maxChunkSize = 10000; + char *paddedBuffer = (char *) malloc(maxChunkSize + 32); + + // dela upp dessa 10 i 5 segment HttpParser httpParser; - int validRequests = 0; + size_t dataBytes = 0; - auto start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < 10; i++) { + memcpy(data + requestLength * i, request, requestLength); + } + + for (int j = 0; j < 1000; j++) { + for (int currentOffset = 0; currentOffset != length; ) { + + int chunkSize = rand() % 10000; + if (currentOffset + chunkSize > length) { + chunkSize = length - currentOffset; + } + + // kpiera skiten + memcpy(paddedBuffer, data + currentOffset, chunkSize); + + httpParser.consumePostPadded(/*data + currentOffset*/ paddedBuffer, chunkSize, nullptr, [&validRequests](void *user, HttpRequest *req) { + + validRequests++; + std::cout << "validRequests: " << validRequests << std::endl; + + + }, [&dataBytes](void *, std::string_view data) { + //std::cout << "data bytes: " << data.length() << std::endl; + dataBytes += data.length(); + }, [](void *) { + + std::cout << "Error!" << std::endl; + return; + }); + + currentOffset += chunkSize; + } + } + + std::cout << "Data bytes: " << dataBytes << std::endl; + + /*auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < 10000000; i++) { httpParser.consumePostPadded(data, sizeof(data) - 2, nullptr, [&validRequests](void *user, HttpRequest *req) { validRequests++; @@ -28,9 +80,9 @@ void testHttpParserPerformance() { }); } - auto stop = std::chrono::high_resolution_clock::now(); + auto stop = std::chrono::high_resolution_clock::now();*/ - std::cout << "Parsed " << validRequests << " in " << std::chrono::duration_cast(stop - start).count() << "ms" << std::endl; + //std::cout << "Parsed " << validRequests << " in " << std::chrono::duration_cast(stop - start).count() << "ms" << std::endl; } #endif // TESTS_H