Skip to content

Commit cac9e19

Browse files
committed
Add tests for untested HTTP methods and request/response getters
- Add HEAD, OPTIONS, TRACE method handlers to complete_test_resource - Add integration tests for HEAD, OPTIONS, TRACE HTTP methods - Add request_info_resource and test for get_requestor(), get_requestor_port(), get_version() - Add content_limit_suite to test content_too_large() with content_size_limit - Add unregister_then_404 test for webserver::unregister_resource() - Create http_response unit test file with tests for response code, headers, footers, and cookies These tests increase coverage for previously untested code paths in webserver.cpp, http_request.cpp, and http_response.cpp.
1 parent 77b089a commit cac9e19

File tree

3 files changed

+320
-1
lines changed

3 files changed

+320
-1
lines changed

test/Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ LDADD += -lcurl
2626

2727
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/
2828
METASOURCES = AUTO
29-
check_PROGRAMS = basic file_upload http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred http_resource
29+
check_PROGRAMS = basic file_upload http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred http_resource http_response
3030

3131
MOSTLYCLEANFILES = *.gcda *.gcno *.gcov
3232

@@ -42,6 +42,7 @@ string_utilities_SOURCES = unit/string_utilities_test.cpp
4242
http_endpoint_SOURCES = unit/http_endpoint_test.cpp
4343
nodelay_SOURCES = integ/nodelay.cpp
4444
http_resource_SOURCES = unit/http_resource_test.cpp
45+
http_response_SOURCES = unit/http_response_test.cpp
4546

4647
noinst_HEADERS = littletest.hpp
4748
AM_CXXFLAGS += -Wall -fPIC -Wno-overloaded-virtual

test/integ/basic.cpp

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,20 @@ class complete_test_resource : public http_resource {
209209
shared_ptr<http_response> render_PATCH(const http_request&) {
210210
return std::make_shared<string_response>("OK", 200, "text/plain");
211211
}
212+
213+
shared_ptr<http_response> render_HEAD(const http_request&) {
214+
return std::make_shared<string_response>("", 200, "text/plain");
215+
}
216+
217+
shared_ptr<http_response> render_OPTIONS(const http_request&) {
218+
auto resp = std::make_shared<string_response>("", 200, "text/plain");
219+
resp->with_header("Allow", "GET, POST, PUT, DELETE, HEAD, OPTIONS");
220+
return resp;
221+
}
222+
223+
shared_ptr<http_response> render_TRACE(const http_request&) {
224+
return std::make_shared<string_response>("TRACE OK", 200, "message/http");
225+
}
212226
};
213227

