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 Vault;
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::Vault *vault_{}; // 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 : void executeOnFilterFail(
158 : const std::vector<std::shared_ptr<Transformation>> &fallbacks,
159 : const std::string &requestUri, const std::string &requestUriPath,
160 : const std::map<std::string, std::string> &requestQueryParametersMap,
161 : const DataPart &requestBodyDataPart, const nghttp2::asio_http2::header_map &requestHeaders,
162 : std::uint64_t generalUniqueServerSequence, TypeConverter &sourceVault,
163 : std::map<std::string, std::string> &variables,
164 : bool usesResponseBodyAsTransformationJsonTarget,
165 : unsigned int &responseStatusCode, nlohmann::json &responseBodyJson, std::string &responseBody,
166 : nghttp2::asio_http2::header_map &responseHeaders, unsigned int &responseDelayMs,
167 : std::string &outState, std::string &outStateMethod, std::string &outStateUri,
168 : std::vector<std::pair<std::string, std::string>> &clientProvisionTriggers, bool &breakCondition) const;
169 :
170 :
171 : public:
172 :
173 : AdminServerProvision();
174 :
175 : // transform logic
176 :
177 : /**
178 : * Applies transformations vector over request received and ongoing response built
179 : * Also checks optional schema validation for incoming and/or outgoing traffic
180 : *
181 : * @param requestUri Request URI
182 : * @param requestUriPath Request URI path part
183 : * @param requestQueryParametersMap Query Parameters Map (if exists)
184 : * @param requestBodyDataPart Request Body data received (could be decoded if needed as source)
185 : * @param requestHeaders Request Headers Received
186 : * @param generalUniqueServerSequence HTTP/2 server monotonically increased sequence for every reception (unique)
187 : *
188 : * @param responseStatusCode Response status code filled by reference (if any transformation applies)
189 : * @param responseHeaders Response headers filled by reference (if any transformation applies)
190 : * @param responseBody Response body filled by reference (if any transformation applies)
191 : * @param responseDelayMs Response delay milliseconds filled by reference (if any transformation applies)
192 : * @param outState out-state for request context created, filled by reference (if any transformation applies)
193 : * @param outStateMethod method inferred towards a virtual server data entry created through a foreign out-state, filled by reference (if any transformation applies)
194 : * @param outStateUri uri inferred towards a virtual server data entry created through a foreign out-state, filled by reference (if any transformation applies)
195 : */
196 : void transform( const std::string &requestUri,
197 : const std::string &requestUriPath,
198 : const std::map<std::string, std::string> &requestQueryParametersMap,
199 : DataPart &requestBodyDataPart,
200 : const nghttp2::asio_http2::header_map &requestHeaders,
201 : std::uint64_t generalUniqueServerSequence,
202 :
203 : unsigned int &responseStatusCode,
204 : nghttp2::asio_http2::header_map &responseHeaders,
205 : std::string &responseBody,
206 : unsigned int &responseDelayMs,
207 : std::string &outState,
208 : std::string &outStateMethod,
209 : std::string &outStateUri,
210 : std::vector<std::pair<std::string, std::string>> &clientProvisionTriggers,
211 : std::map<std::string, std::string> &variables
212 : );
213 :
214 : // setters:
215 :
216 : /**
217 : * Load provision information
218 : *
219 : * @param j Json provision object
220 : * @param regexMatchingConfigured provision load depends on matching configuration (priority regexp)
221 : *
222 : * @return Operation success
223 : */
224 : bool load(const nlohmann::json &j, bool regexMatchingConfigured);
225 :
226 : /**
227 : * Sets the internal mock server data,
228 : * just in case it is used in event source
229 : */
230 167 : void setMockServerData(model::MockServerData *p) {
231 167 : mock_server_events_data_ = p;
232 167 : }
233 :
234 : /**
235 : * Sets the internal mock client data,
236 : * just in case it is used in event source
237 : */
238 167 : void setMockClientData(model::MockClientData *p) {
239 167 : mock_client_events_data_ = p;
240 167 : }
241 :
242 : /**
243 : * Sets the admin data reference,
244 : * just in case it is used in SchemaId filter
245 : */
246 167 : void setAdminData(model::AdminData *p) {
247 167 : admin_data_ = p;
248 167 : }
249 :
250 : /**
251 : * Sets the configuration reference,
252 : * just in case it is used in event target
253 : */
254 167 : void setConfiguration(model::Configuration *p) {
255 167 : configuration_ = p;
256 167 : }
257 :
258 : /**
259 : * Sets the vault data reference,
260 : * just in case it is used in event source
261 : */
262 167 : void setVault(model::Vault *p) {
263 167 : vault_ = p;
264 167 : }
265 :
266 : /**
267 : * Sets the file manager reference,
268 : * just in case it is used in event target
269 : */
270 167 : void setFileManager(model::FileManager *p) {
271 167 : file_manager_ = p;
272 167 : }
273 :
274 : /**
275 : * Sets the socket manager reference,
276 : * just in case it is used in event target
277 : */
278 167 : void setSocketManager(model::SocketManager *p) {
279 167 : socket_manager_ = p;
280 167 : }
281 :
282 : /**
283 : * Provision is being employed
284 : */
285 0 : void employ() {
286 0 : employed_ = true;
287 0 : }
288 :
289 : // getters:
290 :
291 : /**
292 : * Gets the provision request uri which could be a regular expression
293 : * or a full-matched URI string
294 : *
295 : * @return Provision request URI
296 : */
297 0 : const admin_server_provision_key_t &getRequestUri() const {
298 0 : return request_uri_;
299 : }
300 :
301 : /**
302 : * Gets the provision key as '<in-state>|<request-method>|<request-uri>'
303 : *
304 : * @return Provision key
305 : */
306 167 : const admin_server_provision_key_t &getKey() const {
307 167 : return key_;
308 : }
309 :
310 : /**
311 : * Json for class information
312 : *
313 : * @return Json object
314 : */
315 3 : const nlohmann::json &getJson() const {
316 3 : return json_;
317 : }
318 :
319 : /**
320 : * Precompiled regex for provision key
321 : *
322 : * @return regex
323 : */
324 4 : const std::regex &getRegex() const {
325 4 : return regex_;
326 : }
327 :
328 : /** Provisioned out state
329 : *
330 : * @return Out state
331 : */
332 153 : const std::string &getOutState() const {
333 153 : return out_state_;
334 : }
335 :
336 : /** Provisioned in state
337 : *
338 : * @return In state
339 : */
340 3 : const std::string &getInState() const {
341 3 : return in_state_;
342 : }
343 :
344 : /** Provisioned response code
345 : *
346 : * @return Response code
347 : */
348 153 : unsigned int getResponseCode() const {
349 153 : return response_code_;
350 : }
351 :
352 : /** Provisioned response headers
353 : *
354 : * @return Response headers
355 : */
356 153 : const nghttp2::asio_http2::header_map &getResponseHeaders() const {
357 153 : return response_headers_;
358 : }
359 :
360 : /** Provisioned response body as json representation
361 : *
362 : * @return Response body as json representation
363 : */
364 54 : const nlohmann::json &getResponseBody() const {
365 54 : return response_body_;
366 : }
367 :
368 : /** Provisioned response body as string.
369 : *
370 : * This is useful as cached response data when the provision
371 : * response is not modified with transformation items.
372 : *
373 : * When the object is not a valid json, the data is
374 : * assumed as a readable string (TODO: refactor for multipart support)
375 : *
376 : * @return Response body string
377 : */
378 103 : const std::string &getResponseBodyAsString() const {
379 103 : return response_body_string_;
380 : }
381 :
382 : /** Provisioned response delay milliseconds
383 : *
384 : * @return Response delay milliseconds
385 : */
386 153 : unsigned int getResponseDelayMilliseconds() const {
387 153 : return response_delay_ms_;
388 : }
389 :
390 : /** Provisioned request schema reference
391 : *
392 : * @return Request schema to validate incoming traffic, nullptr if missing
393 : */
394 : std::shared_ptr<h2agent::model::AdminSchema> getRequestSchema();
395 :
396 : /** Provisioned response schema reference
397 : *
398 : * @return Response schema to validate outgoing traffic, nullptr if missing
399 : */
400 : std::shared_ptr<h2agent::model::AdminSchema> getResponseSchema();
401 :
402 : /** Provision was employed
403 : *
404 : * @return Boolean about if this provision has been used
405 : */
406 0 : bool employed() const {
407 0 : return employed_;
408 : }
409 :
410 : /**
411 : * Checks if this provision references event-dependent transformation types
412 : * (serverEvent/clientEvent as source or target — target with eraser source deletes events)
413 : *
414 : * @return True if any transformation requires stored events
415 : */
416 8 : bool needsStorage() const {
417 8 : for (const auto& t : transformations_) {
418 3 : auto st = t->getSourceType();
419 3 : auto tt = t->getTargetType();
420 3 : if (st == Transformation::ServerEvent || st == Transformation::ClientEvent ||
421 0 : tt == Transformation::ServerEventToPurge || tt == Transformation::ClientEventToPurge)
422 3 : return true;
423 : }
424 5 : return false;
425 : }
426 : };
427 :
428 : }
429 : }
430 :
|