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";
290 const auto slen = snprintf(sizebuf, sizeof(buf), "%" PRIx64 "\r\n", len);
293 sent += DecoratedRestfulClient<T>::send_body(sizebuf, slen);
294 sent += DecoratedRestfulClient<T>::send_body(buf, len);
295 sent += DecoratedRestfulClient<T>::send_body(HEADER_END,
296 sizeof(HEADER_END) - 1);
301 size_t complete_request() override {
304 if (chunking_enabled) {
305 static constexpr char CHUNKED_RESP_END[] = "0\r\n\r\n";
306 sent += DecoratedRestfulClient<T>::send_body(CHUNKED_RESP_END,
307 sizeof(CHUNKED_RESP_END) - 1);
310 return sent + DecoratedRestfulClient<T>::complete_request();
314 template <typename T> static inline
315 ChunkingFilter<T> add_chunking(T&& t) {
316 return ChunkingFilter<T>(std::forward<T>(t));
320 /* Class that controls and inhibits the process of sending Content-Length HTTP
321 * header where RFC 7230 requests so. The cases worth our attention are 204 No
322 * Content as well as 304 Not Modified. */
323 template <typename T>
324 class ConLenControllingFilter : public DecoratedRestfulClient<T> {
326 enum class ContentLengthAction {
333 template <typename U>
334 ConLenControllingFilter(U&& decoratee)
335 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
336 action(ContentLengthAction::UNKNOWN) {
339 size_t send_status(const int status,
340 const char* const status_name) override {
341 if ((204 == status || 304 == status) &&
342 ! g_conf->rgw_print_prohibited_content_length) {
343 action = ContentLengthAction::INHIBIT;
345 action = ContentLengthAction::FORWARD;
348 return DecoratedRestfulClient<T>::send_status(status, status_name);
351 size_t send_content_length(const uint64_t len) override {
353 case ContentLengthAction::FORWARD:
354 return DecoratedRestfulClient<T>::send_content_length(len);
355 case ContentLengthAction::INHIBIT:
357 case ContentLengthAction::UNKNOWN:
364 template <typename T> static inline
365 ConLenControllingFilter<T> add_conlen_controlling(T&& t) {
366 return ConLenControllingFilter<T>(std::forward<T>(t));
370 /* Filter that rectifies the wrong behaviour of some clients of the RGWRestfulIO
371 * interface. Should be removed after fixing those clients. */
372 template <typename T>
373 class ReorderingFilter : public DecoratedRestfulClient<T> {
375 enum class ReorderState {
376 RGW_EARLY_HEADERS, /* Got headers sent before calling send_status. */
377 RGW_STATUS_SEEN, /* Status has been seen. */
378 RGW_DATA /* Header has been completed. */
381 boost::optional<uint64_t> content_length;
383 std::vector<std::pair<std::string, std::string>> headers;
385 size_t send_header(const boost::string_ref& name,
386 const boost::string_ref& value) override {
388 case ReorderState::RGW_EARLY_HEADERS:
389 case ReorderState::RGW_STATUS_SEEN:
390 headers.emplace_back(std::make_pair(std::string(name.data(), name.size()),
391 std::string(value.data(), value.size())));
393 case ReorderState::RGW_DATA:
394 return DecoratedRestfulClient<T>::send_header(name, value);
401 template <typename U>
402 ReorderingFilter(U&& decoratee)
403 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
404 phase(ReorderState::RGW_EARLY_HEADERS) {
407 size_t send_status(const int status,
408 const char* const status_name) override {
409 phase = ReorderState::RGW_STATUS_SEEN;
411 return DecoratedRestfulClient<T>::send_status(status, status_name);
414 size_t send_content_length(const uint64_t len) override {
415 if (ReorderState::RGW_EARLY_HEADERS == phase) {
416 /* Oh great, someone tries to send content length before status. */
417 content_length = len;
420 return DecoratedRestfulClient<T>::send_content_length(len);
424 size_t complete_header() override {
427 /* Change state in order to immediately send everything we get. */
428 phase = ReorderState::RGW_DATA;
430 /* Sent content length if necessary. */
431 if (content_length) {
432 sent += DecoratedRestfulClient<T>::send_content_length(*content_length);
435 /* Header data in buffers are already counted. */
436 for (const auto& kv : headers) {
437 sent += DecoratedRestfulClient<T>::send_header(kv.first, kv.second);
441 return sent + DecoratedRestfulClient<T>::complete_header();
445 template <typename T> static inline
446 ReorderingFilter<T> add_reordering(T&& t) {
447 return ReorderingFilter<T>(std::forward<T>(t));
451 } /* namespace rgw */
452 #endif /* CEPH_RGW_CLIENT_IO_DECOIMPL_H */