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_DECOIMPL_H
5 #define CEPH_RGW_CLIENT_IO_DECOIMPL_H
9 #include <boost/optional.hpp>
11 #include "rgw_common.h"
12 #include "rgw_client_io.h"
18 class AccountingFilter
: public DecoratedRestfulClient
<T
>,
22 uint64_t total_received
;
27 AccountingFilter(CephContext
*cct
, U
&& decoratee
)
28 : DecoratedRestfulClient
<T
>(std::forward
<U
>(decoratee
)),
31 total_received(0), cct(cct
) {
34 size_t send_status(const int status
,
35 const char* const status_name
) override
{
36 const auto sent
= DecoratedRestfulClient
<T
>::send_status(status
,
38 lsubdout(cct
, rgw
, 30) << "AccountingFilter::send_status: e="
39 << (enabled
? "1" : "0") << ", sent=" << sent
<< ", total="
40 << total_sent
<< dendl
;
47 size_t send_100_continue() override
{
48 const auto sent
= DecoratedRestfulClient
<T
>::send_100_continue();
49 lsubdout(cct
, rgw
, 30) << "AccountingFilter::send_100_continue: e="
50 << (enabled
? "1" : "0") << ", sent=" << sent
<< ", total="
51 << total_sent
<< dendl
;
58 size_t send_header(const boost::string_ref
& name
,
59 const boost::string_ref
& value
) override
{
60 const auto sent
= DecoratedRestfulClient
<T
>::send_header(name
, value
);
61 lsubdout(cct
, rgw
, 30) << "AccountingFilter::send_header: e="
62 << (enabled
? "1" : "0") << ", sent=" << sent
<< ", total="
63 << total_sent
<< dendl
;
70 size_t send_content_length(const uint64_t len
) override
{
71 const auto sent
= DecoratedRestfulClient
<T
>::send_content_length(len
);
72 lsubdout(cct
, rgw
, 30) << "AccountingFilter::send_content_length: e="
73 << (enabled
? "1" : "0") << ", sent=" << sent
<< ", total="
74 << total_sent
<< dendl
;
81 size_t send_chunked_transfer_encoding() override
{
82 const auto sent
= DecoratedRestfulClient
<T
>::send_chunked_transfer_encoding();
83 lsubdout(cct
, rgw
, 30) << "AccountingFilter::send_chunked_transfer_encoding: e="
84 << (enabled
? "1" : "0") << ", sent=" << sent
<< ", total="
85 << total_sent
<< dendl
;
92 size_t complete_header() override
{
93 const auto sent
= DecoratedRestfulClient
<T
>::complete_header();
94 lsubdout(cct
, rgw
, 30) << "AccountingFilter::complete_header: e="
95 << (enabled
? "1" : "0") << ", sent=" << sent
<< ", total="
96 << total_sent
<< dendl
;
103 size_t recv_body(char* buf
, size_t max
) override
{
104 const auto received
= DecoratedRestfulClient
<T
>::recv_body(buf
, max
);
105 lsubdout(cct
, rgw
, 30) << "AccountingFilter::recv_body: e="
106 << (enabled
? "1" : "0") << ", received=" << received
<< dendl
;
108 total_received
+= received
;
113 size_t send_body(const char* const buf
,
114 const size_t len
) override
{
115 const auto sent
= DecoratedRestfulClient
<T
>::send_body(buf
, len
);
116 lsubdout(cct
, rgw
, 30) << "AccountingFilter::send_body: e="
117 << (enabled
? "1" : "0") << ", sent=" << sent
<< ", total="
118 << total_sent
<< dendl
;
125 size_t complete_request() override
{
126 const auto sent
= DecoratedRestfulClient
<T
>::complete_request();
127 lsubdout(cct
, rgw
, 30) << "AccountingFilter::complete_request: e="
128 << (enabled
? "1" : "0") << ", sent=" << sent
<< ", total="
129 << total_sent
<< dendl
;
136 uint64_t get_bytes_sent() const override
{
140 uint64_t get_bytes_received() const override
{
141 return total_received
;
144 void set_account(bool enabled
) override
{
145 this->enabled
= enabled
;
146 lsubdout(cct
, rgw
, 30) << "AccountingFilter::set_account: e="
147 << (enabled
? "1" : "0") << dendl
;
152 /* Filter for in-memory buffering incoming data and calculating the content
153 * length header if it isn't present. */
154 template <typename T
>
155 class BufferingFilter
: public DecoratedRestfulClient
<T
> {
156 template<typename Td
> friend class DecoratedRestfulClient
;
158 ceph::bufferlist data
;
160 bool has_content_length
;
165 template <typename U
>
166 BufferingFilter(CephContext
*cct
, U
&& decoratee
)
167 : DecoratedRestfulClient
<T
>(std::forward
<U
>(decoratee
)),
168 has_content_length(false),
169 buffer_data(false), cct(cct
) {
172 size_t send_content_length(const uint64_t len
) override
;
173 size_t send_chunked_transfer_encoding() override
;
174 size_t complete_header() override
;
175 size_t send_body(const char* buf
, size_t len
) override
;
176 size_t complete_request() override
;
179 template <typename T
>
180 size_t BufferingFilter
<T
>::send_body(const char* const buf
,
184 data
.append(buf
, len
);
186 lsubdout(cct
, rgw
, 30) << "BufferingFilter<T>::send_body: defer count = "
191 return DecoratedRestfulClient
<T
>::send_body(buf
, len
);
194 template <typename T
>
195 size_t BufferingFilter
<T
>::send_content_length(const uint64_t len
)
197 has_content_length
= true;
198 return DecoratedRestfulClient
<T
>::send_content_length(len
);
201 template <typename T
>
202 size_t BufferingFilter
<T
>::send_chunked_transfer_encoding()
204 has_content_length
= true;
205 return DecoratedRestfulClient
<T
>::send_chunked_transfer_encoding();
208 template <typename T
>
209 size_t BufferingFilter
<T
>::complete_header()
211 if (! has_content_length
) {
212 /* We will dump everything in complete_request(). */
214 lsubdout(cct
, rgw
, 30) << "BufferingFilter<T>::complete_header: has_content_length="
215 << (has_content_length
? "1" : "0") << dendl
;
219 return DecoratedRestfulClient
<T
>::complete_header();
222 template <typename T
>
223 size_t BufferingFilter
<T
>::complete_request()
227 if (! has_content_length
) {
228 /* It is not correct to count these bytes here,
229 * because they can only be part of the header.
230 * Therefore force count to 0.
232 sent
+= DecoratedRestfulClient
<T
>::send_content_length(data
.length());
233 sent
+= DecoratedRestfulClient
<T
>::complete_header();
234 lsubdout(cct
, rgw
, 30) <<
235 "BufferingFilter::complete_request: !has_content_length: IGNORE: sent="
241 /* We are sending each buffer separately to avoid extra memory shuffling
242 * that would occur on data.c_str() to provide a continuous memory area. */
243 for (const auto& ptr
: data
.buffers()) {
244 sent
+= DecoratedRestfulClient
<T
>::send_body(ptr
.c_str(),
249 lsubdout(cct
, rgw
, 30) << "BufferingFilter::complete_request: buffer_data: sent="
253 return sent
+ DecoratedRestfulClient
<T
>::complete_request();
256 template <typename T
> static inline
257 BufferingFilter
<T
> add_buffering(
260 return BufferingFilter
<T
>(cct
, std::forward
<T
>(t
));
264 template <typename T
>
265 class ChunkingFilter
: public DecoratedRestfulClient
<T
> {
266 template<typename Td
> friend class DecoratedRestfulClient
;
268 bool chunking_enabled
;
271 template <typename U
>
272 ChunkingFilter(U
&& decoratee
)
273 : DecoratedRestfulClient
<T
>(std::forward
<U
>(decoratee
)),
274 chunking_enabled(false) {
277 size_t send_chunked_transfer_encoding() override
{
278 chunking_enabled
= true;
279 return DecoratedRestfulClient
<T
>::send_header("Transfer-Encoding",
283 size_t send_body(const char* buf
,
284 const size_t len
) override
{
285 if (! chunking_enabled
) {
286 return DecoratedRestfulClient
<T
>::send_body(buf
, len
);
288 static constexpr char HEADER_END
[] = "\r\n";
289 /* https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 */
290 // TODO: we have no support for sending chunked-encoding
291 // extensions/trailing headers.
293 const auto chunk_size_len
= snprintf(chunk_size
, sizeof(chunk_size
),
294 "%" PRIx64
"\r\n", len
);
297 sent
+= DecoratedRestfulClient
<T
>::send_body(chunk_size
, chunk_size_len
);
298 sent
+= DecoratedRestfulClient
<T
>::send_body(buf
, len
);
299 sent
+= DecoratedRestfulClient
<T
>::send_body(HEADER_END
,
300 sizeof(HEADER_END
) - 1);
305 size_t complete_request() override
{
308 if (chunking_enabled
) {
309 static constexpr char CHUNKED_RESP_END
[] = "0\r\n\r\n";
310 sent
+= DecoratedRestfulClient
<T
>::send_body(CHUNKED_RESP_END
,
311 sizeof(CHUNKED_RESP_END
) - 1);
314 return sent
+ DecoratedRestfulClient
<T
>::complete_request();
318 template <typename T
> static inline
319 ChunkingFilter
<T
> add_chunking(T
&& t
) {
320 return ChunkingFilter
<T
>(std::forward
<T
>(t
));
324 /* Class that controls and inhibits the process of sending Content-Length HTTP
325 * header where RFC 7230 requests so. The cases worth our attention are 204 No
326 * Content as well as 304 Not Modified. */
327 template <typename T
>
328 class ConLenControllingFilter
: public DecoratedRestfulClient
<T
> {
330 enum class ContentLengthAction
{
337 template <typename U
>
338 ConLenControllingFilter(U
&& decoratee
)
339 : DecoratedRestfulClient
<T
>(std::forward
<U
>(decoratee
)),
340 action(ContentLengthAction::UNKNOWN
) {
343 size_t send_status(const int status
,
344 const char* const status_name
) override
{
345 if ((204 == status
|| 304 == status
) &&
346 ! g_conf
->rgw_print_prohibited_content_length
) {
347 action
= ContentLengthAction::INHIBIT
;
349 action
= ContentLengthAction::FORWARD
;
352 return DecoratedRestfulClient
<T
>::send_status(status
, status_name
);
355 size_t send_content_length(const uint64_t len
) override
{
357 case ContentLengthAction::FORWARD
:
358 return DecoratedRestfulClient
<T
>::send_content_length(len
);
359 case ContentLengthAction::INHIBIT
:
361 case ContentLengthAction::UNKNOWN
:
368 template <typename T
> static inline
369 ConLenControllingFilter
<T
> add_conlen_controlling(T
&& t
) {
370 return ConLenControllingFilter
<T
>(std::forward
<T
>(t
));
374 /* Filter that rectifies the wrong behaviour of some clients of the RGWRestfulIO
375 * interface. Should be removed after fixing those clients. */
376 template <typename T
>
377 class ReorderingFilter
: public DecoratedRestfulClient
<T
> {
379 enum class ReorderState
{
380 RGW_EARLY_HEADERS
, /* Got headers sent before calling send_status. */
381 RGW_STATUS_SEEN
, /* Status has been seen. */
382 RGW_DATA
/* Header has been completed. */
385 boost::optional
<uint64_t> content_length
;
387 std::vector
<std::pair
<std::string
, std::string
>> headers
;
389 size_t send_header(const boost::string_ref
& name
,
390 const boost::string_ref
& value
) override
{
392 case ReorderState::RGW_EARLY_HEADERS
:
393 case ReorderState::RGW_STATUS_SEEN
:
394 headers
.emplace_back(std::make_pair(std::string(name
.data(), name
.size()),
395 std::string(value
.data(), value
.size())));
397 case ReorderState::RGW_DATA
:
398 return DecoratedRestfulClient
<T
>::send_header(name
, value
);
405 template <typename U
>
406 ReorderingFilter(U
&& decoratee
)
407 : DecoratedRestfulClient
<T
>(std::forward
<U
>(decoratee
)),
408 phase(ReorderState::RGW_EARLY_HEADERS
) {
411 size_t send_status(const int status
,
412 const char* const status_name
) override
{
413 phase
= ReorderState::RGW_STATUS_SEEN
;
415 return DecoratedRestfulClient
<T
>::send_status(status
, status_name
);
418 size_t send_content_length(const uint64_t len
) override
{
419 if (ReorderState::RGW_EARLY_HEADERS
== phase
) {
420 /* Oh great, someone tries to send content length before status. */
421 content_length
= len
;
424 return DecoratedRestfulClient
<T
>::send_content_length(len
);
428 size_t complete_header() override
{
431 /* Change state in order to immediately send everything we get. */
432 phase
= ReorderState::RGW_DATA
;
434 /* Sent content length if necessary. */
435 if (content_length
) {
436 sent
+= DecoratedRestfulClient
<T
>::send_content_length(*content_length
);
439 /* Header data in buffers are already counted. */
440 for (const auto& kv
: headers
) {
441 sent
+= DecoratedRestfulClient
<T
>::send_header(kv
.first
, kv
.second
);
445 return sent
+ DecoratedRestfulClient
<T
>::complete_header();
449 template <typename T
> static inline
450 ReorderingFilter
<T
> add_reordering(T
&& t
) {
451 return ReorderingFilter
<T
>(std::forward
<T
>(t
));
455 } /* namespace rgw */
456 #endif /* CEPH_RGW_CLIENT_IO_DECOIMPL_H */