Skip to content

Commit f4a5530

Browse files
etrclaude
andcommitted
TASK-013: remove v1 *_response subclasses, seal http_response
Deletes the eight public *_response subclasses (string/file/iovec/pipe/ deferred/empty/basic_auth_fail/digest_auth_fail) and the dispatch virtuals (get_raw_response/decorate_response/enqueue_response) from http_response, making the new factory-based surface (TASK-009..012) the only way to build a response. Phase 1 — migrate every consumer to the v2 surface: * webserver.cpp/http_resource.cpp internal callers switched off string_response to http_response::string()/empty(). * test/unit + test/integ + examples/* migrated to factories + with_status()/with_header() chains. * test/unit/iovec_response_test.cpp deleted (covered by http_response_factories_test). Phase 2 — delete the v1 surface: * Eight v1 hpp + cpp pairs deleted. * src/Makefile.am drops the deleted sources/headers and the HAVE_BAUTH conditional. * <httpserver.hpp> umbrella drops the eight removed includes. * http_response gains `final`; destructor de-virtualised; the legacy 2-arg constructor (security-reviewer #24 from TASK-012) and the get_response_code() shim are gone; struct MHD_Connection/MHD_Response forward declarations dropped from the public header. * webserver gains static dispatch helpers materialize_response() and decorate_mhd_response(); friended into http_response so it can read body_ without widening the public API. * file() factory now defaults Content-Type to application/octet-stream (matching v1 file_response). * empty_render returns a default-constructed http_response so the status_code = -1 sentinel keeps routing to internal_error_page (the default_render_method test pins this). * finalize_answer's catch blocks now wrap internal_error_page() with an inner try/catch that falls back to force_our=true so a throwing user-supplied internal_error_resource can never escape into MHD. Acceptance: * grep 'class \w+_response :' src/httpserver/*.hpp returns no public subclass declarations. * grep 'get_raw_response|decorate_response|enqueue_response' src/httpserver/*.hpp returns only doxygen prose. * static_assert(std::is_final_v<http_response>) compiles (test/unit/http_response_sbo_test.cpp). * make check: 25 PASS / 1 XFAIL (header_hygiene, expected pre-M5). Behaviour change accepted per plan §2 / §10 and PRD §3.5: v1's basic_auth_fail and digest_auth_fail bound to MHD's nonce/opaque state machine via MHD_queue_basic/auth_required_response3; v2's unauthorized() emits a static WWW-Authenticate challenge and enqueues via MHD_queue_response. Four digest-auth round-trip tests in test/integ/authentication.cpp updated to assert the v2 contract (challenge issued, handshake does not complete, body remains FAIL). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 67db6a4 commit f4a5530

73 files changed

Lines changed: 531 additions & 1663 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

examples/allowing_disallowing_methods.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
class hello_world_resource : public httpserver::http_resource {
2626
public:
2727
std::shared_ptr<httpserver::http_response> render(const httpserver::http_request&) {
28-
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response("Hello, World!"));
28+
return std::shared_ptr<httpserver::http_response>(new httpserver::http_response(httpserver::http_response::string("Hello, World!")));
2929
}
3030
};
3131

examples/args_processing.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class args_resource : public httpserver::http_resource {
8080
response_body << "name (via get_arg_flat): " << name_flat << "\n";
8181
}
8282

83-
return std::make_shared<httpserver::string_response>(response_body.str(), 200, "text/plain");
83+
return std::make_shared<httpserver::http_response>(httpserver::http_response::string(response_body.str()));
8484
}
8585
};
8686

examples/basic_authentication.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ class user_pass_resource : public httpserver::http_resource {
2727
public:
2828
std::shared_ptr<httpserver::http_response> render_GET(const httpserver::http_request& req) {
2929
if (req.get_user() != "myuser" || req.get_pass() != "mypass") {
30-
return std::shared_ptr<httpserver::basic_auth_fail_response>(new httpserver::basic_auth_fail_response("FAIL", "test@example.com"));
30+
return std::make_shared<httpserver::http_response>(
31+
httpserver::http_response::unauthorized("Basic", "test@example.com", "FAIL"));
3132
}
3233

33-
return std::shared_ptr<httpserver::string_response>(new httpserver::string_response(std::string(req.get_user()) + " " + std::string(req.get_pass()), 200, "text/plain"));
34+
return std::shared_ptr<httpserver::http_response>(new httpserver::http_response(httpserver::http_response::string(std::string(req.get_user()) + " " + std::string(req.get_pass()))));
3435
}
3536
};
3637

examples/benchmark_nodelay.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ int main(int argc, char** argv) {
4848
.tcp_nodelay()
4949
.max_threads(atoi(argv[2]));
5050

51-
std::shared_ptr<httpserver::http_response> hello = std::shared_ptr<httpserver::http_response>(new httpserver::string_response(BODY, 200));
51+
std::shared_ptr<httpserver::http_response> hello = std::shared_ptr<httpserver::http_response>(new httpserver::http_response(httpserver::http_response::string(BODY)));
5252
hello->with_header("Server", "libhttpserver");
5353

5454
hello_world_resource hwr(hello);

examples/benchmark_select.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ int main(int argc, char** argv) {
4747
.start_method(httpserver::http::http_utils::INTERNAL_SELECT)
4848
.max_threads(atoi(argv[2]));
4949

50-
std::shared_ptr<httpserver::http_response> hello = std::shared_ptr<httpserver::http_response>(new httpserver::string_response(BODY, 200));
50+
std::shared_ptr<httpserver::http_response> hello = std::shared_ptr<httpserver::http_response>(new httpserver::http_response(httpserver::http_response::string(BODY)));
5151
hello->with_header("Server", "libhttpserver");
5252

5353
hello_world_resource hwr(hello);

examples/benchmark_threads.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ int main(int argc, char** argv) {
4646
httpserver::webserver ws = httpserver::create_webserver(atoi(argv[1]))
4747
.start_method(httpserver::http::http_utils::THREAD_PER_CONNECTION);
4848

49-
std::shared_ptr<httpserver::http_response> hello = std::shared_ptr<httpserver::http_response>(new httpserver::string_response(BODY, 200));
49+
std::shared_ptr<httpserver::http_response> hello = std::shared_ptr<httpserver::http_response>(new httpserver::http_response(httpserver::http_response::string(BODY)));
5050
hello->with_header("Server", "libhttpserver");
5151

5252
hello_world_resource hwr(hello);

examples/binary_buffer_response.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ class image_resource : public httpserver::http_resource {
6666

6767
// Use string_response with the appropriate content type. The response
6868
// will send the exact bytes contained in the string.
69-
return std::make_shared<httpserver::string_response>(
70-
std::move(image_data), 200, "image/png");
69+
return std::make_shared<httpserver::http_response>(httpserver::http_response::string(std::move(image_data), "image/png"));
7170
}
7271
};
7372

examples/centralized_authentication.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,26 @@ using httpserver::http_response;
2828
using httpserver::http_resource;
2929
using httpserver::webserver;
3030
using httpserver::create_webserver;
31-
using httpserver::string_response;
32-
using httpserver::basic_auth_fail_response;
33-
3431
// Simple resource that doesn't need to handle auth itself
3532
class hello_resource : public http_resource {
3633
public:
3734
std::shared_ptr<http_response> render_GET(const http_request&) {
38-
return std::make_shared<string_response>("Hello, authenticated user!", 200, "text/plain");
35+
return std::make_shared<http_response>(http_response::string("Hello, authenticated user!"));
3936
}
4037
};
4138

4239
class health_resource : public http_resource {
4340
public:
4441
std::shared_ptr<http_response> render_GET(const http_request&) {
45-
return std::make_shared<string_response>("OK", 200, "text/plain");
42+
return std::make_shared<http_response>(http_response::string("OK"));
4643
}
4744
};
4845

4946
// Centralized authentication handler
5047
// Returns nullptr to allow the request, or an http_response to reject it
5148
std::shared_ptr<http_response> auth_handler(const http_request& req) {
5249
if (req.get_user() != "admin" || req.get_pass() != "secret") {
53-
return std::make_shared<basic_auth_fail_response>("Unauthorized", "MyRealm");
50+
return std::make_shared<http_response>(http_response::unauthorized("Basic", "MyRealm", "Unauthorized"));
5451
}
5552
return nullptr; // Allow request
5653
}

examples/client_cert_auth.cpp

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,7 @@ class secure_resource : public httpserver::http_resource {
6969
std::shared_ptr<httpserver::http_response> render_GET(const httpserver::http_request& req) {
7070
// Check if client provided a certificate
7171
if (!req.has_client_certificate()) {
72-
return std::make_shared<httpserver::string_response>(
73-
"Client certificate required",
74-
httpserver::http::http_utils::http_unauthorized, "text/plain");
72+
return std::make_shared<httpserver::http_response>(httpserver::http_response::string("Client certificate required").with_status(httpserver::http::http_utils::http_unauthorized));
7573
}
7674

7775
// Get certificate information
@@ -83,17 +81,13 @@ class secure_resource : public httpserver::http_resource {
8381

8482
// Check if certificate is verified by our CA
8583
if (!verified) {
86-
return std::make_shared<httpserver::string_response>(
87-
"Certificate not verified by trusted CA",
88-
httpserver::http::http_utils::http_forbidden, "text/plain");
84+
return std::make_shared<httpserver::http_response>(httpserver::http_response::string("Certificate not verified by trusted CA").with_status(httpserver::http::http_utils::http_forbidden));
8985
}
9086

9187
// Optional: Check fingerprint against allowlist
9288
if (!allowed_fingerprints.empty() &&
9389
allowed_fingerprints.find(fingerprint) == allowed_fingerprints.end()) {
94-
return std::make_shared<httpserver::string_response>(
95-
"Certificate not in allowlist",
96-
httpserver::http::http_utils::http_forbidden, "text/plain");
90+
return std::make_shared<httpserver::http_response>(httpserver::http_response::string("Certificate not in allowlist").with_status(httpserver::http::http_utils::http_forbidden));
9791
}
9892

9993
// Check certificate validity times
@@ -102,15 +96,11 @@ class secure_resource : public httpserver::http_resource {
10296
time_t not_after = req.get_client_cert_not_after();
10397

10498
if (now < not_before) {
105-
return std::make_shared<httpserver::string_response>(
106-
"Certificate not yet valid",
107-
httpserver::http::http_utils::http_forbidden, "text/plain");
99+
return std::make_shared<httpserver::http_response>(httpserver::http_response::string("Certificate not yet valid").with_status(httpserver::http::http_utils::http_forbidden));
108100
}
109101

110102
if (now > not_after) {
111-
return std::make_shared<httpserver::string_response>(
112-
"Certificate has expired",
113-
httpserver::http::http_utils::http_forbidden, "text/plain");
103+
return std::make_shared<httpserver::http_response>(httpserver::http_response::string("Certificate has expired").with_status(httpserver::http::http_utils::http_forbidden));
114104
}
115105

116106
// Build response with certificate info
@@ -121,7 +111,7 @@ class secure_resource : public httpserver::http_resource {
121111
response += " Fingerprint (SHA-256): " + fingerprint + "\n";
122112
response += " Verified: " + std::string(verified ? "Yes" : "No") + "\n";
123113

124-
return std::make_shared<httpserver::string_response>(response, 200, "text/plain");
114+
return std::make_shared<httpserver::http_response>(httpserver::http_response::string(response));
125115
}
126116
};
127117

@@ -140,7 +130,7 @@ class info_resource : public httpserver::http_resource {
140130
response += "Use --cert and --key with curl to provide one.\n";
141131
}
142132

143-
return std::make_shared<httpserver::string_response>(response, 200, "text/plain");
133+
return std::make_shared<httpserver::http_response>(httpserver::http_response::string(response));
144134
}
145135
};
146136

examples/custom_access_log.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ void custom_access_log(const std::string& url) {
3131
class hello_world_resource : public httpserver::http_resource {
3232
public:
3333
std::shared_ptr<httpserver::http_response> render(const httpserver::http_request&) {
34-
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response("Hello, World!"));
34+
return std::shared_ptr<httpserver::http_response>(new httpserver::http_response(httpserver::http_response::string("Hello, World!")));
3535
}
3636
};
3737

0 commit comments

Comments
 (0)