214228
class only_render_resource : public http_resource {
@@ -343,6 +357,25 @@ class print_response_resource : public http_resource {
343357
stringstream* ss;
344358
};
345359

360+
class request_info_resource : public http_resource {
361+
public:
362+
shared_ptr<http_response> render_GET(const http_request& req) {
363+
stringstream ss;
364+
ss << "requestor=" << req.get_requestor()
365+
<< "&port=" << req.get_requestor_port()
366+
<< "&version=" << req.get_version();
367+
return std::make_shared<string_response>(ss.str(), 200, "text/plain");
368+
}
369+
};
370+
371+
class content_limit_resource : public http_resource {
372+
public:
373+
shared_ptr<http_response> render_POST(const http_request& req) {
374+
return std::make_shared<string_response>(
375+
req.content_too_large() ? "TOO_LARGE" : "OK", 200, "text/plain");
376+
}
377+
};
378+
346379
#ifdef HTTPSERVER_PORT
347380
#define PORT HTTPSERVER_PORT
348381
#else
@@ -353,6 +386,7 @@ class print_response_resource : public http_resource {
353386
#define STR(p) STR2(p)
354387
#define PORT_STRING STR(PORT)
355388

389+
356390
LT_BEGIN_SUITE(basic_suite)
357391
std::unique_ptr<webserver> ws;
358392

@@ -1627,6 +1661,66 @@ LT_BEGIN_AUTO_TEST(basic_suite, method_not_allowed_header)
16271661
curl_easy_cleanup(curl);
16281662
LT_END_AUTO_TEST(method_not_allowed_header)
16291663

1664+
LT_BEGIN_AUTO_TEST(basic_suite, request_info_getters)
1665+
request_info_resource resource;
1666+
LT_ASSERT_EQ(true, ws->register_resource("request_info", &resource));
1667+
curl_global_init(CURL_GLOBAL_ALL);
1668+
string s;
1669+
CURL *curl = curl_easy_init();
1670+
CURLcode res;
1671+
curl_easy_setopt(curl, CURLOPT_URL, "localhost:" PORT_STRING "/request_info");
1672+
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
1673+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
1674+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
1675+
res = curl_easy_perform(curl);
1676+
LT_ASSERT_EQ(res, 0);
1677+
LT_CHECK_NEQ(s.find("127.0.0.1"), string::npos);
1678+
LT_CHECK_NEQ(s.find("HTTP/1.1"), string::npos);
1679+
LT_CHECK_NEQ(s.find("port="), string::npos);
1680+
curl_easy_cleanup(curl);
1681+
LT_END_AUTO_TEST(request_info_getters)
1682+
1683+
LT_BEGIN_AUTO_TEST(basic_suite, unregister_then_404)
1684+
simple_resource res;
1685+
LT_ASSERT_EQ(true, ws->register_resource("temp", &res));
1686+
curl_global_init(CURL_GLOBAL_ALL);
1687+
1688+
{
1689+
string s;
1690+
CURL *curl = curl_easy_init();
1691+
CURLcode result;
1692+
curl_easy_setopt(curl, CURLOPT_URL, "localhost:" PORT_STRING "/temp");
1693+
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
1694+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
1695+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
1696+
result = curl_easy_perform(curl);
1697+
LT_ASSERT_EQ(result, 0);
1698+
int64_t http_code = 0;
1699+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
1700+
LT_CHECK_EQ(http_code, 200);
1701+
LT_CHECK_EQ(s, "OK");
1702+
curl_easy_cleanup(curl);
1703+
}
1704+
1705+
ws->unregister_resource("temp");
1706+
1707+
{
1708+
string s;
1709+
CURL *curl = curl_easy_init();
1710+
CURLcode result;
1711+
curl_easy_setopt(curl, CURLOPT_URL, "localhost:" PORT_STRING "/temp");
1712+
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
1713+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
1714+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
1715+
result = curl_easy_perform(curl);
1716+
LT_ASSERT_EQ(result, 0);
1717+
int64_t http_code = 0;
1718+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
1719+
LT_CHECK_EQ(http_code, 404);
1720+
curl_easy_cleanup(curl);
1721+
}
1722+
LT_END_AUTO_TEST(unregister_then_404)
1723+
16301724
LT_BEGIN_AUTO_TEST(basic_suite, thread_safety)
16311725
simple_resource resource;
16321726

@@ -1665,6 +1759,130 @@ LT_BEGIN_AUTO_TEST(basic_suite, thread_safety)
16651759
LT_CHECK_EQ(1, 1);
16661760
LT_END_AUTO_TEST(thread_safety)
16671761

1762+
LT_BEGIN_AUTO_TEST(basic_suite, head_request)
1763+
complete_test_resource resource;
1764+
LT_ASSERT_EQ(true, ws->register_resource("base", &resource));
1765+
curl_global_init(CURL_GLOBAL_ALL);
1766+
string s;
1767+
CURL *curl = curl_easy_init();
1768+
CURLcode res;
1769+
curl_easy_setopt(curl, CURLOPT_URL, "localhost:" PORT_STRING "/base");
1770+
curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
1771+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
1772+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
1773+
res = curl_easy_perform(curl);
1774+
LT_ASSERT_EQ(res, 0);
1775+
int64_t http_code = 0;
1776+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
1777+
LT_CHECK_EQ(http_code, 200);
1778+
LT_CHECK_EQ(s, "");
1779+
curl_easy_cleanup(curl);
1780+
LT_END_AUTO_TEST(head_request)
1781+
1782+
LT_BEGIN_AUTO_TEST(basic_suite, options_request)
1783+
complete_test_resource resource;
1784+
LT_ASSERT_EQ(true, ws->register_resource("base", &resource));
1785+
curl_global_init(CURL_GLOBAL_ALL);
1786+
string s;
1787+
map<string, string> ss;
1788+
CURL *curl = curl_easy_init();
1789+
CURLcode res;
1790+
curl_easy_setopt(curl, CURLOPT_URL, "localhost:" PORT_STRING "/base");
1791+
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "OPTIONS");
1792+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
1793+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
1794+
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerfunc);
1795+
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &ss);
1796+
res = curl_easy_perform(curl);
1797+
LT_ASSERT_EQ(res, 0);
1798+
int64_t http_code = 0;
1799+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
1800+
LT_CHECK_EQ(http_code, 200);
1801+
LT_CHECK_EQ(ss["Allow"], "GET, POST, PUT, DELETE, HEAD, OPTIONS");
1802+
curl_easy_cleanup(curl);
1803+
LT_END_AUTO_TEST(options_request)
1804+
1805+
LT_BEGIN_AUTO_TEST(basic_suite, trace_request)
1806+
complete_test_resource resource;
1807+
LT_ASSERT_EQ(true, ws->register_resource("base", &resource));
1808+
curl_global_init(CURL_GLOBAL_ALL);
1809+
string s;
1810+
CURL *curl = curl_easy_init();
1811+
CURLcode res;
1812+
curl_easy_setopt(curl, CURLOPT_URL, "localhost:" PORT_STRING "/base");
1813+
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "TRACE");
1814+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
1815+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
1816+
res = curl_easy_perform(curl);
1817+
LT_ASSERT_EQ(res, 0);
1818+
int64_t http_code = 0;
1819+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
1820+
LT_CHECK_EQ(http_code, 200);
1821+
LT_CHECK_EQ(s, "TRACE OK");
1822+
curl_easy_cleanup(curl);
1823+
LT_END_AUTO_TEST(trace_request)
1824+
1825+
LT_BEGIN_SUITE(content_limit_suite)
1826+
std::unique_ptr<webserver> ws;
1827+
int content_limit_port;
1828+
string content_limit_url;
1829+
1830+
void set_up() {
1831+
content_limit_port = PORT + 10;
1832+
content_limit_url = "localhost:" + std::to_string(content_limit_port) + "/limit";
1833+
ws = std::make_unique<webserver>(create_webserver(content_limit_port).content_size_limit(100));
1834+
ws->start(false);
1835+
}
1836+
1837+
void tear_down() {
1838+
ws->stop();
1839+
}
1840+
LT_END_SUITE(content_limit_suite)
1841+
1842+
LT_BEGIN_AUTO_TEST(content_limit_suite, content_exceeds_limit)
1843+
content_limit_resource resource;
1844+
LT_ASSERT_EQ(true, ws->register_resource("limit", &resource));
1845+
curl_global_init(CURL_GLOBAL_ALL);
1846+
string s;
1847+
CURL *curl = curl_easy_init();
1848+
CURLcode res;
1849+
1850+
std::string large_data(200, 'X');
1851+
1852+
curl_easy_setopt(curl, CURLOPT_URL, content_limit_url.c_str());
1853+
curl_easy_setopt(curl, CURLOPT_POST, 1L);
1854+
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, large_data.c_str());
1855+
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, large_data.size());
1856+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
1857+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
1858+
res = curl_easy_perform(curl);
1859+
LT_ASSERT_EQ(res, 0);
1860+
LT_CHECK_EQ(s, "TOO_LARGE");
1861+
curl_easy_cleanup(curl);
1862+
LT_END_AUTO_TEST(content_exceeds_limit)
1863+
1864+
LT_BEGIN_AUTO_TEST(content_limit_suite, content_within_limit)
1865+
content_limit_resource resource;
1866+
LT_ASSERT_EQ(true, ws->register_resource("limit", &resource));
1867+
curl_global_init(CURL_GLOBAL_ALL);
1868+
string s;
1869+
CURL *curl = curl_easy_init();
1870+
CURLcode res;
1871+
1872+
std::string small_data(50, 'X');
1873+
1874+
curl_easy_setopt(curl, CURLOPT_URL, content_limit_url.c_str());
1875+
curl_easy_setopt(curl, CURLOPT_POST, 1L);
1876+
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, small_data.c_str());
1877+
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, small_data.size());
1878+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
1879+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
1880+
res = curl_easy_perform(curl);
1881+
LT_ASSERT_EQ(res, 0);
1882+
LT_CHECK_EQ(s, "OK");
1883+
curl_easy_cleanup(curl);
1884+
LT_END_AUTO_TEST(content_within_limit)
1885+
16681886
LT_BEGIN_AUTO_TEST_ENV()
16691887
AUTORUN_TESTS()
16701888
LT_END_AUTO_TEST_ENV()

