Line data Source code
1 : /**
2 : Copyright (c) 2024 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "SPWebUnixRequest.h"
24 : #include "SPWebInputFilter.h"
25 : #include "SPWebHostController.h"
26 :
27 : namespace STAPPLER_VERSIONIZED stappler::web {
28 :
29 3100 : UnixRequestController::UnixRequestController(pool_t *pool, RequestInfo &&info, ConnectionWorker::Client *client)
30 3100 : : RequestController(pool, move(info)) {
31 3100 : _client = client;
32 :
33 3100 : _info.useragentIp = client->addr;
34 3100 : _info.useragentPort = client->port;
35 3100 : }
36 :
37 25 : UnixRequestController::UnixRequestController(pool_t *pool, RequestInfo &&info, UnixWebsocketSim *sock)
38 25 : : RequestController(pool, move(info)) {
39 25 : _websocket = sock;
40 25 : _info.useragentIp = StringView("127.0.0.1");
41 25 : _info.useragentPort = 80;
42 25 : }
43 :
44 0 : void UnixRequestController::startResponseTransmission() {
45 :
46 0 : }
47 :
48 0 : size_t UnixRequestController::getBytesSent() const {
49 0 : return _client->bytesSent;
50 : }
51 :
52 0 : void UnixRequestController::putc(int c) {
53 0 : uint8_t ch = c;
54 0 : _client->write(_client->response, &ch, 1);
55 0 : }
56 :
57 1221125 : size_t UnixRequestController::write(const uint8_t *buf, size_t size) {
58 1221125 : _client->write(_client->response, buf, size);
59 1221125 : return size;
60 : }
61 :
62 0 : void UnixRequestController::flush() { }
63 :
64 1500 : bool UnixRequestController::isSecureConnection() const {
65 1500 : return false;
66 : }
67 :
68 0 : void UnixRequestController::setDocumentRoot(StringView val) {
69 0 : _info.documentRoot = val.pdup(_pool);
70 0 : }
71 :
72 2300 : void UnixRequestController::setContentType(StringView val) {
73 2300 : _info.contentType = val.pdup(_pool);
74 2300 : }
75 :
76 0 : void UnixRequestController::setHandler(StringView val) {
77 0 : _info.handler = val.pdup(_pool);
78 0 : }
79 :
80 0 : void UnixRequestController::setContentEncoding(StringView val) {
81 0 : _info.contentEncoding = val.pdup(_pool);
82 0 : }
83 :
84 3125 : void UnixRequestController::setStatus(Status status, StringView val) {
85 3125 : _info.status = status;
86 3125 : if (!val.empty()) {
87 0 : _info.statusLine = val.pdup(_pool);
88 : } else {
89 3125 : _info.statusLine = getStatusLine(Status(status));
90 : }
91 3125 : }
92 :
93 150 : StringView UnixRequestController::getCookie(StringView name, bool removeFromHeadersTable) {
94 150 : auto it = _inputCookies.find(name);
95 150 : if (it != _inputCookies.end()) {
96 150 : auto ret = it->second;
97 150 : if (removeFromHeadersTable) {
98 75 : _inputCookies.erase(ret);
99 : }
100 150 : return ret;
101 : }
102 0 : return StringView();
103 : }
104 :
105 100 : void UnixRequestController::setFilename(StringView val, bool updateStat, Time mtime) {
106 100 : if (!val.starts_with(_info.documentRoot)) {
107 0 : auto path = filepath::merge<Interface>(_info.documentRoot, val);
108 0 : if (filesystem::exists(path)) {
109 0 : val = StringView(path).pdup(_pool);
110 : } else {
111 0 : return;
112 : }
113 0 : } else {
114 100 : if (filesystem::exists(val)) {
115 100 : val = val.pdup(_pool);
116 : } else {
117 0 : return;
118 : }
119 : }
120 :
121 100 : _info.filename = val;
122 100 : if (updateStat) {
123 100 : filesystem::stat(_info.filename, _info.stat);
124 100 : if (mtime != nullptr) {
125 25 : _info.stat.mtime = mtime;
126 : }
127 : }
128 : }
129 :
130 19725 : StringView UnixRequestController::getRequestHeader(StringView key) const {
131 19725 : auto tmp = key.str<memory::StandartInterface>();
132 19725 : string::apply_tolower_c(tmp);
133 :
134 19725 : auto it = _requestHeaders.find(StringView(tmp));
135 19725 : if (it != _requestHeaders.end()) {
136 6175 : return it->second;
137 : }
138 13550 : return StringView();
139 19725 : }
140 :
141 100 : void UnixRequestController::foreachRequestHeaders(const Callback<void(StringView, StringView)> &cb) const {
142 650 : for (auto &it : _requestHeaders) {
143 550 : cb(it.first, it.second);
144 : }
145 100 : }
146 :
147 17125 : void UnixRequestController::setRequestHeader(StringView key, StringView val) {
148 17125 : auto tmp = key.str<memory::StandartInterface>();
149 17125 : string::apply_tolower_c(tmp);
150 :
151 17125 : auto it = _requestHeaders.find(StringView(tmp));
152 17125 : if (it != _requestHeaders.end()) {
153 0 : it->second = val.pdup(_pool);
154 : } else {
155 17125 : it = _requestHeaders.emplace(StringView(tmp).pdup(_pool), val.pdup(_pool)).first;
156 : }
157 :
158 17125 : if (it->first == "host") {
159 3125 : StringView r(it->second);
160 3125 : auto h = r.readUntil<StringView::Chars<':'>>();
161 3125 : _info.url.host = h;
162 3125 : if (r.is(':')) {
163 3100 : ++ r;
164 3100 : _info.url.port = r;
165 : }
166 14000 : } else if (it->first == "content-length") {
167 650 : _info.contentLength = StringView(it->second).readInteger(10).get(0);
168 13350 : } else if (it->first == "cookie") {
169 2775 : auto d = data::readUrlencoded<Interface>(it->second, maxOf<size_t>());
170 5550 : for (auto &iit : d.asDict()) {
171 2775 : _inputCookies.emplace(StringView(iit.first).pdup(_pool), StringView(iit.second.asString()).pdup(_pool));
172 : }
173 2775 : }
174 17125 : }
175 :
176 825 : StringView UnixRequestController::getResponseHeader(StringView key) const {
177 825 : auto tmp = key.str<memory::StandartInterface>();
178 825 : string::apply_tolower_c(tmp);
179 :
180 825 : auto it = _responseHeaders.find(StringView(tmp));
181 825 : if (it != _responseHeaders.end()) {
182 0 : return it->second;
183 : }
184 825 : return StringView();
185 825 : }
186 :
187 0 : void UnixRequestController::foreachResponseHeaders(const Callback<void(StringView, StringView)> &cb) const {
188 0 : for (auto &it : _responseHeaders) {
189 0 : cb(it.first, it.second);
190 : }
191 0 : }
192 :
193 11350 : void UnixRequestController::setResponseHeader(StringView key, StringView val) {
194 11350 : auto tmp = key.str<memory::StandartInterface>();
195 11350 : string::apply_tolower_c(tmp);
196 :
197 11350 : auto it = _responseHeaders.find(StringView(tmp));
198 11350 : if (it != _responseHeaders.end()) {
199 0 : it->second = val.pdup(_pool);
200 : } else {
201 11350 : _responseHeaders.emplace(StringView(tmp).pdup(_pool), val.pdup(_pool));
202 : }
203 11350 : }
204 :
205 25 : void UnixRequestController::clearResponseHeaders() {
206 25 : _responseHeaders.clear();
207 25 : }
208 :
209 0 : StringView UnixRequestController::getErrorHeader(StringView key) const {
210 0 : auto tmp = key.str<memory::StandartInterface>();
211 0 : string::apply_tolower_c(tmp);
212 :
213 0 : auto it = _errorHeaders.find(StringView(tmp));
214 0 : if (it != _errorHeaders.end()) {
215 0 : return it->second;
216 : }
217 0 : return StringView();
218 0 : }
219 :
220 0 : void UnixRequestController::foreachErrorHeaders(const Callback<void(StringView, StringView)> &cb) const {
221 0 : for (auto &it : _errorHeaders) {
222 0 : cb(it.first, it.second);
223 : }
224 0 : }
225 :
226 6450 : void UnixRequestController::setErrorHeader(StringView key, StringView val) {
227 6450 : auto tmp = key.str<memory::StandartInterface>();
228 6450 : string::apply_tolower_c(tmp);
229 :
230 6450 : auto it = _errorHeaders.find(StringView(tmp));
231 6450 : if (it != _errorHeaders.end()) {
232 0 : it->second = val.pdup(_pool);
233 : } else {
234 6450 : _errorHeaders.emplace(StringView(tmp).pdup(_pool), val.pdup(_pool));
235 : }
236 6450 : }
237 :
238 0 : void UnixRequestController::clearErrorHeaders() {
239 0 : _errorHeaders.clear();
240 0 : }
241 :
242 650 : Status UnixRequestController::processInput(ConnectionWorker::BufferChain &chain) {
243 650 : if (!_filter) {
244 0 : return DECLINED;
245 : }
246 :
247 650 : auto ret = chain.read([&, this] (const ConnectionWorker::Buffer *, const uint8_t *data, size_t len) {
248 850 : auto size = std::min(len, size_t(_info.contentLength));
249 850 : BytesView r(data, size);
250 :
251 850 : _info.contentLength -= size;
252 :
253 850 : bool success = true;
254 850 : perform([&] {
255 850 : if (!_filter->step(r)) {
256 0 : success = false;
257 : }
258 850 : }, _filter->getPool(), config::TAG_REQUEST, this);
259 :
260 850 : if (!success) {
261 0 : return int(DECLINED);
262 : }
263 :
264 850 : if (_info.contentLength > 0) {
265 200 : return int(size);
266 : }
267 :
268 650 : perform([&] {
269 650 : _filter->finalize();
270 650 : }, _filter->getPool(), config::TAG_REQUEST, this);
271 650 : return int(DONE);
272 : }, true);
273 :
274 650 : if (ret == DECLINED) {
275 0 : _filter = nullptr;
276 0 : return DECLINED;
277 : }
278 :
279 650 : return _info.contentLength > 0 ? SUSPENDED : ret;
280 : }
281 :
282 3100 : void UnixRequestController::submitResponse(Status status) {
283 3100 : if (status > OK) {
284 475 : setStatus(status, StringView());
285 : }
286 :
287 3100 : if (_info.status < HTTP_OK) {
288 2100 : switch (status) {
289 2100 : case DONE:
290 2100 : setStatus(HTTP_OK, StringView());
291 2100 : break;
292 0 : case OK:
293 0 : setStatus(HTTP_OK, StringView());
294 0 : break;
295 0 : case SUSPENDED:
296 : case DECLINED:
297 0 : setStatus(HTTP_BAD_REQUEST, StringView());
298 0 : break;
299 0 : default:
300 0 : break;
301 : }
302 : }
303 :
304 3100 : if (!_info.filename.empty() && !_info.stat.isDir) {
305 50 : _client->writeFile(_client->response, _info.filename, 0, _info.stat.size, ConnectionWorker::Buffer::Eos);
306 : }
307 :
308 3100 : if (_client->response.empty() && !_info.headerRequest) {
309 : // create default response
310 450 : auto result = getDefaultResult();
311 450 : bool allowCbor = isAcceptable("application/cbor") > 0.0f;
312 :
313 450 : auto data = data::write<Interface>(result, allowCbor ? data::EncodeFormat::Cbor : data::EncodeFormat::Json);
314 :
315 450 : _info.contentType = (allowCbor ? StringView("application/cbor") : StringView("application/json; charset=utf-8"));
316 :
317 450 : _client->response.write(_client->pool, data.data(), data.size(), ConnectionWorker::Buffer::Eos);
318 450 : } else {
319 2650 : _client->response.write(_client->pool, nullptr, 0, ConnectionWorker::Buffer::Eos);
320 : }
321 :
322 3100 : Time date = Time::now();
323 3100 : auto contentLength = _client->response.size();
324 :
325 3100 : StringView crlf("\r\n");
326 3100 : StringView statusLine = getStatusLine(status);
327 3100 : if (statusLine.empty()) {
328 0 : statusLine = getStatusLine(HTTP_INTERNAL_SERVER_ERROR);
329 : }
330 :
331 3100 : sp_time_exp_t xt(date);
332 3100 : char dateBuf[30] = { 0 };
333 3100 : xt.encodeRfc822(dateBuf);
334 :
335 82175 : auto outFn = [&, this] (StringView str) {
336 82175 : _client->write(_client->output, str);
337 85275 : };
338 :
339 3100 : auto out = Callback<void(StringView)>(outFn);
340 :
341 3100 : auto writeCookies = [&] (CookieFlags flags) {
342 3175 : for (auto &it : _cookies) {
343 75 : if ((it.second.flags & flags) ==flags) {
344 75 : out << "set-cookie: " << it.first << "=" << it.second.data;
345 75 : if (it.second.maxAge) {
346 50 : out << ";Max-Age=" << it.second.maxAge.toSeconds();
347 : }
348 75 : if ((it.second.flags & CookieFlags::HttpOnly) == CookieFlags::HttpOnly) {
349 75 : out << ";HttpOnly";
350 : }
351 75 : auto sameSite = (it.second.flags & CookieFlags::SameSiteStrict);
352 75 : switch (sameSite) {
353 0 : case CookieFlags::SameSiteStrict: out << ";SameSite=Strict"; break;
354 0 : case CookieFlags::SameSiteLux: out << ";SameSite=Lux"; break;
355 75 : default: out << ";SameSite=None"; break;
356 : }
357 75 : out << ";Path=/;Version=1" << crlf;
358 : }
359 : }
360 3100 : };
361 :
362 3100 : out << StringView("HTTP/1.1 ") << statusLine << crlf;
363 3100 : if (_info.status >= HTTP_BAD_REQUEST) {
364 100 : setErrorHeader("Date", dateBuf);
365 100 : setErrorHeader("Connection", "close");
366 100 : setErrorHeader("Server", _host->getRoot()->getServerNameLine());
367 100 : if (contentLength) {
368 100 : setErrorHeader("Content-Type", _info.contentType);
369 100 : setErrorHeader("Content-Length", toString(contentLength));
370 : }
371 100 : if (!_info.contentEncoding.empty()) {
372 0 : setErrorHeader("Content-Encoding", _info.contentEncoding);
373 : }
374 :
375 600 : for (auto &it : _errorHeaders) {
376 500 : out << it.first << StringView(": ") << it.second << crlf;
377 : }
378 :
379 100 : writeCookies(CookieFlags::SetOnError);
380 : } else {
381 3000 : setResponseHeader("Date", dateBuf);
382 3000 : setResponseHeader("Connection", "close");
383 3000 : setResponseHeader("Server", _host->getRoot()->getServerNameLine());
384 3000 : if (contentLength) {
385 2975 : setErrorHeader("Content-Type", _info.contentType);
386 2975 : setErrorHeader("Content-Length", toString(contentLength));
387 : }
388 3000 : if (!_info.contentEncoding.empty()) {
389 0 : setErrorHeader("Content-Encoding", _info.contentEncoding);
390 : }
391 :
392 8950 : for (auto &it : _errorHeaders) {
393 5950 : auto iit = _responseHeaders.find(it.first);
394 5950 : if (iit == _responseHeaders.end()) {
395 5625 : _responseHeaders.emplace(it.first, it.second);
396 : }
397 : }
398 :
399 19775 : for (auto &it : _responseHeaders) {
400 16775 : out << it.first << StringView(": ") << it.second << crlf;
401 : }
402 :
403 3000 : writeCookies(CookieFlags::SetOnSuccess);
404 : }
405 :
406 3100 : if (!_client->response.empty()) {
407 3075 : out << crlf;
408 3075 : _client->write(_client->output, _client->response);
409 : } else {
410 25 : _client->write(_client->output, (const uint8_t *)crlf.data(), crlf.size(), ConnectionWorker::Buffer::Eos);
411 : }
412 3100 : }
413 :
414 25 : WebsocketConnection *UnixRequestController::convertToWebsocket(WebsocketHandler *handler, allocator_t *a, pool_t *p) {
415 25 : WebsocketConnection *ret = nullptr;
416 25 : if (_websocket) {
417 25 : perform([&] {
418 25 : auto r = new (p) UnixWebsocketConnectionSim(a, p, _host, _websocket);
419 25 : _websocket->attachSocket(r);
420 25 : ret = r;
421 25 : }, p);
422 : }
423 25 : return ret;
424 : }
425 :
426 : }
|