Skip to content

Commit 6b87fea

Browse files
committed
TASK-003: Add httpserver::feature_unavailable exception type
Introduce a new public header `src/httpserver/feature_unavailable.hpp` defining `class feature_unavailable : public std::runtime_error`. The constructor takes `(std::string_view feature, std::string_view build_flag)` and composes a `what()` message that names both, e.g. `"feature 'tls' unavailable: built without HAVE_GNUTLS"`. The class is header-only and inline. It has no library dependencies (only <stdexcept>, <string>, <string_view>), so any TU — including later tasks like TASK-034 that need to throw it from sites in build-time-disabled code paths — can include it without circular header coupling. Keeping it inline also avoids ABI churn for what is effectively a labelled std::runtime_error and keeps libhttpserver_la sources untouched. The header is re-exported from the umbrella `<httpserver.hpp>` unconditionally (no `#ifdef HAVE_*` wrap): even a build with no optional features must let consumers name `feature_unavailable` so they can write `try { ... } catch (const httpserver::feature_unavailable&)`. The TASK-002 inclusion gate is applied verbatim — direct inclusion of the header without the umbrella or `HTTPSERVER_COMPILATION` errors out, and `_HTTPSERVER_HPP_INSIDE_` does not leak post-umbrella (both verified by the existing check-headers A.1–A.4 recipes). A new unit test `test/unit/feature_unavailable_test.cpp` provides: - a TU-scope `static_assert(std::is_base_of_v<std::runtime_error, httpserver::feature_unavailable>)` (acceptance criterion 1), - a test that catches as `std::runtime_error` and asserts both the feature name and the build flag appear in `what()` (AC 2), - a test that catches as the concrete type and confirms it slices to `runtime_error` correctly, - a test with a different (feature, flag) pair to guard against hard-coded message text. Verified locally: - `make check`: 18/18 PASS (was 17, +1 for feature_unavailable), - check-headers A.1–A.4 PASS, - check-install-layout PASS (no details/ leak), - staged install ships exactly one feature_unavailable.hpp at $(prefix)/include/httpserver/feature_unavailable.hpp, - debug build (--enable-debug, -Werror -Wextra -pedantic) builds and tests cleanly. Refs: PRD-FLG-REQ-004, PRD-FLG-REQ-005; §7 (feature availability).
1 parent b9ef39d commit 6b87fea

5 files changed

Lines changed: 150 additions & 2 deletions

File tree

src/Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp fil
2424
# Detail headers (httpserver/details/*.hpp) live here so they cannot leak to
2525
# downstream consumers — the public surface comes in through <httpserver.hpp>.
2626
noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_endpoint.hpp gettext.h
27-
nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/file_info.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/string_response.hpp httpserver/digest_auth_fail_response.hpp httpserver/deferred_response.hpp httpserver/file_response.hpp httpserver/pipe_response.hpp httpserver/empty_response.hpp httpserver/iovec_response.hpp httpserver/http_arg_value.hpp
27+
nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/file_info.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/string_response.hpp httpserver/digest_auth_fail_response.hpp httpserver/deferred_response.hpp httpserver/file_response.hpp httpserver/pipe_response.hpp httpserver/empty_response.hpp httpserver/feature_unavailable.hpp httpserver/iovec_response.hpp httpserver/http_arg_value.hpp
2828

2929
if HAVE_BAUTH
3030
libhttpserver_la_SOURCES += basic_auth_fail_response.cpp

src/httpserver.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "httpserver/digest_auth_fail_response.hpp"
3636
#endif // HAVE_DAUTH
3737
#include "httpserver/empty_response.hpp"
38+
#include "httpserver/feature_unavailable.hpp"
3839
#include "httpserver/file_response.hpp"
3940
#include "httpserver/http_arg_value.hpp"
4041
#include "httpserver/http_request.hpp"
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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+
#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION)
22+
#error "Only <httpserver.hpp> or <httpserverpp> can be included directly."
23+
#endif
24+
25+
#ifndef SRC_HTTPSERVER_FEATURE_UNAVAILABLE_HPP_
26+
#define SRC_HTTPSERVER_FEATURE_UNAVAILABLE_HPP_
27+
28+
#include <stdexcept>
29+
#include <string>
30+
#include <string_view>
31+
32+
namespace httpserver {
33+
34+
// Exception thrown when a build-time-disabled feature is invoked at runtime.
35+
// The class is unconditionally available regardless of HAVE_* flags so that
36+
// downstream code can always write
37+
// try { ... } catch (const httpserver::feature_unavailable&) { ... }
38+
// even in builds that compiled out the optional feature in question.
39+
//
40+
// The class is header-only (and inline) on purpose: it has no library
41+
// dependencies, must be cheap to throw from anywhere in the codebase, and
42+
// avoids ABI churn for what is effectively a labelled std::runtime_error.
43+
class feature_unavailable : public std::runtime_error {
44+
public:
45+
feature_unavailable(std::string_view feature, std::string_view build_flag)
46+
: std::runtime_error(compose_message(feature, build_flag)) {}
47+
48+
private:
49+
static std::string compose_message(std::string_view feature,
50+
std::string_view build_flag) {
51+
std::string msg;
52+
msg.reserve(feature.size() + build_flag.size() + 32);
53+
msg.append("feature '");
54+
msg.append(feature);
55+
msg.append("' unavailable: built without ");
56+
msg.append(build_flag);
57+
return msg;
58+
}
59+
};
60+
61+
} // namespace httpserver
62+
#endif // SRC_HTTPSERVER_FEATURE_UNAVAILABLE_HPP_

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/ -DHTTPSERVER_COMPILATION
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 http_response create_webserver new_response_types daemon_info uri_log
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 create_webserver new_response_types daemon_info uri_log feature_unavailable
3030

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

