WebFramework v3.0.12
Web framework for C++.
Loading...
Searching...
No Matches
ExecutorsManager.cpp
1#include "ExecutorsManager.h"
2
3#include "Log.h"
4
5#include "Exceptions/FileDoesNotExistException.h"
6#include "Exceptions/BadRequestException.h"
7#include "Exceptions/DatabaseException.h"
8
9using namespace std;
10
11namespace framework
12{
13 bool ExecutorsManager::isFileRequest(string_view parameters)
14 {
15 size_t index = parameters.find('.');
16
17 if (index == string_view::npos)
18 {
19 return false;
20 }
21
22 string_view fileExtension(parameters.begin() + index, parameters.end());
23
24 return fileExtension.size() > 1 && ranges::all_of(fileExtension, [](char c) { return c != '/'; });
25 }
26
27 bool ExecutorsManager::isHeavyOperation(BaseExecutor* executor)
28 {
29 if (!executor)
30 {
31 throw exceptions::BadRequestException(); // 400
32 }
33
34 BaseExecutor::executorType executorType = executor->getType();
35
36 return executorType == BaseExecutor::executorType::heavyOperationStateless ||
37 executorType == BaseExecutor::executorType::heavyOperationStateful;
38 }
39
40 void ExecutorsManager::parseRouteParameters(const string& parameters, HTTPRequest& request, vector<utility::RouteParameters>::iterator it)
41 {
42 size_t i = 0;
43 size_t startParameter = it->baseRoute.size() + 1;
44 size_t endParameter;
45
46 do
47 {
48 endParameter = parameters.find('/', startParameter);
49
50 switch (static_cast<utility::RouteParameters::routeParametersType>(it->parameters[it->indices[i]].index()))
51 {
52 case utility::RouteParameters::routeParametersType::stringTypeIndex:
53 request.routeParameters[it->indices[i++]] = parameters.substr(startParameter, endParameter - startParameter);
54
55 break;
56
57 case utility::RouteParameters::routeParametersType::integerTypeIndex:
58 try
59 {
60 request.routeParameters[it->indices[i++]] = stoll(parameters.substr(startParameter, endParameter - startParameter));
61 }
62 catch (const invalid_argument&)
63 {
64 throw exceptions::BadRequestException("Can't convert to int64_t"); // 400
65 }
66 catch (const out_of_range&)
67 {
68 throw exceptions::BadRequestException("Out of range of int64_t"); // 400
69 }
70
71 break;
72
73 case utility::RouteParameters::routeParametersType::doubleTypeIndex:
74 try
75 {
76 request.routeParameters[it->indices[i++]] = stod(parameters.substr(startParameter, endParameter - startParameter));
77 }
78 catch (const invalid_argument&)
79 {
80 throw exceptions::BadRequestException("Can't convert to double"); // 400
81 }
82 catch (const out_of_range&)
83 {
84 throw exceptions::BadRequestException("Out of range of double"); // 400
85 }
86
87 break;
88
89 default:
90 throw runtime_error("Wrong routeParametersType");
91 }
92
93 startParameter = endParameter + 1;
94 } while (endParameter != string::npos);
95 }
96
97 BaseExecutor* ExecutorsManager::getExecutor(string& parameters, HTTPRequest& request, unordered_map<string, unique_ptr<BaseExecutor>>& statefulExecutors)
98 {
99 auto executor = statefulExecutors.find(parameters);
100
101 if (executor == statefulExecutors.end())
102 {
103 unique_lock<mutex> scopeLock(checkExecutor);
104
105 executor = routes.find(parameters);
106
107 if (executor == routes.end())
108 {
109 auto executorSettings = settings.find(parameters);
110
111 if (executorSettings == settings.end())
112 {
113 auto it = find_if(routeParameters.begin(), routeParameters.end(),
114 [&parameters](const utility::RouteParameters& value) { return parameters.find(value.baseRoute) != string::npos; });
115
116 if (it == routeParameters.end())
117 {
118 return nullptr;
119 }
120
121 executorSettings = settings.find(it->baseRoute);
122
123 if (executorSettings == settings.end())
124 {
125 return nullptr;
126 }
127
128 ExecutorsManager::parseRouteParameters(parameters, request, it);
129
130 parameters = it->baseRoute;
131 }
132
133 executor = routes.try_emplace
134 (
135 move(parameters),
136 unique_ptr<BaseExecutor>(static_cast<BaseExecutor*>(creators[executorSettings->second.name]()))
137 ).first;
138 executor->second->init(executorSettings->second);
139
140 BaseExecutor::executorType executorType = executor->second->getType();
141
142 if (executorType == BaseExecutor::executorType::stateful || executorType == BaseExecutor::executorType::heavyOperationStateful)
143 {
144 executor = statefulExecutors.insert(routes.extract(executor)).position; //-V837 //-V823
145 }
146 }
147 }
148
149 return executor->second.get();
150 }
151
152 ExecutorsManager::ExecutorsManager() :
153 serverType(webServerType::multiThreaded)
154 {
155
156 }
157
158 ExecutorsManager::ExecutorsManager(ExecutorsManager&& other) noexcept
159 {
160 (*this) = move(other);
161 }
162
163 ExecutorsManager& ExecutorsManager::operator = (ExecutorsManager&& other) noexcept
164 {
165 this->routes = move(other.routes);
166 this->creators = move(other.creators);
167 this->settings = move(other.settings);
168 this->resources = move(other.resources);
169
170 return *this;
171 }
172
173 void ExecutorsManager::init
174 (
175 const json::JSONParser& configuraion,
176 const filesystem::path& assets,
177 uint64_t cachingSize,
178 const filesystem::path& pathToTemplates,
179 unordered_map<string, unique_ptr<BaseExecutor>>&& routes,
180 unordered_map<string, createExecutorFunction>&& creators,
181 unordered_map<string, utility::JSONSettingsParser::ExecutorSettings>&& settings,
182 vector<utility::RouteParameters>&& routeParameters
183 )
184 {
185 const unordered_map<string_view, webServerType> types =
186 {
187 { json_settings::multiThreadedWebServerTypeValue, webServerType::multiThreaded },
188 { json_settings::threadPoolWebServerTypeValue, webServerType::threadPool },
189 { json_settings::loadBalancerWebServerTypeValue, webServerType::loadBalancer },
190 { json_settings::proxyWebServerTypeValue, webServerType::proxy }
191 };
192
193 this->routes = move(routes);
194 this->creators = move(creators);
195 this->settings = move(settings);
196 this->routeParameters = move(routeParameters);
197
198 resources = make_shared<ResourceExecutor>(configuraion, assets, cachingSize, pathToTemplates);
199
200 resources->init(utility::JSONSettingsParser::ExecutorSettings());
201
202 serverType = types.at(configuraion.getObject(json_settings::webFrameworkObject).getString(json_settings::webServerTypeKey));
203 }
204
205 optional<function<void(HTTPRequest&, HTTPResponse&)>> ExecutorsManager::service(HTTPRequest& request, HTTPResponse& response, unordered_map<string, unique_ptr<BaseExecutor>>& statefulExecutors)
206 {
207 static const unordered_map<string, void(BaseExecutor::*)(HTTPRequest&, HTTPResponse&)> methods =
208 {
209 { "GET", &BaseExecutor::doGet },
210 { "POST", &BaseExecutor::doPost },
211 { "HEAD", &BaseExecutor::doHead },
212 { "PUT", &BaseExecutor::doPut },
213 { "DELETE", &BaseExecutor::doDelete },
214 { "PATCH", &BaseExecutor::doPatch },
215 { "OPTIONS",&BaseExecutor::doOptions },
216 { "TRACE", &BaseExecutor::doTrace },
217 { "CONNECT", &BaseExecutor::doConnect }
218 };
219
220 try
221 {
222 string parameters = request.getRawParameters();
223 BaseExecutor* executor = nullptr;
224 bool fileRequest = ExecutorsManager::isFileRequest(parameters);
225
226 if (parameters.find('?') != string::npos)
227 {
228 parameters.resize(parameters.find('?'));
229 }
230
231 executor = this->getExecutor(parameters, request, statefulExecutors);
232
233 if (!fileRequest && !executor)
234 {
235 throw exceptions::BadRequestException(); // 400
236 }
237
238 if (fileRequest && executor)
239 {
240 fileRequest = false;
241 }
242
243 void (BaseExecutor:: * method)(HTTPRequest&, HTTPResponse&) = methods.at(request.getMethod());
244
245 if (serverType == webServerType::threadPool && (fileRequest ? false : ExecutorsManager::isHeavyOperation(executor)))
246 {
247 return bind(method, executor, placeholders::_1, placeholders::_2);
248 }
249 else
250 {
251 fileRequest ?
252 invoke(method, resources.get(), request, response) :
253 invoke(method, executor, request, response);
254 }
255
256 return {};
257 }
258 catch (const exceptions::BaseExecutorException& e)
259 {
260 if (Log::isValid())
261 {
262 Log::error("Executor exception: {}", "LogExecutor", e.what());
263 }
264
265 throw;
266 }
267 catch (const file_manager::exceptions::FileDoesNotExistException& e)
268 {
269 if (Log::isValid())
270 {
271 Log::error("File request exception. {}", "LogExecutor", e.what());
272 }
273
274 throw;
275 }
276 catch (const exceptions::DatabaseException& e)
277 {
278 if (Log::isValid())
279 {
280 Log::error("Database exception: {}", "LogWebFrameworkDatabase", e.what());
281 }
282
283 throw;
284 }
285 catch (const out_of_range&)
286 {
287 if (Log::isValid())
288 {
289 Log::error("Out of range", "LogExecutor");
290 }
291
292 throw;
293 }
294 catch (const exception& e)
295 {
296 if (Log::isValid())
297 {
298 Log::error("Executor manager exception: {}", "LogExecutorsManager", e.what());
299 }
300
301 throw;
302 }
303 }
304
305 shared_ptr<ResourceExecutor> ExecutorsManager::getResourceExecutor() const
306 {
307 return resources;
308 }
309}