rippled
Loading...
Searching...
No Matches
BaseHTTPPeer.h
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright(c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#ifndef RIPPLE_SERVER_BASEHTTPPEER_H_INCLUDED
21#define RIPPLE_SERVER_BASEHTTPPEER_H_INCLUDED
22
23#include <xrpl/basics/Log.h>
24#include <xrpl/beast/net/IPAddressConversion.h>
25#include <xrpl/beast/utility/instrumentation.h>
26#include <xrpl/server/Session.h>
27#include <xrpl/server/detail/io_list.h>
28
29#include <boost/asio/ip/tcp.hpp>
30#include <boost/asio/spawn.hpp>
31#include <boost/asio/ssl/stream.hpp>
32#include <boost/asio/streambuf.hpp>
33#include <boost/beast/core/stream_traits.hpp>
34#include <boost/beast/http/dynamic_body.hpp>
35#include <boost/beast/http/message.hpp>
36#include <boost/beast/http/parser.hpp>
37#include <boost/beast/http/read.hpp>
38
39#include <atomic>
40#include <chrono>
41#include <functional>
42#include <memory>
43#include <mutex>
44#include <vector>
45
46namespace ripple {
47
49template <class Handler, class Impl>
50class BaseHTTPPeer : public io_list::work, public Session
51{
52protected:
54 using error_code = boost::system::error_code;
55 using endpoint_type = boost::asio::ip::tcp::endpoint;
56 using yield_context = boost::asio::yield_context;
57
58 enum {
59 // Size of our read/write buffer
60 bufferSize = 4 * 1024,
61
62 // Max seconds without completing a message
64 timeoutSecondsLocal = 3 // used for localhost clients
65 };
66
67 struct buffer
68 {
69 buffer(void const* ptr, std::size_t len)
70 : data(new char[len]), bytes(len), used(0)
71 {
72 memcpy(data.get(), ptr, len);
73 }
74
78 };
79
80 Port const& port_;
81 Handler& handler_;
82 boost::asio::executor_work_guard<boost::asio::executor> work_;
83 boost::asio::strand<boost::asio::executor> strand_;
86
89
90 boost::asio::streambuf read_buf_;
95 bool graceful_ = false;
96 bool complete_ = false;
97 boost::system::error_code ec_;
98
102
103 //--------------------------------------------------------------------------
104
105public:
106 template <class ConstBufferSequence>
108 Port const& port,
109 Handler& handler,
110 boost::asio::executor const& executor,
112 endpoint_type remote_address,
113 ConstBufferSequence const& buffers);
114
115 virtual ~BaseHTTPPeer();
116
117 Session&
119 {
120 return *this;
121 }
122
123 void
124 close() override;
125
126protected:
127 Impl&
129 {
130 return *static_cast<Impl*>(this);
131 }
132
133 void
134 fail(error_code ec, char const* what);
135
136 void
138
139 void
141
142 void
144
145 void
147
148 void
149 on_write(error_code const& ec, std::size_t bytes_transferred);
150
151 void
153 std::shared_ptr<Writer> const& writer,
154 bool keep_alive,
155 yield_context do_yield);
156
157 virtual void
159
160 virtual void
161 do_close() = 0;
162
163 // Session
164
166 journal() override
167 {
168 return journal_;
169 }
170
171 Port const&
172 port() override
173 {
174 return port_;
175 }
176
178 remoteAddress() override
179 {
181 }
182
184 request() override
185 {
186 return message_;
187 }
188
189 void
190 write(void const* buffer, std::size_t bytes) override;
191
192 void
193 write(std::shared_ptr<Writer> const& writer, bool keep_alive) override;
194
196 detach() override;
197
198 void
199 complete() override;
200
201 void
202 close(bool graceful) override;
203};
204
205//------------------------------------------------------------------------------
206
207template <class Handler, class Impl>
208template <class ConstBufferSequence>
210 Port const& port,
211 Handler& handler,
212 boost::asio::executor const& executor,
213 beast::Journal journal,
214 endpoint_type remote_address,
215 ConstBufferSequence const& buffers)
216 : port_(port)
217 , handler_(handler)
218 , work_(executor)
219 , strand_(executor)
220 , remote_address_(remote_address)
221 , journal_(journal)
222{
223 read_buf_.commit(boost::asio::buffer_copy(
224 read_buf_.prepare(boost::asio::buffer_size(buffers)), buffers));
225 static std::atomic<int> sid;
226 nid_ = ++sid;
227 id_ = std::string("#") + std::to_string(nid_) + " ";
228 JLOG(journal_.trace()) << id_ << "accept: " << remote_address_.address();
229}
230
231template <class Handler, class Impl>
233{
234 handler_.onClose(session(), ec_);
235 JLOG(journal_.trace()) << id_ << "destroyed: " << request_count_
236 << ((request_count_ == 1) ? " request"
237 : " requests");
238}
239
240template <class Handler, class Impl>
241void
243{
244 if (!strand_.running_in_this_thread())
245 return post(
246 strand_,
247 std::bind(
248 (void(BaseHTTPPeer::*)(void)) & BaseHTTPPeer::close,
249 impl().shared_from_this()));
250 boost::beast::get_lowest_layer(impl().stream_).close();
251}
252
253//------------------------------------------------------------------------------
254
255template <class Handler, class Impl>
256void
258{
259 if (!ec_ && ec != boost::asio::error::operation_aborted)
260 {
261 ec_ = ec;
262 JLOG(journal_.trace())
263 << id_ << std::string(what) << ": " << ec.message();
264 boost::beast::get_lowest_layer(impl().stream_).close();
265 }
266}
267
268template <class Handler, class Impl>
269void
271{
272 boost::beast::get_lowest_layer(impl().stream_)
273 .expires_after(std::chrono::seconds(
274 remote_address_.address().is_loopback() ? timeoutSecondsLocal
275 : timeoutSeconds));
276}
277
278// Convenience for discarding the error code
279template <class Handler, class Impl>
280void
282{
283 boost::beast::get_lowest_layer(impl().stream_).expires_never();
284}
285
286// Called when session times out
287template <class Handler, class Impl>
288void
290{
291 auto ec =
292 boost::system::errc::make_error_code(boost::system::errc::timed_out);
293 fail(ec, "timer");
294}
295
296//------------------------------------------------------------------------------
297
298template <class Handler, class Impl>
299void
301{
302 complete_ = false;
303 error_code ec;
304 start_timer();
305 boost::beast::http::async_read(
306 impl().stream_, read_buf_, message_, do_yield[ec]);
307 cancel_timer();
308 if (ec == boost::beast::http::error::end_of_stream)
309 return do_close();
310 if (ec == boost::beast::error::timeout)
311 return on_timer();
312 if (ec)
313 return fail(ec, "http::read");
314 do_request();
315}
316
317// Send everything in the write queue.
318// The write queue must not be empty upon entry.
319template <class Handler, class Impl>
320void
322 error_code const& ec,
323 std::size_t bytes_transferred)
324{
325 cancel_timer();
326 if (ec == boost::beast::error::timeout)
327 return on_timer();
328 if (ec)
329 return fail(ec, "write");
330 bytes_out_ += bytes_transferred;
331 {
332 std::lock_guard lock(mutex_);
333 wq2_.clear();
334 wq2_.reserve(wq_.size());
335 std::swap(wq2_, wq_);
336 }
337 if (!wq2_.empty())
338 {
340 v.reserve(wq2_.size());
341 for (auto const& b : wq2_)
342 v.emplace_back(b.data.get(), b.bytes);
343 start_timer();
344 return boost::asio::async_write(
345 impl().stream_,
346 v,
347 bind_executor(
348 strand_,
349 std::bind(
351 impl().shared_from_this(),
352 std::placeholders::_1,
353 std::placeholders::_2)));
354 }
355 if (!complete_)
356 return;
357 if (graceful_)
358 return do_close();
359 boost::asio::spawn(
360 strand_,
361 std::bind(
363 impl().shared_from_this(),
364 std::placeholders::_1));
365}
366
367template <class Handler, class Impl>
368void
370 std::shared_ptr<Writer> const& writer,
371 bool keep_alive,
372 yield_context do_yield)
373{
374 std::function<void(void)> resume;
375 {
376 auto const p = impl().shared_from_this();
377 resume = std::function<void(void)>([this, p, writer, keep_alive]() {
378 boost::asio::spawn(
379 strand_,
380 std::bind(
382 p,
383 writer,
384 keep_alive,
385 std::placeholders::_1));
386 });
387 }
388
389 for (;;)
390 {
391 if (!writer->prepare(bufferSize, resume))
392 return;
393 error_code ec;
394 auto const bytes_transferred = boost::asio::async_write(
395 impl().stream_,
396 writer->data(),
397 boost::asio::transfer_at_least(1),
398 do_yield[ec]);
399 if (ec)
400 return fail(ec, "writer");
401 writer->consume(bytes_transferred);
402 if (writer->complete())
403 break;
404 }
405
406 if (!keep_alive)
407 return do_close();
408
409 boost::asio::spawn(
410 strand_,
411 std::bind(
413 impl().shared_from_this(),
414 std::placeholders::_1));
415}
416
417//------------------------------------------------------------------------------
418
419// Send a copy of the data.
420template <class Handler, class Impl>
421void
423{
424 if (bytes == 0)
425 return;
426 if ([&] {
427 std::lock_guard lock(mutex_);
428 wq_.emplace_back(buf, bytes);
429 return wq_.size() == 1 && wq2_.size() == 0;
430 }())
431 {
432 if (!strand_.running_in_this_thread())
433 return post(
434 strand_,
435 std::bind(
437 impl().shared_from_this(),
438 error_code{},
439 0));
440 else
441 return on_write(error_code{}, 0);
442 }
443}
444
445template <class Handler, class Impl>
446void
448 std::shared_ptr<Writer> const& writer,
449 bool keep_alive)
450{
451 boost::asio::spawn(bind_executor(
452 strand_,
453 std::bind(
455 impl().shared_from_this(),
456 writer,
457 keep_alive,
458 std::placeholders::_1)));
459}
460
461// DEPRECATED
462// Make the Session asynchronous
463template <class Handler, class Impl>
466{
467 return impl().shared_from_this();
468}
469
470// DEPRECATED
471// Called to indicate the response has been written(but not sent)
472template <class Handler, class Impl>
473void
475{
476 if (!strand_.running_in_this_thread())
477 return post(
478 strand_,
479 std::bind(
481 impl().shared_from_this()));
482
483 message_ = {};
484 complete_ = true;
485
486 {
487 std::lock_guard lock(mutex_);
488 if (!wq_.empty() && !wq2_.empty())
489 return;
490 }
491
492 // keep-alive
493 boost::asio::spawn(bind_executor(
494 strand_,
495 std::bind(
497 impl().shared_from_this(),
498 std::placeholders::_1)));
499}
500
501// DEPRECATED
502// Called from the Handler to close the session.
503template <class Handler, class Impl>
504void
506{
507 if (!strand_.running_in_this_thread())
508 return post(
509 strand_,
510 std::bind(
511 (void(BaseHTTPPeer::*)(bool)) &
513 impl().shared_from_this(),
514 graceful));
515
516 complete_ = true;
517 if (graceful)
518 {
519 graceful_ = true;
520 {
521 std::lock_guard lock(mutex_);
522 if (!wq_.empty() || !wq2_.empty())
523 return;
524 }
525 return do_close();
526 }
527
528 boost::beast::get_lowest_layer(impl().stream_).close();
529}
530
531} // namespace ripple
532
533#endif
T bind(T... args)
A version-independent IP address and port combination.
Definition: IPEndpoint.h:38
A generic endpoint for log messages.
Definition: Journal.h:60
Stream trace() const
Severity stream access functions.
Definition: Journal.h:322
Represents an active connection.
Definition: BaseHTTPPeer.h:51
BaseHTTPPeer(Port const &port, Handler &handler, boost::asio::executor const &executor, beast::Journal journal, endpoint_type remote_address, ConstBufferSequence const &buffers)
Definition: BaseHTTPPeer.h:209
boost::asio::yield_context yield_context
Definition: BaseHTTPPeer.h:56
void write(void const *buffer, std::size_t bytes) override
Definition: BaseHTTPPeer.h:422
boost::system::error_code error_code
Definition: BaseHTTPPeer.h:54
Port const & port() override
Returns the Port settings for this connection.
Definition: BaseHTTPPeer.h:172
http_request_type message_
Definition: BaseHTTPPeer.h:91
std::vector< buffer > wq2_
Definition: BaseHTTPPeer.h:93
boost::asio::strand< boost::asio::executor > strand_
Definition: BaseHTTPPeer.h:83
void on_write(error_code const &ec, std::size_t bytes_transferred)
Definition: BaseHTTPPeer.h:321
void write(std::shared_ptr< Writer > const &writer, bool keep_alive) override
Definition: BaseHTTPPeer.h:447
virtual void do_close()=0
beast::Journal const journal_
Definition: BaseHTTPPeer.h:85
endpoint_type remote_address_
Definition: BaseHTTPPeer.h:84
boost::asio::ip::tcp::endpoint endpoint_type
Definition: BaseHTTPPeer.h:55
std::size_t bytes_out_
Definition: BaseHTTPPeer.h:101
virtual void do_request()=0
std::size_t bytes_in_
Definition: BaseHTTPPeer.h:100
boost::asio::streambuf read_buf_
Definition: BaseHTTPPeer.h:90
std::vector< buffer > wq_
Definition: BaseHTTPPeer.h:92
boost::system::error_code ec_
Definition: BaseHTTPPeer.h:97
beast::IP::Endpoint remoteAddress() override
Returns the remote address of the connection.
Definition: BaseHTTPPeer.h:178
void close(bool graceful) override
Close the session.
Definition: BaseHTTPPeer.h:505
Port const & port_
Definition: BaseHTTPPeer.h:80
std::shared_ptr< Session > detach() override
Detach the session.
Definition: BaseHTTPPeer.h:465
http_request_type & request() override
Returns the current HTTP request.
Definition: BaseHTTPPeer.h:184
void complete() override
Indicate that the response is complete.
Definition: BaseHTTPPeer.h:474
void do_writer(std::shared_ptr< Writer > const &writer, bool keep_alive, yield_context do_yield)
Definition: BaseHTTPPeer.h:369
void fail(error_code ec, char const *what)
Definition: BaseHTTPPeer.h:257
void do_read(yield_context do_yield)
Definition: BaseHTTPPeer.h:300
void close() override
Definition: BaseHTTPPeer.h:242
boost::asio::executor_work_guard< boost::asio::executor > work_
Definition: BaseHTTPPeer.h:82
beast::Journal journal() override
Returns the Journal to use for logging.
Definition: BaseHTTPPeer.h:166
Persistent state information for a connection session.
Definition: Session.h:43
T emplace_back(T... args)
T get(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:25
boost::beast::http::request< boost::beast::http::dynamic_body > http_request_type
Definition: Handoff.h:33
T reserve(T... args)
static IP::Endpoint from_asio(boost::asio::ip::address const &address)
buffer(void const *ptr, std::size_t len)
Definition: BaseHTTPPeer.h:69
std::unique_ptr< char[]> data
Definition: BaseHTTPPeer.h:75
Configuration information for a Server listening port.
Definition: Port.h:50
T swap(T... args)
T to_string(T... args)