-
Notifications
You must be signed in to change notification settings - Fork 191
Expand file tree
/
Copy pathhook_handle.hpp
More file actions
183 lines (164 loc) · 7.18 KB
/
Copy pathhook_handle.hpp
File metadata and controls
183 lines (164 loc) · 7.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
This file is part of libhttpserver
Copyright (C) 2011-2026 Sebastiano Merlino
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
*/
#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION)
#error "Only <httpserver.hpp> or <httpserverpp> can be included directly."
#endif
#ifndef SRC_HTTPSERVER_HOOK_HANDLE_HPP_
#define SRC_HTTPSERVER_HOOK_HANDLE_HPP_
#include <cstdint>
#include <memory>
#include <utility>
#include "httpserver/hook_phase.hpp"
/**
* @file hook_handle.hpp
* @brief Move-only RAII receipt returned by webserver / http_resource
* `add_hook` calls.
*
* TASK-045 / §5.6 / DR-012. The PIMPL backing classes are forward-
* declared so this header carries no libmicrohttpd / pthread / gnutls
* baggage.
*/
namespace httpserver {
namespace detail { class webserver_impl; }
// TASK-051: per-resource hook table -- the per-route handle holds a
// weak_ptr<resource_hook_table>, expired when the resource is
// destroyed (remove() then becomes a safe no-op).
namespace detail { class resource_hook_table; }
class webserver;
class http_resource;
/**
* @brief Move-only RAII receipt for a hook registration.
*
* Returned by `webserver::add_hook` (server-wide) or
* `http_resource::add_hook` (per-route). Destruction or explicit
* `remove()` re-takes the hook table's writer lock and erases the
* registration. `detach()` disarms the destructor so the registration
* persists for the webserver's lifetime.
*
* Back-reference: `(phase, slot_id, impl_ or table_weak_)` where
* `slot_id` is a monotonically-increasing 64-bit counter. Reallocation
* of the phase vector cannot invalidate this handle: `remove()` walks
* the vector linearly looking for `slot_id`; a not-found result is
* the idempotent no-op path.
*
* @warning **Lifetime requirement:** A `hook_handle` produced by
* `webserver::add_hook` holds a raw non-owning pointer to the
* webserver's implementation object. A non-detached handle **must not
* outlive the webserver that produced it**. Destroying an armed
* `hook_handle` after its webserver has been destroyed is undefined
* behavior (use-after-free). CWE-416.
*
* Safe patterns:
* - Store handles as members of a class that also owns the `webserver`.
* - Call `h.detach()` if you want the registration to persist for the
* webserver's entire lifetime without the handle.
* - Call `h.remove()` explicitly before destroying the webserver.
*
* Per-route handles (`http_resource::add_hook`) hold a
* `weak_ptr<resource_hook_table>` instead of a raw pointer; their
* `remove()` is safe even after the resource is destroyed (the
* weak_ptr expires and the call becomes a no-op).
*
* @note **Alias slots are construction-time-only.** The v1-derived
* setters on `create_webserver` (`log_access`,
* `internal_error_handler`, `not_found_handler`,
* `method_not_allowed_handler`, `auth_handler`) wire their slot
* exactly once during webserver construction and are immutable
* thereafter. To add or remove an observation/intervention point at
* runtime, use `webserver::add_hook(phase, ...)` and the returned
* `hook_handle`. See DR-012 / §4.10.
*/
class hook_handle {
public:
/**
* @brief Default-construct a disarmed handle.
*
* `remove()` is a no-op and the destructor does not touch any
* impl. Three paths converge on this state: default ctor,
* post-`remove()`, and post-move-from.
*/
hook_handle() noexcept = default;
/// hook_handle is non-copyable.
hook_handle(const hook_handle&) = delete;
/// hook_handle is non-copy-assignable.
hook_handle& operator=(const hook_handle&) = delete;
/// Move-construct from @p other, leaving it disarmed.
hook_handle(hook_handle&& other) noexcept;
/// Move-assign from @p other, leaving it disarmed.
hook_handle& operator=(hook_handle&& other) noexcept;
/**
* @brief Destructor.
*
* Calls `remove()` unless the handle has been `detach()`-ed,
* moved-from, or already `remove()`-ed.
*/
~hook_handle();
/**
* @brief Erase the registration this handle refers to.
*
* Idempotent: a second call on the same handle is a no-op. After
* this call the handle is disarmed (the destructor will not touch
* the impl). Safe to call on a default-constructed or moved-from
* handle.
*/
void remove() noexcept;
/**
* @brief Disarm the destructor.
*
* The underlying registration persists for the webserver's
* lifetime. The source handle is left in the disarmed state.
* Conventionally invoked as `auto h2 = std::move(h).detach();`.
*
* @return a new handle that owns the registration (or a disarmed
* handle if the source was already disarmed).
*/
hook_handle detach() && noexcept;
private:
// Constructor used by webserver::add_hook to build an armed handle
// bound to the server-wide hook table.
hook_handle(detail::webserver_impl* impl,
hook_phase phase,
std::uint64_t slot_id) noexcept
: impl_(impl), slot_id_(slot_id), phase_(phase), armed_(true) {}
// TASK-051: constructor used by http_resource::add_hook to build an
// armed handle bound to a per-resource hook table. Discriminator is
// the non-empty weak_ptr -- remove() inspects table_weak_ first, and
// falls through to the server-wide impl_ path only if the weak_ptr
// is empty (default-constructed = "never had a table" sentinel).
//
// The handle holds a weak_ptr so a resource that outlives the handle
// is not kept alive by the handle (the handle is non-owning), and a
// resource destroyed before the handle expires the weak_ptr cleanly
// (lock() returns null, remove() is a no-op).
hook_handle(std::weak_ptr<detail::resource_hook_table> table_weak,
hook_phase phase,
std::uint64_t slot_id) noexcept
: slot_id_(slot_id), phase_(phase), armed_(true),
table_weak_(std::move(table_weak)) {}
detail::webserver_impl* impl_ = nullptr; // non-owning back-ref
std::uint64_t slot_id_ = 0;
hook_phase phase_ = hook_phase::count_;
bool armed_ = false;
// Non-empty (has-a-control-block) iff this handle is for a per-route
// hook on an http_resource. Empty otherwise.
std::weak_ptr<detail::resource_hook_table> table_weak_{};
friend class ::httpserver::webserver;
friend class ::httpserver::http_resource;
friend class ::httpserver::detail::webserver_impl;
};
} // namespace httpserver
#endif // SRC_HTTPSERVER_HOOK_HANDLE_HPP_