#include #include #include "http_head.h" using namespace roro::release; void setUp() {} void tearDown() {} void test_a_head_in_pieces() { std::string response = "HTTP/1.1 200 OK\r\nContent-Type: application/json;charset=utf-8\r\nTransfer-Encoding: chunked\r\n" "X-Other: a: b\r\n\r\n5\r\nhello\r\n0\r\n\r\n"; for (size_t piece : {size_t(1), size_t(9), response.size()}) { HttpHeadParser p; size_t used = 0; for (size_t at = 0; at < response.size() && !p.complete(); at += piece) used += p.feed(response.data() + at, std::min(piece, response.size() - at)); TEST_ASSERT_TRUE(p.complete()); TEST_ASSERT_FALSE(p.failed()); TEST_ASSERT_EQUAL_INT(200, p.head().status); TEST_ASSERT_TRUE(p.head().chunked); TEST_ASSERT_EQUAL_STRING("application/json;charset=utf-8", p.head().contentType.c_str()); TEST_ASSERT_EQUAL(std::string("HTTP/1.1 200 OK\r\nContent-Type: application/json;charset=utf-8\r\nTransfer-Encoding: chunked\r\nX-Other: a: b\r\n\r\n").size(), used); } } void test_a_download_head() { std::string head = "HTTP/1.1 200 OK\r\ncontent-length: 1885712\r\nCONTENT-DISPOSITION: inline; filename=a.ota\r\n\r\n\x01\x02"; HttpHeadParser p; size_t used = p.feed(head.data(), head.size()); TEST_ASSERT_EQUAL_size_t(head.size() - 2, used); // the body isn't touched TEST_ASSERT_EQUAL_INT(200, p.head().status); TEST_ASSERT_EQUAL_INT(1885712, p.head().contentLength); TEST_ASSERT_FALSE(p.head().chunked); std::string redirect = "HTTP/1.1 302 Found\r\nLocation: https://elsewhere.example/x\r\n\r\n"; HttpHeadParser r; r.feed(redirect.data(), redirect.size()); TEST_ASSERT_EQUAL_INT(302, r.head().status); TEST_ASSERT_EQUAL_STRING("https://elsewhere.example/x", r.head().location.c_str()); } void test_a_head_that_is_not_http() { HttpHeadParser p; std::string junk = "\x16\x03\x01 not http at all\r\n\r\n"; p.feed(junk.data(), junk.size()); TEST_ASSERT_TRUE(p.failed()); HttpHeadParser big; std::string endless = "HTTP/1.1 200 OK\r\n" + std::string(5000, 'x'); big.feed(endless.data(), endless.size()); TEST_ASSERT_TRUE(big.failed()); HttpHeadParser odd; std::string status = "HTTP/1.1 999 What\r\n\r\n"; odd.feed(status.data(), status.size()); TEST_ASSERT_TRUE(odd.failed()); } static std::string unchunk(const std::string& in, size_t piece, bool* done = nullptr, bool* failed = nullptr) { ChunkedDecoder d; std::string out; for (size_t at = 0; at < in.size(); at += piece) { size_t n = std::min(piece, in.size() - at); std::string buf(n, '\0'); size_t w = d.decode(reinterpret_cast(in.data() + at), n, reinterpret_cast(&buf[0])); out.append(buf, 0, w); } if (done) *done = d.done(); if (failed) *failed = d.failed(); return out; } void test_chunked_bodies() { std::string body = "5\r\nhello\r\n6;ext=1\r\n, worl\r\n1\r\nd\r\n0\r\nTrailer: x\r\n\r\n"; for (size_t piece : {size_t(1), size_t(2), size_t(5), body.size()}) { bool done, failed; TEST_ASSERT_EQUAL_STRING("hello, world", unchunk(body, piece, &done, &failed).c_str()); TEST_ASSERT_TRUE(done); TEST_ASSERT_FALSE(failed); } bool done; TEST_ASSERT_EQUAL_STRING("abc12345", unchunk("3\r\nabc\r\nA\r\n12345", 4, &done).c_str()); // cut short: what came is given TEST_ASSERT_FALSE(done); std::string hex = "1F\r\n" + std::string(31, 'x') + "\r\n0\r\n\r\n"; TEST_ASSERT_EQUAL_size_t(31, unchunk(hex, 3).size()); bool failed; unchunk("zz\r\n", 1, nullptr, &failed); TEST_ASSERT_TRUE(failed); unchunk("3\r\nabcXX", 1, nullptr, &failed); // no CRLF after the data TEST_ASSERT_TRUE(failed); unchunk("FFFFFFFFFFFFFFFFFFFF\r\n", 1, nullptr, &failed); TEST_ASSERT_TRUE(failed); } void test_urls() { Url u = parseUrl("https://git.twis.la/api/v1/repos/x?limit=10"); TEST_ASSERT_TRUE(u.ok); TEST_ASSERT_TRUE(u.https); TEST_ASSERT_EQUAL_STRING("git.twis.la", u.host.c_str()); TEST_ASSERT_EQUAL_STRING("/api/v1/repos/x?limit=10", u.path.c_str()); TEST_ASSERT_EQUAL_INT(443, u.port); u = parseUrl("http://Example.COM:8080"); TEST_ASSERT_TRUE(u.ok); TEST_ASSERT_EQUAL_STRING("example.com", u.host.c_str()); TEST_ASSERT_EQUAL_STRING("/", u.path.c_str()); TEST_ASSERT_EQUAL_INT(8080, u.port); for (const char* bad : {"", "git.twis.la/x", "ftp://a/b", "https:///x", "https://u:p@host/x", "https://host:0/x", "https://ho st/x", "https://host/a b", "https://host:99999/x", "https://ho_st/x"}) TEST_ASSERT_FALSE_MESSAGE(parseUrl(bad).ok, bad); } int main() { UNITY_BEGIN(); RUN_TEST(test_a_head_in_pieces); RUN_TEST(test_a_download_head); RUN_TEST(test_a_head_that_is_not_http); RUN_TEST(test_chunked_bodies); RUN_TEST(test_urls); return UNITY_END(); }