CCF
Loading...
Searching...
No Matches
http_rpc_context.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
5#include "ccf/odata_error.h"
6#include "ccf/rpc_context.h"
7#include "ds/actors.h"
8#include "http_parser.h"
10
11namespace http
12{
13 inline std::vector<uint8_t> error(ccf::ErrorDetails&& error)
14 {
15 nlohmann::json body = ccf::ODataErrorResponse{
16 ccf::ODataError{std::move(error.code), std::move(error.msg), {}}};
17 const auto s = body.dump();
18
19 std::vector<uint8_t> data(s.begin(), s.end());
20 auto response = ::http::Response(error.status);
21
22 response.set_header(
23 ccf::http::headers::CONTENT_TYPE,
24 ccf::http::headervalues::contenttype::JSON);
25 response.set_body(&data);
26
27 return response.build_response();
28 }
29
30 inline std::vector<uint8_t> error(
31 ccf::http_status status, const std::string& code, std::string&& msg)
32 {
33 return error({status, code, std::move(msg)});
34 }
35
37 {
38 private:
39 ccf::RESTVerb verb;
40 std::string url;
41
42 std::string whole_path;
43 std::string path;
44 std::string query;
45 std::string fragment;
46
47 ccf::http::HeaderMap request_headers;
48
49 std::vector<uint8_t> request_body;
50
51 std::vector<uint8_t> serialised_request;
52
53 ccf::http::HeaderMap response_headers;
54 ccf::http::HeaderMap response_trailers;
55 std::vector<uint8_t> response_body;
56 ccf::http_status response_status = HTTP_STATUS_OK;
57
58 bool serialised = false;
59
60 std::optional<bool> explicit_apply_writes = std::nullopt;
61
62 void serialise()
63 {
64 if (!serialised)
65 {
66 const auto request_prefix = fmt::format(
67 "{} {} HTTP/1.1\r\n"
68 "{}"
69 "\r\n",
70 verb.c_str(),
71 url,
72 ::http::get_header_string(request_headers));
73
74 serialised_request.resize(request_prefix.size() + request_body.size());
75 ::memcpy(
76 serialised_request.data(),
77 request_prefix.data(),
78 request_prefix.size());
79 if (!request_body.empty())
80 {
81 ::memcpy(
82 serialised_request.data() + request_prefix.size(),
83 request_body.data(),
84 request_body.size());
85 }
86 }
87
88 serialised = true;
89 }
90
91 public:
93 std::shared_ptr<ccf::SessionContext> s,
95 llhttp_method verb_,
96 const std::string_view& url_,
97 ccf::http::HeaderMap headers_,
98 const std::vector<uint8_t>& body_,
99 const std::vector<uint8_t>& raw_request_ = {}) :
101 verb(verb_),
102 url(url_),
103 request_headers(std::move(headers_)),
104 request_body(body_),
105 serialised_request(raw_request_)
106 {
107 const auto [path_, query_, fragment_] = split_url_path(url);
108 // NOLINTBEGIN(cppcoreguidelines-prefer-member-initializer)
109 path = path_;
110 whole_path = path_;
111 query = url_decode(query_);
112 fragment = url_decode(fragment_);
113
114 if (!serialised_request.empty())
115 {
116 serialised = true;
117 }
118 // NOLINTEND(cppcoreguidelines-prefer-member-initializer)
119 }
120
122 {
123 return response_headers;
124 }
125
127 {
128 return response_trailers;
129 }
130
132 {
133 return response_status;
134 }
135
136 [[nodiscard]] ccf::FrameFormat frame_format() const override
137 {
139 }
140
141 [[nodiscard]] const std::vector<uint8_t>& get_request_body() const override
142 {
143 return request_body;
144 }
145
146 [[nodiscard]] const std::string& get_request_query() const override
147 {
148 return query;
149 }
150
151 [[nodiscard]] const ccf::RESTVerb& get_request_verb() const override
152 {
153 return verb;
154 }
155
156 [[nodiscard]] std::string get_request_path() const override
157 {
158 return whole_path;
159 }
160
161 const std::vector<uint8_t>& get_serialised_request() override
162 {
163 serialise();
164 return serialised_request;
165 }
166
167 [[nodiscard]] std::string get_method() const override
168 {
169 return path;
170 }
171
172 void set_method(const std::string_view& p)
173 {
174 path = p;
175 }
176
178 const override
179 {
180 return request_headers;
181 }
182
183 [[nodiscard]] std::optional<std::string> get_request_header(
184 const std::string_view& name) const override
185 {
186 const auto it = request_headers.find(name);
187 if (it != request_headers.end())
188 {
189 return it->second;
190 }
191
192 return std::nullopt;
193 }
194
195 [[nodiscard]] const std::string& get_request_url() const override
196 {
197 return url;
198 }
199
200 template <typename T>
201 void _set_response_body(T&& body)
202 {
203 // HEAD responses must not contain a body - clients will ignore it
204 if (verb != HTTP_HEAD)
205 {
206 if constexpr (std::is_same_v<T, std::string>)
207 {
208 response_body = std::vector<uint8_t>(body.begin(), body.end());
209 }
210 else
211 {
212 response_body = std::forward<T>(body);
213 }
214 }
215 }
216
217 void set_response_body(const std::vector<uint8_t>& body) override
218 {
219 _set_response_body(body);
220 }
221
222 void set_response_body(std::vector<uint8_t>&& body) override
223 {
224 _set_response_body(std::move(body));
225 }
226
227 void set_response_body(std::string&& body) override
228 {
229 _set_response_body(std::move(body));
230 }
231
232 [[nodiscard]] const std::vector<uint8_t>& get_response_body() const override
233 {
234 return response_body;
235 }
236
237 std::vector<uint8_t>&& take_response_body() override
238 {
239 return std::move(response_body);
240 }
241
242 void set_response_status(int status) override
243 {
244 response_status = (ccf::http_status)status;
245 }
246
247 [[nodiscard]] int get_response_status() const override
248 {
249 return response_status;
250 }
251
253 const std::string_view& name, const std::string_view& value) override
254 {
255 response_headers[std::string(name)] = value;
256 }
257
259 {
260 response_headers.clear();
261 }
262
264 const std::string_view& name, const std::string_view& value) override
265 {
266 response_trailers[std::string(name)] = value;
267 }
268
269 void set_apply_writes(bool apply) override
270 {
271 explicit_apply_writes = apply;
272 }
273
274 [[nodiscard]] bool should_apply_writes() const override
275 {
276 if (explicit_apply_writes.has_value())
277 {
278 return explicit_apply_writes.value();
279 }
280
281 // Default is to apply any 2xx status
282 return status_success(response_status);
283 }
284
285 void reset_response() override
286 {
287 response_headers.clear();
288 response_body.clear();
289 response_status = HTTP_STATUS_OK;
290 explicit_apply_writes.reset();
291 consensus_committed_func = nullptr;
292 }
293
294 [[nodiscard]] std::vector<uint8_t> serialise_response() const override
295 {
296 auto http_response = ::http::Response(response_status);
297
298 for (const auto& [k, v] : response_headers)
299 {
300 http_response.set_header(k, v);
301 }
302
303 http_response.set_body(&response_body);
304 return http_response.build_response();
305 }
306 };
307
308 inline static std::optional<std::string> extract_actor(HttpRpcContext& ctx)
309 {
310 const auto path = ctx.get_method();
311 const auto first_slash = path.find_first_of('/');
312 const auto second_slash = path.find_first_of('/', first_slash + 1);
313
314 if (first_slash != 0 || second_slash == std::string::npos)
315 {
316 return std::nullopt;
317 }
318
319 auto actor = path.substr(first_slash + 1, second_slash - first_slash - 1);
320 auto remaining_path = path.substr(second_slash);
321
322 if (actor.empty() || remaining_path.empty())
323 {
324 return std::nullopt;
325 }
326
327 // if the extracted actor is a known type, set the remaining path
328 if (ccf::is_valid_actor(actor))
329 {
330 ctx.set_method(remaining_path);
331 }
332 return actor;
333 }
334
335 inline static std::shared_ptr<ccf::RpcHandler> fetch_rpc_handler(
336 std::shared_ptr<http::HttpRpcContext>& ctx,
337 std::shared_ptr<ccf::RPCMap>& rpc_map)
338 {
339 const auto actor_opt = http::extract_actor(*ctx);
340 std::optional<std::shared_ptr<ccf::RpcHandler>> search;
342
343 if (actor_opt.has_value())
344 {
345 const auto& actor_s = actor_opt.value();
346 actor = rpc_map->resolve(actor_s);
347 search = rpc_map->find(actor);
348 }
349 if (
350 !actor_opt.has_value() || actor == ccf::ActorsType::unknown ||
351 !search.has_value())
352 {
353 // if there is no actor, proceed with the "app" as the ActorType and
354 // process the request
355 search = rpc_map->find(ccf::ActorsType::users);
356 }
357 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
358 return *search;
359 }
360}
361
362namespace ccf
363{
364 inline std::shared_ptr<::http::HttpRpcContext> make_rpc_context(
365 std::shared_ptr<ccf::SessionContext> s, const std::vector<uint8_t>& packed)
366 {
368 ::http::RequestParser parser(processor, http::permissive_configuration());
369 parser.execute(packed.data(), packed.size());
370
371 if (processor.received.size() != 1)
372 {
373 throw std::logic_error(fmt::format(
374 "Expected packed to contain a single complete HTTP message. Actually "
375 "parsed {} messages",
376 processor.received.size()));
377 }
378
379 const auto& msg = processor.received.front();
380
381 return std::make_shared<::http::HttpRpcContext>(
382 s,
384 msg.method,
385 msg.url,
386 msg.headers,
387 msg.body,
388 packed);
389 }
390
391 inline std::shared_ptr<::http::HttpRpcContext> make_fwd_rpc_context(
392 std::shared_ptr<ccf::SessionContext> s,
393 const std::vector<uint8_t>& packed,
394 ccf::FrameFormat frame_format)
395 {
396 switch (frame_format)
397 {
399 {
400 return make_rpc_context(s, packed);
401 }
402 default:
403 throw std::logic_error("Unknown Frame Format");
404 }
405 }
406}
Definition rest_verb.h:45
const char * c_str() const
Definition rest_verb.h:62
Definition rpc_context_impl.h:22
RpcContextImpl(const std::shared_ptr< SessionContext > &s, HttpVersion v=HttpVersion::HTTP1)
Definition rpc_context_impl.h:30
HttpVersion http_version
Definition rpc_context_impl.h:25
ccf::endpoints::ConsensusCommittedEndpointFunction consensus_committed_func
Definition rpc_context_impl.h:111
Definition http_rpc_context.h:37
ccf::FrameFormat frame_format() const override
Definition http_rpc_context.h:136
void set_apply_writes(bool apply) override
Definition http_rpc_context.h:269
void reset_response() override
Definition http_rpc_context.h:285
int get_response_status() const override
Definition http_rpc_context.h:247
void set_method(const std::string_view &p)
Definition http_rpc_context.h:172
std::optional< std::string > get_request_header(const std::string_view &name) const override
Definition http_rpc_context.h:183
bool should_apply_writes() const override
Definition http_rpc_context.h:274
void set_response_header(const std::string_view &name, const std::string_view &value) override
Definition http_rpc_context.h:252
void set_response_body(const std::vector< uint8_t > &body) override
Sets the main body or payload of the response.
Definition http_rpc_context.h:217
const ccf::RESTVerb & get_request_verb() const override
Definition http_rpc_context.h:151
HttpRpcContext(std::shared_ptr< ccf::SessionContext > s, ccf::HttpVersion http_version, llhttp_method verb_, const std::string_view &url_, ccf::http::HeaderMap headers_, const std::vector< uint8_t > &body_, const std::vector< uint8_t > &raw_request_={})
Definition http_rpc_context.h:92
std::vector< uint8_t > serialise_response() const override
Definition http_rpc_context.h:294
ccf::http::HeaderMap get_response_trailers() const
Definition http_rpc_context.h:126
void set_response_body(std::vector< uint8_t > &&body) override
Sets the main body or payload of the response.
Definition http_rpc_context.h:222
void _set_response_body(T &&body)
Definition http_rpc_context.h:201
const std::vector< uint8_t > & get_request_body() const override
Definition http_rpc_context.h:141
const std::vector< uint8_t > & get_serialised_request() override
Definition http_rpc_context.h:161
const std::string & get_request_query() const override
Definition http_rpc_context.h:146
std::string get_method() const override
Definition http_rpc_context.h:167
const ccf::http::HeaderMap & get_request_headers() const override
Returns map of all headers found in the request.
Definition http_rpc_context.h:177
void set_response_trailer(const std::string_view &name, const std::string_view &value) override
Definition http_rpc_context.h:263
ccf::http::HeaderMap get_response_headers() const
Definition http_rpc_context.h:121
const std::string & get_request_url() const override
Definition http_rpc_context.h:195
void set_response_body(std::string &&body) override
Sets the main body or payload of the response.
Definition http_rpc_context.h:227
const std::vector< uint8_t > & get_response_body() const override
Definition http_rpc_context.h:232
std::vector< uint8_t > && take_response_body() override
Definition http_rpc_context.h:237
std::string get_request_path() const override
Definition http_rpc_context.h:156
ccf::http_status get_response_http_status() const
Definition http_rpc_context.h:131
void set_response_status(int status) override
Sets initial status code summarising result of RPC.
Definition http_rpc_context.h:242
void clear_response_headers() override
Definition http_rpc_context.h:258
void execute(const uint8_t *data, size_t size)
Definition http_parser.h:232
Definition http_parser.h:406
Definition http_builder.h:200
std::map< std::string, std::string, std::less<> > HeaderMap
Definition http_header_map.h:10
Definition app_interface.h:13
std::shared_ptr<::http::HttpRpcContext > make_fwd_rpc_context(std::shared_ptr< ccf::SessionContext > s, const std::vector< uint8_t > &packed, ccf::FrameFormat frame_format)
Definition http_rpc_context.h:391
bool is_valid_actor(const std::string &actor)
Definition actors.h:19
llhttp_status http_status
Definition http_status.h:9
HttpVersion
Definition rpc_context_impl.h:12
FrameFormat
Definition frame_format.h:8
ActorsType
Definition actors.h:11
std::shared_ptr<::http::HttpRpcContext > make_rpc_context(std::shared_ptr< ccf::SessionContext > s, const std::vector< uint8_t > &packed)
Definition http_rpc_context.h:364
Definition error_reporter.h:8
std::vector< uint8_t > error(ccf::ErrorDetails &&error)
Definition http_rpc_context.h:13
auto split_url_path(const std::string_view &url)
Definition http_parser.h:23
bool status_success(ccf::http_status status)
Definition http_parser.h:73
STL namespace.
Definition odata_error.h:58
Definition odata_error.h:50
Definition odata_error.h:37
Definition http_parser.h:79
std::queue< Request > received
Definition http_parser.h:91