]>
git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_client_io.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #ifndef CEPH_RGW_CLIENT_IO_H
5 #define CEPH_RGW_CLIENT_IO_H
12 #include <system_error>
14 #include <boost/utility/string_ref.hpp>
16 #include "include/types.h"
17 #include "rgw_common.h"
25 using Exception
= std::system_error
;
27 /* The minimal and simplest subset of methods that a client of RadosGW can be
31 virtual void init_env(CephContext
*cct
) = 0;
34 virtual ~BasicClient() = default;
36 /* Initialize the BasicClient and inject CephContext. */
37 void init(CephContext
*cct
);
39 /* Return the RGWEnv describing the environment that a given request lives in.
40 * The method does not throw exceptions. */
41 virtual RGWEnv
& get_env() noexcept
= 0;
44 * On success returns number of bytes generated for a direct client of RadosGW.
45 * On failure throws rgw::io::Exception containing errno. */
46 virtual size_t complete_request() = 0;
47 }; /* rgw::io::Client */
52 virtual ~Accounter() = default;
54 /* Enable or disable the accounting of both sent and received data. Changing
55 * the state does not affect the counters. */
56 virtual void set_account(bool enabled
) = 0;
58 /* Return number of bytes sent to a direct client of RadosGW (direct means
59 * eg. a web server instance in the case of using FastCGI front-end) when
60 * the accounting was enabled. */
61 virtual uint64_t get_bytes_sent() const = 0;
63 /* Return number of bytes received from a direct client of RadosGW (direct
64 * means eg. a web server instance in the case of using FastCGI front-end)
65 * when the accounting was enabled. */
66 virtual uint64_t get_bytes_received() const = 0;
67 }; /* rgw::io::Accounter */
70 /* Interface abstracting restful interactions with clients, usually through
71 * the HTTP protocol. The methods participating in the response generation
72 * process should be called in the specific order:
73 * 1. send_100_continue() - at most once,
74 * 2. send_status() - exactly once,
77 * b. send_content_length() XOR send_chunked_transfer_encoding()
78 * Please note that only one of those two methods must be called
80 * 4. complete_header() - exactly once,
82 * 6. complete_request() - exactly once.
83 * There are no restrictions on flush() - it may be called in any moment.
85 * Receiving data from a client isn't a subject to any further call order
86 * restrictions besides those imposed by BasicClient. That is, get_env()
87 * and recv_body can be mixed. */
88 class RestfulClient
: public BasicClient
{
89 template<typename T
> friend class DecoratedRestfulClient
;
92 /* Generate the 100 Continue message.
93 * On success returns number of bytes generated for a direct client of RadosGW.
94 * On failure throws rgw::io::Exception containing errno. */
95 virtual size_t send_100_continue() = 0;
97 /* Generate the response's status part taking the HTTP status code as @status
98 * and its name pointed in @status_name.
99 * On success returns number of bytes generated for a direct client of RadosGW.
100 * On failure throws rgw::io::Exception containing errno. */
101 virtual size_t send_status(int status
, const char *status_name
) = 0;
103 /* Generate header. On success returns number of bytes generated for a direct
104 * client of RadosGW. On failure throws rgw::io::Exception containing errno.
106 * boost::string_ref is being used because of length it internally carries. */
107 virtual size_t send_header(const boost::string_ref
& name
,
108 const boost::string_ref
& value
) = 0;
110 /* Inform a client about a content length. Takes number of bytes as @len.
111 * On success returns number of bytes generated for a direct client of RadosGW.
112 * On failure throws rgw::io::Exception containing errno.
115 * - The method must be called EXACTLY ONCE.
116 * - The method is interchangeable with send_chunked_transfer_encoding(). */
117 virtual size_t send_content_length(uint64_t len
) = 0;
119 /* Inform a client that the chunked transfer encoding will be used.
120 * On success returns number of bytes generated for a direct client of RadosGW.
121 * On failure throws rgw::io::Exception containing errno.
124 * - The method must be called EXACTLY ONCE.
125 * - The method is interchangeable with send_content_length(). */
126 virtual size_t send_chunked_transfer_encoding() {
127 /* This is a null implementation. We don't send anything here, even the HTTP
128 * header. The intended behaviour should be provided through a decorator or
129 * directly by a given front-end. */
133 /* Generate completion (the CRLF sequence separating headers and body in
134 * the case of HTTP) of headers. On success returns number of generated bytes
135 * for a direct client of RadosGW. On failure throws rgw::io::Exception with
137 virtual size_t complete_header() = 0;
139 /* Receive no more than @max bytes from a request's body and store it in
140 * buffer pointed by @buf. On success returns number of bytes received from
141 * a direct client of RadosGW that has been stored in @buf. On failure throws
142 * rgw::io::Exception containing errno. */
143 virtual size_t recv_body(char* buf
, size_t max
) = 0;
145 /* Generate a part of response's body by taking exactly @len bytes from
146 * the buffer pointed by @buf. On success returns number of generated bytes
147 * of response's body. On failure throws rgw::io::Exception. */
148 virtual size_t send_body(const char* buf
, size_t len
) = 0;
150 /* Flushes all already generated data to a direct client of RadosGW.
151 * On failure throws rgw::io::Exception containing errno. */
152 virtual void flush() = 0;
153 } /* rgw::io::RestfulClient */;
156 /* Abstract decorator over any implementation of rgw::io::RestfulClient
157 * which could be provided both as a pointer-to-object or the object itself. */
158 template <typename DecorateeT
>
159 class DecoratedRestfulClient
: public RestfulClient
{
160 template<typename T
> friend class DecoratedRestfulClient
;
163 typedef typename
std::remove_pointer
<DecorateeT
>::type DerefedDecorateeT
;
165 static_assert(std::is_base_of
<RestfulClient
, DerefedDecorateeT
>::value
,
166 "DecorateeT must be a subclass of rgw::io::RestfulClient");
168 DecorateeT decoratee
;
170 /* There is an indirection layer over accessing decoratee to share the same
171 * code base between dynamic and static decorators. The difference is about
172 * what we store internally: pointer to a decorated object versus the whole
174 template <typename T
= void,
175 typename
std::enable_if
<
176 ! std::is_pointer
<DecorateeT
>::value
, T
>::type
* = nullptr>
177 DerefedDecorateeT
& get_decoratee() {
182 template <typename T
= void,
183 typename
std::enable_if
<
184 std::is_pointer
<DecorateeT
>::value
, T
>::type
* = nullptr>
185 DerefedDecorateeT
& get_decoratee() {
189 /* Dynamic decorators (those storing a pointer instead of the decorated
190 * object itself) can be reconfigured on-the-fly. HOWEVER: there are no
191 * facilities for orchestrating such changes. Callers must take care of
192 * atomicity and thread-safety. */
193 template <typename T
= void,
194 typename
std::enable_if
<
195 std::is_pointer
<DecorateeT
>::value
, T
>::type
* = nullptr>
196 void set_decoratee(DerefedDecorateeT
& new_dec
) {
197 decoratee
= &new_dec
;
200 void init_env(CephContext
*cct
) override
{
201 return get_decoratee().init_env(cct
);
205 DecoratedRestfulClient(DecorateeT
&& decoratee
)
206 : decoratee(std::forward
<DecorateeT
>(decoratee
)) {
209 size_t send_status(const int status
,
210 const char* const status_name
) override
{
211 return get_decoratee().send_status(status
, status_name
);
214 size_t send_100_continue() override
{
215 return get_decoratee().send_100_continue();
218 size_t send_header(const boost::string_ref
& name
,
219 const boost::string_ref
& value
) override
{
220 return get_decoratee().send_header(name
, value
);
223 size_t send_content_length(const uint64_t len
) override
{
224 return get_decoratee().send_content_length(len
);
227 size_t send_chunked_transfer_encoding() override
{
228 return get_decoratee().send_chunked_transfer_encoding();
231 size_t complete_header() override
{
232 return get_decoratee().complete_header();
235 size_t recv_body(char* const buf
, const size_t max
) override
{
236 return get_decoratee().recv_body(buf
, max
);
239 size_t send_body(const char* const buf
,
240 const size_t len
) override
{
241 return get_decoratee().send_body(buf
, len
);
244 void flush() override
{
245 return get_decoratee().flush();
248 RGWEnv
& get_env() noexcept override
{
249 return get_decoratee().get_env();
252 size_t complete_request() override
{
253 return get_decoratee().complete_request();
255 } /* rgw::io::DecoratedRestfulClient */;
258 /* Interface that should be provided by a front-end class wanting to to use
259 * the low-level buffering offered by i.e. StaticOutputBufferer. */
262 virtual ~BuffererSink() = default;
264 /* Send exactly @len bytes from the memory location pointed by @buf.
265 * On success returns @len. On failure throws rgw::io::Exception. */
266 virtual size_t write_data(const char *buf
, size_t len
) = 0;
269 /* Utility class providing RestfulClient's implementations with facilities
270 * for low-level buffering without relying on dynamic memory allocations.
271 * The buffer is carried entirely on stack. This narrows down applicability
272 * to these situations where buffers are relatively small. This perfectly
273 * fits the needs of composing an HTTP header. Without that a front-end
274 * might need to issue a lot of small IO operations leading to increased
275 * overhead on syscalls and fragmentation of a message if the Nagle's
276 * algorithm won't be able to form a single TCP segment (usually when
277 * running on extremely fast network interfaces like the loopback). */
278 template <size_t BufferSizeV
= 4096>
279 class StaticOutputBufferer
: public std::streambuf
{
280 static_assert(BufferSizeV
>= sizeof(std::streambuf::char_type
),
281 "Buffer size must be bigger than a single char_type.");
283 using std::streambuf::int_type
;
285 int_type
overflow(const int_type c
) override
{
287 pbump(sizeof(std::streambuf::char_type
));
290 /* No error, the buffer has been successfully synchronized. */
293 return std::streambuf::traits_type::eof();
297 int sync() override
{
298 const auto len
= static_cast<size_t>(std::streambuf::pptr() -
299 std::streambuf::pbase());
300 std::streambuf::pbump(-len
);
301 sink
.write_data(std::streambuf::pbase(), len
);
302 /* Always return success here. In case of failure write_data() will throw
303 * rgw::io::Exception. */
308 std::streambuf::char_type buffer
[BufferSizeV
];
311 StaticOutputBufferer(BuffererSink
& sink
)
313 constexpr size_t len
= sizeof(buffer
) - sizeof(std::streambuf::char_type
);
314 std::streambuf::setp(buffer
, buffer
+ len
);
318 } /* namespace rgw */
322 /* We're doing this nasty thing only because of extensive usage of templates
323 * to implement the static decorator pattern. C++ templates de facto enforce
324 * mixing interfaces with implementation. Additionally, those classes derive
325 * from RGWRestfulIO defined here. I believe that including in the middle of
326 * file is still better than polluting it directly. */
327 #include "rgw_client_io_filters.h"
330 /* RGWRestfulIO: high level interface to interact with RESTful clients. What
331 * differentiates it from rgw::io::RestfulClient is providing more specific APIs
332 * like rgw::io::Accounter or the AWS Auth v4 stuff implemented by filters
333 * while hiding the pipelined architecture from clients.
335 * rgw::io::Accounter came in as a part of rgw::io::AccountingFilter. */
336 class RGWRestfulIO
: public rgw::io::AccountingFilter
<rgw::io::RestfulClient
*> {
337 std::vector
<std::shared_ptr
<DecoratedRestfulClient
>> filters
;
340 ~RGWRestfulIO() override
= default;
342 RGWRestfulIO(rgw::io::RestfulClient
* engine
)
343 : AccountingFilter
<rgw::io::RestfulClient
*>(std::move(engine
)) {
346 void add_filter(std::shared_ptr
<DecoratedRestfulClient
> new_filter
) {
347 new_filter
->set_decoratee(this->get_decoratee());
348 this->set_decoratee(*new_filter
);
349 filters
.emplace_back(std::move(new_filter
));
351 }; /* RGWRestfulIO */
354 /* Type conversions to work around lack of req_state type hierarchy matching
355 * (e.g.) REST backends (may be replaced w/dynamic typed req_state). */
356 static inline rgw::io::RestfulClient
* RESTFUL_IO(struct req_state
* s
) {
357 assert(dynamic_cast<rgw::io::RestfulClient
*>(s
->cio
) != nullptr);
359 return static_cast<rgw::io::RestfulClient
*>(s
->cio
);
362 static inline rgw::io::Accounter
* ACCOUNTING_IO(struct req_state
* s
) {
363 auto ptr
= dynamic_cast<rgw::io::Accounter
*>(s
->cio
);
364 assert(ptr
!= nullptr);
369 static inline RGWRestfulIO
* AWS_AUTHv4_IO(const req_state
* const s
) {
370 assert(dynamic_cast<RGWRestfulIO
*>(s
->cio
) != nullptr);
372 return static_cast<RGWRestfulIO
*>(s
->cio
);
376 class RGWClientIOStreamBuf
: public std::streambuf
{
379 size_t const window_size
;
380 size_t const putback_size
;
381 std::vector
<char> buffer
;
384 RGWClientIOStreamBuf(RGWRestfulIO
&rio
, size_t ws
, size_t ps
= 1)
390 setg(nullptr, nullptr, nullptr);
393 std::streambuf::int_type
underflow() override
{
394 if (gptr() < egptr()) {
395 return traits_type::to_int_type(*gptr());
398 char * const base
= buffer
.data();
401 if (nullptr != eback()) {
402 /* We need to skip moving bytes on first underflow. In such case
403 * there is simply no previous data we should preserve for unget()
404 * or something similar. */
405 std::memmove(base
, egptr() - putback_size
, putback_size
);
406 start
= base
+ putback_size
;
413 read_len
= rio
.recv_body(base
, window_size
);
414 } catch (rgw::io::Exception
&) {
415 return traits_type::eof();
418 return traits_type::eof();
421 setg(base
, start
, start
+ read_len
);
423 return traits_type::to_int_type(*gptr());
427 class RGWClientIOStream
: private RGWClientIOStreamBuf
, public std::istream
{
428 /* Inheritance from RGWClientIOStreamBuf is a kind of shadow, undirect
429 * form of composition here. We cannot do that explicitly because istream
430 * ctor is being called prior to construction of any member of this class. */
433 explicit RGWClientIOStream(RGWRestfulIO
&s
)
434 : RGWClientIOStreamBuf(s
, 1, 2),
435 istream(static_cast<RGWClientIOStreamBuf
*>(this)) {
439 #endif /* CEPH_RGW_CLIENT_IO_H */