CCF
Loading...
Searching...
No Matches
http_session.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
8#include "enclave/rpc_map.h"
9#include "error_reporter.h"
10#include "http_parser.h"
11#include "http_responder.h"
12#include "http_rpc_context.h"
13
14namespace http
15{
17
21 {
22 private:
23 http::RequestParser request_parser;
24
25 std::shared_ptr<ccf::RPCMap> rpc_map;
26 std::shared_ptr<ccf::RpcHandler> handler;
27 std::shared_ptr<ccf::SessionContext> session_ctx;
28 std::shared_ptr<ErrorReporter> error_reporter;
29 std::shared_ptr<ccf::CommitCallbackSubsystem> commit_callbacks;
30 ccf::ListenInterfaceID interface_id;
31
32 public:
34 std::shared_ptr<ccf::RPCMap> rpc_map_,
35 ::tcp::ConnID session_id_,
36 ccf::ListenInterfaceID interface_id_,
38 std::unique_ptr<ccf::tls::Context> ctx,
39 const ccf::http::ParserConfiguration& configuration,
40 const std::shared_ptr<ErrorReporter>& error_reporter_,
41 const std::shared_ptr<ccf::CommitCallbackSubsystem>& commit_callbacks_) :
42 HTTPSession(session_id_, writer_factory, std::move(ctx)),
43 request_parser(*this, configuration),
44 rpc_map(std::move(rpc_map_)),
45 error_reporter(error_reporter_),
46 commit_callbacks(commit_callbacks_),
47 interface_id(std::move(interface_id_))
48 {}
49
50 bool parse(std::span<const uint8_t> data) override
51 {
52 // Catch request parsing errors and convert them to error responses
53 try
54 {
55 request_parser.execute(data.data(), data.size());
56
57 return true;
58 }
60 {
61 if (error_reporter)
62 {
63 error_reporter->report_request_payload_too_large_error(interface_id);
64 }
65
66 LOG_DEBUG_FMT("Request is too large: {}", e.what());
67
69 HTTP_STATUS_PAYLOAD_TOO_LARGE,
70 ccf::errors::RequestBodyTooLarge,
71 e.what()});
72
74 }
76 {
77 if (error_reporter)
78 {
79 error_reporter->report_request_header_too_large_error(interface_id);
80 }
81
82 LOG_DEBUG_FMT("Request header is too large: {}", e.what());
83
85 HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE,
86 ccf::errors::RequestHeaderTooLarge,
87 e.what()});
88
90 }
91 catch (const std::exception& e)
92 {
93 if (error_reporter)
94 {
95 error_reporter->report_parsing_error(interface_id);
96 }
97 LOG_DEBUG_FMT("Error parsing HTTP request: {}", e.what());
98
100 headers[ccf::http::headers::CONTENT_TYPE] =
101 ccf::http::headervalues::contenttype::TEXT;
102
103 // NB: Avoid formatting input data a string, as it may contain null
104 // bytes. Instead insert it at the end of this message, verbatim
105 auto body_s = fmt::format(
106 "Unable to parse data as a HTTP request. Error message is: {}\n"
107 "Error occurred while parsing fragment:\n",
108 e.what());
109 std::vector<uint8_t> response_body(
110 std::begin(body_s), std::end(body_s));
111 response_body.insert(response_body.end(), data.begin(), data.end());
112
114 HTTP_STATUS_BAD_REQUEST,
115 std::move(headers),
116 {},
117 std::move(response_body));
118
120 }
121
122 return false;
123 }
124
126 llhttp_method verb,
127 const std::string_view& url,
128 ccf::http::HeaderMap&& headers,
129 std::vector<uint8_t>&& body,
130 int32_t /*stream_id*/) override
131 {
133 "Processing msg({}, {} [{} bytes])",
134 llhttp_method_name(verb),
135 url,
136 body.size());
137
138 try
139 {
140 if (session_ctx == nullptr)
141 {
142 session_ctx = std::make_shared<ccf::SessionContext>(
143 session_id, tls_io->peer_cert(), interface_id);
144 }
145
146 std::shared_ptr<http::HttpRpcContext> rpc_ctx = nullptr;
147 try
148 {
149 rpc_ctx = std::make_shared<HttpRpcContext>(
150 session_ctx,
152 verb,
153 url,
154 std::move(headers),
155 std::move(body));
156 }
157 catch (std::exception& e)
158 {
160 HTTP_STATUS_INTERNAL_SERVER_ERROR,
161 ccf::errors::InternalError,
162 fmt::format("Error constructing RpcContext: {}", e.what())});
164 return;
165 }
166
167 std::shared_ptr<ccf::RpcHandler> search =
168 http::fetch_rpc_handler(rpc_ctx, rpc_map);
169
170 search->process(rpc_ctx);
171
172 if (rpc_ctx->response_is_pending)
173 {
174 // If the RPC is pending, hold the connection.
175 LOG_TRACE_FMT("Pending");
176 return;
177 }
178
179 const auto& respond_on_commit = rpc_ctx->respond_on_commit;
180 if (respond_on_commit.has_value())
181 {
182 const auto& info = respond_on_commit.value();
183 auto tx_id = info.tx_id;
184 auto committed_func = info.committed_func;
185 auto ws_digest = info.write_set_digest;
186 auto ce = info.commit_evidence;
187 auto claims = info.claims_digest;
188
189 // Block any future work from happening on this session, to
190 // maintain session consistency
192
193 // shared_from_this returns a base session type
194 std::shared_ptr<ccf::ThreadedSession> self = shared_from_this();
195
196 // Register for a callback when this TxID is committed (or
197 // invalidated)
198 commit_callbacks->add_callback(
199 tx_id,
200 [self, rpc_ctx, paused_task, committed_func, ws_digest, ce, claims](
201 ccf::TxID transaction_id, ccf::FinalTxStatus status) {
202 try
203 {
204 // Build the context and let the handler modify the response
206 rpc_ctx, transaction_id, status, ws_digest, ce, claims};
207 committed_func(info);
208
209 // Write the response
211 *self,
212 rpc_ctx->get_response_http_status(),
213 rpc_ctx->get_response_headers(),
214 rpc_ctx->get_response_trailers(),
215 std::move(rpc_ctx->take_response_body()));
216 }
217 catch (const std::exception& e)
218 {
220 "Exception thrown while executing commit callback for {}: {}",
221 transaction_id.to_str(),
222 e.what());
223 rpc_ctx->terminate_session = true;
224 }
225
226 if (rpc_ctx->terminate_session)
227 {
228 self->close_session();
229 }
230
231 // Resume processing work for this session
232 ccf::tasks::resume_task(paused_task);
233 });
234 }
235 else
236 {
238 rpc_ctx->get_response_http_status(),
239 rpc_ctx->get_response_headers(),
240 rpc_ctx->get_response_trailers(),
241 std::move(rpc_ctx->take_response_body()));
242
243 if (rpc_ctx->terminate_session)
244 {
246 }
247 }
248 }
249 catch (const std::exception& e)
250 {
252 HTTP_STATUS_INTERNAL_SERVER_ERROR,
253 ccf::errors::InternalError,
254 fmt::format("Exception: {}", e.what())});
255
256 // On any exception, close the connection.
257 LOG_FAIL_FMT("Closing connection");
258 LOG_DEBUG_FMT("Closing connection due to exception: {}", e.what());
260 throw;
261 }
262 }
263
265 ccf::ThreadedSession& session,
266 ccf::http_status status_code,
267 ccf::http::HeaderMap&& headers,
268 ccf::http::HeaderMap&& trailers,
269 std::vector<uint8_t>&& body)
270 {
271 if (!trailers.empty())
272 {
273 throw std::logic_error("Cannot return trailers over HTTP/1");
274 }
275
276 auto response = ::http::Response(status_code);
277 for (const auto& [k, v] : headers)
278 {
279 response.set_header(k, v);
280 }
281
282 response.set_body(
283 body.data(),
284 body.size(),
285 false /* Don't overwrite any existing content-length header */
286 );
287
288 session.send_data(response.build_response());
289 return true;
290 }
291
293 ccf::http_status status_code,
294 ccf::http::HeaderMap&& headers,
295 ccf::http::HeaderMap&& trailers,
296 std::vector<uint8_t>&& body) override
297 {
298 return send_response_impl(
299 *this,
300 status_code,
301 std::move(headers),
302 std::move(trailers),
303 std::move(body));
304 }
305 };
306
308 public ccf::ClientSession,
310 {
311 private:
312 ::http::ResponseParser response_parser;
313
314 public:
316 ::tcp::ConnID session_id_,
317 ringbuffer::AbstractWriterFactory& writer_factory,
318 std::unique_ptr<ccf::tls::Context> ctx) :
319 HTTPSession(session_id_, writer_factory, std::move(ctx)),
320 ClientSession(session_id_, writer_factory),
321 response_parser(*this)
322 {}
323
324 bool parse(std::span<const uint8_t> data) override
325 {
326 // Catch response parsing errors and log them
327 try
328 {
329 response_parser.execute(data.data(), data.size());
330
331 return true;
332 }
333 catch (const std::exception& e)
334 {
335 LOG_FAIL_FMT("Error parsing HTTP response on session {}", session_id);
336 LOG_DEBUG_FMT("Error parsing HTTP response: {}", e.what());
338 "Error occurred while parsing fragment {} byte fragment:\n{}",
339 data.size(),
340 std::string_view(
341 reinterpret_cast<char const*>(data.data()), data.size()));
342
344 }
345 return false;
346 }
347
348 void send_request(http::Request&& request) override
349 {
350 auto data = request.build_request();
351 send_data(std::move(data));
352 }
353
355 const std::string& hostname,
356 const std::string& service,
357 const HandleDataCallback f,
358 const HandleErrorCallback e) override
359 {
360 tls_io->set_handshake_error_cb([e](std::string&& error_msg) {
361 if (e)
362 {
363 e(error_msg);
364 }
365 else
366 {
367 LOG_FAIL_FMT("{}", error_msg);
368 }
369 });
370
371 ccf::ClientSession::connect(hostname, service, f, e);
372 }
373
375 ccf::http_status status,
376 ccf::http::HeaderMap&& headers,
377 std::vector<uint8_t>&& body) override
378 {
379 handle_data_cb(status, std::move(headers), std::move(body));
380
381 LOG_TRACE_FMT("Closing connection, message handled");
383 }
384 };
385
387
389 public ccf::ClientSession,
391 {
392 private:
393 ::http::ResponseParser response_parser;
394
395 public:
397 ::tcp::ConnID session_id_,
398 ringbuffer::AbstractWriterFactory& writer_factory) :
399 UnencryptedHTTPSession(session_id_, writer_factory),
400 ClientSession(session_id_, writer_factory),
401 response_parser(*this)
402 {}
403
404 bool parse(std::span<const uint8_t> data) override
405 {
406 try
407 {
408 response_parser.execute(data.data(), data.size());
409 return true;
410 }
411 catch (const std::exception& e)
412 {
413 LOG_FAIL_FMT("Error parsing HTTP response on session {}", session_id);
414 LOG_DEBUG_FMT("Error parsing HTTP response: {}", e.what());
416 "Error occurred while parsing fragment {} byte fragment:\n{}",
417 data.size(),
418 std::string_view(
419 reinterpret_cast<char const*>(data.data()), data.size()));
420
422 }
423 return false;
424 }
425
426 void send_request(http::Request&& request) override
427 {
428 auto data = request.build_request();
429 send_data(std::move(data));
430 }
431
433 const std::string& hostname,
434 const std::string& service,
435 const HandleDataCallback f,
436 const HandleErrorCallback e) override
437 {
438 ccf::ClientSession::connect(hostname, service, f, e);
439 }
440
442 ccf::http_status status,
443 ccf::http::HeaderMap&& headers,
444 std::vector<uint8_t>&& body) override
445 {
446 handle_data_cb(status, std::move(headers), std::move(body));
447 LOG_TRACE_FMT("Closing connection, message handled");
449 }
450 };
451}
Definition client_session.h:11
virtual void connect(const std::string &hostname, const std::string &service, const HandleDataCallback f, const HandleErrorCallback e=nullptr)
Definition client_session.h:41
std::function< void(const std::string &error_msg)> HandleErrorCallback
Definition client_session.h:21
std::function< void(ccf::http_status status, http::HeaderMap &&headers, std::vector< uint8_t > &&body)> HandleDataCallback
Definition client_session.h:18
HandleDataCallback handle_data_cb
Definition client_session.h:24
Definition session.h:121
::tcp::ConnID session_id
Definition session.h:127
std::shared_ptr< ccf::TLSSession > tls_io
Definition session.h:126
Definition session.h:18
void close_session() override
Definition session.h:109
void send_data(std::vector< uint8_t > &&data) override
Definition session.h:101
Definition session.h:190
::tcp::ConnID session_id
Definition session.h:195
Definition http_responder.h:14
bool send_odata_error_response(ccf::ErrorDetails &&error)
Definition http_responder.h:24
Definition http_session.h:310
HTTPClientSession(::tcp::ConnID session_id_, ringbuffer::AbstractWriterFactory &writer_factory, std::unique_ptr< ccf::tls::Context > ctx)
Definition http_session.h:315
void send_request(http::Request &&request) override
Definition http_session.h:348
void handle_response(ccf::http_status status, ccf::http::HeaderMap &&headers, std::vector< uint8_t > &&body) override
Definition http_session.h:374
bool parse(std::span< const uint8_t > data) override
Definition http_session.h:324
void connect(const std::string &hostname, const std::string &service, const HandleDataCallback f, const HandleErrorCallback e) override
Definition http_session.h:354
Definition http_session.h:21
static bool send_response_impl(ccf::ThreadedSession &session, ccf::http_status status_code, ccf::http::HeaderMap &&headers, ccf::http::HeaderMap &&trailers, std::vector< uint8_t > &&body)
Definition http_session.h:264
bool send_response(ccf::http_status status_code, ccf::http::HeaderMap &&headers, ccf::http::HeaderMap &&trailers, std::vector< uint8_t > &&body) override
Definition http_session.h:292
void handle_request(llhttp_method verb, const std::string_view &url, ccf::http::HeaderMap &&headers, std::vector< uint8_t > &&body, int32_t) override
Definition http_session.h:125
bool parse(std::span< const uint8_t > data) override
Definition http_session.h:50
HTTPServerSession(std::shared_ptr< ccf::RPCMap > rpc_map_, ::tcp::ConnID session_id_, ccf::ListenInterfaceID interface_id_, ringbuffer::AbstractWriterFactory &writer_factory, std::unique_ptr< ccf::tls::Context > ctx, const ccf::http::ParserConfiguration &configuration, const std::shared_ptr< ErrorReporter > &error_reporter_, const std::shared_ptr< ccf::CommitCallbackSubsystem > &commit_callbacks_)
Definition http_session.h:33
void execute(const uint8_t *data, size_t size)
Definition http_parser.h:232
Definition http_exceptions.h:40
Definition http_parser.h:406
Definition http_exceptions.h:31
Definition http_proc.h:20
Definition http_builder.h:117
Definition http_parser.h:466
Definition http_proc.h:33
Definition http_builder.h:200
Definition http_session.h:391
bool parse(std::span< const uint8_t > data) override
Definition http_session.h:404
void send_request(http::Request &&request) override
Definition http_session.h:426
void handle_response(ccf::http_status status, ccf::http::HeaderMap &&headers, std::vector< uint8_t > &&body) override
Definition http_session.h:441
UnencryptedHTTPClientSession(::tcp::ConnID session_id_, ringbuffer::AbstractWriterFactory &writer_factory)
Definition http_session.h:396
void connect(const std::string &hostname, const std::string &service, const HandleDataCallback f, const HandleErrorCallback e) override
Definition http_session.h:432
Definition ring_buffer_types.h:157
#define LOG_TRACE_FMT
Definition internal_logger.h:13
#define LOG_DEBUG_FMT
Definition internal_logger.h:14
#define LOG_FAIL_FMT
Definition internal_logger.h:16
std::map< std::string, std::string, std::less<> > HeaderMap
Definition http_header_map.h:10
void resume_task(Resumable resumable)
Definition task_system.cpp:106
std::shared_ptr< IResumable > Resumable
Definition resumable.h:10
Resumable pause_current_task()
Definition task_system.cpp:90
FinalTxStatus
Definition tx_status.h:36
std::string ListenInterfaceID
Definition rpc_context.h:21
llhttp_status http_status
Definition http_status.h:9
Definition error_reporter.h:8
STL namespace.
int64_t ConnID
Definition msg_types.h:9
Definition odata_error.h:58
Definition tx_id.h:44
std::string to_str() const
Definition tx_id.h:48
Definition endpoint_context.h:72
Definition http_configuration.h:24