]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_client_io_filters.h
0862f33d3ef570a9d29471bd5e2efba09c9bbedc
[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
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
14 namespace rgw {
15 namespace io {
16
17 template <typename T>
18 class AccountingFilter : public DecoratedRestfulClient<T>,
19 public Accounter {
20 bool enabled;
21 uint64_t total_sent;
22 uint64_t total_received;
23
24 public:
25 template <typename U>
26 AccountingFilter(U&& decoratee)
27 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
28 enabled(false),
29 total_sent(0),
30 total_received(0) {
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 if (enabled) {
38 total_sent += sent;
39 }
40 return sent;
41 }
42
43 size_t send_100_continue() override {
44 const auto sent = DecoratedRestfulClient<T>::send_100_continue();
45 if (enabled) {
46 total_sent += sent;
47 }
48 return sent;
49 }
50
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);
54 if (enabled) {
55 total_sent += sent;
56 }
57 return sent;
58 }
59
60 size_t send_content_length(const uint64_t len) override {
61 const auto sent = DecoratedRestfulClient<T>::send_content_length(len);
62 if (enabled) {
63 total_sent += sent;
64 }
65 return sent;
66 }
67
68 size_t send_chunked_transfer_encoding() override {
69 const auto sent = DecoratedRestfulClient<T>::send_chunked_transfer_encoding();
70 if (enabled) {
71 total_sent += sent;
72 }
73 return sent;
74 }
75
76 size_t complete_header() override {
77 const auto sent = DecoratedRestfulClient<T>::complete_header();
78 if (enabled) {
79 total_sent += sent;
80 }
81 return sent;
82 }
83
84 size_t recv_body(char* buf, size_t max) override {
85 const auto received = DecoratedRestfulClient<T>::recv_body(buf, max);
86 if (enabled) {
87 total_received += received;
88 }
89 return received;
90 }
91
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);
95 if (enabled) {
96 total_sent += sent;
97 }
98 return sent;
99 }
100
101 uint64_t get_bytes_sent() const override {
102 return total_sent;
103 }
104
105 uint64_t get_bytes_received() const override {
106 return total_received;
107 }
108
109 void set_account(bool enabled) override {
110 this->enabled = enabled;
111 }
112 };
113
114
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;
120 protected:
121 ceph::bufferlist data;
122
123 bool has_content_length;
124 bool buffer_data;
125
126 public:
127 template <typename U>
128 BufferingFilter(U&& decoratee)
129 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
130 has_content_length(false),
131 buffer_data(false) {
132 }
133
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;
139 };
140
141 template <typename T>
142 size_t BufferingFilter<T>::send_body(const char* const buf,
143 const size_t len)
144 {
145 if (buffer_data) {
146 data.append(buf, len);
147 return 0;
148 }
149
150 return DecoratedRestfulClient<T>::send_body(buf, len);
151 }
152
153 template <typename T>
154 size_t BufferingFilter<T>::send_content_length(const uint64_t len)
155 {
156 has_content_length = true;
157 return DecoratedRestfulClient<T>::send_content_length(len);
158 }
159
160 template <typename T>
161 size_t BufferingFilter<T>::send_chunked_transfer_encoding()
162 {
163 has_content_length = true;
164 return DecoratedRestfulClient<T>::send_chunked_transfer_encoding();
165 }
166
167 template <typename T>
168 size_t BufferingFilter<T>::complete_header()
169 {
170 if (! has_content_length) {
171 /* We will dump everything in complete_request(). */
172 buffer_data = true;
173 return 0;
174 }
175
176 return DecoratedRestfulClient<T>::complete_header();
177 }
178
179 template <typename T>
180 size_t BufferingFilter<T>::complete_request()
181 {
182 size_t sent = 0;
183
184 if (! has_content_length) {
185 sent += DecoratedRestfulClient<T>::send_content_length(data.length());
186 sent += DecoratedRestfulClient<T>::complete_header();
187 }
188
189 if (buffer_data) {
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(),
194 ptr.length());
195 }
196 data.clear();
197 buffer_data = false;
198 }
199
200 return sent + DecoratedRestfulClient<T>::complete_request();
201 }
202
203 template <typename T> static inline
204 BufferingFilter<T> add_buffering(T&& t) {
205 return BufferingFilter<T>(std::forward<T>(t));
206 }
207
208
209 template <typename T>
210 class ChunkingFilter : public DecoratedRestfulClient<T> {
211 template<typename Td> friend class DecoratedRestfulClient;
212 protected:
213 bool chunking_enabled;
214
215 public:
216 template <typename U>
217 ChunkingFilter(U&& decoratee)
218 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
219 chunking_enabled(false) {
220 }
221
222 size_t send_chunked_transfer_encoding() override {
223 chunking_enabled = true;
224 return DecoratedRestfulClient<T>::send_header("Transfer-Encoding",
225 "chunked");
226 }
227
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);
232 } else {
233 static constexpr char HEADER_END[] = "\r\n";
234 char sizebuf[32];
235 const auto slen = snprintf(sizebuf, sizeof(buf), "%" PRIx64 "\r\n", len);
236 size_t sent = 0;
237
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);
242 return sent;
243 }
244 }
245
246 size_t complete_request() override {
247 size_t sent = 0;
248
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);
253 }
254
255 return sent + DecoratedRestfulClient<T>::complete_request();
256 }
257 };
258
259 template <typename T> static inline
260 ChunkingFilter<T> add_chunking(T&& t) {
261 return ChunkingFilter<T>(std::forward<T>(t));
262 }
263
264
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> {
270 protected:
271 enum class ContentLengthAction {
272 FORWARD,
273 INHIBIT,
274 UNKNOWN
275 } action;
276
277 public:
278 template <typename U>
279 ConLenControllingFilter(U&& decoratee)
280 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
281 action(ContentLengthAction::UNKNOWN) {
282 }
283
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;
289 } else {
290 action = ContentLengthAction::FORWARD;
291 }
292
293 return DecoratedRestfulClient<T>::send_status(status, status_name);
294 }
295
296 size_t send_content_length(const uint64_t len) override {
297 switch(action) {
298 case ContentLengthAction::FORWARD:
299 return DecoratedRestfulClient<T>::send_content_length(len);
300 case ContentLengthAction::INHIBIT:
301 return 0;
302 case ContentLengthAction::UNKNOWN:
303 default:
304 return -EINVAL;
305 }
306 }
307 };
308
309 template <typename T> static inline
310 ConLenControllingFilter<T> add_conlen_controlling(T&& t) {
311 return ConLenControllingFilter<T>(std::forward<T>(t));
312 }
313
314
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> {
319 protected:
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. */
324 } phase;
325
326 boost::optional<uint64_t> content_length;
327
328 std::vector<std::pair<std::string, std::string>> headers;
329
330 size_t send_header(const boost::string_ref& name,
331 const boost::string_ref& value) override {
332 switch (phase) {
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())));
337 return 0;
338 case ReorderState::RGW_DATA:
339 return DecoratedRestfulClient<T>::send_header(name, value);
340 }
341
342 return -EIO;
343 }
344
345 public:
346 template <typename U>
347 ReorderingFilter(U&& decoratee)
348 : DecoratedRestfulClient<T>(std::forward<U>(decoratee)),
349 phase(ReorderState::RGW_EARLY_HEADERS) {
350 }
351
352 size_t send_status(const int status,
353 const char* const status_name) override {
354 phase = ReorderState::RGW_STATUS_SEEN;
355
356 return DecoratedRestfulClient<T>::send_status(status, status_name);
357 }
358
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;
363 return 0;
364 } else {
365 return DecoratedRestfulClient<T>::send_content_length(len);
366 }
367 }
368
369 size_t complete_header() override {
370 size_t sent = 0;
371
372 /* Change state in order to immediately send everything we get. */
373 phase = ReorderState::RGW_DATA;
374
375 /* Sent content length if necessary. */
376 if (content_length) {
377 sent += DecoratedRestfulClient<T>::send_content_length(*content_length);
378 }
379
380 /* Header data in buffers are already counted. */
381 for (const auto& kv : headers) {
382 sent += DecoratedRestfulClient<T>::send_header(kv.first, kv.second);
383 }
384 headers.clear();
385
386 return sent + DecoratedRestfulClient<T>::complete_header();
387 }
388 };
389
390 template <typename T> static inline
391 ReorderingFilter<T> add_reordering(T&& t) {
392 return ReorderingFilter<T>(std::forward<T>(t));
393 }
394
395 } /* namespace io */
396 } /* namespace rgw */
397 #endif /* CEPH_RGW_CLIENT_IO_DECOIMPL_H */