stoobly-agent 1.9.2__py3-none-any.whl → 1.9.4__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.
- stoobly_agent/__init__.py +1 -1
- stoobly_agent/app/cli/scaffold/app_config.py +13 -1
- stoobly_agent/app/cli/scaffold/app_create_command.py +8 -0
- stoobly_agent/app/cli/scaffold/constants.py +1 -0
- stoobly_agent/app/cli/scaffold/docker/service/configure_gateway.py +10 -4
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/.docker-compose.base.yml +0 -3
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/.docker-compose.mock.yml +1 -1
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/.docker-compose.record.yml +1 -1
- stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/init +5 -1
- stoobly_agent/app/cli/scaffold_cli.py +67 -6
- stoobly_agent/app/models/adapters/joined_request_adapter.py +38 -6
- stoobly_agent/app/models/helpers/apply.py +10 -9
- stoobly_agent/cli.py +2 -2
- stoobly_agent/test/app/models/adapters/joined_rquest_adapter_test.py +38 -0
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
- {stoobly_agent-1.9.2.dist-info → stoobly_agent-1.9.4.dist-info}/METADATA +1 -1
- {stoobly_agent-1.9.2.dist-info → stoobly_agent-1.9.4.dist-info}/RECORD +20 -19
- {stoobly_agent-1.9.2.dist-info → stoobly_agent-1.9.4.dist-info}/LICENSE +0 -0
- {stoobly_agent-1.9.2.dist-info → stoobly_agent-1.9.4.dist-info}/WHEEL +0 -0
- {stoobly_agent-1.9.2.dist-info → stoobly_agent-1.9.4.dist-info}/entry_points.txt +0 -0
stoobly_agent/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
COMMAND = 'stoobly-agent'
|
2
|
-
VERSION = '1.9.
|
2
|
+
VERSION = '1.9.4'
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from .config import Config
|
2
|
-
from .constants import APP_NAME_ENV, APP_NETWORK_ENV
|
2
|
+
from .constants import APP_NAME_ENV, APP_NETWORK_ENV, APP_UI_PORT_ENV
|
3
3
|
|
4
4
|
class AppConfig(Config):
|
5
5
|
|
@@ -27,11 +27,20 @@ class AppConfig(Config):
|
|
27
27
|
def network(self, v):
|
28
28
|
self.__network = v
|
29
29
|
|
30
|
+
@property
|
31
|
+
def ui_port(self):
|
32
|
+
return self.__ui_port
|
33
|
+
|
34
|
+
@ui_port.setter
|
35
|
+
def ui_port(self, v):
|
36
|
+
self.__ui_port = v
|
37
|
+
|
30
38
|
def load(self, config = None):
|
31
39
|
config = config or self.read()
|
32
40
|
|
33
41
|
self.name = config.get(APP_NAME_ENV)
|
34
42
|
self.network = config.get(APP_NETWORK_ENV)
|
43
|
+
self.ui_port = config.get(APP_UI_PORT_ENV)
|
35
44
|
|
36
45
|
def write(self):
|
37
46
|
config = {}
|
@@ -42,4 +51,7 @@ class AppConfig(Config):
|
|
42
51
|
if self.network:
|
43
52
|
config[APP_NETWORK_ENV] = self.network
|
44
53
|
|
54
|
+
if self.ui_port:
|
55
|
+
config[APP_UI_PORT_ENV] = self.ui_port
|
56
|
+
|
45
57
|
super().write(config)
|
@@ -9,6 +9,7 @@ from .app_command import AppCommand
|
|
9
9
|
class AppCreateOptions(TypedDict):
|
10
10
|
name: str
|
11
11
|
network: str
|
12
|
+
ui_port: int
|
12
13
|
|
13
14
|
class AppCreateCommand(AppCommand):
|
14
15
|
|
@@ -21,6 +22,9 @@ class AppCreateCommand(AppCommand):
|
|
21
22
|
if kwargs.get('network'):
|
22
23
|
self.app_config.network = kwargs['network']
|
23
24
|
|
25
|
+
if kwargs.get('ui_port'):
|
26
|
+
self.app_config.ui_port = kwargs['ui_port']
|
27
|
+
|
24
28
|
@property
|
25
29
|
def app_name(self):
|
26
30
|
return self.app_config.name
|
@@ -29,6 +33,10 @@ class AppCreateCommand(AppCommand):
|
|
29
33
|
def app_network(self):
|
30
34
|
return self.app_config.network
|
31
35
|
|
36
|
+
@property
|
37
|
+
def app_ui_port(self):
|
38
|
+
return self.app_config.ui_port
|
39
|
+
|
32
40
|
def build(self):
|
33
41
|
dest = self.scaffold_namespace_path
|
34
42
|
|
@@ -51,6 +51,8 @@ def __with_networks(config: dict, hostnames: List[str]):
|
|
51
51
|
}
|
52
52
|
|
53
53
|
def __with_traefik_config(service_paths: str, compose: dict, app_dir_path: str):
|
54
|
+
config_dest = '/etc/traefik/traefik.yml'
|
55
|
+
|
54
56
|
if not compose['volumes']:
|
55
57
|
compose['volumes'] = []
|
56
58
|
|
@@ -68,11 +70,15 @@ def __with_traefik_config(service_paths: str, compose: dict, app_dir_path: str):
|
|
68
70
|
'providers': {
|
69
71
|
'docker': {
|
70
72
|
'exposedByDefault': False
|
73
|
+
},
|
74
|
+
'file': {
|
75
|
+
'fileName': config_dest,
|
76
|
+
'watch': False
|
71
77
|
}
|
72
78
|
},
|
73
79
|
'tls': {
|
74
|
-
'certificates': certificates
|
75
|
-
}
|
80
|
+
'certificates': certificates,
|
81
|
+
},
|
76
82
|
}
|
77
83
|
|
78
84
|
for path in service_paths:
|
@@ -88,7 +94,7 @@ def __with_traefik_config(service_paths: str, compose: dict, app_dir_path: str):
|
|
88
94
|
if config.scheme == 'https':
|
89
95
|
certificates.append({
|
90
96
|
'certFile': f"/certs/{config.hostname}.crt",
|
91
|
-
'keyFile': f"/certs/{config.hostname}.key"
|
97
|
+
'keyFile': f"/certs/{config.hostname}.key",
|
92
98
|
})
|
93
99
|
|
94
100
|
# Create traefik.yml in .stoobly/tmp
|
@@ -102,7 +108,7 @@ def __with_traefik_config(service_paths: str, compose: dict, app_dir_path: str):
|
|
102
108
|
fp.write(yaml.dump(traefik_config))
|
103
109
|
|
104
110
|
compose['volumes'].append(
|
105
|
-
f"{os.path.join(APP_DIR, traefik_template_relative_path)}
|
111
|
+
f"{os.path.join(APP_DIR, traefik_template_relative_path)}:{config_dest}:ro"
|
106
112
|
)
|
107
113
|
|
108
114
|
def __find_hosts(service_paths):
|
@@ -2,4 +2,8 @@
|
|
2
2
|
|
3
3
|
# Add custom initialization here
|
4
4
|
|
5
|
-
app_path=$1 # Path to application source files
|
5
|
+
app_path=$1 # Path to application source files
|
6
|
+
|
7
|
+
# For example, the below commands copies files inside a dist folder into the public folder.
|
8
|
+
# The public folder is used when no recorded mocks are found.
|
9
|
+
#cp -r $app_path/dist/. public
|
@@ -6,6 +6,7 @@ import sys
|
|
6
6
|
|
7
7
|
from io import TextIOWrapper
|
8
8
|
from typing import List
|
9
|
+
from urllib.parse import urlparse
|
9
10
|
|
10
11
|
from stoobly_agent.app.cli.helpers.certificate_authority import CertificateAuthority
|
11
12
|
from stoobly_agent.app.cli.helpers.shell import exec_stream
|
@@ -88,6 +89,7 @@ def hostname(ctx):
|
|
88
89
|
@click.option('--app-dir-path', default=current_working_dir, help='Path to create the app scaffold.')
|
89
90
|
@click.option('--force', is_flag=True, help='Overwrite maintained scaffolded app files.')
|
90
91
|
@click.option('--network', help='App default network name. Defaults to app name.')
|
92
|
+
@click.option('--ui-port', default=4200, type=click.IntRange(1, 65535), help='UI service port.')
|
91
93
|
@click.argument('app_name')
|
92
94
|
def create(**kwargs):
|
93
95
|
__validate_app_dir(kwargs['app_dir_path'])
|
@@ -148,10 +150,10 @@ def create(**kwargs):
|
|
148
150
|
sys.exit(1)
|
149
151
|
|
150
152
|
if kwargs.get('hostname'):
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
153
|
+
__validate_hostname(kwargs.get('hostname'))
|
154
|
+
|
155
|
+
if kwargs.get("proxy_mode"):
|
156
|
+
__validate_proxy_mode(kwargs.get("proxy_mode"))
|
155
157
|
|
156
158
|
app = App(kwargs['app_dir_path'], DOCKER_NAMESPACE)
|
157
159
|
|
@@ -216,6 +218,13 @@ def delete(**kwargs):
|
|
216
218
|
@click.option('--port', type=click.IntRange(1, 65535), help='Service port.')
|
217
219
|
@click.option('--priority', default=5, type=click.FloatRange(1.0, 9.0), help='Determines the service run order. Lower values run first.')
|
218
220
|
@click.option('--scheme', type=click.Choice(['http', 'https']), help='Defaults to https if hostname is set.')
|
221
|
+
@click.option('--name', type=click.STRING, help='New name of the service to update to.')
|
222
|
+
@click.option('--proxy-mode', help='''
|
223
|
+
Proxy mode can be "regular", "transparent", "socks5",
|
224
|
+
"reverse:SPEC", or "upstream:SPEC". For reverse and
|
225
|
+
upstream proxy modes, SPEC is host specification in
|
226
|
+
the form of "http[s]://host[:port]".
|
227
|
+
''')
|
219
228
|
@click.argument('service_name')
|
220
229
|
def update(**kwargs):
|
221
230
|
app = App(kwargs['app_dir_path'], DOCKER_NAMESPACE)
|
@@ -228,7 +237,20 @@ def update(**kwargs):
|
|
228
237
|
service_config = ServiceConfig(service.dir_path)
|
229
238
|
|
230
239
|
if kwargs['hostname']:
|
231
|
-
|
240
|
+
__validate_hostname(kwargs['hostname'])
|
241
|
+
|
242
|
+
old_hostname = service_config.hostname
|
243
|
+
|
244
|
+
if old_hostname != kwargs['hostname']:
|
245
|
+
service_config.hostname = kwargs['hostname']
|
246
|
+
|
247
|
+
# If this is the default proxy_mode and the origin matches the original hostname, assume it is safe to update with the new hostname
|
248
|
+
if service_config.proxy_mode.startswith("reverse:"):
|
249
|
+
old_origin = service_config.proxy_mode.split("reverse:")[1]
|
250
|
+
parsed_origin_url = urlparse(old_origin)
|
251
|
+
|
252
|
+
if old_hostname == parsed_origin_url.hostname:
|
253
|
+
service_config.proxy_mode = service_config.proxy_mode.replace(old_hostname, service_config.hostname)
|
232
254
|
|
233
255
|
if kwargs['priority']:
|
234
256
|
service_config.priority = kwargs['priority']
|
@@ -239,6 +261,10 @@ def update(**kwargs):
|
|
239
261
|
if kwargs['scheme']:
|
240
262
|
service_config.scheme = kwargs['scheme']
|
241
263
|
|
264
|
+
if kwargs['proxy_mode']:
|
265
|
+
__validate_proxy_mode(kwargs['proxy_mode'])
|
266
|
+
service_config.proxy_mode = kwargs['proxy_mode']
|
267
|
+
|
242
268
|
service_config.write()
|
243
269
|
|
244
270
|
@workflow.command(
|
@@ -759,7 +785,42 @@ def __validate_app_dir(app_dir_path):
|
|
759
785
|
|
760
786
|
def __validate_service_dir(service_dir_path):
|
761
787
|
if not os.path.exists(service_dir_path):
|
762
|
-
print(f"Error: {service_dir_path} does not exist, please scaffold this service", file=sys.stderr)
|
788
|
+
print(f"Error: '{service_dir_path}' does not exist, please scaffold this service", file=sys.stderr)
|
789
|
+
sys.exit(1)
|
790
|
+
|
791
|
+
def __validate_proxy_mode(proxy_mode: str) -> None:
|
792
|
+
valid_exact_matches = {
|
793
|
+
"regular": None,
|
794
|
+
"transparent": None,
|
795
|
+
"socks5": None,
|
796
|
+
}
|
797
|
+
|
798
|
+
valid_prefixes = {
|
799
|
+
"reverse": None,
|
800
|
+
"upstream": None
|
801
|
+
}
|
802
|
+
|
803
|
+
if proxy_mode in valid_exact_matches:
|
804
|
+
return
|
805
|
+
|
806
|
+
split_str = proxy_mode.split(":", 1)
|
807
|
+
if len(split_str) != 2:
|
808
|
+
print(f"Error: {proxy_mode} is invalid.", file=sys.stderr)
|
809
|
+
sys.exit(1)
|
810
|
+
|
811
|
+
prefix = split_str[0]
|
812
|
+
spec = split_str[1]
|
813
|
+
|
814
|
+
if prefix not in valid_prefixes:
|
815
|
+
print(f"Error: {proxy_mode} is invalid.", file=sys.stderr)
|
816
|
+
sys.exit(1)
|
817
|
+
|
818
|
+
# TODO: validate SPEC
|
819
|
+
|
820
|
+
def __validate_hostname(hostname: str) -> None:
|
821
|
+
hostname_regex = re.compile(r'^[a-zA-Z0-9.-]+$')
|
822
|
+
if not re.search(hostname_regex, hostname):
|
823
|
+
print(f"Error: {hostname} is invalid.", file=sys.stderr)
|
763
824
|
sys.exit(1)
|
764
825
|
|
765
826
|
def __workflow_create(app, **kwargs):
|
@@ -17,9 +17,7 @@ class JoinedRequestAdapter():
|
|
17
17
|
if isinstance(payloads_delimitter, str):
|
18
18
|
payloads_delimitter = payloads_delimitter.encode()
|
19
19
|
|
20
|
-
self.__split_joined_request_string =
|
21
|
-
if len(self.__split_joined_request_string) != 2:
|
22
|
-
self.__split_joined_request_string = joined_request_string.split(payloads_delimitter.replace(b"\n", b"\r\n"))
|
20
|
+
self.__split_joined_request_string = self.raw_request_split(joined_request_string, payloads_delimitter)
|
23
21
|
|
24
22
|
if len(self.__split_joined_request_string) != 2:
|
25
23
|
raise ValueError(f"Could not split by {payloads_delimitter}")
|
@@ -47,7 +45,7 @@ class JoinedRequestAdapter():
|
|
47
45
|
request_string = RequestString(None)
|
48
46
|
|
49
47
|
delimitter = RequestStringCLRF
|
50
|
-
request_string_toks = self.__split_joined_request_string[0]
|
48
|
+
request_string_toks = self.repaired_string_toks(self.__split_joined_request_string[0], delimitter)
|
51
49
|
request_string.set(self.raw_request_string or delimitter.join(request_string_toks[1:]))
|
52
50
|
request_string.control = request_string_toks[0]
|
53
51
|
|
@@ -57,7 +55,7 @@ class JoinedRequestAdapter():
|
|
57
55
|
response_string = ResponseString(None, None)
|
58
56
|
|
59
57
|
delimitter = ResponseStringCLRF
|
60
|
-
response_string_toks = self.__split_joined_request_string[1]
|
58
|
+
response_string_toks = self.repaired_string_toks(self.__split_joined_request_string[1], delimitter)
|
61
59
|
response_string.set(self.raw_response_string or delimitter.join(response_string_toks[1:]))
|
62
60
|
response_string.control = response_string_toks[0]
|
63
61
|
|
@@ -68,4 +66,38 @@ class JoinedRequestAdapter():
|
|
68
66
|
|
69
67
|
joined_request.request_string = self.build_request_string()
|
70
68
|
joined_request.response_string = self.build_response_string()
|
71
|
-
return joined_request
|
69
|
+
return joined_request
|
70
|
+
|
71
|
+
# If all CRLF characters have been replaced with LF e.g. visual studio code
|
72
|
+
# Then try to repair the raw string, see https://github.com/Stoobly/stoobly-agent/issues/415
|
73
|
+
@staticmethod
|
74
|
+
def repaired_string_toks(raw_string: bytes, delimitter: bytes):
|
75
|
+
toks = raw_string.split(delimitter)
|
76
|
+
|
77
|
+
if len(toks) == 1:
|
78
|
+
lf = b"\n"
|
79
|
+
toks = raw_string.split(lf)
|
80
|
+
|
81
|
+
# See for request: https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html
|
82
|
+
# See for response: https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html
|
83
|
+
i = 0
|
84
|
+
for line in toks:
|
85
|
+
i += 1
|
86
|
+
|
87
|
+
# On two lf characters, then the following lines are the body
|
88
|
+
if line == b'':
|
89
|
+
break
|
90
|
+
|
91
|
+
toks = toks[:i] + [lf.join(toks[i:])]
|
92
|
+
|
93
|
+
if len(toks) == 1:
|
94
|
+
raise ValueError(f"Could not split request by {delimitter}")
|
95
|
+
|
96
|
+
return toks
|
97
|
+
|
98
|
+
@staticmethod
|
99
|
+
def raw_request_split(raw_string: bytes, payloads_delimitter = REQUEST_DELIMITTER):
|
100
|
+
toks = raw_string.split(payloads_delimitter)
|
101
|
+
if len(toks) != 2:
|
102
|
+
toks = raw_string.split(payloads_delimitter.replace(b"\n", b"\r\n"))
|
103
|
+
return toks
|
@@ -2,10 +2,10 @@ import pdb
|
|
2
2
|
|
3
3
|
from typing import List
|
4
4
|
|
5
|
+
from stoobly_agent.app.models.adapters.joined_request_adapter import JoinedRequestAdapter
|
5
6
|
from stoobly_agent.app.models.factories.resource.local_db.helpers.log import Log
|
6
7
|
from stoobly_agent.app.models.factories.resource.local_db.helpers.request_snapshot import RequestSnapshot
|
7
8
|
from stoobly_agent.app.models.factories.resource.local_db.helpers.scenario_snapshot import ScenarioSnapshot
|
8
|
-
from stoobly_agent.app.proxy.record import REQUEST_STRING_CLRF
|
9
9
|
from stoobly_agent.app.settings import Settings
|
10
10
|
from stoobly_agent.lib.logger import bcolors
|
11
11
|
|
@@ -165,7 +165,12 @@ class Apply():
|
|
165
165
|
self.__logger(f"{bcolors.WARNING}Skipping Request{bcolors.ENDC} {error}")
|
166
166
|
return error, 301
|
167
167
|
|
168
|
-
|
168
|
+
res, status = self.__put_request(uuid, raw_request)
|
169
|
+
|
170
|
+
if status != 200:
|
171
|
+
return f"{res} {snapshot.path}", status
|
172
|
+
|
173
|
+
return res, status
|
169
174
|
|
170
175
|
def __apply_delete_scenario(self, uuid: str):
|
171
176
|
res, status = self.scenario_model.destroy(uuid, force=self.__force)
|
@@ -227,8 +232,8 @@ class Apply():
|
|
227
232
|
|
228
233
|
if not raw_request:
|
229
234
|
return f"{request_snapshot.path} is missing", 400
|
230
|
-
|
231
|
-
toks =
|
235
|
+
|
236
|
+
toks = JoinedRequestAdapter.raw_request_split(raw_request)
|
232
237
|
if len(toks) != 2:
|
233
238
|
return f"{request_snapshot.path} contains an invalid request", 400
|
234
239
|
|
@@ -236,7 +241,7 @@ class Apply():
|
|
236
241
|
res, status = self.__put_request(uuid, raw_request, scenario_id=scenario['id'])
|
237
242
|
|
238
243
|
if status != 200:
|
239
|
-
return res, status
|
244
|
+
return f"{res} {request_snapshot.path}", status
|
240
245
|
|
241
246
|
snapshot_requests[uuid] = res
|
242
247
|
|
@@ -274,8 +279,6 @@ class Apply():
|
|
274
279
|
|
275
280
|
if self.__logger and status == 200:
|
276
281
|
self.__logger(f"{bcolors.OKGREEN}Created Request{bcolors.ENDC} {res['list'][0]['url']}")
|
277
|
-
else:
|
278
|
-
self.__logger(f"{bcolors.FAIL}{status}{bcolors.ENDC} {res}")
|
279
282
|
elif status == 200:
|
280
283
|
params = {
|
281
284
|
'is_deleted': False,
|
@@ -287,7 +290,5 @@ class Apply():
|
|
287
290
|
if self.__logger:
|
288
291
|
if status == 200:
|
289
292
|
self.__logger(f"{bcolors.OKCYAN}Updated Request{bcolors.ENDC} {res['url']}")
|
290
|
-
else:
|
291
|
-
self.__logger(f"{bcolors.FAIL}{status}{bcolors.ENDC} {res}")
|
292
293
|
|
293
294
|
return res, status
|
stoobly_agent/cli.py
CHANGED
@@ -115,12 +115,12 @@ def init(**kwargs):
|
|
115
115
|
upstream proxy modes, SPEC is host specification in
|
116
116
|
the form of "http[s]://host[:port]".
|
117
117
|
''')
|
118
|
-
@click.option('--proxy-port', default=8080, help='Proxy service port.')
|
118
|
+
@click.option('--proxy-port', default=8080, type=click.IntRange(1, 65535), help='Proxy service port.')
|
119
119
|
@click.option('--public-directory-path', help='Path to public files. Used for mocking requests.')
|
120
120
|
@click.option('--response-fixtures-path', help='Path to response fixtures yaml. Used for mocking requests.')
|
121
121
|
@click.option('--ssl-insecure', is_flag=True, default=False, help='Do not verify upstream server SSL/TLS certificates.')
|
122
122
|
@click.option('--ui-host', default='0.0.0.0', help='Address to bind UI to.')
|
123
|
-
@click.option('--ui-port', default=4200, help='UI service port.')
|
123
|
+
@click.option('--ui-port', default=4200, type=click.IntRange(1, 65535), help='UI service port.')
|
124
124
|
def run(**kwargs):
|
125
125
|
from .app.proxy.run import run as run_proxy
|
126
126
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import pytest
|
2
|
+
|
3
|
+
from stoobly_agent.app.models.adapters.joined_request_adapter import RequestStringCLRF, JoinedRequestAdapter
|
4
|
+
|
5
|
+
class TestJoindRequestStringAdapter():
|
6
|
+
|
7
|
+
@pytest.fixture(scope='class', autouse=True)
|
8
|
+
def corrupted_raw_string(self):
|
9
|
+
toks = [
|
10
|
+
b"control",
|
11
|
+
b"header1",
|
12
|
+
b"header2",
|
13
|
+
b'',
|
14
|
+
b'body1',
|
15
|
+
b'body2'
|
16
|
+
]
|
17
|
+
return b"\n".join(toks)
|
18
|
+
|
19
|
+
@pytest.fixture(scope='class', autouse=True)
|
20
|
+
def repaired_toks(self, corrupted_raw_string: bytes):
|
21
|
+
# [b'control', b'header1', b'header2', b'', b'body1\nbody2']
|
22
|
+
return JoinedRequestAdapter.repaired_string_toks(corrupted_raw_string, RequestStringCLRF)
|
23
|
+
|
24
|
+
def test_control(self, repaired_toks: list):
|
25
|
+
assert repaired_toks[0] == b'control'
|
26
|
+
|
27
|
+
def test_header1(self, repaired_toks: list):
|
28
|
+
assert repaired_toks[1] == b'header1'
|
29
|
+
|
30
|
+
def test_header2(self, repaired_toks: list):
|
31
|
+
assert repaired_toks[2] == b'header2'
|
32
|
+
|
33
|
+
def test_clrf(self, repaired_toks: list):
|
34
|
+
assert repaired_toks[3] == b''
|
35
|
+
|
36
|
+
def test_body(self, repaired_toks: list):
|
37
|
+
assert repaired_toks[4] == b'body1\nbody2'
|
38
|
+
|
@@ -1 +1 @@
|
|
1
|
-
1.9.
|
1
|
+
1.9.3
|
@@ -1,4 +1,4 @@
|
|
1
|
-
stoobly_agent/__init__.py,sha256=
|
1
|
+
stoobly_agent/__init__.py,sha256=xHYLozasADB35BWCrRP8wlaWwr4WTbSnYvQyjTdvED4,44
|
2
2
|
stoobly_agent/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
stoobly_agent/app/api/__init__.py,sha256=ctkB8KR-eXO0SFhj602huHiyvQ3PslFWd8fkcufgrAI,1000
|
4
4
|
stoobly_agent/app/api/application_http_request_handler.py,sha256=Vvz53yB0bR7J-QqMAkLlhcZrA4P64ZEN7w8cMbgl6o0,5261
|
@@ -69,11 +69,11 @@ stoobly_agent/app/cli/request_cli.py,sha256=THNloW111l9MLE0oPg4y7hVXL1U7OXoz760g
|
|
69
69
|
stoobly_agent/app/cli/scaffold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
70
70
|
stoobly_agent/app/cli/scaffold/app.py,sha256=bgNc_dWZtgkuDAkS9aHSCMxCrjX907Phlfb2bOasswI,3435
|
71
71
|
stoobly_agent/app/cli/scaffold/app_command.py,sha256=sliaYulMNmaxbRXpJIDtBDWEBGEv5Tht4rpErzC_xxw,2368
|
72
|
-
stoobly_agent/app/cli/scaffold/app_config.py,sha256=
|
73
|
-
stoobly_agent/app/cli/scaffold/app_create_command.py,sha256=
|
72
|
+
stoobly_agent/app/cli/scaffold/app_config.py,sha256=UGVJ7DVmXh-o_gYBlAAEjngNIUZPQUiXXk2oMStcsPg,1075
|
73
|
+
stoobly_agent/app/cli/scaffold/app_create_command.py,sha256=0ogYliGbq1PYP5rFs-ML33q_Z4t_rAgPWjhk7rhnGw0,1153
|
74
74
|
stoobly_agent/app/cli/scaffold/command.py,sha256=aoTsdkkBzyu7TkVSMdNQQGk0Gj874jNgFcjR14y3TuM,254
|
75
75
|
stoobly_agent/app/cli/scaffold/config.py,sha256=HZU5tkvr3dkPr4JMXZtrJlu2wxxO-134Em6jReFFcq0,688
|
76
|
-
stoobly_agent/app/cli/scaffold/constants.py,sha256=
|
76
|
+
stoobly_agent/app/cli/scaffold/constants.py,sha256=dpCGvKzNtGQyaZfghfy0F6bJsZNp1KPACX3Z3rd13sw,2416
|
77
77
|
stoobly_agent/app/cli/scaffold/containerized_app.py,sha256=dAjn4RwcZV3aEL0POUmrbF_DC-r9h6s1zx7gT2t45v0,175
|
78
78
|
stoobly_agent/app/cli/scaffold/docker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
79
79
|
stoobly_agent/app/cli/scaffold/docker/app_builder.py,sha256=7z5pk5JKlRDHx2USxY-WurttLyyUkIVYfl34_u1x9dE,501
|
@@ -82,7 +82,7 @@ stoobly_agent/app/cli/scaffold/docker/constants.py,sha256=1khQdgTaQ89ykGRNdTQh_e
|
|
82
82
|
stoobly_agent/app/cli/scaffold/docker/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
83
83
|
stoobly_agent/app/cli/scaffold/docker/service/build_decorator.py,sha256=ZU7z4bkvdS3OK5O4fJhlA9_PNwnFtZW6t7vNF7V5obQ,1003
|
84
84
|
stoobly_agent/app/cli/scaffold/docker/service/builder.py,sha256=4cIMSYvgrkGWVuuYymiwlrR829O91qQl9ML8FhaDMj4,5857
|
85
|
-
stoobly_agent/app/cli/scaffold/docker/service/configure_gateway.py,sha256=
|
85
|
+
stoobly_agent/app/cli/scaffold/docker/service/configure_gateway.py,sha256=872qX9atoNWMXvSZcnG5cvVXarWShxBS9cJ_Q-YrCW8,3850
|
86
86
|
stoobly_agent/app/cli/scaffold/docker/service/types.py,sha256=qB-yYHlu-PZDc0HYgTUvE5bWNpHxaSThC3JUG8okR1k,88
|
87
87
|
stoobly_agent/app/cli/scaffold/docker/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
88
88
|
stoobly_agent/app/cli/scaffold/docker/workflow/build_decorator.py,sha256=VKD9hXbJGRIWHS5IeYXeX0-FQ0F43zG8VmsegL6eYwA,703
|
@@ -142,10 +142,10 @@ stoobly_agent/app/cli/scaffold/templates/app/gateway/mock/.docker-compose.mock.y
|
|
142
142
|
stoobly_agent/app/cli/scaffold/templates/app/gateway/record/.docker-compose.record.yml,sha256=eyLH2h33Peunus8M1sUKL9AALCG2ABhV_heiJKhvgwo,138
|
143
143
|
stoobly_agent/app/cli/scaffold/templates/app/gateway/test/.docker-compose.test.yml,sha256=oJO6i0lsuQaQeIH80yoPZo3Vs0LzUAH2WRl853yLq6g,136
|
144
144
|
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/.config.yml,sha256=XnLQZMzzMMIwVycjyPN5QXsmRztkTFAna1kIHYuDfJQ,19
|
145
|
-
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/.docker-compose.base.yml,sha256=
|
145
|
+
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/.docker-compose.base.yml,sha256=bxrtZqf3YtaJCukzScslh5PgWC5q8xkGIP1wKJf33LA,111
|
146
146
|
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/.docker-compose.exec.yml,sha256=JN89sU5uRf6YqHvN_O63K8rwQIAPJHbhFDLFmuUjKNM,304
|
147
|
-
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/.docker-compose.mock.yml,sha256=
|
148
|
-
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/.docker-compose.record.yml,sha256=
|
147
|
+
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/.docker-compose.mock.yml,sha256=2wg-YArlQVjat-bin_PLOnULQJW7mOmZjP7CFp6Knbs,242
|
148
|
+
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/.docker-compose.record.yml,sha256=yDw8OSX7rFsW6J51K5Qf33qM-isGL8V4xPAYayIfnvQ,245
|
149
149
|
stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.configure,sha256=SKvht2K_3tW08K24rl8_j0jMYOhq1k-GsVwhoHwjxYA,337
|
150
150
|
stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.init,sha256=ecmyS6EZpa3op0CmO7bvd3pmAwRb0oLwj1qsTkee9_o,247
|
151
151
|
stoobly_agent/app/cli/scaffold/templates/build/services/build/record/.configure,sha256=eXp9eKJ-TORE5B0zLW4-t43ogS3nxtj2SmSeDALbi1U,278
|
@@ -188,7 +188,7 @@ stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/configure,sha256=5k
|
|
188
188
|
stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/init,sha256=EaoFDyoJbHc9Ui8ELYKmfweXAycJptVOQblszeh3XTE,94
|
189
189
|
stoobly_agent/app/cli/scaffold/templates/workflow/record/lifecycle_hooks.py,sha256=4vaVc_gnDTCLEqtcZybIk5dcmXrKmGuesF6gc3-_kX8,473
|
190
190
|
stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/configure,sha256=GAU7AfSPcyDSI9RJ7mynT83YqgN9r_E9HZYx0RXE1lU,279
|
191
|
-
stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/init,sha256=
|
191
|
+
stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/init,sha256=_jnP53I1yyCtv5TXOnMtQcEMbU43tpZ-za7s8Ely6P0,281
|
192
192
|
stoobly_agent/app/cli/scaffold/templates/workflow/test/fixtures.yml,sha256=CJlZ_kugygZpmyqIauBjNZxqk7XyLaa3yl3AWj8KV28,259
|
193
193
|
stoobly_agent/app/cli/scaffold/templates/workflow/test/lifecycle_hooks.py,sha256=U7mlzT_wBR3uhHSG6CAyt5tBUNAvdIrCw33gdB-F294,467
|
194
194
|
stoobly_agent/app/cli/scaffold/templates/workflow/test/public/.gitignore,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -203,7 +203,7 @@ stoobly_agent/app/cli/scaffold/workflow_env.py,sha256=x8V5pJmIiklD3f2q2-qq-CORf4
|
|
203
203
|
stoobly_agent/app/cli/scaffold/workflow_log_command.py,sha256=Bke4lMOMxuDUFuAx9nlXHbKgYMO4KAg9ASHvjz4aVWc,1372
|
204
204
|
stoobly_agent/app/cli/scaffold/workflow_run_command.py,sha256=eF3aaK4OIZXYuSBEAeBnhAL7EZrS1G4mSYrJbEiXt2o,11082
|
205
205
|
stoobly_agent/app/cli/scaffold/workflow_validate_command.py,sha256=Uo_yo6rVR1ZR7xpvsQvlH48AyMBVLRupd4G-bRjzm_Q,5584
|
206
|
-
stoobly_agent/app/cli/scaffold_cli.py,sha256=
|
206
|
+
stoobly_agent/app/cli/scaffold_cli.py,sha256=huVZUpBUW6sfcNmhAlcQTKYVCCg0kHhcjezxt_Fm4sk,32043
|
207
207
|
stoobly_agent/app/cli/scenario_cli.py,sha256=3J1EiJOvunkfWrEkOsanw-XrKkOk78ij_GjBlE9p7CE,8229
|
208
208
|
stoobly_agent/app/cli/snapshot_cli.py,sha256=Uf6g6ivsD0hUY8G99eU0fxzS3FymncAhI70PxV7Uaac,11919
|
209
209
|
stoobly_agent/app/cli/trace_cli.py,sha256=K7E-vx3JUcqEDSWOdIOi_AieKNQz7dBfmRrVvKDkzFI,4605
|
@@ -216,7 +216,7 @@ stoobly_agent/app/cli/types/snapshot_migration.py,sha256=4_Re46FKjsflcTOO3qhNsbW
|
|
216
216
|
stoobly_agent/app/cli/types/test.py,sha256=1c458B7DFBWsEk5Q1CrZ2CUi84YzEzcs-W4qTcudwAk,714
|
217
217
|
stoobly_agent/app/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
218
218
|
stoobly_agent/app/models/adapters/__init__.py,sha256=cEEE--Bvrvk6DAsHx_uPgFhLnZJETP4zSBtWjMqyIKc,233
|
219
|
-
stoobly_agent/app/models/adapters/joined_request_adapter.py,sha256=
|
219
|
+
stoobly_agent/app/models/adapters/joined_request_adapter.py,sha256=fSq16n3AAlxi8KJdBESHp3JGio_M9uzMnHbnQU8VI3w,3598
|
220
220
|
stoobly_agent/app/models/adapters/mitmproxy/__init__.py,sha256=f14D_0y3_Pz6NpRXQMIwJQiWBGhwKk0Wk_AmlY3twh0,105
|
221
221
|
stoobly_agent/app/models/adapters/mitmproxy/request/__init__.py,sha256=UZeK_-vxQKD02Tuu-YHBtpR92k5mK3XFgwmd99h2VHA,281
|
222
222
|
stoobly_agent/app/models/adapters/mitmproxy/request/python_adapter.py,sha256=lWSppm63oOv7RDmTsUI0EmQkUWlQhRGdjtViBEDU-AA,377
|
@@ -287,7 +287,7 @@ stoobly_agent/app/models/factories/resource/stoobly/request_adapter.py,sha256=Zr
|
|
287
287
|
stoobly_agent/app/models/factories/resource/stoobly/scenario_adapter.py,sha256=HnM4g5Qdv16QXj8u4JCiJm2Dbw9OhAxmn9e_R8oaHG4,1105
|
288
288
|
stoobly_agent/app/models/header_model.py,sha256=m91upRZr8GfE5um0d5dguUESKigBMWhSyu_X3HFk28Y,1406
|
289
289
|
stoobly_agent/app/models/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
290
|
-
stoobly_agent/app/models/helpers/apply.py,sha256=
|
290
|
+
stoobly_agent/app/models/helpers/apply.py,sha256=VWHZnZ0BnpyyO1MU6eyohimn1HGQClkDQuvYk51BttA,8491
|
291
291
|
stoobly_agent/app/models/helpers/create_request_params_service.py,sha256=o_VB2FsTGBX55UBBw1yQ8MtsJ4-0YXcxd4kNQ9l21nM,2070
|
292
292
|
stoobly_agent/app/models/model.py,sha256=77ZTByQmH5sWBcSrCF3kG_C4muHggcFyH1DWsOhIgvg,1180
|
293
293
|
stoobly_agent/app/models/query_param_model.py,sha256=EBj76phSJ9_45KgP0vIZGbkkG6-tSn_U1fNW_7qLy_4,1455
|
@@ -415,7 +415,7 @@ stoobly_agent/app/settings/types/remote_settings.py,sha256=4PvEGKULXM0zv29XTDzV7
|
|
415
415
|
stoobly_agent/app/settings/types/ui_settings.py,sha256=BqPy2F32AbODqzi2mp2kRk28QVUydQIwVmvftn46pco,84
|
416
416
|
stoobly_agent/app/settings/ui_settings.py,sha256=YDEUMPuJFh0SLHaGz6O-Gpz8nwsunNzeuc-TzO9OUbM,1170
|
417
417
|
stoobly_agent/app/settings/url_rule.py,sha256=Qx7YrIpVRSC-4LeNiCAfCtE50Jou4423hojMW-4qUYg,954
|
418
|
-
stoobly_agent/cli.py,sha256=
|
418
|
+
stoobly_agent/cli.py,sha256=FS-V4gkzDE4PgXokqvWV7cFzWq5v6WNMro6B5Mi9E-4,10317
|
419
419
|
stoobly_agent/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
420
420
|
stoobly_agent/config/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
421
421
|
stoobly_agent/config/constants/alias_resolve_strategy.py,sha256=_R1tVqFnyGxCraVS5-dhSskaDj_X8-NthsY7i_bEt9M,119
|
@@ -693,6 +693,7 @@ stoobly_agent/test/app/cli/snapshot/snapshot_copy_test.py,sha256=Yg78-FhSiG_r6Jp
|
|
693
693
|
stoobly_agent/test/app/cli/snapshot/snapshot_migrate_test.py,sha256=voEvblK6CMGCrSJDTHVmkUkLXj0auNb78jxlGiiBBQQ,7370
|
694
694
|
stoobly_agent/test/app/cli/snapshot/snapshot_prune_test.py,sha256=bn4yUU7Eb4-6GnwnRaPZPi5Cn7XEaIsrJ_mB7jydgWw,6693
|
695
695
|
stoobly_agent/test/app/cli/snapshot/snapshot_update_test.py,sha256=fILsX2M5j4wuLRP6LJTHe4CPB8gvaEbsSoYmFCHmKVk,4514
|
696
|
+
stoobly_agent/test/app/models/adapters/joined_rquest_adapter_test.py,sha256=bF7WMrAiASQDNzDTvIXGJhsWLNhfYOmdQpSDo0hyWYY,1098
|
696
697
|
stoobly_agent/test/app/models/adapters/orm/joined_request_string_adapter_test.py,sha256=a2IHTk3l7aiLyYF7vtqissrk0MFTF2wlUBiaKWyJKfU,2667
|
697
698
|
stoobly_agent/test/app/models/adapters/orm/request/orm_mitmproxy_request_adapter_test.py,sha256=PbJsAaxPUEbF9vM7DX4z858biWf4qlGnvE8KBuy8SgY,2763
|
698
699
|
stoobly_agent/test/app/models/adapters/orm/request/orm_python_request_adapter_test.py,sha256=1rHywokXUj7z3laHhfnei8j1GVmAHDOULvRWmtIyWuQ,2488
|
@@ -707,7 +708,7 @@ stoobly_agent/test/app/models/factories/resource/local_db/helpers/log_test.py,sh
|
|
707
708
|
stoobly_agent/test/app/models/factories/resource/local_db/helpers/tiebreak_scenario_request_test.py,sha256=a1SFLyEyRRLuADvAw6ckQQKORFXvyK1lyrbkaLWx8oU,3399
|
708
709
|
stoobly_agent/test/app/models/factories/resource/local_db/request_adapter_test.py,sha256=Pzq1cBPnP9oSWG-p0c-VoymoHxgp483QmNwmV1b78RA,8453
|
709
710
|
stoobly_agent/test/app/models/factories/resource/local_db/response_adapter_test.py,sha256=9P95EKH5rZGOrmRkRIDlQZqtiLJHk9735og18Ffwpfw,2204
|
710
|
-
stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=
|
711
|
+
stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=JyvJHgCQJn-THkr7jL9H-s1Ea2VWcQz-X3BiHK64uxk,6
|
711
712
|
stoobly_agent/test/app/models/schemas/.stoobly/db/stoobly_agent.sqlite3,sha256=ch8gNx6zIelLKQx65gwFx_LRNqUD3EC5xcHZ0ukIQiU,188416
|
712
713
|
stoobly_agent/test/app/models/schemas/.stoobly/settings.yml,sha256=vLwMjweKOdod6tSLtIlyBefPQuNXq9wio4kBaODKtAU,726
|
713
714
|
stoobly_agent/test/app/models/schemas/.stoobly/tmp/options.json,sha256=OTRzarwus48CTrItedXCrgQttJHSEZonEYc7R_knvYg,2212
|
@@ -748,8 +749,8 @@ stoobly_agent/test/mock_data/scaffold/docker-compose-local-service.yml,sha256=1W
|
|
748
749
|
stoobly_agent/test/mock_data/scaffold/index.html,sha256=qJwuYajKZ4ihWZrJQ3BNObV5kf1VGnnm_vqlPJzdqLE,258
|
749
750
|
stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
|
750
751
|
stoobly_agent/test/test_helper.py,sha256=m_oAI7tmRYCNZdKfNqISWhMv3e44tjeYViQ3nTUfnos,1007
|
751
|
-
stoobly_agent-1.9.
|
752
|
-
stoobly_agent-1.9.
|
753
|
-
stoobly_agent-1.9.
|
754
|
-
stoobly_agent-1.9.
|
755
|
-
stoobly_agent-1.9.
|
752
|
+
stoobly_agent-1.9.4.dist-info/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
|
753
|
+
stoobly_agent-1.9.4.dist-info/METADATA,sha256=xDOjRS-p08haoG5tLVE1xI3X2qrM6gaplT5nmlIN8s8,3087
|
754
|
+
stoobly_agent-1.9.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
755
|
+
stoobly_agent-1.9.4.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
|
756
|
+
stoobly_agent-1.9.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|