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 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 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 const 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(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 path = path_;
114 whole_path = path_;
115 query = url_decode(query_);
116 fragment = url_decode(fragment_);
117
118 if (!serialised_request.empty())
119 {
120 serialised = true;
121 }
122 }
123
125 {
126 return response_headers;
127 }
128
130 {
131 return response_trailers;
132 }
133
134 std::vector<uint8_t>& get_response_body()
135 {
136 return response_body;
137 }
138
140 {
141 return response_status;
142 }
143
144 virtual ccf::FrameFormat frame_format() const override
145 {
147 }
148
149 virtual void set_tx_id(const ccf::TxID& tx_id) override
150 {
151 set_response_header(ccf::http::headers::CCF_TX_ID, tx_id.to_str());
152 }
153
154 virtual const std::vector<uint8_t>& get_request_body() const override
155 {
156 return request_body;
157 }
158
159 virtual const std::string& get_request_query() const override
160 {
161 return query;
162 }
163
164 virtual const ccf::RESTVerb& get_request_verb() const override
165 {
166 return verb;
167 }
168
169 virtual std::string get_request_path() const override
170 {
171 return whole_path;
172 }
173
174 virtual const std::vector<uint8_t>& get_serialised_request() override
175 {
176 serialise();
177 return serialised_request;
178 }
179
180 virtual std::string get_method() const override
181 {
182 return path;
183 }
184
185 void set_method(const std::string_view& p)
186 {
187 path = p;
188 }
189
190 virtual const ccf::http::HeaderMap& get_request_headers() const override
191 {
192 return request_headers;
193 }
194
195 virtual std::optional<std::string> get_request_header(
196 const std::string_view& name) const override
197 {
198 const auto it = request_headers.find(name);
199 if (it != request_headers.end())
200 {
201 return it->second;
202 }
203
204 return std::nullopt;
205 }
206
207 virtual const std::string& get_request_url() const override
208 {
209 return url;
210 }
211
212 virtual std::shared_ptr<ccf::http::HTTPResponder> get_responder()
213 const override
214 {
215 return responder;
216 }
217
218 virtual void set_response_body(const std::vector<uint8_t>& body) override
219 {
220 response_body = body;
221 }
222
223 virtual void set_response_body(std::vector<uint8_t>&& body) override
224 {
225 response_body = std::move(body);
226 }
227
228 virtual void set_response_body(std::string&& body) override
229 {
230 response_body = std::vector<uint8_t>(body.begin(), body.end());
231 }
232
233 virtual const std::vector<uint8_t>& get_response_body() const override
234 {
235 return response_body;
236 }
237
238 virtual void set_response_status(int status) override
239 {
240 response_status = (http_status)status;
241 }
242
243 virtual int get_response_status() const override
244 {
245 return response_status;
246 }
247
249 const std::string_view& name, const std::string_view& value) override
250 {
251 response_headers[std::string(name)] = value;
252 }
253
254 virtual void clear_response_headers() override
255 {
256 response_headers.clear();
257 }
258
260 const std::string_view& name, const std::string_view& value) override
261 {
262 response_trailers[std::string(name)] = value;
263 }
264
265 virtual void set_apply_writes(bool apply) override
266 {
267 explicit_apply_writes = apply;
268 }
269
270 virtual bool should_apply_writes() const override
271 {
272 if (explicit_apply_writes.has_value())
273 {
274 return explicit_apply_writes.value();
275 }
276
277 // Default is to apply any 2xx status
278 return status_success(response_status);
279 }
280
281 virtual void reset_response() override
282 {
283 response_headers.clear();
284 response_body.clear();
285 response_status = HTTP_STATUS_OK;
286 explicit_apply_writes.reset();
287 }
288
289 virtual std::vector<uint8_t> serialise_response() const override
290 {
291 auto http_response = ::http::Response(response_status);
292
293 for (const auto& [k, v] : response_headers)
294 {
295 http_response.set_header(k, v);
296 }
297
298 http_response.set_body(&response_body);
299 return http_response.build_response();
300 }
301 };
302
303 inline static std::optional<std::string> extract_actor(HttpRpcContext& ctx)
304 {
305 const auto path = ctx.get_method();
306 const auto first_slash = path.find_first_of('/');
307 const auto second_slash = path.find_first_of('/', first_slash + 1);
308
309 if (first_slash != 0 || second_slash == std::string::npos)
310 {
311 return std::nullopt;
312 }
313
314 auto actor = path.substr(first_slash + 1, second_slash - first_slash - 1);
315 auto remaining_path = path.substr(second_slash);
316
317 // The "actor" is generally just a single path component, eg. `gov` or
318 // `node`. We make an exception for .well-known paths however, which use
319 // two components, ie. `.well-known/acme-challenge`. This restricts the
320 // ACME frontend to just that particular sub-directory, and allows the
321 // application to handle other .well-known paths.
322 if (actor == ".well-known")
323 {
324 const auto third_slash = path.find_first_of('/', second_slash + 1);
325 if (third_slash == std::string::npos)
326 {
327 return std::nullopt;
328 }
329
330 actor = path.substr(first_slash + 1, third_slash - first_slash - 1);
331 remaining_path = path.substr(third_slash);
332 }
333
334 if (actor.empty() || remaining_path.empty())
335 {
336 return std::nullopt;
337 }
338
339 // if the extracted actor is a known type, set the remaining path
340 if (ccf::is_valid_actor(actor))
341 {
342 ctx.set_method(remaining_path);
343 }
344 return actor;
345 }
346
347 inline static std::shared_ptr<ccf::RpcHandler> fetch_rpc_handler(
348 std::shared_ptr<http::HttpRpcContext>& ctx,
349 std::shared_ptr<ccf::RPCMap>& rpc_map)
350 {
351 const auto actor_opt = http::extract_actor(*ctx);
352 std::optional<std::shared_ptr<ccf::RpcHandler>> search;
354
355 if (actor_opt.has_value())
356 {
357 const auto& actor_s = actor_opt.value();
358 actor = rpc_map->resolve(actor_s);
359 search = rpc_map->find(actor);
360 }
361 if (
362 !actor_opt.has_value() || actor == ccf::ActorsType::unknown ||
363 !search.has_value())
364 {
365 // if there is no actor, proceed with the "app" as the ActorType and
366 // process the request
367 search = rpc_map->find(ccf::ActorsType::users);
368 }
369 return search.value();
370 }
371}
372
373namespace ccf
374{
375 inline std::shared_ptr<::http::HttpRpcContext> make_rpc_context(
376 std::shared_ptr<ccf::SessionContext> s, const std::vector<uint8_t>& packed)
377 {
379 ::http::RequestParser parser(processor, http::permissive_configuration());
380 parser.execute(packed.data(), packed.size());
381
382 if (processor.received.size() != 1)
383 {
384 throw std::logic_error(fmt::format(
385 "Expected packed to contain a single complete HTTP message. Actually "
386 "parsed {} messages",
387 processor.received.size()));
388 }
389
390 const auto& msg = processor.received.front();
391
392 return std::make_shared<::http::HttpRpcContext>(
393 s,
395 msg.method,
396 msg.url,
397 msg.headers,
398 msg.body,
399 nullptr,
400 packed);
401 }
402
403 inline std::shared_ptr<::http::HttpRpcContext> make_fwd_rpc_context(
404 std::shared_ptr<ccf::SessionContext> s,
405 const std::vector<uint8_t>& packed,
406 ccf::FrameFormat frame_format)
407 {
408 switch (frame_format)
409 {
411 {
412 return make_rpc_context(s, packed);
413 }
414 default:
415 throw std::logic_error("Unknown Frame Format");
416 }
417 }
418}
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
Definition http_rpc_context.h:38
std::vector< uint8_t > & get_response_body()
Definition http_rpc_context.h:134
virtual void set_apply_writes(bool apply) override
Definition http_rpc_context.h:265
virtual void set_response_body(std::vector< uint8_t > &&body) override
Sets the main body or payload of the response.
Definition http_rpc_context.h:223
void set_method(const std::string_view &p)
Definition http_rpc_context.h:185
virtual std::string get_request_path() const override
Definition http_rpc_context.h:169
virtual const std::vector< uint8_t > & get_response_body() const override
Definition http_rpc_context.h:233
virtual const std::vector< uint8_t > & get_request_body() const override
Definition http_rpc_context.h:154
virtual void set_tx_id(const ccf::TxID &tx_id) override
Definition http_rpc_context.h:149
ccf::http::HeaderMap get_response_trailers() const
Definition http_rpc_context.h:129
virtual void set_response_body(std::string &&body) override
Sets the main body or payload of the response.
Definition http_rpc_context.h:228
virtual const std::string & get_request_query() const override
Definition http_rpc_context.h:159
virtual ccf::FrameFormat frame_format() const override
Definition http_rpc_context.h:144
virtual void set_response_trailer(const std::string_view &name, const std::string_view &value) override
Definition http_rpc_context.h:259
virtual std::optional< std::string > get_request_header(const std::string_view &name) const override
Definition http_rpc_context.h:195
virtual void clear_response_headers() override
Definition http_rpc_context.h:254
http_status get_response_http_status() const
Definition http_rpc_context.h:139
virtual std::vector< uint8_t > serialise_response() const override
Definition http_rpc_context.h:289
virtual const ccf::http::HeaderMap & get_request_headers() const override
Returns map of all headers found in the request.
Definition http_rpc_context.h:190
HttpRpcContext(std::shared_ptr< ccf::SessionContext > s, ccf::HttpVersion http_version, llhttp_method verb_, const std::string_view &url_, const 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
virtual std::string get_method() const override
Definition http_rpc_context.h:180
ccf::http::HeaderMap get_response_headers() const
Definition http_rpc_context.h:124
virtual const std::string & get_request_url() const override
Definition http_rpc_context.h:207
virtual const std::vector< uint8_t > & get_serialised_request() override
Definition http_rpc_context.h:174
virtual bool should_apply_writes() const override
Definition http_rpc_context.h:270
virtual int get_response_status() const override
Definition http_rpc_context.h:243
virtual 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:218
virtual std::shared_ptr< ccf::http::HTTPResponder > get_responder() const override
Definition http_rpc_context.h:212
virtual void set_response_status(int status) override
Sets initial status code summarising result of RPC.
Definition http_rpc_context.h:238
virtual void set_response_header(const std::string_view &name, const std::string_view &value) override
Definition http_rpc_context.h:248
virtual void reset_response() override
Definition http_rpc_context.h:281
virtual const ccf::RESTVerb & get_request_verb() const override
Definition http_rpc_context.h:164
void execute(const uint8_t *data, size_t size)
Definition http_parser.h:221
Definition http_parser.h:394
Definition http_builder.h:188
llhttp_status http_status
Definition http_status.h:7
std::map< std::string, std::string, std::less<> > HeaderMap
Definition http_header_map.h:10
Definition app_interface.h:15
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:403
bool is_valid_actor(const std::string &actor)
Definition actors.h:20
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:375
Definition error_reporter.h:6
bool status_success(http_status status)
Definition http_parser.h:71
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
Definition odata_error.h:56
Definition odata_error.h:48
Definition odata_error.h:37
Definition tx_id.h:44
std::string to_str() const
Definition tx_id.h:48
Definition http_parser.h:77
std::queue< Request > received
Definition http_parser.h:87