]>
Commit | Line | Data |
---|---|---|
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 | #pragma once | |
23 | ||
24 | #include <seastar/http/request_parser.hh> | |
25 | #include <seastar/http/request.hh> | |
f67539c2 | 26 | #include <seastar/core/seastar.hh> |
11fdf7f2 TL |
27 | #include <seastar/core/sstring.hh> |
28 | #include <seastar/core/app-template.hh> | |
29 | #include <seastar/core/circular_buffer.hh> | |
30 | #include <seastar/core/distributed.hh> | |
31 | #include <seastar/core/queue.hh> | |
f67539c2 | 32 | #include <seastar/core/gate.hh> |
11fdf7f2 TL |
33 | #include <seastar/core/metrics_registration.hh> |
34 | #include <seastar/util/std-compat.hh> | |
35 | #include <iostream> | |
36 | #include <algorithm> | |
37 | #include <unordered_map> | |
38 | #include <queue> | |
39 | #include <bitset> | |
40 | #include <limits> | |
41 | #include <cctype> | |
42 | #include <vector> | |
43 | #include <boost/intrusive/list.hpp> | |
44 | #include <seastar/http/routes.hh> | |
9f95a23c TL |
45 | #include <seastar/net/tls.hh> |
46 | #include <seastar/core/shared_ptr.hh> | |
11fdf7f2 TL |
47 | |
48 | namespace seastar { | |
49 | ||
50 | namespace httpd { | |
51 | ||
52 | class http_server; | |
53 | class http_stats; | |
9f95a23c | 54 | struct reply; |
11fdf7f2 TL |
55 | |
56 | using namespace std::chrono_literals; | |
57 | ||
58 | class http_stats { | |
59 | metrics::metric_groups _metric_groups; | |
60 | public: | |
61 | http_stats(http_server& server, const sstring& name); | |
62 | }; | |
63 | ||
64 | class connection : public boost::intrusive::list_base_hook<> { | |
65 | http_server& _server; | |
66 | connected_socket _fd; | |
67 | input_stream<char> _read_buf; | |
68 | output_stream<char> _write_buf; | |
69 | static constexpr size_t limit = 4096; | |
70 | using tmp_buf = temporary_buffer<char>; | |
71 | http_request_parser _parser; | |
72 | std::unique_ptr<request> _req; | |
73 | std::unique_ptr<reply> _resp; | |
74 | // null element marks eof | |
75 | queue<std::unique_ptr<reply>> _replies { 10 }; | |
76 | bool _done = false; | |
77 | public: | |
78 | connection(http_server& server, connected_socket&& fd, | |
79 | socket_address addr) | |
80 | : _server(server), _fd(std::move(fd)), _read_buf(_fd.input()), _write_buf( | |
81 | _fd.output()) { | |
82 | on_new_connection(); | |
83 | } | |
84 | ~connection(); | |
85 | void on_new_connection(); | |
86 | ||
f67539c2 TL |
87 | future<> process(); |
88 | void shutdown(); | |
11fdf7f2 TL |
89 | future<> read(); |
90 | future<> read_one(); | |
91 | future<> respond(); | |
92 | future<> do_response_loop(); | |
93 | ||
94 | void set_headers(reply& resp); | |
95 | ||
96 | future<> start_response(); | |
f67539c2 | 97 | future<> write_reply_headers(std::unordered_map<sstring, sstring>::iterator hi); |
11fdf7f2 | 98 | |
f67539c2 | 99 | static short hex_to_byte(char c); |
11fdf7f2 TL |
100 | |
101 | /** | |
102 | * Convert a hex encoded 2 bytes substring to char | |
103 | */ | |
f67539c2 | 104 | static char hexstr_to_char(const std::string_view& in, size_t from); |
11fdf7f2 TL |
105 | |
106 | /** | |
107 | * URL_decode a substring and place it in the given out sstring | |
108 | */ | |
f67539c2 | 109 | static bool url_decode(const std::string_view& in, sstring& out); |
11fdf7f2 TL |
110 | |
111 | /** | |
112 | * Add a single query parameter to the parameter list | |
113 | */ | |
f67539c2 | 114 | static void add_param(request& req, const std::string_view& param); |
11fdf7f2 TL |
115 | |
116 | /** | |
117 | * Set the query parameters in the request objects. | |
118 | * query param appear after the question mark and are separated | |
119 | * by the ampersand sign | |
120 | */ | |
f67539c2 | 121 | static sstring set_query_param(request& req); |
11fdf7f2 TL |
122 | |
123 | future<bool> generate_reply(std::unique_ptr<request> req); | |
9f95a23c | 124 | void generate_error_reply_and_close(std::unique_ptr<request> req, reply::status_type status, const sstring& msg); |
11fdf7f2 TL |
125 | |
126 | future<> write_body(); | |
127 | ||
f67539c2 | 128 | output_stream<char>& out(); |
11fdf7f2 TL |
129 | }; |
130 | ||
131 | class http_server_tester; | |
132 | ||
133 | class http_server { | |
f67539c2 | 134 | std::vector<server_socket> _listeners; |
11fdf7f2 TL |
135 | http_stats _stats; |
136 | uint64_t _total_connections = 0; | |
137 | uint64_t _current_connections = 0; | |
138 | uint64_t _requests_served = 0; | |
11fdf7f2 TL |
139 | uint64_t _read_errors = 0; |
140 | uint64_t _respond_errors = 0; | |
9f95a23c | 141 | shared_ptr<seastar::tls::server_credentials> _credentials; |
11fdf7f2 TL |
142 | sstring _date = http_date(); |
143 | timer<> _date_format_timer { [this] {_date = http_date();} }; | |
9f95a23c | 144 | size_t _content_length_limit = std::numeric_limits<size_t>::max(); |
20effc67 | 145 | bool _content_streaming = false; |
f67539c2 | 146 | gate _task_gate; |
11fdf7f2 TL |
147 | public: |
148 | routes _routes; | |
149 | using connection = seastar::httpd::connection; | |
150 | explicit http_server(const sstring& name) : _stats(*this, name) { | |
151 | _date_format_timer.arm_periodic(1s); | |
152 | } | |
9f95a23c TL |
153 | /*! |
154 | * \brief set tls credentials for the server | |
155 | * Setting the tls credentials will set the http-server to work in https mode. | |
156 | * | |
157 | * To use the https, create server credentials and pass it to the server before it starts. | |
158 | * | |
159 | * Use case example using seastar threads for clarity: | |
160 | ||
161 | distributed<http_server> server; // typical server | |
162 | ||
163 | seastar::shared_ptr<seastar::tls::credentials_builder> creds = seastar::make_shared<seastar::tls::credentials_builder>(); | |
164 | sstring ms_cert = "MyCertificate.crt"; | |
165 | sstring ms_key = "MyKey.key"; | |
166 | ||
167 | creds->set_dh_level(seastar::tls::dh_params::level::MEDIUM); | |
168 | ||
169 | creds->set_x509_key_file(ms_cert, ms_key, seastar::tls::x509_crt_format::PEM).get(); | |
170 | creds->set_system_trust().get(); | |
171 | ||
172 | ||
173 | server.invoke_on_all([creds](http_server& server) { | |
174 | server.set_tls_credentials(creds->build_server_credentials()); | |
175 | return make_ready_future<>(); | |
176 | }).get(); | |
177 | * | |
178 | */ | |
f67539c2 | 179 | void set_tls_credentials(shared_ptr<seastar::tls::server_credentials> credentials); |
9f95a23c | 180 | |
f67539c2 | 181 | size_t get_content_length_limit() const; |
9f95a23c | 182 | |
f67539c2 | 183 | void set_content_length_limit(size_t limit); |
9f95a23c | 184 | |
20effc67 TL |
185 | bool get_content_streaming() const; |
186 | ||
187 | void set_content_streaming(bool b); | |
188 | ||
f67539c2 TL |
189 | future<> listen(socket_address addr, listen_options lo); |
190 | future<> listen(socket_address addr); | |
191 | future<> stop(); | |
11fdf7f2 | 192 | |
f67539c2 | 193 | future<> do_accepts(int which); |
11fdf7f2 | 194 | |
f67539c2 TL |
195 | uint64_t total_connections() const; |
196 | uint64_t current_connections() const; | |
197 | uint64_t requests_served() const; | |
198 | uint64_t read_errors() const; | |
199 | uint64_t reply_errors() const; | |
200 | // Write the current date in the specific "preferred format" defined in | |
201 | // RFC 7231, Section 7.1.1.1. | |
202 | static sstring http_date(); | |
11fdf7f2 | 203 | private: |
f67539c2 | 204 | future<> do_accept_one(int which); |
11fdf7f2 TL |
205 | boost::intrusive::list<connection> _connections; |
206 | friend class seastar::httpd::connection; | |
207 | friend class http_server_tester; | |
208 | }; | |
209 | ||
210 | class http_server_tester { | |
211 | public: | |
f67539c2 | 212 | static std::vector<server_socket>& listeners(http_server& server) { |
11fdf7f2 TL |
213 | return server._listeners; |
214 | } | |
215 | }; | |
216 | ||
217 | /* | |
218 | * A helper class to start, set and listen an http server | |
219 | * typical use would be: | |
220 | * | |
221 | * auto server = new http_server_control(); | |
222 | * server->start().then([server] { | |
223 | * server->set_routes(set_routes); | |
224 | * }).then([server, port] { | |
225 | * server->listen(port); | |
226 | * }).then([port] { | |
227 | * std::cout << "Seastar HTTP server listening on port " << port << " ...\n"; | |
228 | * }); | |
229 | */ | |
230 | class http_server_control { | |
231 | std::unique_ptr<distributed<http_server>> _server_dist; | |
232 | private: | |
233 | static sstring generate_server_name(); | |
234 | public: | |
235 | http_server_control() : _server_dist(new distributed<http_server>) { | |
236 | } | |
237 | ||
f67539c2 TL |
238 | future<> start(const sstring& name = generate_server_name()); |
239 | future<> stop(); | |
240 | future<> set_routes(std::function<void(routes& r)> fun); | |
241 | future<> listen(socket_address addr); | |
242 | future<> listen(socket_address addr, listen_options lo); | |
243 | distributed<http_server>& server(); | |
11fdf7f2 TL |
244 | }; |
245 | ||
246 | } | |
247 | ||
248 | } |