]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/src/http/routes.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / seastar / src / http / routes.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
22#include <seastar/http/routes.hh>
23#include <seastar/http/reply.hh>
24#include <seastar/http/exception.hh>
25#include <seastar/http/json_path.hh>
26
27namespace seastar {
28
29namespace httpd {
30
31using namespace std;
32
33void verify_param(const request& req, const sstring& param) {
34 if (req.get_query_param(param) == "") {
35 throw missing_param_exception(param);
36 }
37}
38routes::routes() : _general_handler([this](std::exception_ptr eptr) mutable {
39 return exception_reply(eptr);
40}) {}
41
42routes::~routes() {
43 for (int i = 0; i < NUM_OPERATION; i++) {
44 for (auto kv : _map[i]) {
45 delete kv.second;
46 }
47 }
48 for (int i = 0; i < NUM_OPERATION; i++) {
49 for (auto r : _rules[i]) {
f67539c2 50 delete r.second;
11fdf7f2
TL
51 }
52 }
53
54}
55
56std::unique_ptr<reply> routes::exception_reply(std::exception_ptr eptr) {
57 auto rep = std::make_unique<reply>();
58 try {
59 // go over the register exception handler
60 // if one of them handle the exception, return.
61 for (auto e: _exceptions) {
62 try {
63 return e.second(eptr);
64 } catch (...) {
65 // this is needed if there are more then one register exception handler
66 // so if the exception handler throw a new exception, they would
67 // get the new exception and not the original one.
68 eptr = std::current_exception();
69 }
70 }
71 std::rethrow_exception(eptr);
72 } catch (const base_exception& e) {
73 rep->set_status(e.status(), json_exception(e).to_json());
11fdf7f2
TL
74 } catch (...) {
75 rep->set_status(reply::status_type::internal_server_error,
9f95a23c 76 json_exception(std::current_exception()).to_json());
11fdf7f2 77 }
9f95a23c 78
11fdf7f2
TL
79 rep->done("json");
80 return rep;
81}
82
83future<std::unique_ptr<reply> > routes::handle(const sstring& path, std::unique_ptr<request> req, std::unique_ptr<reply> rep) {
84 handler_base* handler = get_handler(str2type(req->_method),
85 normalize_url(path), req->param);
86 if (handler != nullptr) {
87 try {
88 for (auto& i : handler->_mandatory_param) {
89 verify_param(*req.get(), i);
90 }
91 auto r = handler->handle(path, std::move(req), std::move(rep));
92 return r.handle_exception(_general_handler);
93 } catch (const redirect_exception& _e) {
94 rep.reset(new reply());
95 rep->add_header("Location", _e.url).set_status(_e.status()).done(
96 "json");
97 } catch (...) {
98 rep = exception_reply(std::current_exception());
99 }
100 } else {
101 rep.reset(new reply());
102 json_exception ex(not_found_exception("Not found"));
103 rep->set_status(reply::status_type::not_found, ex.to_json()).done(
104 "json");
105 }
106 return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
107}
108
109sstring routes::normalize_url(const sstring& url) {
110 if (url.length() < 2 || url.at(url.length() - 1) != '/') {
111 return url;
112 }
113 return url.substr(0, url.length() - 1);
114}
115
116handler_base* routes::get_handler(operation_type type, const sstring& url,
117 parameters& params) {
118 handler_base* handler = get_exact_match(type, url);
119 if (handler != nullptr) {
120 return handler;
121 }
122
f67539c2
TL
123 for (auto&& rule : _rules[type]) {
124 handler = rule.second->get(url, params);
11fdf7f2
TL
125 if (handler != nullptr) {
126 return handler;
127 }
128 params.clear();
129 }
130 return nullptr;
131}
132
133routes& routes::add(operation_type type, const url& url,
134 handler_base* handler) {
135 match_rule* rule = new match_rule(handler);
136 rule->add_str(url._path);
137 if (url._param != "") {
138 rule->add_param(url._param, true);
139 }
140 return add(rule, type);
141}
142
f67539c2
TL
143template <typename Map, typename Key>
144static auto delete_rule_from(operation_type type, Key& key, Map& map) {
145 auto& bucket = map[type];
146 auto ret = bucket.find(key);
147 using ret_type = decltype(ret->second);
148 if (ret != bucket.end()) {
149 ret_type v = ret->second;
150 bucket.erase(ret);
151 return v;
152 }
153 return static_cast<ret_type>(nullptr);
154}
155
156handler_base* routes::drop(operation_type type, const sstring& url) {
157 return delete_rule_from(type, url, _map);
158}
159
160routes& routes::put(operation_type type, const sstring& url, handler_base* handler) {
161 auto it = _map[type].emplace(url, handler);
162 if (it.second == false) {
163 throw std::runtime_error(format("Handler for {} already exists.", url));
164 }
165 return *this;
166}
167
168match_rule* routes::del_cookie(rule_cookie cookie, operation_type type) {
169 return delete_rule_from(type, cookie, _rules);
170}
171
11fdf7f2
TL
172void routes::add_alias(const path_description& old_path, const path_description& new_path) {
173 httpd::parameters p;
174 stringstream path;
175 path << old_path.path;
176 for (const auto& p : old_path.params) {
177 // the path_description path does not contains the path parameters
178 // so just add a fake parameter to the path for each of the parameters,
179 // and add the string for each fixed string part.
180 if (p.type == path_description::url_component_type::FIXED_STRING) {
181 path << p.name;
182 } else {
183 path << "/k";
184 }
185
186 }
187 auto a = get_handler(old_path.operations.method, path.str(), p);
188 if (!a) {
189 throw std::runtime_error("routes::add_alias path_description not found: " + old_path.path);
190 }
191 // if a handler is found then it must be a function_handler
192 new_path.set(*this, new function_handler(*static_cast<function_handler*>(a)));
193}
194
f67539c2
TL
195rule_registration::rule_registration(routes& rs, match_rule& rule, operation_type op)
196 : _routes(rs) , _op(op)
197 , _cookie(_routes.add_cookie(&rule, _op)) {}
198
199rule_registration::~rule_registration() {
200 _routes.del_cookie(_cookie, _op);
201}
202
203handler_registration::handler_registration(routes& rs, handler_base& h, const sstring& url, operation_type op)
204 : _routes(rs), _url(url), _op(op) {
205 _routes.put(_op, _url, &h);
206}
207
208handler_registration::~handler_registration() {
209 _routes.drop(_op, _url);
210}
211
11fdf7f2
TL
212}
213
214}