Example #1
0
bool test_http1_1_keepalive() {
    Socket acceptSocket;
    SocketError socketErr;
    HttpServer server;
    HttpError serverError;
    std::mutex serverMutex;
    std::condition_variable serverCond;
    bool isShutdown = false;

    std::tie(acceptSocket, socketErr) = getAcceptingSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, 0));
    if (socketErr != SocketError::Ok) {
        testf("Failed to bind socket for accept with: %d", socketErr);
        return false;
    }

    uint16_t boundPort = acceptSocket.getLocalAddress().getPort();
    StreamSourceSocket streamSource(std::move(acceptSocket));

    server.addHandler("/index.html", [](HttpRequest& request, HttpResponse& response) {
        response.setStatus(200, "OK");
        response.addHeader("Content-Length", "11");

        HttpOutputStream* outputStream;
        std::tie(outputStream, std::ignore) = response.getOutputStream();
        outputStream->write("Hello World", 11);
        outputStream->close();
    });

    Async::runAsync([&streamSource, &server, &serverError, &serverMutex, &serverCond, &isShutdown] {
        HttpError err = server.start(&streamSource);

        std::unique_lock<std::mutex> lock(serverMutex);
        isShutdown = true;
        serverError = err;
        serverCond.notify_one();
    });

    std::vector<StringRef> requests = {
        "GET /index.html HTTP/1.1\r\n"
        "Connection: keep-alive\r\n"
        "Host: localhost\r\n"
        "\r\n"
        "GET /index.html HTTP/1.1\r\n"
        "Host: localhost\r\n"
        "\r\n"
        "GET /index.html HTTP/1.1\r\n"
        "Host: localhost\r\n"
        "Connection: close\r\n"
        "\r\n",

        "GET /index.html HTTP/1.1\r\n"
        "Host: localhost\r\n"
        "Connection: close\r\n"
        "\r\n"
        "GET /index.html HTTP/1.1\r\n"
        "Host: localhost\r\n"
        "\r\n",
    };
    std::vector<StringRef> expectedResponses = {
        "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World"
        "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World"
        "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World",

        "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World",
    };

    for (size_t i = 0; i < requests.size(); i++) {
        StringRef request = requests[i];
        StringRef expectedResponse = expectedResponses[i];

        char responseBuffer[1024];

        Socket requestSocket;
        std::tie(requestSocket, socketErr) = getConnectedSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, boundPort));
        if (socketErr != SocketError::Ok) {
            testf("Failed to connect to HTTP socket with: %d", socketErr);
            return false;
        }
        socketErr = requestSocket.write(request.data(), request.length());
        if (socketErr != SocketError::Ok) {
            testf("Failed to write to HTTP socket with: %d", socketErr);
            return false;
        }
        uint32_t totalBytesRead = 0;
        std::tie(totalBytesRead, socketErr) = readFully(&requestSocket, responseBuffer, sizeof(responseBuffer));
        if (socketErr != SocketError::Ok) {
            testf("Failed to read from HTTP socket with: %d", socketErr);
            return false;
        }
        if (StringRef(responseBuffer, totalBytesRead) != expectedResponse) {
            testf("Did not receive expected response. Got:\n%.*s", (size_t)totalBytesRead, responseBuffer);
            return false;
        }
    }

    server.shutdown();

    // Wait for server shutdown and check its error
    std::unique_lock<std::mutex> lock(serverMutex);
    serverCond.wait(lock, [&isShutdown] {return isShutdown; });

    if (serverError != HttpError::Ok) {
        testf("Failed to start HTTP server with: %d", serverError);
        return false;
    }

    return true;
}
Example #2
0
bool test_http1_1_chunked_request() {
    Socket acceptSocket;
    SocketError socketErr;
    HttpServer server;
    HttpError serverError;
    std::mutex serverMutex;
    std::condition_variable serverCond;
    bool isShutdown = false;

    bool gotExpectedPost = false;
    HttpError postError = HttpError::Ok;
    bool hadCorrectHeader = false;

    std::tie(acceptSocket, socketErr) = ::getAcceptingSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, 0));
    if (socketErr != SocketError::Ok) {
        testf("Failed to bind socket for accept with: %d", socketErr);
        return false;
    }

    uint16_t boundPort = acceptSocket.getLocalAddress().getPort();
    StreamSourceSocket streamSource(std::move(acceptSocket));

    server.addHandler("/formthingy", [&gotExpectedPost, &postError, &hadCorrectHeader](HttpRequest& request, HttpResponse& response) {
        char readBuffer[1024];
        HttpInputStream& httpInputStream = request.getInputStream();

        HttpError readErr;
        uint32_t readBytes;
        uint32_t totalRead = 0;
        do {
            std::tie(readBytes, readErr) = httpInputStream.read(readBuffer + totalRead, 3); // Intentionally small reads
            totalRead += readBytes;
        } while (readErr == HttpError::Ok);

        if (readErr != HttpError::Eof) {
            postError = readErr;
            return;
        }

        postError = httpInputStream.close();

        const StringRef postData = "Some form post data";
        if (totalRead != postData.length()) {
            gotExpectedPost = false;
        } else {
            gotExpectedPost = std::memcmp(postData.data(), readBuffer, postData.length()) == 0;
        }

        StringRef transferEncodingHeader;
        bool hasHeader;
        std::tie(transferEncodingHeader, hasHeader) = request.getHeader("Transfer-Encoding");
        if (!hasHeader) {
            hadCorrectHeader = false;
        } else {
            hadCorrectHeader = transferEncodingHeader.equals("chunked");
        }

        response.setStatus(200, "OK");
    });

    Async::runAsync([&streamSource, &server, &serverError, &serverMutex, &serverCond, &isShutdown] {
        HttpError err = server.start(&streamSource);

        std::unique_lock<std::mutex> lock(serverMutex);
        isShutdown = true;
        serverError = err;
        serverCond.notify_one();
    });

    StringRef request =
        "POST /formthingy HTTP/1.1\r\n"
        "Host: localhost\r\n"
        "Transfer-Encoding: chunked\r\n"
        "Connection: close\r\n"
        "\r\n"
        "5\r\n"
        "Some \r\n"
        "e\r\n"
        "form post data\r\n"
        "0\r\n"
        "\r\n";
    StringRef expectedResponse = "HTTP/1.1 200 OK\r\n\r\n";
    char responseBuffer[1024];

    Socket requestSocket;
    std::tie(requestSocket, socketErr) = getConnectedSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, boundPort));
    if (socketErr != SocketError::Ok) {
        testf("Failed to connect to HTTP socket with: %d", socketErr);
        return false;
    }
    socketErr = requestSocket.write(request.data(), request.length());
    if (socketErr != SocketError::Ok) {
        testf("Failed to write to HTTP socket with: %d", socketErr);
        return false;
    }
    uint32_t totalBytesRead = 0;
    std::tie(totalBytesRead, socketErr) = readFully(&requestSocket, responseBuffer, sizeof(responseBuffer));
    if (socketErr != SocketError::Ok) {
        testf("Failed to read from HTTP socket with: %d", socketErr);
        return false;
    }
    if (StringRef(responseBuffer, totalBytesRead) != expectedResponse) {
        testf("Did not receive expected response. Got:\n%.*s", totalBytesRead, responseBuffer);
        return false;
    }

    server.shutdown();

    // Wait for server shutdown and check its error
    std::unique_lock<std::mutex> lock(serverMutex);
    serverCond.wait(lock, [&isShutdown] {return isShutdown; });

    if (serverError != HttpError::Ok) {
        testf("Failed to start HTTP server with: %d", serverError);
        return false;
    }

    if (!gotExpectedPost) {
        testf("Did not read back expected post data");
        return false;
    }
    if (postError != HttpError::Ok) {
        testf("Received error when reading post data: %d", postError);
        return false;
    }
    if (!hadCorrectHeader) {
        testf("Http request did not have expected Transfer-Encoding header");
        return false;
    }

    return true;
}
Example #3
0
// Tests that a chunked header is automatically added, and chunked mode is used,
// if neither Transfer-Encoding or Content-Length are specified.
bool test_http1_1_auto_chunked_response() {
    Socket acceptSocket;
    SocketError socketErr;
    HttpServer server;
    HttpError serverError = HttpError::Ok;
    HttpError responseError = HttpError::Ok;
    std::mutex serverMutex;
    std::condition_variable serverCond;
    bool isShutdown = false;

    std::tie(acceptSocket, socketErr) = getAcceptingSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, 0));
    if (socketErr != SocketError::Ok) {
        testf("Failed to bind socket for accept with: %d", socketErr);
        return false;
    }

    uint16_t boundPort = acceptSocket.getLocalAddress().getPort();
    StreamSourceSocket streamSource(std::move(acceptSocket));

    server.addHandler("/index.html", [&responseError](HttpRequest& request, HttpResponse& response) {
        response.setStatus(200, "OK");

        HttpOutputStream* outputStream;
        std::tie(outputStream, responseError) = response.getOutputStream();
        if (responseError != HttpError::Ok) {
            return;
        }
        responseError = outputStream->write("0123456789", 10);
        if (responseError != HttpError::Ok) {
            return;
        }
        responseError = outputStream->write("abc", 3);
    });

    Async::runAsync([&streamSource, &server, &serverError, &serverMutex, &serverCond, &isShutdown] {
        HttpError err = server.start(&streamSource);

        std::unique_lock<std::mutex> lock(serverMutex);
        isShutdown = true;
        serverError = err;
        serverCond.notify_one();
    });

    StringRef request =
        "GET /index.html HTTP/1.1\r\n"
        "Host: localhost\r\n"
        "Connection: close\r\n"
        "\r\n";
    StringRef expectedResponse =
        "HTTP/1.1 200 OK\r\n"
        "Transfer-Encoding: chunked\r\n"
        "\r\n"
        "A\r\n"
        "0123456789\r\n"
        "3\r\n"
        "abc\r\n"
        "0\r\n"
        "\r\n";
    char responseBuffer[1024];

    Socket requestSocket;
    std::tie(requestSocket, socketErr) = getConnectedSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, boundPort));
    if (socketErr != SocketError::Ok) {
        testf("Failed to connect to HTTP socket with: %d", socketErr);
        return false;
    }
    socketErr = requestSocket.write(request.data(), request.length());
    if (socketErr != SocketError::Ok) {
        testf("Failed to write to HTTP socket with: %d", socketErr);
        return false;
    }
    uint32_t totalBytesRead = 0;
    std::tie(totalBytesRead, socketErr) = readFully(&requestSocket, responseBuffer, sizeof(responseBuffer));
    if (socketErr != SocketError::Ok) {
        testf("Failed to read from HTTP socket with: %d", socketErr);
        return false;
    }

    server.shutdown();

    // Wait for server shutdown and check its error
    std::unique_lock<std::mutex> lock(serverMutex);
    serverCond.wait(lock, [&isShutdown] {return isShutdown; });

    if (serverError != HttpError::Ok) {
        testf("Failed to start HTTP server with: %d", serverError);
        return false;
    }
    if (responseError != HttpError::Ok) {
        testf("Error when generating response: %d", responseError);
        return false;
    }
    if (StringRef(responseBuffer, totalBytesRead) != expectedResponse) {
        testf("Did not receive expected response. Got:\n%.*s", (size_t)totalBytesRead, responseBuffer);
        return false;
    }

    return true;
}