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
;
26 AccountingFilter(U
&& decoratee
)
27 : DecoratedRestfulClient
<T
>(std::forward
<U
>(decoratee
)),
33 size_t send_status(const int status
,
34 const char* const status_name
) override
{
35 const auto sent
= DecoratedRestfulClient
<T
>::send_status(status
,
43 size_t send_100_continue() override
{
44 const auto sent
= DecoratedRestfulClient
<T
>::send_100_continue();
51 size_t send_header(const boost::string_ref
& name
,
52 const boost::string_ref
& value
) override
{
53 const auto sent
= DecoratedRestfulClient
<T
>::send_header(name
, value
);
60 size_t send_content_length(const uint64_t len
) override
{
61 const auto sent
= DecoratedRestfulClient
<T
>::send_content_length(len
);
68 size_t send_chunked_transfer_encoding() override
{
69 const auto sent
= DecoratedRestfulClient
<T
>::send_chunked_transfer_encoding();
76 size_t complete_header() override
{
77 const auto sent
= DecoratedRestfulClient
<T
>::complete_header();
84 size_t recv_body(char* buf
, size_t max
) override
{
85 const auto received
= DecoratedRestfulClient
<T
>::recv_body(buf
, max
);
87 total_received
+= received
;
92 size_t send_body(const char* const buf
,
93 const size_t len
) override
{
94 const auto sent
= DecoratedRestfulClient
<T
>::send_body(buf
, len
);
101 uint64_t get_bytes_sent() const override
{
105 uint64_t get_bytes_received() const override
{
106 return total_received
;
109 void set_account(bool enabled
) override
{
110 this->enabled
= enabled
;
115 /* Filter for in-memory buffering incoming data and calculating the content
116 * length header if it isn't present. */
117 template <typename T
>
118 class BufferingFilter
: public DecoratedRestfulClient
<T
> {
119 template<typename Td
> friend class DecoratedRestfulClient
;
121 ceph::bufferlist data
;
123 bool has_content_length
;
127 template <typename U
>
128 BufferingFilter(U
&& decoratee
)
129 : DecoratedRestfulClient
<T
>(std::forward
<U
>(decoratee
)),
130 has_content_length(false),
134 size_t send_content_length(const uint64_t len
) override
;
135 size_t send_chunked_transfer_encoding() override
;
136 size_t complete_header() override
;
137 size_t send_body(const char* buf
, size_t len
) override
;
138 size_t complete_request() override
;
141 template <typename T
>
142 size_t BufferingFilter
<T
>::send_body(const char* const buf
,
146 data
.append(buf
, len
);
150 return DecoratedRestfulClient
<T
>::send_body(buf
, len
);
153 template <typename T
>
154 size_t BufferingFilter
<T
>::send_content_length(const uint64_t len
)
156 has_content_length
= true;
157 return DecoratedRestfulClient
<T
>::send_content_length(len
);
160 template <typename T
>
161 size_t BufferingFilter
<T
>::send_chunked_transfer_encoding()
163 has_content_length
= true;
164 return DecoratedRestfulClient
<T
>::send_chunked_transfer_encoding();
167 template <typename T
>
168 size_t BufferingFilter
<T
>::complete_header()
170 if (! has_content_length
) {
171 /* We will dump everything in complete_request(). */
176 return DecoratedRestfulClient
<T
>::complete_header();
179 template <typename T
>
180 size_t BufferingFilter
<T
>::complete_request()
184 if (! has_content_length
) {
185 sent
+= DecoratedRestfulClient
<T
>::send_content_length(data
.length());
186 sent
+= DecoratedRestfulClient
<T
>::complete_header();
190 /* We are sending each buffer separately to avoid extra memory shuffling
191 * that would occur on data.c_str() to provide a continuous memory area. */
192 for (const auto& ptr
: data
.buffers()) {
193 sent
+= DecoratedRestfulClient
<T
>::send_body(ptr
.c_str(),
200 return sent
+ DecoratedRestfulClient
<T
>::complete_request();
203 template <typename T
> static inline
204 BufferingFilter
<T
> add_buffering(T
&& t
) {
205 return BufferingFilter
<T
>(std::forward
<T
>(t
));
209 template <typename T
>
210 class ChunkingFilter
: public DecoratedRestfulClient
<T
> {
211 template<typename Td
> friend class DecoratedRestfulClient
;
213 bool chunking_enabled
;
216 template <typename U
>
217 ChunkingFilter(U
&& decoratee
)
218 : DecoratedRestfulClient
<T
>(std::forward
<U
>(decoratee
)),
219 chunking_enabled(false) {
222 size_t send_chunked_transfer_encoding() override
{
223 chunking_enabled
= true;
224 return DecoratedRestfulClient
<T
>::send_header("Transfer-Encoding",
228 size_t send_body(const char* buf
,
229 const size_t len
) override
{
230 if (! chunking_enabled
) {
231 return DecoratedRestfulClient
<T
>::send_body(buf
, len
);
233 static constexpr char HEADER_END
[] = "\r\n";
235 const auto slen
= snprintf(sizebuf
, sizeof(buf
), "%" PRIx64
"\r\n", len
);
238 sent
+= DecoratedRestfulClient
<T
>::send_body(sizebuf
, slen
);
239 sent
+= DecoratedRestfulClient
<T
>::send_body(buf
, len
);
240 sent
+= DecoratedRestfulClient
<T
>::send_body(HEADER_END
,
241 sizeof(HEADER_END
) - 1);
246 size_t complete_request() override
{
249 if (chunking_enabled
) {
250 static constexpr char CHUNKED_RESP_END
[] = "0\r\n\r\n";
251 sent
+= DecoratedRestfulClient
<T
>::send_body(CHUNKED_RESP_END
,
252 sizeof(CHUNKED_RESP_END
) - 1);
255 return sent
+ DecoratedRestfulClient
<T
>::complete_request();
259 template <typename T
> static inline
260 ChunkingFilter
<T
> add_chunking(T
&& t
) {
261 return ChunkingFilter
<T
>(std::forward
<T
>(t
));
265 /* Class that controls and inhibits the process of sending Content-Length HTTP
266 * header where RFC 7230 requests so. The cases worth our attention are 204 No
267 * Content as well as 304 Not Modified. */
268 template <typename T
>
269 class ConLenControllingFilter
: public DecoratedRestfulClient
<T
> {
271 enum class ContentLengthAction
{
278 template <typename U
>
279 ConLenControllingFilter(U
&& decoratee
)
280 : DecoratedRestfulClient
<T
>(std::forward
<U
>(decoratee
)),
281 action(ContentLengthAction::UNKNOWN
) {
284 size_t send_status(const int status
,
285 const char* const status_name
) override
{
286 if ((204 == status
|| 304 == status
) &&
287 ! g_conf
->rgw_print_prohibited_content_length
) {
288 action
= ContentLengthAction::INHIBIT
;
290 action
= ContentLengthAction::FORWARD
;
293 return DecoratedRestfulClient
<T
>::send_status(status
, status_name
);
296 size_t send_content_length(const uint64_t len
) override
{
298 case ContentLengthAction::FORWARD
:
299 return DecoratedRestfulClient
<T
>::send_content_length(len
);
300 case ContentLengthAction::INHIBIT
:
302 case ContentLengthAction::UNKNOWN
:
309 template <typename T
> static inline
310 ConLenControllingFilter
<T
> add_conlen_controlling(T
&& t
) {
311 return ConLenControllingFilter
<T
>(std::forward
<T
>(t
));
315 /* Filter that rectifies the wrong behaviour of some clients of the RGWRestfulIO
316 * interface. Should be removed after fixing those clients. */
317 template <typename T
>
318 class ReorderingFilter
: public DecoratedRestfulClient
<T
> {
320 enum class ReorderState
{
321 RGW_EARLY_HEADERS
, /* Got headers sent before calling send_status. */
322 RGW_STATUS_SEEN
, /* Status has been seen. */
323 RGW_DATA
/* Header has been completed. */
326 boost::optional
<uint64_t> content_length
;
328 std::vector
<std::pair
<std::string
, std::string
>> headers
;
330 size_t send_header(const boost::string_ref
& name
,
331 const boost::string_ref
& value
) override
{
333 case ReorderState::RGW_EARLY_HEADERS
:
334 case ReorderState::RGW_STATUS_SEEN
:
335 headers
.emplace_back(std::make_pair(std::string(name
.data(), name
.size()),
336 std::string(value
.data(), value
.size())));
338 case ReorderState::RGW_DATA
:
339 return DecoratedRestfulClient
<T
>::send_header(name
, value
);
346 template <typename U
>
347 ReorderingFilter(U
&& decoratee
)
348 : DecoratedRestfulClient
<T
>(std::forward
<U
>(decoratee
)),
349 phase(ReorderState::RGW_EARLY_HEADERS
) {
352 size_t send_status(const int status
,
353 const char* const status_name
) override
{
354 phase
= ReorderState::RGW_STATUS_SEEN
;
356 return DecoratedRestfulClient
<T
>::send_status(status
, status_name
);
359 size_t send_content_length(const uint64_t len
) override
{
360 if (ReorderState::RGW_EARLY_HEADERS
== phase
) {
361 /* Oh great, someone tries to send content length before status. */
362 content_length
= len
;
365 return DecoratedRestfulClient
<T
>::send_content_length(len
);
369 size_t complete_header() override
{
372 /* Change state in order to immediately send everything we get. */
373 phase
= ReorderState::RGW_DATA
;
375 /* Sent content length if necessary. */
376 if (content_length
) {
377 sent
+= DecoratedRestfulClient
<T
>::send_content_length(*content_length
);
380 /* Header data in buffers are already counted. */
381 for (const auto& kv
: headers
) {
382 sent
+= DecoratedRestfulClient
<T
>::send_header(kv
.first
, kv
.second
);
386 return sent
+ DecoratedRestfulClient
<T
>::complete_header();
390 template <typename T
> static inline
391 ReorderingFilter
<T
> add_reordering(T
&& t
) {
392 return ReorderingFilter
<T
>(std::forward
<T
>(t
));
396 } /* namespace rgw */
397 #endif /* CEPH_RGW_CLIENT_IO_DECOIMPL_H */