atomicshop 3.0.2__py3-none-any.whl → 3.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of atomicshop might be problematic. Click here for more details.

atomicshop/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """Atomic Basic functions and classes to make developer life easier"""
2
2
 
3
3
  __author__ = "Den Kras"
4
- __version__ = '3.0.2'
4
+ __version__ = '3.1.0'
@@ -180,16 +180,22 @@ def thread_worker_main(
180
180
 
181
181
  network_logger.info("Thread Finished. Will continue listening on the Main thread")
182
182
 
183
+ def create_requester_request(client_message: ClientMessage) -> list[bytes]:
184
+ requests: list = [requester.create_response(client_message)]
185
+
186
+ # Output first 100 characters of all the requests in the list.
187
+ for request_raw_bytes_single in requests:
188
+ requester.logger.info(f"{request_raw_bytes_single[0: 100]}...")
189
+
190
+ return requests
191
+
183
192
  def create_responder_response(client_message: ClientMessage) -> list[bytes]:
184
193
  if client_message.action == 'service_connect':
185
194
  return responder.create_connect_response(client_message)
186
195
  else:
187
- # Since we're in response mode, we'll record the request anyway, after the responder did its job.
188
- # client_message.info = "In Server Response Mode"
189
-
190
- # If it's the first cycle and the protocol is Websocket, then we'll create the HTTP Handshake
196
+ # If we're in offline mode, and it's the first cycle and the protocol is Websocket, then we'll create the HTTP Handshake
191
197
  # response automatically.
192
- if protocol == 'Websocket' and client_receive_count == 1:
198
+ if config_static.MainConfig.offline and protocol == 'Websocket' and client_receive_count == 1:
193
199
  responses: list = list()
194
200
  responses.append(
195
201
  websocket_parse.create_byte_http_response(client_message.request_raw_bytes))
@@ -380,12 +386,14 @@ def thread_worker_main(
380
386
  # Now send it to requester/responder.
381
387
  if side == 'Client':
382
388
  # Send to requester.
383
- bytes_to_send_list: list[bytes] = [client_message.request_raw_bytes]
384
- else:
389
+ bytes_to_send_list: list[bytes] = create_requester_request(client_message)
390
+ elif side == 'Service':
385
391
  bytes_to_send_list: list[bytes] = create_responder_response(client_message)
386
392
  print_api(f"Got responses from responder, count: [{len(bytes_to_send_list)}]",
387
393
  logger=network_logger,
388
394
  logger_method='info')
395
+ else:
396
+ raise ValueError(f"Unknown side [{side}] of the socket: {receiving_socket}")
389
397
 
390
398
  if is_socket_closed:
391
399
  exception_or_close_in_receiving_thread = True
@@ -511,15 +519,16 @@ def thread_worker_main(
511
519
  reference_module=reference_module
512
520
  )
513
521
  parser = found_domain_module.parser_class_object
522
+ requester = found_domain_module.requester_class_object()
514
523
  responder = found_domain_module.responder_class_object()
515
524
  recorder = found_domain_module.recorder_class_object(record_path=config_static.LogRec.recordings_path)
516
525
 
517
526
  network_logger.info(f"Assigned Modules for [{server_name}]: "
518
527
  f"{parser.__name__}, "
528
+ f"{requester.__class__.__name__}, "
519
529
  f"{responder.__class__.__name__}, "
520
530
  f"{recorder.__class__.__name__}")
521
531
 
522
-
523
532
  # Initializing the client message object with current thread's data.
524
533
  # This is needed only to skip error alerts after 'try'.
525
534
  client_message_connection: ClientMessage = ClientMessage()
@@ -0,0 +1,116 @@
1
+ # Using to convert status code to status phrase / string.
2
+ from http import HTTPStatus
3
+ # Parsing PATH template to variables.
4
+ from pathlib import PurePosixPath
5
+ from urllib.parse import unquote
6
+ # Needed to extract parameters after question mark in URL / Path.
7
+ from urllib.parse import urlparse
8
+ from urllib.parse import parse_qs
9
+
10
+ from ...message import ClientMessage
11
+ from .... import http_parse
12
+ from ....print_api import print_api
13
+
14
+ from atomicshop.mitm.shared_functions import create_custom_logger
15
+
16
+
17
+ class RequesterParent:
18
+ """The class that is responsible for generating request to client based on the received message."""
19
+ def __init__(self):
20
+ self.logger = create_custom_logger()
21
+
22
+ def build_byte_request(
23
+ self,
24
+ http_method: str,
25
+ endpoint: str,
26
+ http_version: str,
27
+ headers: dict,
28
+ body: bytes
29
+ ) -> bytes:
30
+ # noinspection GrazieInspection
31
+ """
32
+ Create genuine request from input parameters.
33
+ ---------------
34
+ The request is built from:
35
+ <http_method> <endpoint> <http_version>\r\n
36
+ Headers1: Value\r\n
37
+ Headers2: Value\r\n
38
+ \r\n # This is meant to end the headers' section
39
+ Body # Request doesn't end with '\r\n\r\n'
40
+ ---------------
41
+ Example for POST request:
42
+ POST /api/v1/resource HTTP/1.1\r\n
43
+ Cache-Control: max-age=86400\r\n
44
+ Content-Type: application/json; charset=utf-8\r\n
45
+ \r\n
46
+ {"id":1,"name":"something"}
47
+ ---------------
48
+ You can create response as:
49
+
50
+ ...POST endpoint/api/1 HTTP/1.1
51
+ header1: value
52
+ header2: value
53
+
54
+ {data: value}...
55
+
56
+ Change 3 dots ("...") to 3 double quotes before "POST" and after "value}".
57
+ This way there will be "\n" added automatically after each line.
58
+ While, the HTTP Client does the parsing of the text and not raw data, most probably it will be parsed well,
59
+ but genuine requests from HTTP sources come with "\r\n" at the end of the line, so better use these for
60
+ better compatibility.
61
+ ---------------
62
+
63
+ :param http_method: HTTP Method of Request, e.g. 'GET', 'POST', etc.
64
+ :param endpoint: Endpoint of Request, e.g. '/api/v1/resource'.
65
+ :param http_version: HTTP Version of Response in HTTP Status line.
66
+ :param headers: HTTP Headers of Response.
67
+ :param body: HTTP body data of Response, bytes.
68
+ :return: bytes of the response.
69
+ """
70
+
71
+ try:
72
+ # CHeck if the HTTP method is valid.
73
+ if http_method not in http_parse.get_request_methods():
74
+ raise ValueError(f"Invalid HTTP Method: {http_method}")
75
+
76
+ # Building the full method endpoint string line and the "\r\n" in the end.
77
+ method_full: str = f"{http_method} {endpoint} {http_version}\r\n"
78
+
79
+ # Defining headers string.
80
+ headers_string: str = str()
81
+ # Adding all the headers to the full response
82
+ for keys, values in headers.items():
83
+ headers_string = headers_string + str(keys) + ": " + str(values) + "\r\n"
84
+
85
+ # Building full string request.
86
+ # 1. Adding full method line.
87
+ # 2. Adding headers string.
88
+ # 3. Adding a line that end headers (with "\r\n").
89
+ # 4. Adding body as byte string.
90
+ request_full_no_body: str = method_full + headers_string + "\r\n"
91
+
92
+ # Converting the HTTP Request string to bytes and adding 'body' bytes.
93
+ request_raw_bytes = request_full_no_body.encode() + body
94
+ except ValueError as exception_object:
95
+ message = \
96
+ f'Create Byte request function error, of the of values provided is not standard: {exception_object}'
97
+ print_api(message, error_type=True, logger=self.logger, logger_method='error', color='red')
98
+
99
+ request_raw_bytes = b''
100
+
101
+ # Parsing the request we created.
102
+ request_parse_test = http_parse.HTTPRequestParse(request_raw_bytes)
103
+ # If there were errors during parsing, it means that something is wrong with response created.
104
+ if request_parse_test.error_message:
105
+ self.logger.error(request_parse_test.error_message)
106
+ request_raw_bytes = b''
107
+ else:
108
+ self.logger.info("Created Valid Byte Request.")
109
+
110
+ return request_raw_bytes
111
+
112
+ def create_request(self, class_client_message: ClientMessage):
113
+ """ This function should be overridden in the child class. """
114
+
115
+ request_bytes: bytes = class_client_message.request_raw_bytes
116
+ return request_bytes
@@ -0,0 +1,46 @@
1
+ # These are specified with hardcoded paths instead of relative, because 'create_module_template.py' copies the content.
2
+ from atomicshop.mitm.engines.__parent.requester___parent import RequesterParent
3
+ from atomicshop.mitm.shared_functions import create_custom_logger
4
+ from atomicshop.mitm.message import ClientMessage
5
+
6
+ """
7
+ import time
8
+ datetime
9
+ import binascii
10
+
11
+ # This is 'example' '.proto' file that contains message 'ExampleResponse'.
12
+ from .example_pb2 import ExampleRequest
13
+ # Import from 'protobuf' the 'json_format' library.
14
+ from google.protobuf import json_format
15
+ """
16
+
17
+
18
+ class RequesterGeneral(RequesterParent):
19
+ """The class that is responsible for generating request to client based on the received message."""
20
+ # When initializing main classes through "super" you need to pass parameters to init
21
+ def __init__(self):
22
+ super().__init__()
23
+
24
+ self.logger = create_custom_logger()
25
+
26
+ # def create_request(self, class_client_message: ClientMessage):
27
+ # # noinspection GrazieInspection
28
+ # """
29
+ # For more examples check the responder.
30
+ # Function to create Response based on ClientMessage and its Request.
31
+ #
32
+ # :param class_client_message: contains request and other parameters to help creating response.
33
+ # :return: 1 request in byte string.
34
+ # -----------------------------------
35
+ #
36
+ # # Example of creating byte string using 'build_byte_request' function:
37
+ # request_bytes: bytes = self.build_byte_request(
38
+ # http_method=class_client_message.request_raw_decoded.command,
39
+ # endpoint=class_client_message.request_raw_decoded.path,
40
+ # http_version=class_client_message.request_raw_decoded.request_version,
41
+ # headers=response_headers,
42
+ # body=b''
43
+ # )
44
+ #
45
+ # return request_bytes
46
+ # -----------------------------------
@@ -14,6 +14,7 @@ CONFIG_FILE_NAME: str = "engine_config.toml"
14
14
 
15
15
  REFERENCE_PARSER_FILE_NAME: str = f"parser_{REFERENCE_ENGINE_NAME}.py"
16
16
  REFERENCE_RESPONDER_FILE_NAME: str = f"responder_{REFERENCE_ENGINE_NAME}.py"
17
+ REFERENCE_REQUESTER_FILE_NAME: str = f"requester_{REFERENCE_ENGINE_NAME}.py"
17
18
  REFERENCE_RECORDER_FILE_NAME: str = f"recorder_{REFERENCE_ENGINE_NAME}.py"
18
19
 
19
20
  SCRIPT_DIRECTORY: str = filesystem.get_file_directory(__file__)
@@ -43,10 +44,12 @@ class CreateModuleTemplate:
43
44
  reference_folder_path: str = SCRIPT_DIRECTORY + os.sep + REFERENCE_ENGINE_NAME
44
45
  self.parser_general_path: str = reference_folder_path + os.sep + REFERENCE_PARSER_FILE_NAME
45
46
  self.responder_general_path: str = reference_folder_path + os.sep + REFERENCE_RESPONDER_FILE_NAME
47
+ self.requester_general_path: str = reference_folder_path + os.sep + REFERENCE_REQUESTER_FILE_NAME
46
48
  self.recorder_general_path: str = reference_folder_path + os.sep + REFERENCE_RECORDER_FILE_NAME
47
49
 
48
50
  self.parser_file_name: str = f"parser.py"
49
51
  self.responder_file_name: str = f"responder.py"
52
+ self.requester_file_name: str = f"requester.py"
50
53
  self.recorder_file_name: str = f"recorder.py"
51
54
 
52
55
  self.create_template()
@@ -63,6 +66,7 @@ class CreateModuleTemplate:
63
66
 
64
67
  self._create_engine_module_from_reference(file_path=self.parser_general_path, module_type='parser')
65
68
  self._create_engine_module_from_reference(file_path=self.responder_general_path, module_type='responder')
69
+ self._create_engine_module_from_reference(file_path=self.requester_general_path, module_type='requester')
66
70
  self._create_engine_module_from_reference(file_path=self.recorder_general_path, module_type='recorder')
67
71
 
68
72
  self.create_config_file()
@@ -91,13 +95,15 @@ class CreateModuleTemplate:
91
95
  def _create_engine_module_from_reference(
92
96
  self,
93
97
  file_path: str,
94
- module_type: Literal['parser', 'responder', 'recorder']
98
+ module_type: Literal['parser', 'responder', 'requester', 'recorder']
95
99
  ):
96
100
 
97
101
  if module_type == 'parser':
98
102
  new_module_file_name = self.parser_file_name
99
103
  elif module_type == 'responder':
100
104
  new_module_file_name = self.responder_file_name
105
+ elif module_type == 'requester':
106
+ new_module_file_name = self.requester_file_name
101
107
  elif module_type == 'recorder':
102
108
  new_module_file_name = self.recorder_file_name
103
109
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 3.0.2
3
+ Version: 3.1.0
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=GEOC5iHeOiaybq-qC7KhTLKWjsw1mfBb9KDefy9-GIE,122
1
+ atomicshop/__init__.py,sha256=91TkRXyPpHainjVtqMX8IhnOPsq4mLI4-W-pBKoiPbk,122
2
2
  atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
3
3
  atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
4
4
  atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
@@ -136,7 +136,7 @@ atomicshop/file_io/xmls.py,sha256=zh3SuK-dNaFq2NDNhx6ivcf4GYCfGM8M10PcEwDSpxk,21
136
136
  atomicshop/mitm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
137
137
  atomicshop/mitm/config_static.py,sha256=N3D06C_wUytwm80PQCL90ZGHLMHeQlCcI2XQQU2FZ1I,8145
138
138
  atomicshop/mitm/config_toml_editor.py,sha256=2p1CMcktWRR_NW-SmyDwylu63ad5e0-w1QPMa8ZLDBw,1635
139
- atomicshop/mitm/connection_thread_worker.py,sha256=IZtcdW9ZZrtgL-dawt_o56emGCrzWtbd8fUAwgKX9NU,27972
139
+ atomicshop/mitm/connection_thread_worker.py,sha256=H3Ogwo9iMG4WgKZabJGl69_haIkEsMR4p6l4CGbwBYU,28500
140
140
  atomicshop/mitm/import_config.py,sha256=MbMgX5IpqC6fUZUTHLOtHyHbzab2tG4RgD2O18csKcI,16506
141
141
  atomicshop/mitm/initialize_engines.py,sha256=lucq9LwkWTnrYKsnt8bgs8t9R_y3gOPmzLyEmRuXD8s,6628
142
142
  atomicshop/mitm/message.py,sha256=CDhhm4BTuZE7oNZCjvIZ4BuPOW4MuIzQLOg91hJaxDI,3065
@@ -145,15 +145,17 @@ atomicshop/mitm/recs_files.py,sha256=ZAAD0twun-FtmbSniXe3XQhIlawvANNB_HxwbHj7kwI
145
145
  atomicshop/mitm/shared_functions.py,sha256=0lzeyINd44sVEfFbahJxQmz6KAMWbYrW5ou3UYfItvw,1777
146
146
  atomicshop/mitm/statistic_analyzer.py,sha256=5_sAYGX2Xunzo_pS2W5WijNCwr_BlGJbbOO462y_wN4,27533
147
147
  atomicshop/mitm/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
148
- atomicshop/mitm/engines/create_module_template.py,sha256=OSX6iM54_PFdo45AMO4f4eToYmLN3UTXZF7mVoQOBko,5104
148
+ atomicshop/mitm/engines/create_module_template.py,sha256=nZE99lwHc_Wm4zmEt7P3cEw3E32Ms65dVbhqZbqOV6U,5577
149
149
  atomicshop/mitm/engines/create_module_template_main_example.py,sha256=LeQ44Rp2Gi_KbIDY_4OMS0odkSK3zFZWra_oAka5eJY,243
150
150
  atomicshop/mitm/engines/__parent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
151
151
  atomicshop/mitm/engines/__parent/parser___parent.py,sha256=HHaCXhScl3OlPjz6eUxsDpJaZyk6BNuDMc9xCkeo2Ws,661
152
152
  atomicshop/mitm/engines/__parent/recorder___parent.py,sha256=JbbcpV6j4odvREmvcXQlzKr5_hwLiEHhF0O414PlTes,6010
153
+ atomicshop/mitm/engines/__parent/requester___parent.py,sha256=mKHR1xrfx0CCO74vv164Fr_dVDhptqvYsE3ORoM4_7k,5107
153
154
  atomicshop/mitm/engines/__parent/responder___parent.py,sha256=mtiS_6ej9nxT9UhAQR4ftMqnqL-j_kO3u8KEaoEaI9k,9495
154
155
  atomicshop/mitm/engines/__reference_general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
155
156
  atomicshop/mitm/engines/__reference_general/parser___reference_general.py,sha256=57MEPZMAjTO6xBDZ-yt6lgGJyqRrP0Do5Gk_cgCiPns,2998
156
157
  atomicshop/mitm/engines/__reference_general/recorder___reference_general.py,sha256=El2_YHLoHUCiKfkAmGlXxtFpmSjsUFdsb8I1MvSAFaM,653
158
+ atomicshop/mitm/engines/__reference_general/requester___reference_general.py,sha256=yXw8cJ5_0XlupnEtEhqyOR4rvQEAg68Ok39vKa8vKZY,1949
157
159
  atomicshop/mitm/engines/__reference_general/responder___reference_general.py,sha256=zVMmON1sy6HH-A35uZZuAURBMfKS5X9ivkPVjPl-sCw,9654
158
160
  atomicshop/mitm/statistic_analyzer_helper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
159
161
  atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py,sha256=pk6L1t1ea1kvlBoR9QEJptOmaX-mumhwLsP2GCKukbk,5920
@@ -334,8 +336,8 @@ atomicshop/wrappers/socketw/statistics_csv.py,sha256=WcNyaqEZ82S5-f3kzqi1nllNT2N
334
336
  atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
335
337
  atomicshop/wrappers/winregw/winreg_installed_software.py,sha256=Qzmyktvob1qp6Tjk2DjLfAqr_yXV0sgWzdMW_9kwNjY,2345
336
338
  atomicshop/wrappers/winregw/winreg_network.py,sha256=3Ts1sVqSUiCDsHRHwJCbiZ9EYvv2ELGxF0Y_pibGU4k,9596
337
- atomicshop-3.0.2.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
338
- atomicshop-3.0.2.dist-info/METADATA,sha256=aX3egUmln1ZN5f18IVvne582AmpRUfl03XXcoq4EbBk,10653
339
- atomicshop-3.0.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
340
- atomicshop-3.0.2.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
341
- atomicshop-3.0.2.dist-info/RECORD,,
339
+ atomicshop-3.1.0.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
340
+ atomicshop-3.1.0.dist-info/METADATA,sha256=CvGoKMdS-InZlR0Mk23zWG7_g9YqXprrxx05qwPwHiY,10653
341
+ atomicshop-3.1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
342
+ atomicshop-3.1.0.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
343
+ atomicshop-3.1.0.dist-info/RECORD,,