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 <type_traits>
45#include <vector>
46
47namespace ripple {
48
50template <class Handler, class Impl>
51class BaseHTTPPeer : public io_list::work, public Session
52{
53protected:
55 using error_code = boost::system::error_code;
56 using endpoint_type = boost::asio::ip::tcp::endpoint;
57 using yield_context = boost::asio::yield_context;
58
59 enum {
60 // Size of our read/write buffer
61 bufferSize = 4 * 1024,
62
63 // Max seconds without completing a message
65 timeoutSecondsLocal = 3 // used for localhost clients
66 };
67
68 struct buffer
69 {
70 buffer(void const* ptr, std::size_t len)
71 : data(new char[len]), bytes(len), used(0)
72 {
73 memcpy(data.get(), ptr, len);
74 }
75
79 };
80
81 Port const& port_;
82 Handler& handler_;
83 boost::asio::executor_work_guard<boost::asio::executor> work_;
84 boost::asio::strand<boost::asio::executor> strand_;
87
90
91 boost::asio::streambuf read_buf_;
96 bool graceful_ = false;
97 bool complete_ = false;
98 boost::system::error_code ec_;
99
103
104 //--------------------------------------------------------------------------
105
106public:
107 template <class ConstBufferSequence>
109 Port const& port,
110 Handler& handler,
111 boost::asio::executor const& executor,
113 endpoint_type remote_address,
114 ConstBufferSequence const& buffers);
115
116 virtual ~BaseHTTPPeer();
117
118 Session&
120 {
121 return *this;
122 }
123
124 void
125 close() override;
126
127protected:
128 Impl&
130 {
131 return *static_cast<Impl*>(this);
132 }
133
134 void
135 fail(error_code ec, char const* what);
136
137 void
139
140 void
142
143 void
145
146 void
148
149 void
150 on_write(error_code const& ec, std::size_t bytes_transferred);
151
152 void
154 std::shared_ptr<Writer> const& writer,
155 bool keep_alive,
156 yield_context do_yield);
157
158 virtual void
160
161 virtual void
162 do_close() = 0;
163
164 // Session
165
167 journal() override
168 {
169 return journal_;
170 }
171
172 Port const&
173 port() override
174 {
175 return port_;
176 }
177
179 remoteAddress() override
180 {
182 }
183
185 request() override
186 {
187 return message_;
188 }
189
190 void
191 write(void const* buffer, std::size_t bytes) override;
192
193 void
194 write(std::shared_ptr<Writer> const& writer, bool keep_alive) override;
195
197 detach() override;
198
199 void
200 complete() override;
201
202 void
203 close(bool graceful) override;
204};
205
206//------------------------------------------------------------------------------
207
208template <class Handler, class Impl>
209template <class ConstBufferSequence>
211 Port const& port,
212 Handler& handler,
213 boost::asio::executor const& executor,
214 beast::Journal journal,
215 endpoint_type remote_address,
216 ConstBufferSequence const& buffers)
217 : port_(port)
218 , handler_(handler)
219 , work_(executor)
220 , strand_(executor)
221 , remote_address_(remote_address)
222 , journal_(journal)
223{
224 read_buf_.commit(boost::asio::buffer_copy(
225 read_buf_.prepare(boost::asio::buffer_size(buffers)), buffers));
226 static std::atomic<int> sid;
227 nid_ = ++sid;
228 id_ = std::string("#") + std::to_string(nid_) + " ";
229 JLOG(journal_.trace()) << id_ << "accept: " << remote_address_.address();
230}
231
232template <class Handler, class Impl>
234{
235 handler_.onClose(session(), ec_);
236 JLOG(journal_.trace()) << id_ << "destroyed: " << request_count_
237 << ((request_count_ == 1) ? " request"
238 : " requests");
239}
240
241template <class Handler, class Impl>
242void
244{
245 if (!strand_.running_in_this_thread())
246 return post(
247 strand_,
248 std::bind(
249 (void(BaseHTTPPeer::*)(void)) & BaseHTTPPeer::close,
250 impl().shared_from_this()));
251 boost::beast::get_lowest_layer(impl().stream_).close();
252}
253
254//------------------------------------------------------------------------------
255
256template <class Handler, class Impl>
257void
259{
260 if (!ec_ && ec != boost::asio::error::operation_aborted)
261 {
262 ec_ = ec;
263 JLOG(journal_.trace())
264 << id_ << std::string(what) << ": " << ec.message();
265 boost::beast::get_lowest_layer(impl().stream_).close();
266 }
267}
268
269template <class Handler, class Impl>
270void
272{
273 boost::beast::get_lowest_layer(impl().stream_)
274 .expires_after(std::chrono::seconds(
275 remote_address_.address().is_loopback() ? timeoutSecondsLocal
276 : timeoutSeconds));
277}
278
279// Convenience for discarding the error code
280template <class Handler, class Impl>
281void
283{
284 boost::beast::get_lowest_layer(impl().stream_).expires_never();
285}
286
287// Called when session times out
288template <class Handler, class Impl>
289void
291{
292 auto ec =
293 boost::system::errc::make_error_code(boost::system::errc::timed_out);
294 fail(ec, "timer");
295}
296
297//------------------------------------------------------------------------------
298
299template <class Handler, class Impl>
300void
302{
303 complete_ = false;
304 error_code ec;
305 start_timer();
306 boost::beast::http::async_read(
307 impl().stream_, read_buf_, message_, do_yield[ec]);
308 cancel_timer();
309 if (ec == boost::beast::http::error::end_of_stream)
310 return do_close();
311 if (ec == boost::beast::error::timeout)
312 return on_timer();
313 if (ec)
314 return fail(ec, "http::read");
315 do_request();
316}
317
318// Send everything in the write queue.
319// The write queue must not be empty upon entry.
320template <class Handler, class Impl>
321void
323 error_code const& ec,
324 std::size_t bytes_transferred)
325{
326 cancel_timer();
327 if (ec == boost::beast::error::timeout)
328 return on_timer();
329 if (ec)
330 return fail(ec, "write");
331 bytes_out_ += bytes_transferred;
332 {
333 std::lock_guard lock(mutex_);
334 wq2_.clear();
335 wq2_.reserve(wq_.size());
336 std::swap(wq2_, wq_);
337 }
338 if (!wq2_.empty())
339 {
341 v.reserve(wq2_.size());
342 for (auto const& b : wq2_)
343 v.emplace_back(b.data.get(), b.bytes);
344 start_timer();
345 return boost::asio::async_write(
346 impl().stream_,
347 v,
348 bind_executor(
349 strand_,
350 std::bind(
352 impl().shared_from_this(),
353 std::placeholders::_1,
354 std::placeholders::_2)));
355 }
356 if (!complete_)
357 return;
358 if (graceful_)
359 return do_close();
360 boost::asio::spawn(
361 strand_,
362 std::bind(
364 impl().shared_from_this(),
365 std::placeholders::_1));
366}
367
368template <class Handler, class Impl>
369void
371 std::shared_ptr<Writer> const& writer,
372 bool keep_alive,
373 yield_context do_yield)
374{
375 std::function<void(void)> resume;
376 {
377 auto const p = impl().shared_from_this();
378 resume = std::function<void(void)>([this, p, writer, keep_alive]() {
379 boost::asio::spawn(
380 strand_,
381 std::bind(
383 p,
384 writer,
385 keep_alive,
386 std::placeholders::_1));
387 });
388 }
389
390 for (;;)
391 {
392 if (!writer->prepare(bufferSize, resume))
393 return;
394 error_code ec;
395 auto const bytes_transferred = boost::asio::async_write(
396 impl().stream_,
397 writer->data(),
398 boost::asio::transfer_at_least(1),
399 do_yield[ec]);
400 if (ec)
401 return fail(ec, "writer");
402 writer->consume(bytes_transferred);
403 if (writer->complete())
404 break;
405 }
406
407 if (!keep_alive)
408 return do_close();
409
410 boost::asio::spawn(
411 strand_,
412 std::bind(
414 impl().shared_from_this(),
415 std::placeholders::_1));
416}
417
418//------------------------------------------------------------------------------
419
420// Send a copy of the data.
421template <class Handler, class Impl>
422void
424{
425 if (bytes == 0)
426 return;
427 if ([&] {
428 std::lock_guard lock(mutex_);
429 wq_.emplace_back(buf, bytes);
430 return wq_.size() == 1 && wq2_.size() == 0;
431 }())
432 {
433 if (!strand_.running_in_this_thread())
434 return post(
435 strand_,
436 std::bind(
438 impl().shared_from_this(),
439 error_code{},
440 0));
441 else
442 return on_write(error_code{}, 0);
443 }
444}
445
446template <class Handler, class Impl>
447void
449 std::shared_ptr<Writer> const& writer,
450 bool keep_alive)
451{
452 boost::asio::spawn(bind_executor(
453 strand_,
454 std::bind(
456 impl().shared_from_this(),
457 writer,
458 keep_alive,
459 std::placeholders::_1)));
460}
461
462// DEPRECATED
463// Make the Session asynchronous
464template <class Handler, class Impl>
467{
468 return impl().shared_from_this();
469}
470
471// DEPRECATED
472// Called to indicate the response has been written(but not sent)
473template <class Handler, class Impl>
474void
476{
477 if (!strand_.running_in_this_thread())
478 return post(
479 strand_,
480 std::bind(
482 impl().shared_from_this()));
483
484 message_ = {};
485 complete_ = true;
486
487 {
488 std::lock_guard lock(mutex_);
489 if (!wq_.empty() && !wq2_.empty())
490 return;
491 }
492
493 // keep-alive
494 boost::asio::spawn(bind_executor(
495 strand_,
496 std::bind(
498 impl().shared_from_this(),
499 std::placeholders::_1)));
500}
501
502// DEPRECATED
503// Called from the Handler to close the session.
504template <class Handler, class Impl>
505void
507{
508 if (!strand_.running_in_this_thread())
509 return post(
510 strand_,
511 std::bind(
512 (void(BaseHTTPPeer::*)(bool)) &
514 impl().shared_from_this(),
515 graceful));
516
517 complete_ = true;
518 if (graceful)
519 {
520 graceful_ = true;
521 {
522 std::lock_guard lock(mutex_);
523 if (!wq_.empty() || !wq2_.empty())
524 return;
525 }
526 return do_close();
527 }
528
529 boost::beast::get_lowest_layer(impl().stream_).close();
530}
531
532} // namespace ripple
533
534#endif
T bind(T... args)
A version-independent IP address and port combination.
Definition: IPEndpoint.h:39
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:52
BaseHTTPPeer(Port const &port, Handler &handler, boost::asio::executor const &executor, beast::Journal journal, endpoint_type remote_address, ConstBufferSequence const &buffers)
Definition: BaseHTTPPeer.h:210
boost::asio::yield_context yield_context
Definition: BaseHTTPPeer.h:57
void write(void const *buffer, std::size_t bytes) override
Definition: BaseHTTPPeer.h:423
boost::system::error_code error_code
Definition: BaseHTTPPeer.h:55
Port const & port() override
Returns the Port settings for this connection.
Definition: BaseHTTPPeer.h:173
http_request_type message_
Definition: BaseHTTPPeer.h:92
std::vector< buffer > wq2_
Definition: BaseHTTPPeer.h:94
boost::asio::strand< boost::asio::executor > strand_
Definition: BaseHTTPPeer.h:84
void on_write(error_code const &ec, std::size_t bytes_transferred)
Definition: BaseHTTPPeer.h:322
void write(std::shared_ptr< Writer > const &writer, bool keep_alive) override
Definition: BaseHTTPPeer.h:448
virtual void do_close()=0
beast::Journal const journal_
Definition: BaseHTTPPeer.h:86
endpoint_type remote_address_
Definition: BaseHTTPPeer.h:85
boost::asio::ip::tcp::endpoint endpoint_type
Definition: BaseHTTPPeer.h:56
std::size_t bytes_out_
Definition: BaseHTTPPeer.h:102
virtual void do_request()=0
std::size_t bytes_in_
Definition: BaseHTTPPeer.h:101
boost::asio::streambuf read_buf_
Definition: BaseHTTPPeer.h:91
std::vector< buffer > wq_
Definition: BaseHTTPPeer.h:93
boost::system::error_code ec_
Definition: BaseHTTPPeer.h:98
beast::IP::Endpoint remoteAddress() override
Returns the remote address of the connection.
Definition: BaseHTTPPeer.h:179
void close(bool graceful) override
Close the session.
Definition: BaseHTTPPeer.h:506
Port const & port_
Definition: BaseHTTPPeer.h:81
std::shared_ptr< Session > detach() override
Detach the session.
Definition: BaseHTTPPeer.h:466
http_request_type & request() override
Returns the current HTTP request.
Definition: BaseHTTPPeer.h:185
void complete() override
Indicate that the response is complete.
Definition: BaseHTTPPeer.h:475
void do_writer(std::shared_ptr< Writer > const &writer, bool keep_alive, yield_context do_yield)
Definition: BaseHTTPPeer.h:370
void fail(error_code ec, char const *what)
Definition: BaseHTTPPeer.h:258
void do_read(yield_context do_yield)
Definition: BaseHTTPPeer.h:301
void close() override
Definition: BaseHTTPPeer.h:243
boost::asio::executor_work_guard< boost::asio::executor > work_
Definition: BaseHTTPPeer.h:83
beast::Journal journal() override
Returns the Journal to use for logging.
Definition: BaseHTTPPeer.h:167
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:26
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:70
std::unique_ptr< char[]> data
Definition: BaseHTTPPeer.h:76
Configuration information for a Server listening port.
Definition: Port.h:50
T swap(T... args)
T to_string(T... args)