]>
Commit | Line | Data |
---|---|---|
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 | ||
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; | |
181888fb | 23 | CephContext *cct; |
7c673cae FG |
24 | |
25 | public: | |
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. */ | |
154 | template <typename T> | |
155 | class BufferingFilter : public DecoratedRestfulClient<T> { | |
156 | template<typename Td> friend class DecoratedRestfulClient; | |
157 | protected: | |
158 | ceph::bufferlist data; | |
159 | ||
160 | bool has_content_length; | |
161 | bool buffer_data; | |
181888fb | 162 | CephContext *cct; |
7c673cae FG |
163 | |
164 | public: | |
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 | ||
179 | template <typename T> | |
180 | size_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 | ||
194 | template <typename T> | |
195 | size_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 | ||
201 | template <typename T> | |
202 | size_t BufferingFilter<T>::send_chunked_transfer_encoding() | |
203 | { | |
204 | has_content_length = true; | |
205 | return DecoratedRestfulClient<T>::send_chunked_transfer_encoding(); | |
206 | } | |
207 | ||
208 | template <typename T> | |
209 | size_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 | ||
222 | template <typename T> | |
223 | size_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 | ||
256 | template <typename T> static inline | |
181888fb FG |
257 | BufferingFilter<T> add_buffering( |
258 | CephContext *cct, | |
259 | T&& t) { | |
260 | return BufferingFilter<T>(cct, std::forward<T>(t)); | |
7c673cae FG |
261 | } |
262 | ||
263 | ||
264 | template <typename T> | |
265 | class ChunkingFilter : public DecoratedRestfulClient<T> { | |
266 | template<typename Td> friend class DecoratedRestfulClient; | |
267 | protected: | |
268 | bool chunking_enabled; | |
269 | ||
270 | public: | |
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 | ||
318 | template <typename T> static inline | |
319 | ChunkingFilter<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. */ | |
327 | template <typename T> | |
328 | class ConLenControllingFilter : public DecoratedRestfulClient<T> { | |
329 | protected: | |
330 | enum class ContentLengthAction { | |
331 | FORWARD, | |
332 | INHIBIT, | |
333 | UNKNOWN | |
334 | } action; | |
335 | ||
336 | public: | |
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 | ||
368 | template <typename T> static inline | |
369 | ConLenControllingFilter<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. */ | |
376 | template <typename T> | |
377 | class ReorderingFilter : public DecoratedRestfulClient<T> { | |
378 | protected: | |
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 | ||
404 | public: | |
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 | ||
449 | template <typename T> static inline | |
450 | ReorderingFilter<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 */ |