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
5#include "ccf/ds/logger.h"
9#include "enclave/rpc_map.h"
10#include "error_reporter.h"
11#include "http_parser.h"
12#include "http_rpc_context.h"
13
14namespace http
15{
17 {
18 protected:
19 std::shared_ptr<ccf::TLSSession> tls_io;
20 std::shared_ptr<ErrorReporter> error_reporter;
22
24 ::tcp::ConnID session_id_,
26 std::unique_ptr<ccf::tls::Context> ctx,
27 const std::shared_ptr<ErrorReporter>& error_reporter = nullptr) :
28 ccf::ThreadedSession(session_id_),
29 tls_io(std::make_shared<ccf::TLSSession>(
30 session_id_, writer_factory, std::move(ctx))),
32 session_id(session_id_)
33 {}
34
35 public:
36 virtual bool parse(std::span<const uint8_t> data) = 0;
37
38 void send_data(std::span<const uint8_t> data) override
39 {
40 // Override send_data rather than send_data_thread, as the TLSSession
41 // handles dispatching for thread affinity
42 tls_io->send_raw(data.data(), data.size());
43 }
44
45 void send_data_thread(std::vector<uint8_t>&& data) override
46 {
47 throw std::logic_error("Unimplemented");
48 }
49
50 void close_session() override
51 {
52 tls_io->close();
53 }
54
55 void handle_incoming_data_thread(std::vector<uint8_t>&& data) override
56 {
57 tls_io->recv_buffered(data.data(), data.size());
58
59 LOG_TRACE_FMT("recv called with {} bytes", data.size());
60
61 // Try to parse all incoming data, reusing the vector we were just passed
62 // for storage. Increase the size if the received vector was too small
63 // (for the case where this chunk is very small, but we had some previous
64 // data to continue reading).
65 constexpr auto min_read_block_size = 4096;
66 if (data.size() < min_read_block_size)
67 {
68 data.resize(min_read_block_size);
69 }
70
71 auto n_read = tls_io->read(data.data(), data.size(), false);
72
73 while (true)
74 {
75 if (n_read == 0)
76 {
77 return;
78 }
79
80 LOG_TRACE_FMT("Going to parse {} bytes", n_read);
81
82 bool cont = parse({data.data(), n_read});
83 if (!cont)
84 {
85 return;
86 }
87
88 // Used all provided bytes - check if more are available
89 n_read = tls_io->read(data.data(), data.size(), false);
90 }
91 }
92 };
93
97 {
98 private:
99 http::RequestParser request_parser;
100
101 std::shared_ptr<ccf::RPCMap> rpc_map;
102 std::shared_ptr<ccf::RpcHandler> handler;
103 std::shared_ptr<ccf::SessionContext> session_ctx;
104 ccf::ListenInterfaceID interface_id;
105
106 public:
108 std::shared_ptr<ccf::RPCMap> rpc_map,
109 ::tcp::ConnID session_id_,
110 const ccf::ListenInterfaceID& interface_id,
111 ringbuffer::AbstractWriterFactory& writer_factory,
112 std::unique_ptr<ccf::tls::Context> ctx,
113 const ccf::http::ParserConfiguration& configuration,
114 const std::shared_ptr<ErrorReporter>& error_reporter = nullptr) :
115 HTTPSession(session_id_, writer_factory, std::move(ctx), error_reporter),
116 request_parser(*this, configuration),
117 rpc_map(rpc_map),
118 interface_id(interface_id)
119 {}
120
121 bool parse(std::span<const uint8_t> data) override
122 {
123 // Catch request parsing errors and convert them to error responses
124 try
125 {
126 request_parser.execute(data.data(), data.size());
127
128 return true;
129 }
131 {
132 if (error_reporter)
133 {
134 error_reporter->report_request_payload_too_large_error(interface_id);
135 }
136
137 LOG_DEBUG_FMT("Request is too large: {}", e.what());
138
140 HTTP_STATUS_PAYLOAD_TOO_LARGE,
141 ccf::errors::RequestBodyTooLarge,
142 e.what()});
143
144 tls_io->close();
145 }
147 {
148 if (error_reporter)
149 {
150 error_reporter->report_request_header_too_large_error(interface_id);
151 }
152
153 LOG_DEBUG_FMT("Request header is too large: {}", e.what());
154
156 HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE,
157 ccf::errors::RequestHeaderTooLarge,
158 e.what()});
159
160 tls_io->close();
161 }
162 catch (const std::exception& e)
163 {
164 if (error_reporter)
165 {
166 error_reporter->report_parsing_error(interface_id);
167 }
168 LOG_DEBUG_FMT("Error parsing HTTP request: {}", e.what());
169
170 ccf::http::HeaderMap headers;
171 headers[ccf::http::headers::CONTENT_TYPE] =
172 ccf::http::headervalues::contenttype::TEXT;
173
174 // NB: Avoid formatting input data a string, as it may contain null
175 // bytes. Instead insert it at the end of this message, verbatim
176 auto body_s = fmt::format(
177 "Unable to parse data as a HTTP request. Error message is: {}\n"
178 "Error occurred while parsing fragment:\n",
179 e.what());
180 std::vector<uint8_t> response_body(
181 std::begin(body_s), std::end(body_s));
182 response_body.insert(response_body.end(), data.begin(), data.end());
183
185 HTTP_STATUS_BAD_REQUEST,
186 std::move(headers),
187 {},
188 std::move(response_body));
189
190 tls_io->close();
191 }
192
193 return false;
194 }
195
197 llhttp_method verb,
198 const std::string_view& url,
199 ccf::http::HeaderMap&& headers,
200 std::vector<uint8_t>&& body,
201 int32_t) override
202 {
204 "Processing msg({}, {} [{} bytes])",
205 llhttp_method_name(verb),
206 url,
207 body.size());
208
209 try
210 {
211 if (session_ctx == nullptr)
212 {
213 session_ctx = std::make_shared<ccf::SessionContext>(
214 session_id, tls_io->peer_cert(), interface_id);
215 }
216
217 std::shared_ptr<http::HttpRpcContext> rpc_ctx = nullptr;
218 try
219 {
220 rpc_ctx = std::make_shared<HttpRpcContext>(
221 session_ctx,
223 verb,
224 url,
225 std::move(headers),
226 std::move(body));
227 }
228 catch (std::exception& e)
229 {
231 HTTP_STATUS_INTERNAL_SERVER_ERROR,
232 ccf::errors::InternalError,
233 fmt::format("Error constructing RpcContext: {}", e.what())});
234 tls_io->close();
235 }
236
237 std::shared_ptr<ccf::RpcHandler> search =
238 http::fetch_rpc_handler(rpc_ctx, rpc_map);
239
240 search->process(rpc_ctx);
241
242 if (rpc_ctx->response_is_pending)
243 {
244 // If the RPC is pending, hold the connection.
245 LOG_TRACE_FMT("Pending");
246 return;
247 }
248 else
249 {
251 rpc_ctx->get_response_http_status(),
252 rpc_ctx->get_response_headers(),
253 rpc_ctx->get_response_trailers(),
254 std::move(rpc_ctx->get_response_body()));
255
256 if (rpc_ctx->terminate_session)
257 {
258 tls_io->close();
259 }
260 }
261 }
262 catch (const std::exception& e)
263 {
265 HTTP_STATUS_INTERNAL_SERVER_ERROR,
266 ccf::errors::InternalError,
267 fmt::format("Exception: {}", e.what())});
268
269 // On any exception, close the connection.
270 LOG_FAIL_FMT("Closing connection");
271 LOG_DEBUG_FMT("Closing connection due to exception: {}", e.what());
272 tls_io->close();
273 throw;
274 }
275 }
276
278 http_status status_code,
279 ccf::http::HeaderMap&& headers,
280 ccf::http::HeaderMap&& trailers,
281 std::span<const uint8_t> body) override
282 {
283 if (!trailers.empty())
284 {
285 throw std::logic_error("Cannot return trailers over HTTP/1");
286 }
287
288 auto response = ::http::Response(status_code);
289 for (const auto& [k, v] : headers)
290 {
291 response.set_header(k, v);
292 }
293 response.set_body(body.data(), body.size());
294
295 auto data = response.build_response();
296 tls_io->send_raw(data.data(), data.size());
297 return true;
298 }
299
301 http_status status, const ccf::http::HeaderMap& headers) override
302 {
303 throw std::logic_error("Not implemented!");
304 }
305
306 bool stream_data(std::span<const uint8_t> data) override
307 {
308 throw std::logic_error("Not implemented!");
309 }
310
312 {
313 throw std::logic_error("Not implemented!");
314 }
315
318 {
319 throw std::logic_error("Not implemented!");
320 }
321 };
322
324 public ccf::ClientSession,
326 {
327 private:
328 ::http::ResponseParser response_parser;
329
330 public:
332 ::tcp::ConnID session_id_,
333 ringbuffer::AbstractWriterFactory& writer_factory,
334 std::unique_ptr<ccf::tls::Context> ctx) :
335 HTTPSession(session_id_, writer_factory, std::move(ctx)),
336 ClientSession(session_id_, writer_factory),
337 response_parser(*this)
338 {}
339
340 bool parse(std::span<const uint8_t> data) override
341 {
342 // Catch response parsing errors and log them
343 try
344 {
345 response_parser.execute(data.data(), data.size());
346
347 return true;
348 }
349 catch (const std::exception& e)
350 {
351 LOG_FAIL_FMT("Error parsing HTTP response on session {}", session_id);
352 LOG_DEBUG_FMT("Error parsing HTTP response: {}", e.what());
354 "Error occurred while parsing fragment {} byte fragment:\n{}",
355 data.size(),
356 std::string_view((char const*)data.data(), data.size()));
357
359 }
360 return false;
361 }
362
363 void send_request(http::Request&& request) override
364 {
365 auto data = request.build_request();
366 send_data(data);
367 }
368
370 const std::string& hostname,
371 const std::string& service,
372 const HandleDataCallback f,
373 const HandleErrorCallback e) override
374 {
375 tls_io->set_handshake_error_cb([e](std::string&& error_msg) {
376 if (e)
377 {
378 e(error_msg);
379 }
380 else
381 {
382 LOG_FAIL_FMT("{}", error_msg);
383 }
384 });
385
386 ccf::ClientSession::connect(hostname, service, f, e);
387 }
388
390 http_status status,
391 ccf::http::HeaderMap&& headers,
392 std::vector<uint8_t>&& body) override
393 {
394 handle_data_cb(status, std::move(headers), std::move(body));
395
396 LOG_TRACE_FMT("Closing connection, message handled");
398 }
399 };
400
402 {
403 protected:
404 std::shared_ptr<ErrorReporter> error_reporter;
409
411 ::tcp::ConnID session_id_,
412 ringbuffer::AbstractWriterFactory& writer_factory_,
413 const std::shared_ptr<ErrorReporter>& error_reporter = nullptr) :
414 ccf::ThreadedSession(session_id_),
416 session_id(session_id_),
417 writer_factory(writer_factory_),
418 to_host(writer_factory.create_writer_to_outside())
419 {
422 session_id_);
423 }
424
425 public:
426 virtual bool parse(std::span<const uint8_t> data) = 0;
427
428 void send_data_thread(std::vector<uint8_t>&& data) override
429 {
431 {
432 throw std::logic_error(
433 "Called UnencryptedHTTPSession::send_data "
434 "from wrong thread");
435 }
437 ::tcp::tcp_outbound,
438 to_host,
440 serializer::ByteRange{data.data(), data.size()});
441 }
442
443 void close_session() override
444 {
446 {
447 throw std::logic_error(
448 "Called UnencryptedHTTPSession::close_session "
449 "from wrong thread");
450 }
452 ::tcp::tcp_stop, to_host, session_id, std::string("Session closed"));
453 }
454
455 void handle_incoming_data_thread(std::vector<uint8_t>&& data) override
456 {
457 parse(data);
458 }
459 };
460
462 public ccf::ClientSession,
464 {
465 private:
466 ::http::ResponseParser response_parser;
467
468 public:
470 ::tcp::ConnID session_id_,
473 ClientSession(session_id_, writer_factory),
474 response_parser(*this)
475 {}
476
477 bool parse(std::span<const uint8_t> data) override
478 {
479 try
480 {
481 response_parser.execute(data.data(), data.size());
482 return true;
483 }
484 catch (const std::exception& e)
485 {
486 LOG_FAIL_FMT("Error parsing HTTP response on session {}", session_id);
487 LOG_DEBUG_FMT("Error parsing HTTP response: {}", e.what());
489 "Error occurred while parsing fragment {} byte fragment:\n{}",
490 data.size(),
491 std::string_view((char const*)data.data(), data.size()));
492
494 }
495 return false;
496 }
497
498 void send_request(http::Request&& request) override
499 {
500 auto data = request.build_request();
501 send_data(data);
502 }
503
505 const std::string& hostname,
506 const std::string& service,
507 const HandleDataCallback f,
508 const HandleErrorCallback e) override
509 {
510 ccf::ClientSession::connect(hostname, service, f, e);
511 }
512
514 http_status status,
515 ccf::http::HeaderMap&& headers,
516 std::vector<uint8_t>&& body) override
517 {
518 handle_data_cb(status, std::move(headers), std::move(body));
519 LOG_TRACE_FMT("Closing connection, message handled");
521 }
522 };
523}
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:39
std::function< void(const std::string &error_msg)> HandleErrorCallback
Definition client_session.h:19
HandleDataCallback handle_data_cb
Definition client_session.h:22
std::function< void(http_status status, http::HeaderMap &&headers, std::vector< uint8_t > &&body)> HandleDataCallback
Definition client_session.h:16
Definition tls_session.h:29
Definition session.h:15
void send_data(std::span< const uint8_t > data) override
Definition session.h:58
Definition http_responder.h:16
bool send_odata_error_response(ccf::ErrorDetails &&error)
Definition http_responder.h:35
Definition http_session.h:326
HTTPClientSession(::tcp::ConnID session_id_, ringbuffer::AbstractWriterFactory &writer_factory, std::unique_ptr< ccf::tls::Context > ctx)
Definition http_session.h:331
void send_request(http::Request &&request) override
Definition http_session.h:363
bool parse(std::span< const uint8_t > data) override
Definition http_session.h:340
void connect(const std::string &hostname, const std::string &service, const HandleDataCallback f, const HandleErrorCallback e) override
Definition http_session.h:369
void handle_response(http_status status, ccf::http::HeaderMap &&headers, std::vector< uint8_t > &&body) override
Definition http_session.h:389
Definition http_session.h:97
bool set_on_stream_close_callback(ccf::http::StreamOnCloseCallback cb) override
Definition http_session.h:316
bool close_stream(ccf::http::HeaderMap &&) override
Definition http_session.h:311
bool stream_data(std::span< const uint8_t > data) override
Definition http_session.h:306
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:196
bool send_response(http_status status_code, ccf::http::HeaderMap &&headers, ccf::http::HeaderMap &&trailers, std::span< const uint8_t > body) override
Definition http_session.h:277
HTTPServerSession(std::shared_ptr< ccf::RPCMap > rpc_map, ::tcp::ConnID session_id_, const 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=nullptr)
Definition http_session.h:107
bool parse(std::span< const uint8_t > data) override
Definition http_session.h:121
bool start_stream(http_status status, const ccf::http::HeaderMap &headers) override
Definition http_session.h:300
Definition http_session.h:17
void send_data(std::span< const uint8_t > data) override
Definition http_session.h:38
std::shared_ptr< ccf::TLSSession > tls_io
Definition http_session.h:19
std::shared_ptr< ErrorReporter > error_reporter
Definition http_session.h:20
void send_data_thread(std::vector< uint8_t > &&data) override
Definition http_session.h:45
void close_session() override
Definition http_session.h:50
HTTPSession(::tcp::ConnID session_id_, ringbuffer::AbstractWriterFactory &writer_factory, std::unique_ptr< ccf::tls::Context > ctx, const std::shared_ptr< ErrorReporter > &error_reporter=nullptr)
Definition http_session.h:23
void handle_incoming_data_thread(std::vector< uint8_t > &&data) override
Definition http_session.h:55
virtual bool parse(std::span< const uint8_t > data)=0
::tcp::ConnID session_id
Definition http_session.h:21
void execute(const uint8_t *data, size_t size)
Definition http_parser.h:221
Definition http_exceptions.h:40
Definition http_parser.h:394
Definition http_exceptions.h:31
Definition http_proc.h:20
Definition http_builder.h:106
Definition http_parser.h:452
Definition http_proc.h:31
Definition http_builder.h:188
Definition http_session.h:464
bool parse(std::span< const uint8_t > data) override
Definition http_session.h:477
void send_request(http::Request &&request) override
Definition http_session.h:498
UnencryptedHTTPClientSession(::tcp::ConnID session_id_, ringbuffer::AbstractWriterFactory &writer_factory)
Definition http_session.h:469
void connect(const std::string &hostname, const std::string &service, const HandleDataCallback f, const HandleErrorCallback e) override
Definition http_session.h:504
void handle_response(http_status status, ccf::http::HeaderMap &&headers, std::vector< uint8_t > &&body) override
Definition http_session.h:513
Definition http_session.h:402
size_t execution_thread
Definition http_session.h:408
::tcp::ConnID session_id
Definition http_session.h:405
void handle_incoming_data_thread(std::vector< uint8_t > &&data) override
Definition http_session.h:455
void send_data_thread(std::vector< uint8_t > &&data) override
Definition http_session.h:428
void close_session() override
Definition http_session.h:443
ringbuffer::WriterPtr to_host
Definition http_session.h:407
UnencryptedHTTPSession(::tcp::ConnID session_id_, ringbuffer::AbstractWriterFactory &writer_factory_, const std::shared_ptr< ErrorReporter > &error_reporter=nullptr)
Definition http_session.h:410
ringbuffer::AbstractWriterFactory & writer_factory
Definition http_session.h:406
std::shared_ptr< ErrorReporter > error_reporter
Definition http_session.h:404
virtual bool parse(std::span< const uint8_t > data)=0
Definition ring_buffer_types.h:153
static ThreadMessaging & instance()
Definition thread_messaging.h:278
uint16_t get_execution_thread(uint32_t i)
Definition thread_messaging.h:365
llhttp_status http_status
Definition http_status.h:7
#define LOG_TRACE_FMT
Definition logger.h:378
#define LOG_DEBUG_FMT
Definition logger.h:380
#define LOG_FAIL_FMT
Definition logger.h:396
std::map< std::string, std::string, std::less<> > HeaderMap
Definition http_header_map.h:10
std::function< void(void)> StreamOnCloseCallback
Definition http_responder.h:13
uint16_t get_current_thread_id()
Definition thread_local.cpp:9
Definition app_interface.h:15
std::string ListenInterfaceID
Definition rpc_context.h:21
Definition error_reporter.h:6
std::shared_ptr< AbstractWriter > WriterPtr
Definition ring_buffer_types.h:150
STL namespace.
int64_t ConnID
Definition msg_types.h:9
#define RINGBUFFER_WRITE_MESSAGE(MSG,...)
Definition ring_buffer_types.h:255
Definition odata_error.h:56
Definition http_configuration.h:24
Definition serializer.h:27
const uint8_t * data
Definition serializer.h:28