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 "SPWebUnixRoot.h"
24 : #include "SPWebUnixHost.h"
25 : #include "SPWebUnixConnectionQueue.h"
26 : #include "SPWebUnixRequest.h"
27 : #include "SPWebRequest.h"
28 : #include "SPThread.h"
29 :
30 : namespace STAPPLER_VERSIONIZED stappler::web {
31 :
32 25 : SharedRc<UnixRoot> UnixRoot::create(Config &&cfg) {
33 25 : return SharedRc<UnixRoot>::create(SharedMode::Allocator, move(cfg));
34 : }
35 :
36 50 : UnixRoot::~UnixRoot() { }
37 :
38 25 : UnixRoot::UnixRoot(pool_t *p) : Root(p) { }
39 :
40 25 : bool UnixRoot::init(Config &&config) {
41 50 : return perform([&, this] {
42 25 : size_t workers = std::thread::hardware_concurrency();
43 25 : if (config.nworkers >= 2 && config.nworkers <= 256) {
44 25 : workers = size_t(config.nworkers);
45 : }
46 :
47 50 : for (auto &it : config.hosts) {
48 25 : auto p = pool::create(_rootPool);
49 25 : pool::push(p);
50 :
51 25 : auto host = new (p) UnixHostController(this, p, it);
52 :
53 25 : _hosts.emplace(host->getHostInfo().hostname, host);
54 :
55 25 : pool::pop();
56 : }
57 :
58 25 : initDatabases();
59 :
60 25 : _queue = new (_rootPool) ConnectionQueue(this, _rootPool, workers, move(config));
61 :
62 25 : std::unique_lock<std::mutex> lock(_mutex);
63 25 : if (_queue->run()) {
64 25 : _running = true;
65 :
66 50 : for (auto &it : _hosts) {
67 25 : perform([&] {
68 25 : it.second->init(Host(it.second));
69 50 : }, _rootPool, config::TAG_HOST, it.second);
70 : }
71 :
72 50 : for (auto &it : _hosts) {
73 25 : perform([&] {
74 25 : Host(it.second).handleChildInit(_rootPool);
75 50 : }, _rootPool, config::TAG_HOST, it.second);
76 : }
77 :
78 25 : return true;
79 : }
80 0 : return false;
81 75 : }, _rootPool);
82 : }
83 :
84 25 : void UnixRoot::cancel() {
85 25 : if (!_running || !_queue) {
86 0 : return;
87 : }
88 :
89 25 : _running = false;
90 25 : _queue->cancel();
91 25 : _queue->release();
92 25 : _queue = nullptr;
93 : }
94 :
95 100 : bool UnixRoot::performTask(const Host &host, AsyncTask *task, bool performFirst) {
96 100 : if (_queue) {
97 100 : task->setHost(host);
98 100 : _queue->pushTask(task);
99 100 : return true;
100 : }
101 0 : return false;
102 : }
103 :
104 0 : bool UnixRoot::scheduleTask(const Host &host, AsyncTask *task, TimeInterval ival) {
105 0 : if (_queue) {
106 0 : task->setHost(host);
107 0 : _queue->pushTask(task);
108 0 : return true;
109 : }
110 0 : return false;
111 : }
112 :
113 25 : void UnixRoot::foreachHost(const Callback<void(Host &)> &cb) {
114 50 : for (auto &it : _hosts) {
115 25 : Host serv(it.second);
116 25 : cb(serv);
117 : }
118 25 : }
119 :
120 3100 : Status UnixRoot::processRequest(RequestController *req) {
121 3100 : Status ret = DECLINED;
122 3100 : auto host = req->getInfo().url.host;
123 :
124 3100 : auto it = _hosts.find(host);
125 3100 : if (it != _hosts.end()) {
126 3100 : req->bind(it->second);
127 : }
128 :
129 3100 : if (!req->init()) {
130 0 : return ret;
131 : }
132 :
133 3100 : Request rctx(req);
134 :
135 3100 : ret = runPostReadRequest(rctx);
136 3100 : switch (ret) {
137 3025 : case OK:
138 : // continue processing
139 3025 : break;
140 50 : case DECLINED:
141 50 : return runDefaultProcessing(rctx);
142 : break;
143 25 : default:
144 25 : return ret;
145 : break;
146 : }
147 :
148 3025 : ret = runTranslateName(rctx);
149 3025 : switch (ret) {
150 675 : case OK:
151 : // continue processing
152 675 : break;
153 0 : case DECLINED:
154 0 : return runDefaultProcessing(rctx);
155 : break;
156 2350 : default:
157 2350 : return ret;
158 : break;
159 : }
160 :
161 675 : ret = runCheckAccess(rctx);
162 675 : switch (ret) {
163 675 : case OK:
164 : // continue processing
165 675 : break;
166 0 : case DECLINED:
167 0 : return HTTP_FORBIDDEN;
168 : break;
169 0 : default:
170 0 : return ret;
171 : break;
172 : }
173 :
174 675 : ret = runQuickHandler(rctx, 0);
175 675 : switch (ret) {
176 675 : case OK:
177 : case DECLINED:
178 : // continue processing
179 675 : break;
180 0 : default:
181 0 : return ret;
182 : break;
183 : }
184 :
185 675 : runInsertFilter(rctx);
186 :
187 675 : switch (req->getInfo().status) {
188 675 : case OK:
189 : case DECLINED:
190 : case SUSPENDED:
191 : // continue processing
192 675 : break;
193 0 : default:
194 0 : return ret;
195 : break;
196 : }
197 :
198 675 : ret = runHandler(rctx);
199 675 : switch (ret) {
200 650 : case OK:
201 650 : if (rctx.getInputFilter()) {
202 650 : return SUSPENDED;
203 : }
204 0 : return DONE;
205 : break;
206 25 : case DECLINED:
207 25 : if (rctx.getInputFilter()) {
208 0 : return SUSPENDED;
209 : } else {
210 25 : return runDefaultProcessing(rctx);
211 : }
212 : break;
213 0 : default:
214 0 : break;
215 : }
216 :
217 0 : return ret;
218 3100 : }
219 :
220 25 : bool UnixRoot::simulateWebsocket(UnixWebsocketSim *sim, StringView hostname, StringView url) {
221 25 : auto it = _hosts.find(hostname);
222 25 : if (it == _hosts.end()) {
223 0 : return false;
224 : }
225 :
226 25 : return it->second->simulateWebsocket(sim, url);
227 : }
228 :
229 75 : Status UnixRoot::runDefaultProcessing(Request &rctx) {
230 75 : auto &info = rctx.getInfo();
231 75 : auto filename = info.filename;
232 75 : if (filename.empty()) {
233 50 : rctx.setFilename(filepath::merge<Interface>(info.documentRoot, info.url.path), true);
234 : }
235 :
236 75 : if (info.filename.empty() || info.stat.isDir) {
237 25 : return HTTP_NOT_FOUND;
238 : } else {
239 50 : if (info.contentType.empty()) {
240 25 : if (runTypeChecker(rctx) == DECLINED) {
241 0 : return HTTP_NOT_FOUND;
242 : }
243 : }
244 :
245 50 : if (runCheckAccess(rctx) != OK) {
246 0 : return HTTP_FORBIDDEN;
247 : }
248 50 : return DONE;
249 : }
250 : }
251 :
252 : }
|