Skip to content

Commit 3d828bc

Browse files
committed
Move HttpDaemon logic into httpd.cpp
Refactors `HttpDaemon` by moving constructor and request callback implementation out of the header into a new `httpd.cpp` translation unit. The handler table is replaced with explicit raw storage in a dedicated `.httpd` section and constructed via placement new, with `GetHandler()`/`std::launder` used for safe access. Also tightens class lifetime semantics by deleting copy/move operations and trims now-unneeded header includes.
1 parent ac2e9ca commit 3d828bc

2 files changed

Lines changed: 126 additions & 34 deletions

File tree

lib-remoteconfig/include/httpd/httpd.h

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,12 @@
3030
#define HTTPD_HTTPD_H_
3131

3232
#include <cstdint>
33-
#include <cassert>
34-
#include <new>
3533

3634
#include "core/protocol/iana.h"
3735
#include "httpdhandlerequest.h"
3836
#include "network_tcp.h"
3937
#include "apps/mdns.h"
4038
#include "../../lib-network/config/net_config.h"
41-
#include "firmware/debug/debug_debug.h"
4239

4340
namespace httpd {
4441
inline constexpr auto kPort =
@@ -57,41 +54,34 @@ inline constexpr auto kService =
5754

5855
class HttpDaemon {
5956
public:
60-
HttpDaemon() {
61-
DEBUG_ENTRY();
62-
assert(is_listening_ == false);
63-
64-
is_listening_ = network::tcp::Listen(httpd::kPort, Data);
65-
assert(is_listening_ == true);
66-
67-
// IMPORTANT:
68-
// Connection handles are GLOBAL indices into s_Tcbs[].
69-
// Therefore the HTTP request handler table must also be global-sized.
70-
for (uint32_t i = 0; i < TCP_MAX_TCBS_ALLOWED; ++i) {
71-
// Each HttpDeamonHandleRequest corresponds to ONE possible TCB slot.
72-
// It can be addressed directly by conn_handle.
73-
new (&s_handle_request[i]) HttpDeamonHandleRequest(i);
74-
}
75-
76-
network::apps::mdns::ServiceRecordAdd(nullptr, httpd::kService);
77-
78-
DEBUG_EXIT();
79-
}
57+
HttpDaemon();
8058

59+
// The daemon and its handlers live for the lifetime of the firmware.
8160
~HttpDaemon() = default;
8261

83-
private:
84-
static void Data(network::tcp::ConnHandle conn_handle, const uint8_t* buffer, uint32_t size, [[maybe_unused]] void* context) {
85-
assert(conn_handle < TCP_MAX_TCBS_ALLOWED);
86-
s_handle_request[conn_handle].HandleRequest(size, const_cast<char*>(reinterpret_cast<const char*>(buffer)));
87-
}
62+
HttpDaemon(const HttpDaemon&) = delete;
63+
HttpDaemon& operator=(const HttpDaemon&) = delete;
64+
HttpDaemon(HttpDaemon&&) = delete;
65+
HttpDaemon& operator=(HttpDaemon&&) = delete;
8866

89-
#if defined(GD32F207RG) || defined(GD32F450VE) || defined(GD32F470ZK)
90-
#define SECTION_HTTPD __attribute__((section(".httpd")))
91-
#else
92-
#define SECTION_HTTPD
93-
#endif
94-
static inline HttpDeamonHandleRequest s_handle_request[TCP_MAX_TCBS_ALLOWED] __attribute__((aligned(4))) SECTION_HTTPD;
67+
private:
68+
/**
69+
* Raw storage for one HttpDeamonHandleRequest.
70+
*
71+
* The storage itself has trivial initialization, so it can safely be
72+
* placed in a NOLOAD linker section. The actual handler object is created
73+
* explicitly with placement new in HttpDaemon::HttpDaemon().
74+
*/
75+
struct HandlerStorage {
76+
alignas(HttpDeamonHandleRequest) unsigned char data[sizeof(HttpDeamonHandleRequest)];
77+
};
78+
79+
[[nodiscard]]
80+
static HttpDeamonHandleRequest& GetHandler(uint32_t index);
81+
82+
static void Data(network::tcp::ConnHandle conn_handle, const uint8_t* buffer, uint32_t size, void* context);
83+
84+
static HandlerStorage s_handle_request_storage[TCP_MAX_TCBS_ALLOWED];
9585

9686
bool is_listening_{false};
9787
};
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* @file httpd.cpp
3+
* @brief HTTP daemon class for managing HTTP server tasks.
4+
*
5+
* This class handles HTTP requests and integrates with the network and mDNS subsystems.
6+
* It uses placement new to construct and destruct request handlers explicitly.
7+
*/
8+
/* Copyright (C) 2025-2026 by Arjan van Vught mailto:info@gd32-dmx.org
9+
*
10+
* Permission is hereby granted, free of charge, to any person obtaining a copy
11+
* of this software and associated documentation files (the "Software"), to deal
12+
* in the Software without restriction, including without limitation the rights
13+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14+
* copies of the Software, and to permit persons to whom the Software is
15+
* furnished to do so, subject to the following conditions:
16+
17+
* The above copyright notice and this permission notice shall be included in
18+
* all copies or substantial portions of the Software.
19+
20+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26+
* THE SOFTWARE.
27+
*/
28+
29+
#include <cassert>
30+
#include <new>
31+
32+
#include "firmware/debug/debug_debug.h"
33+
#include "httpd/httpd.h"
34+
#include "network_tcp.h"
35+
#include "apps/mdns.h"
36+
#include "../../lib-network/config/net_config.h"
37+
38+
/*
39+
* Define the storage exactly once, outside the header.
40+
*
41+
* gnu::used:
42+
* Ensures that the compiler emits the object.
43+
*
44+
* .httpd:
45+
* Is collected by the linker script into the dedicated HTTP RAM section.
46+
*
47+
* The linker script should contain:
48+
*
49+
* .http (NOLOAD) :
50+
* {
51+
* . = ALIGN(4);
52+
* _shttp = .;
53+
* KEEP(*(.http))
54+
* KEEP(*(.http.*))
55+
* KEEP(*(.httpd))
56+
* KEEP(*(.httpd.*))
57+
* . = ALIGN(4);
58+
* _ehttp = .;
59+
* } > RAM2
60+
*/
61+
[[gnu::section(".httpd"), gnu::aligned(alignof(HttpDeamonHandleRequest)), gnu::used]]
62+
HttpDaemon::HandlerStorage HttpDaemon::s_handle_request_storage[TCP_MAX_TCBS_ALLOWED];
63+
64+
HttpDaemon::HttpDaemon() {
65+
DEBUG_ENTRY();
66+
67+
assert(!is_listening_);
68+
69+
is_listening_ = network::tcp::Listen(httpd::kPort, Data);
70+
assert(is_listening_);
71+
72+
/*
73+
* Connection handles are global indices into s_Tcbs[].
74+
* Therefore the request-handler table must have one entry for every
75+
* possible global TCB slot.
76+
*/
77+
for (uint32_t i = 0; i < TCP_MAX_TCBS_ALLOWED; ++i) {
78+
// Construct exactly one handler in each raw-storage slot.
79+
::new (static_cast<void*>(s_handle_request_storage[i].data)) HttpDeamonHandleRequest(i);
80+
}
81+
82+
network::apps::mdns::ServiceRecordAdd(nullptr, httpd::kService);
83+
84+
DEBUG_EXIT();
85+
}
86+
87+
HttpDeamonHandleRequest& HttpDaemon::GetHandler(uint32_t index) {
88+
assert(index < TCP_MAX_TCBS_ALLOWED);
89+
90+
/*
91+
* std::launder is appropriate after constructing an object in raw storage
92+
* and subsequently obtaining a pointer through the storage address.
93+
*/
94+
return *std::launder(reinterpret_cast<HttpDeamonHandleRequest*>(s_handle_request_storage[index].data));
95+
}
96+
97+
void HttpDaemon::Data(network::tcp::ConnHandle conn_handle, const uint8_t* buffer, uint32_t size, [[maybe_unused]] void* context) {
98+
assert(conn_handle < TCP_MAX_TCBS_ALLOWED);
99+
assert(buffer != nullptr);
100+
101+
GetHandler(conn_handle).HandleRequest(size, const_cast<char*>(reinterpret_cast<const char*>(buffer)));
102+
}

0 commit comments

Comments
 (0)