/
Neitrinok
/
seastar
Обзор
Документация
Войти
/
Neitrinok
/
seastar
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/http/transformers.cc
324 строки
10 KB
Kefu Chai
treewide: add C++ modules support
24 май 2023, 13:36
24 май 2023, 13:36
aa1c8b3
Код
Авторство
О чём код?
/* * This file is open source software, licensed to you under the terms * of the Apache License, Version 2.0 (the "License"). See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. You may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Copyright 2015 Cloudius Systems */ #ifdef SEASTAR_MODULE module; #endif #include <boost/algorithm/string/replace.hpp> #include <list> #include <memory> #ifdef SEASTAR_MODULE module seastar; #else #include <seastar/core/do_with.hh> #include <seastar/core/loop.hh> #include <seastar/http/transformers.hh> #endif namespace seastar { namespace httpd { using namespace std; struct potential_match_entry { const char* begin; const char* end; size_t pos; }; /*! * \brief holds the buffer replace object current state * The way the matching algorithm works, is that when there's a match * it will be the first entry */ class buffer_replace_state { std::list<potential_match_entry> _potential_match; public: using iterator = std::list<potential_match_entry>::iterator; void add_potential_match(const char* s, const char* e, size_t pos) { _potential_match.emplace_back(potential_match_entry{s, e, pos}); } iterator begin() { return _potential_match.begin(); } iterator end() { return _potential_match.end(); } bool empty() const { return _potential_match.empty(); } bool last() const { return _potential_match.size() == 1; } auto erase(const iterator& i) { return _potential_match.erase(i); } /*! * \brief gets the key/value position in the buffer_replace of the match */ size_t get_pos() const { return (*_potential_match.begin()).pos; } /*! * \brief gets the length of the remaining string */ size_t get_remaining_length() const { return _potential_match.begin()->end - _potential_match.begin()->begin; } void clear() { _potential_match.clear(); } }; /*! *\brief a helper class to replace strings in a buffer * The keys to replace are surrounded by braces */ class buffer_replace { std::vector<std::tuple<sstring, sstring>> _values; buffer_replace_state _current; const sstring& get_value(size_t pos) const; const sstring& get_key(size_t pos) const; public: /*! * \brief Add a key and value to be replaced */ buffer_replace& add(sstring key, sstring value) { _values.emplace_back(std::make_tuple("{{" + key + "}}", value)); return *this; } /*! * \brief if there are no more buffers to consume get * the remaining chars stored in the buffer_replace */ temporary_buffer<char> get_remaining(); /*! * \brief check if the given buffer still match any of the current potential matches * */ temporary_buffer<char> match(temporary_buffer<char>& buf); /*! * \brief replace the buffer content * * The returned result is after translation. The method consumes what it read * from the buf, so the caller should check that buf is not empty. * * For example: if buf is: "abcd{{key}}" * res = replace(buf); * * res will be "abcd" * and buf will be "{{key}}" */ temporary_buffer<char> replace(temporary_buffer<char>& buf); /*! * \brief check if we are currently in the middle of consuming */ bool is_consuming() const { return !_current.empty(); } }; class content_replace_data_sink_impl : public data_sink_impl { output_stream<char> _out; buffer_replace _br; public: content_replace_data_sink_impl(output_stream<char>&& out, std::vector<std::tuple<sstring,sstring>>&& key_value) : _out(std::move(out)) { for (auto& i : key_value) { _br.add(std::get<0>(i), std::get<1>(i)); } } virtual future<> put(net::packet data) override { return make_ready_future<>(); } using data_sink_impl::put; virtual future<> put(temporary_buffer<char> buf) override { if (buf.empty()) { return make_ready_future<>(); } return do_with(temporary_buffer<char>(std::move(buf)), [this] (temporary_buffer<char>& buf) { return repeat([&buf, this] { auto bf = _br.replace(buf); return _out.write(bf.get(), bf.size()).then([&buf] { return (buf.empty()) ? stop_iteration::yes : stop_iteration::no; }); }); }); } virtual future<> flush() override { return _out.flush(); } virtual future<> close() override { // if we are in the middle of a consuming a key // there will be no match, write the remaining. if (_br.is_consuming()) { return do_with(temporary_buffer<char>(_br.get_remaining()), [this](temporary_buffer<char>& buf) { return _out.write(buf.get(), buf.size()).then([this] { return _out.flush(); }); }); } return _out.flush(); } }; class content_replace_data_sink : public data_sink { public: content_replace_data_sink(output_stream<char>&& out, std::vector<std::tuple<sstring,sstring>> key_value) : data_sink(std::make_unique<content_replace_data_sink_impl>( std::move(out), std::move(key_value))) {} }; output_stream<char> content_replace::transform(std::unique_ptr<http::request> req, const sstring& extension, output_stream<char>&& s) { sstring host = req->get_header("Host"); if (host == "" || (this->extension != "" && extension != this->extension)) { return std::move(s); } sstring protocol = req->get_protocol_name(); output_stream_options opts; opts.trim_to_size = true; return output_stream<char>(content_replace_data_sink(std::move(s), {std::make_tuple("Protocol", protocol), std::make_tuple("Host", host)}), 32000, opts); } /*! * \brief find the open brace that surround a parameter * it is either two consecutive braces or a single brace, if it's the last char in the buffer */ ssize_t find_braces(const char* s, const char* end) { for (size_t i = 0; s != end; s++, i++) { if (*s == '{' && ((s + 1) == end || *(s + 1) == '{')) { return i; } } return -1; } const sstring& buffer_replace::get_value(size_t pos) const { return std::get<1>(_values[pos]); } const sstring& buffer_replace::get_key(size_t pos) const { return std::get<0>(_values[pos]); } temporary_buffer<char> buffer_replace::match(temporary_buffer<char>& buf) { if (_current.empty()) { return temporary_buffer<char>(); } auto buf_len = buf.size(); auto first = _current.begin(); while (first != _current.end()) { auto& pos = first->begin; auto end = first->end; size_t len_compare = std::min(buf_len, static_cast<size_t>(end - pos)); if (strncmp(pos, buf.begin(), len_compare)) { // No match remove the entry unless it's the last one // In that case, there is no match // we should return what we consumed so far; if (_current.last()) { auto res = get_remaining(); _current.erase(first); return res; } first = _current.erase(first); } else { // we found a match if (pos + len_compare == end) { // this is a full match, there could be only one so this is the first // consume the buffer and return const sstring& value = get_value(_current.get_pos()); temporary_buffer<char> res(value.data(), value.size()); buf.trim_front(len_compare); _current.clear(); return res; } // only partial match pos += len_compare; ++first; } } // if we are here we run out of buffer buf.trim_front(buf_len); return temporary_buffer<char>(); } temporary_buffer<char> buffer_replace::get_remaining() { if (!is_consuming()) { return temporary_buffer<char>(); } size_t pos = _current.get_pos(); const sstring& key = get_key(pos); auto size = key.size() - _current.get_remaining_length(); return temporary_buffer<char>(key.data(), size); } temporary_buffer<char> buffer_replace::replace(temporary_buffer<char>& buf) { if (buf.empty()) { return std::move(buf); } if (is_consuming()) { return match(buf); } auto start = find_braces(buf.begin(), buf.end()); if (start >= 0) { // we found an opening brace that is followed by a second brace or buffer end. // 1. All values are a possible match // 2. We can output the beginning of the buffer // 3. Need to continue matching the remaining buffer size_t pos = 0; for (auto&& i : _values) { sstring& key = std::get<0>(i); _current.add_potential_match(key.data() + 1, key.data() + key.size(), pos++); } temporary_buffer<char> res = buf.share(0, start); buf.trim_front(start + 1); return res; } return std::move(buf); } } }