]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_client_io_filters.h
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / rgw / rgw_client_io_filters.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
3
4 #pragma once
5
6 #include <type_traits>
7
8 #include <boost/optional.hpp>
9
10 #include "rgw_common.h"
11 #include "rgw_client_io.h"
12
13 namespace rgw {
14 namespace io {
15
16 template <typename T>
17 class AccountingFilter : public DecoratedRestfulClient<T>,
18 public Accounter {
19 bool enabled;
20 uint64_t total_sent;
21 uint64_t total_received;
22 CephContext *cct;
23
24 public:
25 template <typename U>
26 AccountingFilter(CephContext *cct, U&& decoratee)
27 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
28 enabled(false),
29 total_sent(0),
30 total_received(0), cct(cct) {
31 }
32
33 size_t send_status(const int status,
34 const char* const status_name) override {
35 const auto sent = DecoratedRestfulClient<T>::send_status(status,
36 status_name);
37 lsubdout(cct, rgw, 30) << "AccountingFilter::send_status: e="
38 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
39 << total_sent << dendl;
40 if (enabled) {
41 total_sent += sent;
42 }
43 return sent;
44 }
45
46 size_t send_100_continue() override {
47 const auto sent = DecoratedRestfulClient<T>::send_100_continue();
48 lsubdout(cct, rgw, 30) << "AccountingFilter::send_100_continue: e="
49 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
50 << total_sent << dendl;
51 if (enabled) {
52 total_sent += sent;
53 }
54 return sent;
55 }
56
57 size_t send_header(const std::string_view& name,
58 const std::string_view& value) override {
59 const auto sent = DecoratedRestfulClient<T>::send_header(name, value);
60 lsubdout(cct, rgw, 30) << "AccountingFilter::send_header: e="
61 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
62 << total_sent << dendl;
63 if (enabled) {
64 total_sent += sent;
65 }
66 return sent;
67 }
68
69 size_t send_content_length(const uint64_t len) override {
70 const auto sent = DecoratedRestfulClient<T>::send_content_length(len);
71 lsubdout(cct, rgw, 30) << "AccountingFilter::send_content_length: e="
72 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
73 << total_sent << dendl;
74 if (enabled) {
75 total_sent += sent;
76 }
77 return sent;
78 }
79
80 size_t send_chunked_transfer_encoding() override {
81 const auto sent = DecoratedRestfulClient<T>::send_chunked_transfer_encoding();
82 lsubdout(cct, rgw, 30) << "AccountingFilter::send_chunked_transfer_encoding: e="
83 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
84 << total_sent << dendl;
85 if (enabled) {
86 total_sent += sent;
87 }
88 return sent;
89 }
90
91 size_t complete_header() override {
92 const auto sent = DecoratedRestfulClient<T>::complete_header();
93 lsubdout(cct, rgw, 30) << "AccountingFilter::complete_header: e="
94 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
95 << total_sent << dendl;
96 if (enabled) {
97 total_sent += sent;
98 }
99 return sent;
100 }
101
102 size_t recv_body(char* buf, size_t max) override {
103 const auto received = DecoratedRestfulClient<T>::recv_body(buf, max);
104 lsubdout(cct, rgw, 30) << "AccountingFilter::recv_body: e="
105 << (enabled ? "1" : "0") << ", received=" << received << dendl;
106 if (enabled) {
107 total_received += received;
108 }
109 return received;
110 }
111
112 size_t send_body(const char* const buf,
113 const size_t len) override {
114 const auto sent = DecoratedRestfulClient<T>::send_body(buf, len);
115 lsubdout(cct, rgw, 30) << "AccountingFilter::send_body: e="
116 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
117 << total_sent << dendl;
118 if (enabled) {
119 total_sent += sent;
120 }
121 return sent;
122 }
123
124 size_t complete_request() override {
125 const auto sent = DecoratedRestfulClient<T>::complete_request();
126 lsubdout(cct, rgw, 30) << "AccountingFilter::complete_request: e="
127 << (enabled ? "1" : "0") << ", sent=" << sent << ", total="
128 << total_sent << dendl;
129 if (enabled) {
130 total_sent += sent;
131 }
132 return sent;
133 }
134
135 uint64_t get_bytes_sent() const override {
136 return total_sent;
137 }
138
139 uint64_t get_bytes_received() const override {
140 return total_received;
141 }
142
143 void set_account(bool enabled) override {
144 this->enabled = enabled;
145 lsubdout(cct, rgw, 30) << "AccountingFilter::set_account: e="
146 << (enabled ? "1" : "0") << dendl;
147 }
148 };
149
150
151 /* Filter for in-memory buffering incoming data and calculating the content
152 * length header if it isn't present. */
153 template <typename T>
154 class BufferingFilter : public DecoratedRestfulClient<T> {
155 template<typename Td> friend class DecoratedRestfulClient;
156 protected:
157 ceph::bufferlist data;
158
159 bool has_content_length;
160 bool buffer_data;
161 CephContext *cct;
162
163 public:
164 template <typename U>
165 BufferingFilter(CephContext *cct, U&& decoratee)
166 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
167 has_content_length(false),
168 buffer_data(false), cct(cct) {
169 }
170
171 size_t send_content_length(const uint64_t len) override;
172 size_t send_chunked_transfer_encoding() override;
173 size_t complete_header() override;
174 size_t send_body(const char* buf, size_t len) override;
175 size_t complete_request() override;
176 };
177
178 template <typename T>
179 size_t BufferingFilter<T>::send_body(const char* const buf,
180 const size_t len)
181 {
182 if (buffer_data) {
183 data.append(buf, len);
184
185 lsubdout(cct, rgw, 30) << "BufferingFilter<T>::send_body: defer count = "
186 << len << dendl;
187 return 0;
188 }
189
190 return DecoratedRestfulClient<T>::send_body(buf, len);
191 }
192
193 template <typename T>
194 size_t BufferingFilter<T>::send_content_length(const uint64_t len)
195 {
196 has_content_length = true;
197 return DecoratedRestfulClient<T>::send_content_length(len);
198 }
199
200 template <typename T>
201 size_t BufferingFilter<T>::send_chunked_transfer_encoding()
202 {
203 has_content_length = true;
204 return DecoratedRestfulClient<T>::send_chunked_transfer_encoding();
205 }
206
207 template <typename T>
208 size_t BufferingFilter<T>::complete_header()
209 {
210 if (! has_content_length) {
211 /* We will dump everything in complete_request(). */
212 buffer_data = true;
213 lsubdout(cct, rgw, 30) << "BufferingFilter<T>::complete_header: has_content_length="
214 << (has_content_length ? "1" : "0") << dendl;
215 return 0;
216 }
217
218 return DecoratedRestfulClient<T>::complete_header();
219 }
220
221 template <typename T>
222 size_t BufferingFilter<T>::complete_request()
223 {
224 size_t sent = 0;
225
226 if (! has_content_length) {
227 /* It is not correct to count these bytes here,
228 * because they can only be part of the header.
229 * Therefore force count to 0.
230 */
231 sent += DecoratedRestfulClient<T>::send_content_length(data.length());
232 sent += DecoratedRestfulClient<T>::complete_header();
233 lsubdout(cct, rgw, 30) <<
234 "BufferingFilter::complete_request: !has_content_length: IGNORE: sent="
235 << sent << dendl;
236 sent = 0;
237 }
238
239 if (buffer_data) {
240 /* We are sending each buffer separately to avoid extra memory shuffling
241 * that would occur on data.c_str() to provide a continuous memory area. */
242 for (const auto& ptr : data.buffers()) {
243 sent += DecoratedRestfulClient<T>::send_body(ptr.c_str(),
244 ptr.length());
245 }
246 data.clear();
247 buffer_data = false;
248 lsubdout(cct, rgw, 30) << "BufferingFilter::complete_request: buffer_data: sent="
249 << sent << dendl;
250 }
251
252 return sent + DecoratedRestfulClient<T>::complete_request();
253 }
254
255 template <typename T> static inline
256 BufferingFilter<T> add_buffering(
257 CephContext *cct,
258 T&& t) {
259 return BufferingFilter<T>(cct, std::forward<T>(t));
260 }
261
262
263 template <typename T>
264 class ChunkingFilter : public DecoratedRestfulClient<T> {
265 template<typename Td> friend class DecoratedRestfulClient;
266 protected:
267 bool chunking_enabled;
268
269 public:
270 template <typename U>
271 explicit ChunkingFilter(U&& decoratee)
272 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
273 chunking_enabled(false) {
274 }
275
276 size_t send_chunked_transfer_encoding() override {
277 chunking_enabled = true;
278 return DecoratedRestfulClient<T>::send_header("Transfer-Encoding",
279 "chunked");
280 }
281
282 size_t send_body(const char* buf,
283 const size_t len) override {
284 if (! chunking_enabled) {
285 return DecoratedRestfulClient<T>::send_body(buf, len);
286 } else {
287 static constexpr char HEADER_END[] = "\r\n";
288 /* https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 */
289 // TODO: we have no support for sending chunked-encoding
290 // extensions/trailing headers.
291 char chunk_size[32];
292 const auto chunk_size_len = snprintf(chunk_size, sizeof(chunk_size),
293 "%zx\r\n", len);
294 size_t sent = 0;
295
296 sent += DecoratedRestfulClient<T>::send_body(chunk_size, chunk_size_len);
297 sent += DecoratedRestfulClient<T>::send_body(buf, len);
298 sent += DecoratedRestfulClient<T>::send_body(HEADER_END,
299 sizeof(HEADER_END) - 1);
300 return sent;
301 }
302 }
303
304 size_t complete_request() override {
305 size_t sent = 0;
306
307 if (chunking_enabled) {
308 static constexpr char CHUNKED_RESP_END[] = "0\r\n\r\n";
309 sent += DecoratedRestfulClient<T>::send_body(CHUNKED_RESP_END,
310 sizeof(CHUNKED_RESP_END) - 1);
311 }
312
313 return sent + DecoratedRestfulClient<T>::complete_request();
314 }
315 };
316
317 template <typename T> static inline
318 ChunkingFilter<T> add_chunking(T&& t) {
319 return ChunkingFilter<T>(std::forward<T>(t));
320 }
321
322
323 /* Class that controls and inhibits the process of sending Content-Length HTTP
324 * header where RFC 7230 requests so. The cases worth our attention are 204 No
325 * Content as well as 304 Not Modified. */
326 template <typename T>
327 class ConLenControllingFilter : public DecoratedRestfulClient<T> {
328 protected:
329 enum class ContentLengthAction {
330 FORWARD,
331 INHIBIT,
332 UNKNOWN
333 } action;
334
335 public:
336 template <typename U>
337 explicit ConLenControllingFilter(U&& decoratee)
338 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
339 action(ContentLengthAction::UNKNOWN) {
340 }
341
342 size_t send_status(const int status,
343 const char* const status_name) override {
344 if ((204 == status || 304 == status) &&
345 ! g_conf()->rgw_print_prohibited_content_length) {
346 action = ContentLengthAction::INHIBIT;
347 } else {
348 action = ContentLengthAction::FORWARD;
349 }
350
351 return DecoratedRestfulClient<T>::send_status(status, status_name);
352 }
353
354 size_t send_content_length(const uint64_t len) override {
355 switch(action) {
356 case ContentLengthAction::FORWARD:
357 return DecoratedRestfulClient<T>::send_content_length(len);
358 case ContentLengthAction::INHIBIT:
359 return 0;
360 case ContentLengthAction::UNKNOWN:
361 default:
362 return -EINVAL;
363 }
364 }
365 };
366
367 template <typename T> static inline
368 ConLenControllingFilter<T> add_conlen_controlling(T&& t) {
369 return ConLenControllingFilter<T>(std::forward<T>(t));
370 }
371
372
373 /* Filter that rectifies the wrong behaviour of some clients of the RGWRestfulIO
374 * interface. Should be removed after fixing those clients. */
375 template <typename T>
376 class ReorderingFilter : public DecoratedRestfulClient<T> {
377 protected:
378 enum class ReorderState {
379 RGW_EARLY_HEADERS, /* Got headers sent before calling send_status. */
380 RGW_STATUS_SEEN, /* Status has been seen. */
381 RGW_DATA /* Header has been completed. */
382 } phase;
383
384 boost::optional<uint64_t> content_length;
385
386 std::vector<std::pair<std::string, std::string>> headers;
387
388 size_t send_header(const std::string_view& name,
389 const std::string_view& value) override {
390 switch (phase) {
391 case ReorderState::RGW_EARLY_HEADERS:
392 case ReorderState::RGW_STATUS_SEEN:
393 headers.emplace_back(std::make_pair(std::string(name.data(), name.size()),
394 std::string(value.data(), value.size())));
395 return 0;
396 case ReorderState::RGW_DATA:
397 return DecoratedRestfulClient<T>::send_header(name, value);
398 }
399
400 return -EIO;
401 }
402
403 public:
404 template <typename U>
405 explicit ReorderingFilter(U&& decoratee)
406 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
407 phase(ReorderState::RGW_EARLY_HEADERS) {
408 }
409
410 size_t send_status(const int status,
411 const char* const status_name) override {
412 phase = ReorderState::RGW_STATUS_SEEN;
413
414 return DecoratedRestfulClient<T>::send_status(status, status_name);
415 }
416
417 size_t send_content_length(const uint64_t len) override {
418 if (ReorderState::RGW_EARLY_HEADERS == phase) {
419 /* Oh great, someone tries to send content length before status. */
420 content_length = len;
421 return 0;
422 } else {
423 return DecoratedRestfulClient<T>::send_content_length(len);
424 }
425 }
426
427 size_t complete_header() override {
428 size_t sent = 0;
429
430 /* Change state in order to immediately send everything we get. */
431 phase = ReorderState::RGW_DATA;
432
433 /* Sent content length if necessary. */
434 if (content_length) {
435 sent += DecoratedRestfulClient<T>::send_content_length(*content_length);
436 }
437
438 /* Header data in buffers are already counted. */
439 for (const auto& kv : headers) {
440 sent += DecoratedRestfulClient<T>::send_header(kv.first, kv.second);
441 }
442 headers.clear();
443
444 return sent + DecoratedRestfulClient<T>::complete_header();
445 }
446 };
447
448 template <typename T> static inline
449 ReorderingFilter<T> add_reordering(T&& t) {
450 return ReorderingFilter<T>(std::forward<T>(t));
451 }
452
453 } /* namespace io */
454 } /* namespace rgw */