summaryrefslogtreecommitdiffstats
path: root/vespalib/src/tests/portal/http_request/http_request_test.cpp
blob: 6e1527efa4b2fc610696d61a3d6c8299093d4df1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/portal/http_request.h>

using namespace vespalib;
using namespace vespalib::portal;

vespalib::string simple_req("GET /my/path HTTP/1.1\r\n"
                            "Host: my.host.com:80\r\n"
                            "CustomHeader: CustomValue\r\n"
                            "\r\n123456789");
size_t simple_req_padding = 9;
size_t simple_req_size = (simple_req.size() - simple_req_padding);

void verify_simple_req(const HttpRequest &req) {
    EXPECT_TRUE(!req.need_more_data());
    EXPECT_TRUE(req.valid());
    EXPECT_TRUE(req.is_get());
    EXPECT_EQUAL(req.get_uri(), "/my/path");
    EXPECT_EQUAL(req.get_header("host"), "my.host.com:80");
    EXPECT_EQUAL(req.get_header("customheader"), "CustomValue");
    EXPECT_EQUAL(req.get_header("non-existing-header"), "");
}

HttpRequest make_request(const vespalib::string &req) {
    HttpRequest result;
    ASSERT_EQUAL(result.handle_data(req.data(), req.size()), req.size());
    ASSERT_TRUE(result.valid());
    return result;
}

void verify_invalid_request(const vespalib::string &req) {
    HttpRequest result;
    EXPECT_EQUAL(result.handle_data(req.data(), req.size()), req.size());
    EXPECT_TRUE(!result.need_more_data());
    EXPECT_TRUE(!result.valid());
}

TEST("require that request can be parsed in one go") {
    HttpRequest req;
    EXPECT_EQUAL(req.handle_data(simple_req.data(), simple_req_size), simple_req_size);
    TEST_DO(verify_simple_req(req));
}

TEST("require that trailing data is not consumed") {
    HttpRequest req;
    EXPECT_EQUAL(req.handle_data(simple_req.data(), simple_req.size()), simple_req_size);
    TEST_DO(verify_simple_req(req));
}

TEST("require that request can be parsed incrementally") {
    HttpRequest req;
    size_t chunk = 7;
    size_t done = 0;
    while (done < simple_req_size) {
        size_t expect = std::min(simple_req_size - done, chunk);
        EXPECT_EQUAL(req.handle_data(simple_req.data() + done, chunk), expect);
        done += expect;
    }
    EXPECT_EQUAL(done, simple_req_size);
    TEST_DO(verify_simple_req(req));
}

TEST("require that header continuation is replaced by single space") {
    auto req = make_request("GET /my/path HTTP/1.1\r\n"
                            "test: one\r\n"
                            " two\r\n"
                            "\tthree\r\n"
                            "\r\n");
    EXPECT_EQUAL(req.get_header("test"), "one two three");
}

TEST("require that duplicate headers are combined as list") {
    auto req = make_request("GET /my/path HTTP/1.1\r\n"
                            "test: one\r\n"
                            "test: two\r\n"
                            "test: three\r\n"
                            "\r\n");
    EXPECT_EQUAL(req.get_header("test"), "one,two,three");
}

TEST("require that leading and trailing whitespaces are stripped") {
    auto req = make_request("GET /my/path HTTP/1.1\r\n"
                            "test:   one  \r\n"
                            "        , two  \r\n"
                            "test:   three   \r\n"
                            "\r\n");
    EXPECT_EQUAL(req.get_header("test"), "one , two,three");
}

TEST("require that non-GET requests are detected") {
    auto req = make_request("POST /my/path HTTP/1.1\r\n"
                            "\r\n");
    EXPECT_TRUE(!req.is_get());
}

TEST("require that request line must contain all relevant parts") {
    TEST_DO(verify_invalid_request("/my/path HTTP/1.1\r\n"));
    TEST_DO(verify_invalid_request("GET HTTP/1.1\r\n"));
    TEST_DO(verify_invalid_request("GET /my/path\r\n"));
}

TEST("require that first header line cannot be a continuation") {
    TEST_DO(verify_invalid_request("GET /my/path HTTP/1.1\r\n"
                                   " two\r\n"));
}

TEST("require that header name is not allowed to be empty") {
    TEST_DO(verify_invalid_request("GET /my/path HTTP/1.1\r\n"
                                   ": value\r\n"));
}

TEST("require that header line must contain separator") {
    TEST_DO(verify_invalid_request("GET /my/path HTTP/1.1\r\n"
                                   "ok-header: ok-value\r\n"
                                   "missing separator\r\n"));
}

TEST_MAIN() { TEST_RUN_ALL(); }