test/unit/http_response_test.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
This file is part of libhttpserver
3+
Copyright (C) 2011-2019 Sebastiano Merlino
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18+
USA
19+
*/
20+
21+
#include <string>
22+
23+
#include "./littletest.hpp"
24+
#include "./httpserver.hpp"
25+
26+
using std::string;
27+
using httpserver::http_response;
28+
using httpserver::string_response;
29+
30+
LT_BEGIN_SUITE(http_response_suite)
31+
void set_up() {
32+
}
33+
34+
void tear_down() {
35+
}
36+
LT_END_SUITE(http_response_suite)
37+
38+
LT_BEGIN_AUTO_TEST(http_response_suite, default_response_code)
39+
http_response resp;
40+
LT_CHECK_EQ(resp.get_response_code(), -1);
41+
LT_END_AUTO_TEST(default_response_code)
42+
43+
LT_BEGIN_AUTO_TEST(http_response_suite, custom_response_code)
44+
http_response resp(404, "text/plain");
45+
LT_CHECK_EQ(resp.get_response_code(), 404);
46+
LT_END_AUTO_TEST(custom_response_code)
47+
48+
LT_BEGIN_AUTO_TEST(http_response_suite, string_response_code)
49+
string_response resp("Not Found", 404, "text/plain");
50+
LT_CHECK_EQ(resp.get_response_code(), 404);
51+
LT_END_AUTO_TEST(string_response_code)
52+
53+
LT_BEGIN_AUTO_TEST(http_response_suite, header_operations)
54+
http_response resp(200, "text/plain");
55+
resp.with_header("X-Custom-Header", "HeaderValue");
56+
LT_CHECK_EQ(resp.get_header("X-Custom-Header"), "HeaderValue");
57+
LT_END_AUTO_TEST(header_operations)
58+
59+
LT_BEGIN_AUTO_TEST(http_response_suite, footer_operations)
60+
http_response resp(200, "text/plain");
61+
resp.with_footer("X-Footer", "FooterValue");
62+
LT_CHECK_EQ(resp.get_footer("X-Footer"), "FooterValue");
63+
LT_END_AUTO_TEST(footer_operations)
64+
65+
LT_BEGIN_AUTO_TEST(http_response_suite, cookie_operations)
66+
http_response resp(200, "text/plain");
67+
resp.with_cookie("SessionId", "abc123");
68+
LT_CHECK_EQ(resp.get_cookie("SessionId"), "abc123");
69+
LT_END_AUTO_TEST(cookie_operations)
70+
71+
LT_BEGIN_AUTO_TEST(http_response_suite, get_headers)
72+
http_response resp(200, "text/plain");
73+
resp.with_header("Header1", "Value1");
74+
resp.with_header("Header2", "Value2");
75+
auto headers = resp.get_headers();
76+
LT_CHECK_EQ(headers.at("Header1"), "Value1");
77+
LT_CHECK_EQ(headers.at("Header2"), "Value2");
78+
LT_END_AUTO_TEST(get_headers)
79+
80+
LT_BEGIN_AUTO_TEST(http_response_suite, get_footers)
81+
http_response resp(200, "text/plain");
82+
resp.with_footer("Footer1", "Value1");
83+
resp.with_footer("Footer2", "Value2");
84+
auto footers = resp.get_footers();
85+
LT_CHECK_EQ(footers.at("Footer1"), "Value1");
86+
LT_CHECK_EQ(footers.at("Footer2"), "Value2");
87+
LT_END_AUTO_TEST(get_footers)
88+
89+
LT_BEGIN_AUTO_TEST(http_response_suite, get_cookies)
90+
http_response resp(200, "text/plain");
91+
resp.with_cookie("Cookie1", "Value1");
92+
resp.with_cookie("Cookie2", "Value2");
93+
auto cookies = resp.get_cookies();
94+
LT_CHECK_EQ(cookies.at("Cookie1"), "Value1");
95+
LT_CHECK_EQ(cookies.at("Cookie2"), "Value2");
96+
LT_END_AUTO_TEST(get_cookies)
97+
98+
LT_BEGIN_AUTO_TEST_ENV()
99+
AUTORUN_TESTS()
100+
LT_END_AUTO_TEST_ENV()

0 commit comments

Comments
 (0)