]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/src/http/transformers.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / src / http / transformers.cc
CommitLineData
11fdf7f2
TL
1/*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18/*
19 * Copyright 2015 Cloudius Systems
20 */
21
f67539c2
TL
22#include <seastar/core/do_with.hh>
23#include <seastar/core/loop.hh>
11fdf7f2
TL
24#include <boost/algorithm/string/replace.hpp>
25#include <seastar/http/transformers.hh>
26#include <list>
27
28namespace seastar {
29
30namespace httpd {
31
32using namespace std;
33
34struct potential_match_entry {
35 const char* begin;
36 const char* end;
37 size_t pos;
38};
39
40/*!
41 * \brief holds the buffer replace object current state
42 * The way the matching algorithm works, is that when there's a match
43 * it will be the first entry
44 */
45class buffer_replace_state {
46 std::list<potential_match_entry> _potential_match;
47
48public:
49 using iterator = std::list<potential_match_entry>::iterator;
50
51 void add_potential_match(const char* s, const char* e, size_t pos) {
52 _potential_match.emplace_back(potential_match_entry{s, e, pos});
53 }
54
55 iterator begin() {
56 return _potential_match.begin();
57 }
58
59 iterator end() {
60 return _potential_match.end();
61 }
62
63 bool empty() const {
64 return _potential_match.empty();
65 }
66
67 bool last() const {
68 return _potential_match.size() == 1;
69 }
70
71 auto erase(const iterator& i) {
72 return _potential_match.erase(i);
73 }
74
75 /*!
76 * \brief gets the key/value position in the buffer_replace of the match
77 */
78 size_t get_pos() const {
79 return (*_potential_match.begin()).pos;
80 }
81
82 /*!
83 * \brief gets the length of the remaining string
84 */
85 size_t get_remaining_length() const {
86 return _potential_match.begin()->end - _potential_match.begin()->begin;
87 }
88
89 void clear() {
90 _potential_match.clear();
91 }
92};
93
94/*!
95 *\brief a helper class to replace strings in a buffer
96 * The keys to replace are surrounded by braces
97 */
98class buffer_replace {
99 std::vector<std::tuple<sstring, sstring>> _values;
100 buffer_replace_state _current;
101 const sstring& get_value(size_t pos) const;
102 const sstring& get_key(size_t pos) const;
103public:
104 /*!
105 * \brief Add a key and value to be replaced
106 */
107 buffer_replace& add(sstring key, sstring value) {
108 _values.emplace_back(std::make_tuple("{{" + key + "}}", value));
109 return *this;
110 }
111
112 /*!
113 * \brief if there are no more buffers to consume get
114 * the remaining chars stored in the buffer_replace
115 */
116 temporary_buffer<char> get_remaining();
117
118 /*!
119 * \brief check if the given buffer still match any of the current potential matches
120 *
121 */
122 temporary_buffer<char> match(temporary_buffer<char>& buf);
123 /*!
124 * \brief replace the buffer content
125 *
126 * The returned result is after translation. The method consumes what it read
127 * from the buf, so the caller should check that buf is not empty.
128 *
129 * For example: if buf is: "abcd{{key}}"
130 * res = replace(buf);
131 *
132 * res will be "abcd"
133 * and buf will be "{{key}}"
134 */
135 temporary_buffer<char> replace(temporary_buffer<char>& buf);
136
137 /*!
138 * \brief check if we are currently in the middle of consuming
139 */
140 bool is_consuming() const {
141 return !_current.empty();
142 }
143};
144
145
146class content_replace_data_sink_impl : public data_sink_impl {
147 output_stream<char> _out;
148 buffer_replace _br;
149public:
150 content_replace_data_sink_impl(output_stream<char>&& out, std::vector<std::tuple<sstring,sstring>>&& key_value) : _out(std::move(out)) {
151 for (auto& i : key_value) {
152 _br.add(std::get<0>(i), std::get<1>(i));
153 }
154 }
155
156 virtual future<> put(net::packet data) override {
157 return make_ready_future<>();
158 }
159
160 using data_sink_impl::put;
161
162 virtual future<> put(temporary_buffer<char> buf) override {
163 if (buf.empty()) {
164 return make_ready_future<>();
165 }
166 return do_with(temporary_buffer<char>(std::move(buf)), [this] (temporary_buffer<char>& buf) {
167 return repeat([&buf, this] {
168 auto bf = _br.replace(buf);
169 return _out.write(bf.get(), bf.size()).then([&buf] {
170 return (buf.empty()) ? stop_iteration::yes : stop_iteration::no;
171 });
172 });
173 });
174 }
175
176 virtual future<> flush() override {
177 return _out.flush();
178 }
179
180 virtual future<> close() override {
181 // if we are in the middle of a consuming a key
182 // there will be no match, write the remaining.
183 if (_br.is_consuming()) {
184 return do_with(temporary_buffer<char>(_br.get_remaining()), [this](temporary_buffer<char>& buf) {
185 return _out.write(buf.get(), buf.size()).then([this] {
186 return _out.flush();
187 });
188 });
189 }
190 return _out.flush();
191 }
192};
193
194class content_replace_data_sink : public data_sink {
195public:
196 content_replace_data_sink(output_stream<char>&& out, std::vector<std::tuple<sstring,sstring>> key_value)
197 : data_sink(std::make_unique<content_replace_data_sink_impl>(
198 std::move(out), std::move(key_value))) {}
199};
200
1e59de90 201output_stream<char> content_replace::transform(std::unique_ptr<http::request> req,
11fdf7f2
TL
202 const sstring& extension, output_stream<char>&& s) {
203 sstring host = req->get_header("Host");
204 if (host == "" || (this->extension != "" && extension != this->extension)) {
205 return std::move(s);
206 }
207 sstring protocol = req->get_protocol_name();
20effc67
TL
208 output_stream_options opts;
209 opts.trim_to_size = true;
210 return output_stream<char>(content_replace_data_sink(std::move(s), {std::make_tuple("Protocol", protocol), std::make_tuple("Host", host)}), 32000, opts);
11fdf7f2
TL
211
212}
213
214/*!
215 * \brief find the open brace that surround a parameter
216 * it is either two consecutive braces or a single brace, if it's the last char in the buffer
217 */
218ssize_t find_braces(const char* s, const char* end) {
219 for (size_t i = 0; s != end; s++, i++) {
220 if (*s == '{' && ((s + 1) == end || *(s + 1) == '{')) {
221 return i;
222 }
223 }
224 return -1;
225}
226
227const sstring& buffer_replace::get_value(size_t pos) const {
228 return std::get<1>(_values[pos]);
229}
230
231const sstring& buffer_replace::get_key(size_t pos) const {
232 return std::get<0>(_values[pos]);
233}
234
235temporary_buffer<char> buffer_replace::match(temporary_buffer<char>& buf) {
236 if (_current.empty()) {
237 return temporary_buffer<char>();
238 }
239 auto buf_len = buf.size();
240 auto first = _current.begin();
241 while (first != _current.end()) {
242 auto& pos = first->begin;
243 auto end = first->end;
244 size_t len_compare = std::min(buf_len, static_cast<size_t>(end - pos));
245 if (strncmp(pos, buf.begin(), len_compare)) {
246 // No match remove the entry unless it's the last one
247 // In that case, there is no match
248 // we should return what we consumed so far;
249 if (_current.last()) {
250 auto res = get_remaining();
251 _current.erase(first);
9f95a23c 252 return res;
11fdf7f2
TL
253 }
254 first = _current.erase(first);
255 } else {
256 // we found a match
257 if (pos + len_compare == end) {
258 // this is a full match, there could be only one so this is the first
259 // consume the buffer and return
260 const sstring& value = get_value(_current.get_pos());
261 temporary_buffer<char> res(value.data(), value.size());
262 buf.trim_front(len_compare);
263 _current.clear();
9f95a23c 264 return res;
11fdf7f2
TL
265 }
266 // only partial match
267 pos += len_compare;
268 ++first;
269 }
270 }
271 // if we are here we run out of buffer
272 buf.trim_front(buf_len);
273 return temporary_buffer<char>();
274}
275
276temporary_buffer<char> buffer_replace::get_remaining() {
277 if (!is_consuming()) {
278 return temporary_buffer<char>();
279 }
280 size_t pos = _current.get_pos();
281 const sstring& key = get_key(pos);
282 auto size = key.size() - _current.get_remaining_length();
f67539c2 283 return temporary_buffer<char>(key.data(), size);
11fdf7f2
TL
284}
285
286temporary_buffer<char> buffer_replace::replace(temporary_buffer<char>& buf) {
287 if (buf.empty()) {
288 return std::move(buf);
289 }
290 if (is_consuming()) {
291 return match(buf);
292 }
293 auto start = find_braces(buf.begin(), buf.end());
294 if (start >= 0) {
295 // we found an opening brace that is followed by a second brace or buffer end.
296 // 1. All values are a possible match
297 // 2. We can output the beginning of the buffer
298 // 3. Need to continue matching the remaining buffer
299 size_t pos = 0;
300 for (auto&& i : _values) {
301 sstring& key = std::get<0>(i);
f67539c2 302 _current.add_potential_match(key.data() + 1, key.data() + key.size(), pos++);
11fdf7f2
TL
303 }
304 temporary_buffer<char> res = buf.share(0, start);
305 buf.trim_front(start + 1);
306 return res;
307 }
308
309 return std::move(buf);
310}
311
312}
313
314}