atomicshop 2.16.28__py3-none-any.whl → 2.16.30__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 +1 -1
- atomicshop/archiver/zips.py +5 -6
- atomicshop/basics/strings.py +2 -2
- atomicshop/dns.py +8 -6
- atomicshop/file_io/file_io.py +7 -7
- atomicshop/filesystem.py +6 -7
- atomicshop/http_parse.py +82 -67
- atomicshop/mitm/connection_thread_worker.py +187 -231
- atomicshop/mitm/engines/__parent/parser___parent.py +1 -4
- atomicshop/mitm/engines/__parent/recorder___parent.py +6 -2
- atomicshop/mitm/message.py +29 -20
- atomicshop/mitm/mitm_main.py +36 -32
- atomicshop/print_api.py +13 -53
- atomicshop/system_resource_monitor.py +8 -8
- atomicshop/system_resources.py +8 -8
- atomicshop/web.py +10 -10
- atomicshop/wrappers/loggingw/filters.py +23 -0
- atomicshop/wrappers/loggingw/handlers.py +20 -0
- atomicshop/wrappers/loggingw/loggingw.py +130 -7
- atomicshop/wrappers/playwrightw/engine.py +6 -7
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/socketw/dns_server.py +2 -2
- atomicshop/wrappers/socketw/sender.py +56 -28
- atomicshop/wrappers/socketw/sni.py +27 -16
- atomicshop/wrappers/socketw/socket_client.py +6 -5
- atomicshop/wrappers/socketw/socket_wrapper.py +49 -14
- {atomicshop-2.16.28.dist-info → atomicshop-2.16.30.dist-info}/METADATA +1 -1
- {atomicshop-2.16.28.dist-info → atomicshop-2.16.30.dist-info}/RECORD +31 -31
- {atomicshop-2.16.28.dist-info → atomicshop-2.16.30.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.16.28.dist-info → atomicshop-2.16.30.dist-info}/WHEEL +0 -0
- {atomicshop-2.16.28.dist-info → atomicshop-2.16.30.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
atomicshop/archiver/zips.py
CHANGED
|
@@ -4,8 +4,7 @@ import zipfile
|
|
|
4
4
|
from io import BytesIO
|
|
5
5
|
from typing import Union, Literal
|
|
6
6
|
|
|
7
|
-
from .. import filesystem
|
|
8
|
-
from ..print_api import print_api
|
|
7
|
+
from .. import filesystem, print_api
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
def is_zip_zipfile(file_object: Union[str, bytes]) -> bool:
|
|
@@ -42,7 +41,7 @@ def is_zip_magic_number(file_path: str) -> bool:
|
|
|
42
41
|
Each file within the ZIP archive starts with this signature.
|
|
43
42
|
50 4B 05 06: This is the end of central directory record signature.
|
|
44
43
|
It's found at the end of a ZIP file and is essential for identifying the structure of the ZIP archive,
|
|
45
|
-
especially in cases where the file is split or is a
|
|
44
|
+
especially in cases where the file is split or is a multipart archive.
|
|
46
45
|
50 4B 07 08: This signature is used for spanned ZIP archives (also known as split or multi-volume ZIP archives).
|
|
47
46
|
It's found in the end of central directory locator for ZIP files that are split across multiple volumes.
|
|
48
47
|
"""
|
|
@@ -93,7 +92,7 @@ def extract_archive_with_zipfile(
|
|
|
93
92
|
filesystem.get_file_directory(archive_path) + os.sep +
|
|
94
93
|
filesystem.get_file_name_without_extension(archive_path))
|
|
95
94
|
|
|
96
|
-
print_api(f'Extracting to directory: {extract_directory}', **print_kwargs)
|
|
95
|
+
print_api.print_api(f'Extracting to directory: {extract_directory}', **print_kwargs)
|
|
97
96
|
|
|
98
97
|
# initiating the archived file path as 'zipfile.ZipFile' object.
|
|
99
98
|
with zipfile.ZipFile(archive_path) as zip_object:
|
|
@@ -113,7 +112,7 @@ def extract_archive_with_zipfile(
|
|
|
113
112
|
# Cut the first directory from the filename.
|
|
114
113
|
zip_info.filename = zip_info.filename.split('/', maxsplit=1)[1]
|
|
115
114
|
|
|
116
|
-
print_api(f'Extracting: {zip_info.filename}', **print_kwargs)
|
|
115
|
+
print_api.print_api(f'Extracting: {zip_info.filename}', **print_kwargs)
|
|
117
116
|
|
|
118
117
|
# Extract current file from the archive using 'zip_info' of the current file with 'filename' that we
|
|
119
118
|
# updated under specified parameters to specified directory.
|
|
@@ -126,7 +125,7 @@ def extract_archive_with_zipfile(
|
|
|
126
125
|
date_time = time.mktime(zip_info.date_time + (0, 0, -1))
|
|
127
126
|
# Using 'os' library, changed the datetime of the file to the object created in previous step.
|
|
128
127
|
os.utime(extracted_file_path, (date_time, date_time))
|
|
129
|
-
print_api('Extraction done.', color="green", **print_kwargs)
|
|
128
|
+
print_api.print_api('Extraction done.', color="green", **print_kwargs)
|
|
130
129
|
|
|
131
130
|
return extract_directory
|
|
132
131
|
|
atomicshop/basics/strings.py
CHANGED
|
@@ -4,7 +4,7 @@ from pathlib import Path
|
|
|
4
4
|
import argparse
|
|
5
5
|
|
|
6
6
|
from . import lists
|
|
7
|
-
from ..
|
|
7
|
+
from .. import print_api
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def get_nth_character_from_start(input_string: str, nth: int):
|
|
@@ -537,7 +537,7 @@ def replace_string_in_file(
|
|
|
537
537
|
file.writelines(lines)
|
|
538
538
|
|
|
539
539
|
# Output the relevant line numbers
|
|
540
|
-
print_api(f"Target string found on the following lines: {changed_lines}", **(print_kwargs or {}))
|
|
540
|
+
print_api.print_api(f"Target string found on the following lines: {changed_lines}", **(print_kwargs or {}))
|
|
541
541
|
return changed_lines
|
|
542
542
|
|
|
543
543
|
|
atomicshop/dns.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
|
|
3
|
+
# noinspection PyPackageRequirements
|
|
3
4
|
import dns.resolver
|
|
4
5
|
|
|
5
|
-
from .
|
|
6
|
+
from . import print_api
|
|
6
7
|
from .permissions import permissions
|
|
7
8
|
from .wrappers.pywin32w.wmis import win32networkadapter
|
|
8
9
|
from .wrappers.winregw import winreg_network
|
|
@@ -57,10 +58,10 @@ def resolve_dns_localhost(domain_name: str, dns_servers_list: list = None, print
|
|
|
57
58
|
# Get only the first entry of the list of IPs [0]
|
|
58
59
|
connection_ip = function_server_address[0].to_text()
|
|
59
60
|
message = f"Resolved to [{connection_ip}]"
|
|
60
|
-
print_api(message, **print_kwargs)
|
|
61
|
+
print_api.print_api(message, **print_kwargs)
|
|
61
62
|
except dns.resolver.NXDOMAIN:
|
|
62
63
|
message = f"Domain {domain_name} doesn't exist - Couldn't resolve with {dns_servers_list}."
|
|
63
|
-
print_api(message, **print_kwargs, error_type=True, logger_method='error')
|
|
64
|
+
print_api.print_api(message, **print_kwargs, error_type=True, logger_method='error')
|
|
64
65
|
pass
|
|
65
66
|
|
|
66
67
|
return connection_ip
|
|
@@ -137,12 +138,13 @@ def default_dns_gateway_main() -> int:
|
|
|
137
138
|
args = argparse_obj.parse_args()
|
|
138
139
|
|
|
139
140
|
if (args.set or args.dynamic) and not (args.connection_name or args.connection_default):
|
|
140
|
-
print_api(
|
|
141
|
+
print_api.print_api(
|
|
142
|
+
"Please provide the connection name [-cn] or use the default connection [-cd].", color='red')
|
|
141
143
|
return 1
|
|
142
144
|
|
|
143
145
|
if args.set or args.dynamic:
|
|
144
146
|
if not permissions.is_admin():
|
|
145
|
-
print_api("You need to run this script as an administrator", color='red')
|
|
147
|
+
print_api.print_api("You need to run this script as an administrator", color='red')
|
|
146
148
|
return 1
|
|
147
149
|
|
|
148
150
|
if args.get:
|
|
@@ -152,7 +154,7 @@ def default_dns_gateway_main() -> int:
|
|
|
152
154
|
is_dynamic_string = 'Dynamic'
|
|
153
155
|
else:
|
|
154
156
|
is_dynamic_string = 'Static'
|
|
155
|
-
print_api(f'DNS Gateway: {is_dynamic_string} - {dns_servers}', color='blue')
|
|
157
|
+
print_api.print_api(f'DNS Gateway: {is_dynamic_string} - {dns_servers}', color='blue')
|
|
156
158
|
elif args.set:
|
|
157
159
|
# dns_servers_list: list = args.dns_servers.split(',')
|
|
158
160
|
set_connection_dns_gateway_static(
|
atomicshop/file_io/file_io.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Union
|
|
2
2
|
import functools
|
|
3
3
|
|
|
4
|
-
from ..
|
|
4
|
+
from .. import print_api
|
|
5
5
|
from ..inspect_wrapper import get_target_function_default_args_and_combine_with_current
|
|
6
6
|
|
|
7
7
|
|
|
@@ -19,7 +19,7 @@ def write_file_decorator(function_name):
|
|
|
19
19
|
# args, kwargs = put_args_to_kwargs(function_name, *args, **kwargs)
|
|
20
20
|
args, kwargs = get_target_function_default_args_and_combine_with_current(function_name, *args, **kwargs)
|
|
21
21
|
|
|
22
|
-
print_api(message=f"Writing file: {kwargs['file_path']}", **kwargs)
|
|
22
|
+
print_api.print_api(message=f"Writing file: {kwargs['file_path']}", **kwargs)
|
|
23
23
|
|
|
24
24
|
enable_long_file_path = kwargs.get('enable_long_file_path', False)
|
|
25
25
|
if enable_long_file_path:
|
|
@@ -41,7 +41,7 @@ def write_file_decorator(function_name):
|
|
|
41
41
|
except FileExistsError:
|
|
42
42
|
message = f"Can't write file: {kwargs['file_path']}\n" \
|
|
43
43
|
f"File exists, you should enable force/overwrite mode."
|
|
44
|
-
print_api(message, error_type=True, logger_method='critical', **kwargs)
|
|
44
|
+
print_api.print_api(message, error_type=True, logger_method='critical', **kwargs)
|
|
45
45
|
|
|
46
46
|
return wrapper_write_file_decorator
|
|
47
47
|
|
|
@@ -56,7 +56,7 @@ def read_file_decorator(function_name):
|
|
|
56
56
|
continue_loop: bool = True
|
|
57
57
|
while continue_loop:
|
|
58
58
|
try:
|
|
59
|
-
print_api(message=f"Reading file: {kwargs['file_path']}", **kwargs)
|
|
59
|
+
print_api.print_api(message=f"Reading file: {kwargs['file_path']}", **kwargs)
|
|
60
60
|
with open(kwargs['file_path'], kwargs['file_mode'], encoding=kwargs['encoding']) as input_file:
|
|
61
61
|
# Pass the 'output_file' object to kwargs that will pass the object to the executing function.
|
|
62
62
|
kwargs['file_object'] = input_file
|
|
@@ -64,19 +64,19 @@ def read_file_decorator(function_name):
|
|
|
64
64
|
return function_name(**kwargs)
|
|
65
65
|
except FileNotFoundError:
|
|
66
66
|
message = f"File doesn't exist: {kwargs['file_path']}"
|
|
67
|
-
print_api(message, error_type=True, logger_method='critical', **kwargs)
|
|
67
|
+
print_api.print_api(message, error_type=True, logger_method='critical', **kwargs)
|
|
68
68
|
raise
|
|
69
69
|
except UnicodeDecodeError as exception_object:
|
|
70
70
|
if kwargs["encoding"] != 'utf-8':
|
|
71
71
|
message = f'File decode error, current encoding: {kwargs["encoding"]}. Will try "utf-8".'
|
|
72
|
-
print_api(message, logger_method='error', **kwargs)
|
|
72
|
+
print_api.print_api(message, logger_method='error', **kwargs)
|
|
73
73
|
kwargs["encoding"] = 'utf-8'
|
|
74
74
|
pass
|
|
75
75
|
continue
|
|
76
76
|
else:
|
|
77
77
|
message = f'File decode error.\n' \
|
|
78
78
|
f'{exception_object}'
|
|
79
|
-
print_api(message, merror_type=True, logger_method='critical', **kwargs)
|
|
79
|
+
print_api.print_api(message, merror_type=True, logger_method='critical', **kwargs)
|
|
80
80
|
continue_loop = False
|
|
81
81
|
|
|
82
82
|
return wrapper_read_file_decorator
|
atomicshop/filesystem.py
CHANGED
|
@@ -12,10 +12,9 @@ import tempfile
|
|
|
12
12
|
# noinspection PyPackageRequirements
|
|
13
13
|
import psutil
|
|
14
14
|
|
|
15
|
-
from .print_api import print_api, print_status_of_list
|
|
16
15
|
from .basics import strings, list_of_dicts, list_of_classes
|
|
17
16
|
from .file_io import file_io
|
|
18
|
-
from . import hashing, datetimes
|
|
17
|
+
from . import hashing, datetimes, print_api
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
WINDOWS_DIRECTORY_SPECIAL_CHARACTERS = ['<', '>', ':', '"', '/', '\\', '|', '?', '*']
|
|
@@ -280,12 +279,12 @@ def remove_file(file_path: str, **kwargs) -> bool:
|
|
|
280
279
|
|
|
281
280
|
try:
|
|
282
281
|
os.remove(file_path)
|
|
283
|
-
print_api(f'File Removed: {file_path}')
|
|
282
|
+
print_api.print_api(f'File Removed: {file_path}')
|
|
284
283
|
return True
|
|
285
284
|
# Since the file doesn't exist, we actually don't care, since we want to remove it anyway.
|
|
286
285
|
except FileNotFoundError:
|
|
287
286
|
message = f'File Removal Failed, File non-existent: {file_path}'
|
|
288
|
-
print_api(message, error_type=True, logger_method='critical', **kwargs)
|
|
287
|
+
print_api.print_api(message, error_type=True, logger_method='critical', **kwargs)
|
|
289
288
|
return False
|
|
290
289
|
|
|
291
290
|
|
|
@@ -321,12 +320,12 @@ def remove_directory(directory_path: str, force_readonly: bool = False, print_kw
|
|
|
321
320
|
shutil.rmtree(directory_path, onerror=remove_readonly)
|
|
322
321
|
else:
|
|
323
322
|
shutil.rmtree(directory_path)
|
|
324
|
-
print_api(f'Directory Removed: {directory_path}', **print_kwargs)
|
|
323
|
+
print_api.print_api(f'Directory Removed: {directory_path}', **print_kwargs)
|
|
325
324
|
return True
|
|
326
325
|
# Since the directory doesn't exist, we actually don't care, since we want to remove it anyway.
|
|
327
326
|
except FileNotFoundError:
|
|
328
327
|
message = f'Directory Removal Failed, Directory non-existent: {directory_path}'
|
|
329
|
-
print_api(message, error_type=True, logger_method='critical', **print_kwargs)
|
|
328
|
+
print_api.print_api(message, error_type=True, logger_method='critical', **print_kwargs)
|
|
330
329
|
return False
|
|
331
330
|
|
|
332
331
|
|
|
@@ -878,7 +877,7 @@ def get_paths_from_directory(
|
|
|
878
877
|
prefix_string = 'Reading File: '
|
|
879
878
|
|
|
880
879
|
for file_index, file_path in enumerate(object_list):
|
|
881
|
-
print_status_of_list(
|
|
880
|
+
print_api.print_status_of_list(
|
|
882
881
|
list_instance=object_list, prefix_string=prefix_string, current_state=(file_index + 1))
|
|
883
882
|
|
|
884
883
|
# If 'add_binary' was passed.
|
atomicshop/http_parse.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# v1.0.1 - 26.03.2023 23:40
|
|
2
1
|
from http.server import BaseHTTPRequestHandler
|
|
3
2
|
from http.client import HTTPResponse
|
|
4
3
|
import http
|
|
5
4
|
from io import BytesIO
|
|
5
|
+
import socket
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class HTTPRequestParse(BaseHTTPRequestHandler):
|
|
@@ -49,41 +49,27 @@ class HTTPRequestParse(BaseHTTPRequestHandler):
|
|
|
49
49
|
client_message.request_raw_decoded = request_decoded
|
|
50
50
|
"""
|
|
51
51
|
|
|
52
|
+
# noinspection PyMissingConstructor
|
|
52
53
|
def __init__(self, request_text):
|
|
53
|
-
self.
|
|
54
|
-
self.raw_requestline = self.rfile.readline()
|
|
55
|
-
self.error_code = self.error_message = None
|
|
56
|
-
self.parse_request()
|
|
54
|
+
self.request_text = request_text
|
|
57
55
|
|
|
58
|
-
#
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
# noinspection PyTypeChecker
|
|
57
|
+
self.rfile = None
|
|
58
|
+
self.raw_requestline = None
|
|
59
|
+
self.error_code = None
|
|
60
|
+
self.error_message = None
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
# "self.headers.get('Content-Length')" returns number in string format, "int" converts it to integer
|
|
67
|
-
self.content_length = int(self.headers.get('Content-Length'))
|
|
68
|
-
self.body = self.rfile.read(self.content_length)
|
|
69
|
-
else:
|
|
70
|
-
self.content_length = None
|
|
71
|
-
self.body = None
|
|
72
|
-
except Exception:
|
|
73
|
-
self.content_length = None
|
|
74
|
-
self.body = None
|
|
75
|
-
pass
|
|
76
|
-
|
|
77
|
-
# Examples:
|
|
78
|
-
# Getting path: self.path
|
|
79
|
-
# Getting Request Version: self.request_version
|
|
80
|
-
# Getting specific header: self.headers['host']
|
|
62
|
+
self.content_length = None
|
|
63
|
+
self.body = None
|
|
64
|
+
# noinspection PyTypeChecker
|
|
65
|
+
self.path = None
|
|
81
66
|
|
|
67
|
+
# noinspection PyMethodOverriding
|
|
82
68
|
def send_error(self, code, message):
|
|
83
69
|
self.error_code = code
|
|
84
70
|
self.error_message = message
|
|
85
71
|
|
|
86
|
-
def
|
|
72
|
+
def parse(self):
|
|
87
73
|
"""
|
|
88
74
|
Function to check if parsed object is HTTP request or not.
|
|
89
75
|
'reason' will be populated with parsing status and errors.
|
|
@@ -117,10 +103,27 @@ class HTTPRequestParse(BaseHTTPRequestHandler):
|
|
|
117
103
|
|
|
118
104
|
client_message.request_raw_decoded = request_decoded
|
|
119
105
|
"""
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
106
|
+
|
|
107
|
+
error: str = str()
|
|
108
|
+
info: str = str()
|
|
109
|
+
|
|
110
|
+
self.rfile = BytesIO(self.request_text)
|
|
111
|
+
self.raw_requestline = self.rfile.readline()
|
|
112
|
+
self.error_code = self.error_message = None
|
|
113
|
+
self.parse_request()
|
|
114
|
+
|
|
115
|
+
# Before checking for body, we need to make sure that ".headers" property exists, if not, return empty values.
|
|
116
|
+
if hasattr(self, 'headers'):
|
|
117
|
+
# The "body" of request is in the 'Content-Length' key. If it exists in "headers" - get the body
|
|
118
|
+
if 'Content-Length' in self.headers.keys():
|
|
119
|
+
# "self.headers.get('Content-Length')" returns number in string format, "int" converts it to integer
|
|
120
|
+
self.content_length = int(self.headers.get('Content-Length'))
|
|
121
|
+
self.body = self.rfile.read(self.content_length)
|
|
122
|
+
|
|
123
|
+
# Examples:
|
|
124
|
+
# Getting path: self.path
|
|
125
|
+
# Getting Request Version: self.request_version
|
|
126
|
+
# Getting specific header: self.headers['host']
|
|
124
127
|
|
|
125
128
|
# If there's any error in HTTP parsing
|
|
126
129
|
if self.error_message:
|
|
@@ -129,65 +132,82 @@ class HTTPRequestParse(BaseHTTPRequestHandler):
|
|
|
129
132
|
# If it's 'Bad request' this is not HTTP request, so we can
|
|
130
133
|
# continue the execution and parse the code as NON-HTTP Request.
|
|
131
134
|
# Currently, seen 'Bad request syntax' and 'Bad request version'.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
error: False
|
|
135
|
+
error = f"HTTP Request Parsing: Not HTTP request: {self.error_message}"
|
|
136
|
+
is_http = False
|
|
135
137
|
else:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
error = True
|
|
138
|
+
error = f"HTTP Request Parsing: HTTP Request with Script Undocumented ERROR: {self.error_message}"
|
|
139
|
+
is_http = False
|
|
139
140
|
# If there's no error at all in HTTP Parsing, then it's fine HTTP Request
|
|
140
141
|
else:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
error = False
|
|
142
|
+
is_http = True
|
|
143
|
+
info = "HTTP Request Parsing: Valid HTTP request"
|
|
144
144
|
|
|
145
|
-
return
|
|
145
|
+
return self, is_http, info, error
|
|
146
146
|
|
|
147
147
|
|
|
148
148
|
class FakeSocket:
|
|
149
149
|
"""
|
|
150
|
-
FakeSocket
|
|
150
|
+
FakeSocket mimics a socket object for parsing HTTP responses.
|
|
151
151
|
"""
|
|
152
152
|
def __init__(self, response_bytes):
|
|
153
153
|
self._file = BytesIO(response_bytes)
|
|
154
154
|
|
|
155
|
-
def makefile(self,
|
|
155
|
+
def makefile(self, mode='rb', buffering=-1) -> BytesIO:
|
|
156
|
+
"""
|
|
157
|
+
Mimics the socket's makefile method, returning the BytesIO object.
|
|
158
|
+
"""
|
|
156
159
|
return self._file
|
|
157
160
|
|
|
161
|
+
def fileno(self) -> int:
|
|
162
|
+
"""
|
|
163
|
+
Provide a dummy file descriptor, as some code might call this.
|
|
164
|
+
"""
|
|
165
|
+
raise OSError("File descriptor not available in FakeSocket")
|
|
166
|
+
|
|
158
167
|
|
|
159
168
|
class HTTPResponseParse:
|
|
160
169
|
def __init__(self, response_raw_bytes: bytes):
|
|
161
|
-
self.error = None
|
|
162
170
|
self.response_raw_bytes: bytes = response_raw_bytes
|
|
171
|
+
|
|
172
|
+
self.error = None
|
|
173
|
+
self.source = None
|
|
174
|
+
self.response_raw_decoded = None
|
|
175
|
+
self.is_http: bool = False
|
|
176
|
+
|
|
177
|
+
def parse(self):
|
|
163
178
|
# Assigning FakeSocket with response_raw_bytes.
|
|
164
179
|
self.source = FakeSocket(self.response_raw_bytes)
|
|
165
180
|
|
|
166
181
|
# Initializing HTTPResponse class with the FakeSocket with response_raw_bytes as input.
|
|
182
|
+
# noinspection PyTypeChecker
|
|
167
183
|
self.response_raw_decoded = HTTPResponse(self.source)
|
|
168
184
|
|
|
169
185
|
# Try to parse HTTP Response.
|
|
170
186
|
try:
|
|
171
187
|
self.response_raw_decoded.begin()
|
|
188
|
+
self.is_http = True
|
|
172
189
|
# If there were problems with the status line.
|
|
173
190
|
except http.client.BadStatusLine:
|
|
174
191
|
self.error = "HTTP Response Parsing: Not a valid HTTP Response: Bad Status Line."
|
|
175
|
-
|
|
192
|
+
self.is_http = False
|
|
176
193
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
#
|
|
190
|
-
|
|
194
|
+
header_exists: bool = False
|
|
195
|
+
if (self.response_raw_decoded is not None and hasattr(self.response_raw_decoded, 'headers') and
|
|
196
|
+
self.response_raw_decoded.headers is not None):
|
|
197
|
+
header_exists = True
|
|
198
|
+
|
|
199
|
+
if header_exists and self.response_raw_decoded.headers.defects:
|
|
200
|
+
self.error = f"HTTP Response Parsing: Not a valid HTTP Response: Some defects in headers: " \
|
|
201
|
+
f"{self.response_raw_decoded.headers.defects}"
|
|
202
|
+
self.is_http = False
|
|
203
|
+
|
|
204
|
+
if self.is_http:
|
|
205
|
+
# Before checking for body, we need to make sure that ".headers" property exists,
|
|
206
|
+
# if not, return empty values.
|
|
207
|
+
self.response_raw_decoded.content_length = None
|
|
208
|
+
self.response_raw_decoded.body = None
|
|
209
|
+
if header_exists and 'Content-Length' in self.response_raw_decoded.headers.keys():
|
|
210
|
+
# The "body" of response is in the 'Content-Length' key. If it exists in "headers" - get the body.
|
|
191
211
|
# "self.response_raw_decoded.headers.get('Content-Length')" returns number in string format,
|
|
192
212
|
# "int" converts it to integer.
|
|
193
213
|
self.response_raw_decoded.content_length = int(self.response_raw_decoded.headers.get('Content-Length'))
|
|
@@ -195,10 +215,5 @@ class HTTPResponseParse:
|
|
|
195
215
|
# of the response that we received.
|
|
196
216
|
# self.response_raw_bytes[-23:]
|
|
197
217
|
self.response_raw_decoded.body = self.response_raw_bytes[-self.response_raw_decoded.content_length:]
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
self.response_raw_decoded.body = None
|
|
201
|
-
except Exception:
|
|
202
|
-
self.response_raw_decoded.content_length = None
|
|
203
|
-
self.response_raw_decoded.body = None
|
|
204
|
-
pass
|
|
218
|
+
|
|
219
|
+
return self.response_raw_decoded, self.is_http, self.error
|