]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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; | |
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> | |
272 | ChunkingFilter(U&& decoratee) | |
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"; | |
289 | char sizebuf[32]; | |
290 | const auto slen = snprintf(sizebuf, sizeof(buf), "%" PRIx64 "\r\n", len); | |
291 | size_t sent = 0; | |
292 | ||
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); | |
297 | return sent; | |
298 | } | |
299 | } | |
300 | ||
301 | size_t complete_request() override { | |
302 | size_t sent = 0; | |
303 | ||
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); | |
308 | } | |
309 | ||
310 | return sent + DecoratedRestfulClient<T>::complete_request(); | |
311 | } | |
312 | }; | |
313 | ||
314 | template <typename T> static inline | |
315 | ChunkingFilter<T> add_chunking(T&& t) { | |
316 | return ChunkingFilter<T>(std::forward<T>(t)); | |
317 | } | |
318 | ||
319 | ||
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> { | |
325 | protected: | |
326 | enum class ContentLengthAction { | |
327 | FORWARD, | |
328 | INHIBIT, | |
329 | UNKNOWN | |
330 | } action; | |
331 | ||
332 | public: | |
333 | template <typename U> | |
334 | ConLenControllingFilter(U&& decoratee) | |
335 | : DecoratedRestfulClient<T>(std::forward<U>(decoratee)), | |
336 | action(ContentLengthAction::UNKNOWN) { | |
337 | } | |
338 | ||
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; | |
344 | } else { | |
345 | action = ContentLengthAction::FORWARD; | |
346 | } | |
347 | ||
348 | return DecoratedRestfulClient<T>::send_status(status, status_name); | |
349 | } | |
350 | ||
351 | size_t send_content_length(const uint64_t len) override { | |
352 | switch(action) { | |
353 | case ContentLengthAction::FORWARD: | |
354 | return DecoratedRestfulClient<T>::send_content_length(len); | |
355 | case ContentLengthAction::INHIBIT: | |
356 | return 0; | |
357 | case ContentLengthAction::UNKNOWN: | |
358 | default: | |
359 | return -EINVAL; | |
360 | } | |
361 | } | |
362 | }; | |
363 | ||
364 | template <typename T> static inline | |
365 | ConLenControllingFilter<T> add_conlen_controlling(T&& t) { | |
366 | return ConLenControllingFilter<T>(std::forward<T>(t)); | |
367 | } | |
368 | ||
369 | ||
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> { | |
374 | protected: | |
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. */ | |
379 | } phase; | |
380 | ||
381 | boost::optional<uint64_t> content_length; | |
382 | ||
383 | std::vector<std::pair<std::string, std::string>> headers; | |
384 | ||
385 | size_t send_header(const boost::string_ref& name, | |
386 | const boost::string_ref& value) override { | |
387 | switch (phase) { | |
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()))); | |
392 | return 0; | |
393 | case ReorderState::RGW_DATA: | |
394 | return DecoratedRestfulClient<T>::send_header(name, value); | |
395 | } | |
396 | ||
397 | return -EIO; | |
398 | } | |
399 | ||
400 | public: | |
401 | template <typename U> | |
402 | ReorderingFilter(U&& decoratee) | |
403 | : DecoratedRestfulClient<T>(std::forward<U>(decoratee)), | |
404 | phase(ReorderState::RGW_EARLY_HEADERS) { | |
405 | } | |
406 | ||
407 | size_t send_status(const int status, | |
408 | const char* const status_name) override { | |
409 | phase = ReorderState::RGW_STATUS_SEEN; | |
410 | ||
411 | return DecoratedRestfulClient<T>::send_status(status, status_name); | |
412 | } | |
413 | ||
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; | |
418 | return 0; | |
419 | } else { | |
420 | return DecoratedRestfulClient<T>::send_content_length(len); | |
421 | } | |
422 | } | |
423 | ||
424 | size_t complete_header() override { | |
425 | size_t sent = 0; | |
426 | ||
427 | /* Change state in order to immediately send everything we get. */ | |
428 | phase = ReorderState::RGW_DATA; | |
429 | ||
430 | /* Sent content length if necessary. */ | |
431 | if (content_length) { | |
432 | sent += DecoratedRestfulClient<T>::send_content_length(*content_length); | |
433 | } | |
434 | ||
435 | /* Header data in buffers are already counted. */ | |
436 | for (const auto& kv : headers) { | |
437 | sent += DecoratedRestfulClient<T>::send_header(kv.first, kv.second); | |
438 | } | |
439 | headers.clear(); | |
440 | ||
441 | return sent + DecoratedRestfulClient<T>::complete_header(); | |
442 | } | |
443 | }; | |
444 | ||
445 | template <typename T> static inline | |
446 | ReorderingFilter<T> add_reordering(T&& t) { | |
447 | return ReorderingFilter<T>(std::forward<T>(t)); | |
448 | } | |
449 | ||
450 | } /* namespace io */ | |
451 | } /* namespace rgw */ | |
452 | #endif /* CEPH_RGW_CLIENT_IO_DECOIMPL_H */ |