@@ -51,6 +51,7 @@ uri_log_SOURCES = unit/uri_log_test.cpp
5151
# it needs an explicit -lmicrohttpd in its link line on top of the default
5252
# LDADD (modern ld enforces --no-copy-dt-needed-entries).
5353
uri_log_LDADD = $(LDADD) -lmicrohttpd
54+
feature_unavailable_SOURCES = unit/feature_unavailable_test.cpp
5455

5556
noinst_HEADERS = littletest.hpp
5657
AM_CXXFLAGS += -Wall -fPIC -Wno-overloaded-virtual
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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 <stdexcept>
22+
#include <string>
23+
#include <type_traits>
24+
25+
#include "./httpserver.hpp"
26+
#include "./littletest.hpp"
27+
28+
// AC #1: feature_unavailable derives from std::runtime_error. This compile-time
29+
// assertion runs at TU scope and fires if the inheritance is ever broken.
30+
static_assert(
31+
std::is_base_of_v<std::runtime_error, httpserver::feature_unavailable>,
32+
"feature_unavailable must derive from std::runtime_error");
33+
34+
LT_BEGIN_SUITE(feature_unavailable_suite)
35+
void set_up() {
36+
}
37+
38+
void tear_down() {
39+
}
40+
LT_END_SUITE(feature_unavailable_suite)
41+
42+
// AC #2: a unit test catches the exception as std::runtime_error and asserts
43+
// that what() contains both the feature name and the build flag.
44+
LT_BEGIN_AUTO_TEST(feature_unavailable_suite,
45+
catches_as_runtime_error_with_feature_and_flag)
46+
std::string msg;
47+
try {
48+
throw httpserver::feature_unavailable("tls", "HAVE_GNUTLS");
49+
} catch (const std::runtime_error& e) {
50+
msg = e.what();
51+
}
52+
LT_CHECK(msg.find("tls") != std::string::npos);
53+
LT_CHECK(msg.find("HAVE_GNUTLS") != std::string::npos);
54+
LT_END_AUTO_TEST(catches_as_runtime_error_with_feature_and_flag)
55+
56+
// Catching the concrete type still produces a runtime_error-shaped what().
57+
LT_BEGIN_AUTO_TEST(feature_unavailable_suite, catches_as_feature_unavailable_directly)
58+
std::string msg;
59+
try {
60+
throw httpserver::feature_unavailable("tls", "HAVE_GNUTLS");
61+
} catch (const httpserver::feature_unavailable& e) {
62+
const std::runtime_error* base = &e;
63+
msg = base->what();
64+
}
65+
LT_CHECK(msg.find("tls") != std::string::npos);
66+
LT_CHECK(msg.find("HAVE_GNUTLS") != std::string::npos);
67+
LT_END_AUTO_TEST(catches_as_feature_unavailable_directly)
68+
69+
// Guard against a hard-coded message: a different (feature, flag) pair must
70+
// also propagate verbatim into what().
71+
LT_BEGIN_AUTO_TEST(feature_unavailable_suite, composes_message_for_websocket)
72+
std::string msg;
73+
try {
74+
throw httpserver::feature_unavailable("websocket", "HAVE_WEBSOCKET");
75+
} catch (const std::runtime_error& e) {
76+
msg = e.what();
77+
}
78+
LT_CHECK(msg.find("websocket") != std::string::npos);
79+
LT_CHECK(msg.find("HAVE_WEBSOCKET") != std::string::npos);
80+
LT_END_AUTO_TEST(composes_message_for_websocket)
81+
82+
LT_BEGIN_AUTO_TEST_ENV()
83+
AUTORUN_TESTS()
84+
LT_END_AUTO_TEST_ENV()

0 commit comments

Comments
 (0)