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