]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_client_io_filters.h
import 15.2.0 Octopus source
[ceph.git] / ceph / src / rgw / rgw_client_io_filters.h
CommitLineData
7c673cae 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
7c673cae
FG
3
4#ifndef CEPH_RGW_CLIENT_IO_DECOIMPL_H
5#define CEPH_RGW_CLIENT_IO_DECOIMPL_H
6
7#include <type_traits>
8
9#include <boost/optional.hpp>
10
11#include "rgw_common.h"
12#include "rgw_client_io.h"
13
14namespace rgw {
15namespace io {
16
17template <typename T>
18class AccountingFilter : public DecoratedRestfulClient<T>,
19 public Accounter {
20 bool enabled;
21 uint64_t total_sent;
22 uint64_t total_received;
181888fb 23 CephContext *cct;
7c673cae
FG
24
25public:
26 template <typename U>
181888fb 27 AccountingFilter(CephContext *cct, U&& decoratee)
7c673cae
FG
28 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
29 enabled(false),
30 total_sent(0),
181888fb 31 total_received(0), cct(cct) {
7c673cae
FG
32 }
33
34 size_t send_status(const int status,
35 const char* const status_name) override {
36 const auto sent = DecoratedRestfulClient<T>::send_status(status,
37 status_name);
181888fb
FG
38 lsubdout(cct, rgw, 30) << "AccountingFilter::send_status: e="
39 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
40 << total_sent << dendl;
7c673cae
FG
41 if (enabled) {
42 total_sent += sent;
43 }
44 return sent;
45 }
46
47 size_t send_100_continue() override {
48 const auto sent = DecoratedRestfulClient<T>::send_100_continue();
181888fb
FG
49 lsubdout(cct, rgw, 30) << "AccountingFilter::send_100_continue: e="
50 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
51 << total_sent << dendl;
7c673cae
FG
52 if (enabled) {
53 total_sent += sent;
54 }
55 return sent;
56 }
57
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);
181888fb
FG
61 lsubdout(cct, rgw, 30) << "AccountingFilter::send_header: e="
62 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
63 << total_sent << dendl;
7c673cae
FG
64 if (enabled) {
65 total_sent += sent;
66 }
67 return sent;
68 }
69
70 size_t send_content_length(const uint64_t len) override {
71 const auto sent = DecoratedRestfulClient<T>::send_content_length(len);
181888fb
FG
72 lsubdout(cct, rgw, 30) << "AccountingFilter::send_content_length: e="
73 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
74 << total_sent << dendl;
7c673cae
FG
75 if (enabled) {
76 total_sent += sent;
77 }
78 return sent;
79 }
80
81 size_t send_chunked_transfer_encoding() override {
82 const auto sent = DecoratedRestfulClient<T>::send_chunked_transfer_encoding();
181888fb
FG
83 lsubdout(cct, rgw, 30) << "AccountingFilter::send_chunked_transfer_encoding: e="
84 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
85 << total_sent << dendl;
7c673cae
FG
86 if (enabled) {
87 total_sent += sent;
88 }
89 return sent;
90 }
91
92 size_t complete_header() override {
93 const auto sent = DecoratedRestfulClient<T>::complete_header();
181888fb
FG
94 lsubdout(cct, rgw, 30) << "AccountingFilter::complete_header: e="
95 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
96 << total_sent << dendl;
7c673cae
FG
97 if (enabled) {
98 total_sent += sent;
99 }
100 return sent;
101 }
102
103 size_t recv_body(char* buf, size_t max) override {
104 const auto received = DecoratedRestfulClient<T>::recv_body(buf, max);
181888fb
FG
105 lsubdout(cct, rgw, 30) << "AccountingFilter::recv_body: e="
106 << (enabled ? "1" : "0") << ", received=" << received << dendl;
7c673cae
FG
107 if (enabled) {
108 total_received += received;
109 }
110 return received;
111 }
112
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);
181888fb
FG
116 lsubdout(cct, rgw, 30) << "AccountingFilter::send_body: e="
117 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
118 << total_sent << dendl;
119 if (enabled) {
120 total_sent += sent;
121 }
122 return sent;
123 }
124
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;
7c673cae
FG
130 if (enabled) {
131 total_sent += sent;
132 }
133 return sent;
134 }
135
136 uint64_t get_bytes_sent() const override {
137 return total_sent;
138 }
139
140 uint64_t get_bytes_received() const override {
141 return total_received;
142 }
143
144 void set_account(bool enabled) override {
145 this->enabled = enabled;
181888fb
FG
146 lsubdout(cct, rgw, 30) << "AccountingFilter::set_account: e="
147 << (enabled ? "1" : "0") << dendl;
7c673cae
FG
148 }
149};
150
151
152/* Filter for in-memory buffering incoming data and calculating the content
153 * length header if it isn't present. */
154template <typename T>
155class BufferingFilter : public DecoratedRestfulClient<T> {
156 template<typename Td> friend class DecoratedRestfulClient;
157protected:
158 ceph::bufferlist data;
159
160 bool has_content_length;
161 bool buffer_data;
181888fb 162 CephContext *cct;
7c673cae
FG
163
164public:
165 template <typename U>
181888fb 166 BufferingFilter(CephContext *cct, U&& decoratee)
7c673cae
FG
167 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
168 has_content_length(false),
181888fb 169 buffer_data(false), cct(cct) {
7c673cae
FG
170 }
171
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;
177};
178
179template <typename T>
180size_t BufferingFilter<T>::send_body(const char* const buf,
181 const size_t len)
182{
183 if (buffer_data) {
184 data.append(buf, len);
181888fb
FG
185
186 lsubdout(cct, rgw, 30) << "BufferingFilter<T>::send_body: defer count = "
187 << len << dendl;
7c673cae
FG
188 return 0;
189 }
190
191 return DecoratedRestfulClient<T>::send_body(buf, len);
192}
193
194template <typename T>
195size_t BufferingFilter<T>::send_content_length(const uint64_t len)
196{
197 has_content_length = true;
198 return DecoratedRestfulClient<T>::send_content_length(len);
199}
200
201template <typename T>
202size_t BufferingFilter<T>::send_chunked_transfer_encoding()
203{
204 has_content_length = true;
205 return DecoratedRestfulClient<T>::send_chunked_transfer_encoding();
206}
207
208template <typename T>
209size_t BufferingFilter<T>::complete_header()
210{
211 if (! has_content_length) {
212 /* We will dump everything in complete_request(). */
213 buffer_data = true;
181888fb
FG
214 lsubdout(cct, rgw, 30) << "BufferingFilter<T>::complete_header: has_content_length="
215 << (has_content_length ? "1" : "0") << dendl;
7c673cae
FG
216 return 0;
217 }
218
219 return DecoratedRestfulClient<T>::complete_header();
220}
221
222template <typename T>
223size_t BufferingFilter<T>::complete_request()
224{
225 size_t sent = 0;
226
227 if (! has_content_length) {
181888fb
FG
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.
231 */
7c673cae
FG
232 sent += DecoratedRestfulClient<T>::send_content_length(data.length());
233 sent += DecoratedRestfulClient<T>::complete_header();
181888fb
FG
234 lsubdout(cct, rgw, 30) <<
235 "BufferingFilter::complete_request: !has_content_length: IGNORE: sent="
236 << sent << dendl;
237 sent = 0;
7c673cae
FG
238 }
239
240 if (buffer_data) {
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(),
245 ptr.length());
246 }
247 data.clear();
248 buffer_data = false;
181888fb
FG
249 lsubdout(cct, rgw, 30) << "BufferingFilter::complete_request: buffer_data: sent="
250 << sent << dendl;
7c673cae
FG
251 }
252
253 return sent + DecoratedRestfulClient<T>::complete_request();
254}
255
256template <typename T> static inline
181888fb
FG
257BufferingFilter<T> add_buffering(
258CephContext *cct,
259T&& t) {
260 return BufferingFilter<T>(cct, std::forward<T>(t));
7c673cae
FG
261}
262
263
264template <typename T>
265class ChunkingFilter : public DecoratedRestfulClient<T> {
266 template<typename Td> friend class DecoratedRestfulClient;
267protected:
268 bool chunking_enabled;
269
270public:
271 template <typename U>
11fdf7f2 272 explicit ChunkingFilter(U&& decoratee)
7c673cae
FG
273 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
274 chunking_enabled(false) {
275 }
276
277 size_t send_chunked_transfer_encoding() override {
278 chunking_enabled = true;
279 return DecoratedRestfulClient<T>::send_header("Transfer-Encoding",
280 "chunked");
281 }
282
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);
287 } else {
288 static constexpr char HEADER_END[] = "\r\n";
91327a77
AA
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.
292 char chunk_size[32];
293 const auto chunk_size_len = snprintf(chunk_size, sizeof(chunk_size),
9f95a23c 294 "%zx\r\n", len);
7c673cae
FG
295 size_t sent = 0;
296
91327a77 297 sent += DecoratedRestfulClient<T>::send_body(chunk_size, chunk_size_len);
7c673cae
FG
298 sent += DecoratedRestfulClient<T>::send_body(buf, len);
299 sent += DecoratedRestfulClient<T>::send_body(HEADER_END,
300 sizeof(HEADER_END) - 1);
301 return sent;
302 }
303 }
304
305 size_t complete_request() override {
306 size_t sent = 0;
307
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);
312 }
313
314 return sent + DecoratedRestfulClient<T>::complete_request();
315 }
316};
317
318template <typename T> static inline
319ChunkingFilter<T> add_chunking(T&& t) {
320 return ChunkingFilter<T>(std::forward<T>(t));
321}
322
323
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. */
327template <typename T>
328class ConLenControllingFilter : public DecoratedRestfulClient<T> {
329protected:
330 enum class ContentLengthAction {
331 FORWARD,
332 INHIBIT,
333 UNKNOWN
334 } action;
335
336public:
337 template <typename U>
11fdf7f2 338 explicit ConLenControllingFilter(U&& decoratee)
7c673cae
FG
339 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
340 action(ContentLengthAction::UNKNOWN) {
341 }
342
343 size_t send_status(const int status,
344 const char* const status_name) override {
345 if ((204 == status || 304 == status) &&
11fdf7f2 346 ! g_conf()->rgw_print_prohibited_content_length) {
7c673cae
FG
347 action = ContentLengthAction::INHIBIT;
348 } else {
349 action = ContentLengthAction::FORWARD;
350 }
351
352 return DecoratedRestfulClient<T>::send_status(status, status_name);
353 }
354
355 size_t send_content_length(const uint64_t len) override {
356 switch(action) {
357 case ContentLengthAction::FORWARD:
358 return DecoratedRestfulClient<T>::send_content_length(len);
359 case ContentLengthAction::INHIBIT:
360 return 0;
361 case ContentLengthAction::UNKNOWN:
362 default:
363 return -EINVAL;
364 }
365 }
366};
367
368template <typename T> static inline
369ConLenControllingFilter<T> add_conlen_controlling(T&& t) {
370 return ConLenControllingFilter<T>(std::forward<T>(t));
371}
372
373
374/* Filter that rectifies the wrong behaviour of some clients of the RGWRestfulIO
375 * interface. Should be removed after fixing those clients. */
376template <typename T>
377class ReorderingFilter : public DecoratedRestfulClient<T> {
378protected:
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. */
383 } phase;
384
385 boost::optional<uint64_t> content_length;
386
387 std::vector<std::pair<std::string, std::string>> headers;
388
389 size_t send_header(const boost::string_ref& name,
390 const boost::string_ref& value) override {
391 switch (phase) {
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())));
396 return 0;
397 case ReorderState::RGW_DATA:
398 return DecoratedRestfulClient<T>::send_header(name, value);
399 }
400
401 return -EIO;
402 }
403
404public:
405 template <typename U>
11fdf7f2 406 explicit ReorderingFilter(U&& decoratee)
7c673cae
FG
407 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
408 phase(ReorderState::RGW_EARLY_HEADERS) {
409 }
410
411 size_t send_status(const int status,
412 const char* const status_name) override {
413 phase = ReorderState::RGW_STATUS_SEEN;
414
415 return DecoratedRestfulClient<T>::send_status(status, status_name);
416 }
417
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;
422 return 0;
423 } else {
424 return DecoratedRestfulClient<T>::send_content_length(len);
425 }
426 }
427
428 size_t complete_header() override {
429 size_t sent = 0;
430
431 /* Change state in order to immediately send everything we get. */
432 phase = ReorderState::RGW_DATA;
433
434 /* Sent content length if necessary. */
435 if (content_length) {
436 sent += DecoratedRestfulClient<T>::send_content_length(*content_length);
437 }
438
439 /* Header data in buffers are already counted. */
440 for (const auto& kv : headers) {
441 sent += DecoratedRestfulClient<T>::send_header(kv.first, kv.second);
442 }
443 headers.clear();
444
445 return sent + DecoratedRestfulClient<T>::complete_header();
446 }
447};
448
449template <typename T> static inline
450ReorderingFilter<T> add_reordering(T&& t) {
451 return ReorderingFilter<T>(std::forward<T>(t));
452}
453
454} /* namespace io */
455} /* namespace rgw */
456#endif /* CEPH_RGW_CLIENT_IO_DECOIMPL_H */