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