Line data Source code
1 : /*
2 : ___________________________________________
3 : | _ ___ _ |
4 : | | | |__ \ | | |
5 : | | |__ ) |__ _ __ _ ___ _ __ | |_ |
6 : | | '_ \ / // _` |/ _` |/ _ \ '_ \| __| | HTTP/2 AGENT FOR MOCK TESTING
7 : | | | | |/ /| (_| | (_| | __/ | | | |_ | Version 0.0.z
8 : | |_| |_|____\__,_|\__, |\___|_| |_|\__| | https://github.com/testillano/h2agent
9 : | __/ | |
10 : | |___/ |
11 : |___________________________________________|
12 :
13 : Licensed under the MIT License <http://opensource.org/licenses/MIT>.
14 : SPDX-License-Identifier: MIT
15 : Copyright (c) 2021 Eduardo Ramos
16 :
17 : Permission is hereby granted, free of charge, to any person obtaining a copy
18 : of this software and associated documentation files (the "Software"), to deal
19 : in the Software without restriction, including without limitation the rights
20 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21 : copies of the Software, and to permit persons to whom the Software is
22 : furnished to do so, subject to the following conditions:
23 :
24 : The above copyright notice and this permission notice shall be included in all
25 : copies or substantial portions of the Software.
26 :
27 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33 : SOFTWARE.
34 : */
35 :
36 : #pragma once
37 :
38 : #include <nghttp2/asio_http2_server.h>
39 :
40 : #include <memory>
41 : #include <string>
42 : #include <vector>
43 : #include <regex>
44 : #include <cstdint>
45 :
46 : #include <nlohmann/json.hpp>
47 :
48 : #include <AdminSchema.hpp>
49 : #include <Transformation.hpp>
50 : #include <TypeConverter.hpp>
51 : #include <DataPart.hpp>
52 :
53 :
54 : namespace h2agent
55 : {
56 : namespace model
57 : {
58 :
59 : // Provision key:
60 : typedef std::string admin_server_provision_key_t; // <inState>#<method>#<uri>
61 :
62 : class MockServerData;
63 : class MockClientData;
64 : class AdminData;
65 : class Configuration;
66 : class GlobalVariable;
67 : class FileManager;
68 : class SocketManager;
69 :
70 :
71 : class AdminServerProvision
72 : {
73 : bool employed_{};
74 :
75 : nlohmann::json json_{}; // provision reference
76 :
77 : admin_server_provision_key_t key_{}; // calculated in every load()
78 : std::regex regex_{}; // precompile key as possible regex for RegexMatching algorithm
79 :
80 : // Cached information:
81 : std::string in_state_{};
82 : std::string request_method_{};
83 : std::string request_uri_{};
84 :
85 : std::string out_state_{};
86 : unsigned int response_code_{};
87 : nghttp2::asio_http2::header_map response_headers_{};
88 :
89 : nlohmann::json response_body_{};
90 : std::string response_body_string_{};
91 : /* Tatsuhiro sends strings in response:
92 : int response_body_integer_{};
93 : double response_body_number_{};
94 : bool response_body_boolean_{};
95 : bool response_body_null_{};
96 : */
97 :
98 : unsigned int response_delay_ms_{};
99 :
100 : // Schemas:
101 : std::string request_schema_id_{};
102 : std::string response_schema_id_{};
103 : std::shared_ptr<h2agent::model::AdminSchema> request_schema_{};
104 : std::shared_ptr<h2agent::model::AdminSchema> response_schema_{};
105 :
106 : model::MockServerData *mock_server_events_data_{}; // just in case it is used
107 : model::MockClientData *mock_client_events_data_{}; // just in case it is used
108 : model::AdminData *admin_data_{}; // just in case it is used
109 : model::Configuration *configuration_{}; // just in case it is used
110 : model::GlobalVariable *global_variable_{}; // just in case it is used
111 : model::FileManager *file_manager_{}; // just in case it is used
112 : model::SocketManager *socket_manager_{}; // just in case it is used
113 :
114 : void loadTransformation(const nlohmann::json &j);
115 :
116 : std::vector<std::shared_ptr<Transformation>> transformations_{};
117 :
118 : // Three processing stages: get sources, apply filters and store targets:
119 : bool processSources(std::shared_ptr<Transformation> transformation,
120 : TypeConverter& sourceVault,
121 : std::map<std::string, std::string>& variables,
122 : const std::string &requestUri,
123 : const std::string &requestUriPath,
124 : const std::map<std::string, std::string> &requestQueryParametersMap,
125 : const DataPart &requestBodyDataPart,
126 : const nghttp2::asio_http2::header_map &requestHeaders,
127 : bool &eraser,
128 : std::uint64_t generalUniqueServerSequence,
129 : bool usesResponseBodyAsTransformationJsonTarget, const nlohmann::json &responseBodyJson) const; // these two last parameters are used to
130 : // know if original response body provision
131 : // or the one dynamically modified, must be
132 : // used as source
133 :
134 : bool processFilters(std::shared_ptr<Transformation> transformation,
135 : TypeConverter& sourceVault,
136 : const std::map<std::string, std::string>& variables,
137 : std::smatch &matches,
138 : std::string &source) const;
139 :
140 : bool processTargets(std::shared_ptr<Transformation> transformation,
141 : TypeConverter& sourceVault,
142 : std::map<std::string, std::string>& variables,
143 : const std::smatch &matches,
144 : bool eraser,
145 : bool hasFilter,
146 : unsigned int &responseStatusCode,
147 : nlohmann::json &responseBodyJson, // to manipulate json
148 : std::string &responseBodyAsString, // to set native data (readable or not)
149 : nghttp2::asio_http2::header_map &responseHeaders,
150 : unsigned int &responseDelayMs,
151 : std::string &outState,
152 : std::string &outStateMethod,
153 : std::string &outStateUri,
154 : std::vector<std::pair<std::string, std::string>> &clientProvisionTriggers,
155 : bool &breakCondition) const;
156 :
157 :
158 : public:
159 :
160 : AdminServerProvision();
161 :
162 : // transform logic
163 :
164 : /**
165 : * Applies transformations vector over request received and ongoing response built
166 : * Also checks optional schema validation for incoming and/or outgoing traffic
167 : *
168 : * @param requestUri Request URI
169 : * @param requestUriPath Request URI path part
170 : * @param requestQueryParametersMap Query Parameters Map (if exists)
171 : * @param requestBodyDataPart Request Body data received (could be decoded if needed as source)
172 : * @param requestHeaders Request Headers Received
173 : * @param generalUniqueServerSequence HTTP/2 server monotonically increased sequence for every reception (unique)
174 : *
175 : * @param responseStatusCode Response status code filled by reference (if any transformation applies)
176 : * @param responseHeaders Response headers filled by reference (if any transformation applies)
177 : * @param responseBody Response body filled by reference (if any transformation applies)
178 : * @param responseDelayMs Response delay milliseconds filled by reference (if any transformation applies)
179 : * @param outState out-state for request context created, filled by reference (if any transformation applies)
180 : * @param outStateMethod method inferred towards a virtual server data entry created through a foreign out-state, filled by reference (if any transformation applies)
181 : * @param outStateUri uri inferred towards a virtual server data entry created through a foreign out-state, filled by reference (if any transformation applies)
182 : */
183 : void transform( const std::string &requestUri,
184 : const std::string &requestUriPath,
185 : const std::map<std::string, std::string> &requestQueryParametersMap,
186 : DataPart &requestBodyDataPart,
187 : const nghttp2::asio_http2::header_map &requestHeaders,
188 : std::uint64_t generalUniqueServerSequence,
189 :
190 : unsigned int &responseStatusCode,
191 : nghttp2::asio_http2::header_map &responseHeaders,
192 : std::string &responseBody,
193 : unsigned int &responseDelayMs,
194 : std::string &outState,
195 : std::string &outStateMethod,
196 : std::string &outStateUri,
197 : std::vector<std::pair<std::string, std::string>> &clientProvisionTriggers,
198 : std::map<std::string, std::string> &variables
199 : );
200 :
201 : // setters:
202 :
203 : /**
204 : * Load provision information
205 : *
206 : * @param j Json provision object
207 : * @param regexMatchingConfigured provision load depends on matching configuration (priority regexp)
208 : *
209 : * @return Operation success
210 : */
211 : bool load(const nlohmann::json &j, bool regexMatchingConfigured);
212 :
213 : /**
214 : * Sets the internal mock server data,
215 : * just in case it is used in event source
216 : */
217 119 : void setMockServerData(model::MockServerData *p) {
218 119 : mock_server_events_data_ = p;
219 119 : }
220 :
221 : /**
222 : * Sets the internal mock client data,
223 : * just in case it is used in event source
224 : */
225 119 : void setMockClientData(model::MockClientData *p) {
226 119 : mock_client_events_data_ = p;
227 119 : }
228 :
229 : /**
230 : * Sets the admin data reference,
231 : * just in case it is used in SchemaId filter
232 : */
233 119 : void setAdminData(model::AdminData *p) {
234 119 : admin_data_ = p;
235 119 : }
236 :
237 : /**
238 : * Sets the configuration reference,
239 : * just in case it is used in event target
240 : */
241 119 : void setConfiguration(model::Configuration *p) {
242 119 : configuration_ = p;
243 119 : }
244 :
245 : /**
246 : * Sets the global variables data reference,
247 : * just in case it is used in event source
248 : */
249 119 : void setGlobalVariable(model::GlobalVariable *p) {
250 119 : global_variable_ = p;
251 119 : }
252 :
253 : /**
254 : * Sets the file manager reference,
255 : * just in case it is used in event target
256 : */
257 119 : void setFileManager(model::FileManager *p) {
258 119 : file_manager_ = p;
259 119 : }
260 :
261 : /**
262 : * Sets the socket manager reference,
263 : * just in case it is used in event target
264 : */
265 119 : void setSocketManager(model::SocketManager *p) {
266 119 : socket_manager_ = p;
267 119 : }
268 :
269 : /**
270 : * Provision is being employed
271 : */
272 0 : void employ() {
273 0 : employed_ = true;
274 0 : }
275 :
276 : // getters:
277 :
278 : /**
279 : * Gets the provision request uri which could be a regular expression
280 : * or a full-matched URI string
281 : *
282 : * @return Provision request URI
283 : */
284 0 : const admin_server_provision_key_t &getRequestUri() const {
285 0 : return request_uri_;
286 : }
287 :
288 : /**
289 : * Gets the provision key as '<in-state>|<request-method>|<request-uri>'
290 : *
291 : * @return Provision key
292 : */
293 119 : const admin_server_provision_key_t &getKey() const {
294 119 : return key_;
295 : }
296 :
297 : /**
298 : * Json for class information
299 : *
300 : * @return Json object
301 : */
302 3 : const nlohmann::json &getJson() const {
303 3 : return json_;
304 : }
305 :
306 : /**
307 : * Precompiled regex for provision key
308 : *
309 : * @return regex
310 : */
311 4 : const std::regex &getRegex() const {
312 4 : return regex_;
313 : }
314 :
315 : /** Provisioned out state
316 : *
317 : * @return Out state
318 : */
319 105 : const std::string &getOutState() const {
320 105 : return out_state_;
321 : }
322 :
323 : /** Provisioned in state
324 : *
325 : * @return In state
326 : */
327 3 : const std::string &getInState() const {
328 3 : return in_state_;
329 : }
330 :
331 : /** Provisioned response code
332 : *
333 : * @return Response code
334 : */
335 105 : unsigned int getResponseCode() const {
336 105 : return response_code_;
337 : }
338 :
339 : /** Provisioned response headers
340 : *
341 : * @return Response headers
342 : */
343 105 : const nghttp2::asio_http2::header_map &getResponseHeaders() const {
344 105 : return response_headers_;
345 : }
346 :
347 : /** Provisioned response body as json representation
348 : *
349 : * @return Response body as json representation
350 : */
351 33 : const nlohmann::json &getResponseBody() const {
352 33 : return response_body_;
353 : }
354 :
355 : /** Provisioned response body as string.
356 : *
357 : * This is useful as cached response data when the provision
358 : * response is not modified with transformation items.
359 : *
360 : * When the object is not a valid json, the data is
361 : * assumed as a readable string (TODO: refactor for multipart support)
362 : *
363 : * @return Response body string
364 : */
365 76 : const std::string &getResponseBodyAsString() const {
366 76 : return response_body_string_;
367 : }
368 :
369 : /** Provisioned response delay milliseconds
370 : *
371 : * @return Response delay milliseconds
372 : */
373 105 : unsigned int getResponseDelayMilliseconds() const {
374 105 : return response_delay_ms_;
375 : }
376 :
377 : /** Provisioned request schema reference
378 : *
379 : * @return Request schema to validate incoming traffic, nullptr if missing
380 : */
381 : std::shared_ptr<h2agent::model::AdminSchema> getRequestSchema();
382 :
383 : /** Provisioned response schema reference
384 : *
385 : * @return Response schema to validate outgoing traffic, nullptr if missing
386 : */
387 : std::shared_ptr<h2agent::model::AdminSchema> getResponseSchema();
388 :
389 : /** Provision was employed
390 : *
391 : * @return Boolean about if this provision has been used
392 : */
393 0 : bool employed() const {
394 0 : return employed_;
395 : }
396 :
397 : /**
398 : * Checks if this provision references event-dependent transformation types
399 : * (serverEvent/clientEvent sources or serverEventToPurge/clientEventToPurge targets)
400 : *
401 : * @return True if any transformation requires stored events
402 : */
403 8 : bool needsStorage() const {
404 8 : for (const auto& t : transformations_) {
405 3 : auto st = t->getSourceType();
406 3 : auto tt = t->getTargetType();
407 3 : if (st == Transformation::ServerEvent || st == Transformation::ClientEvent ||
408 0 : tt == Transformation::ServerEventToPurge || tt == Transformation::ClientEventToPurge)
409 3 : return true;
410 : }
411 5 : return false;
412 : }
413 : };
414 :
415 : }
416 : }
417 :
|