mcp-proxy-adapter 6.3.4__py3-none-any.whl ā 6.3.5__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.
- mcp_proxy_adapter/__init__.py +9 -5
- mcp_proxy_adapter/__main__.py +1 -1
- mcp_proxy_adapter/api/app.py +227 -176
- mcp_proxy_adapter/api/handlers.py +68 -60
- mcp_proxy_adapter/api/middleware/__init__.py +7 -5
- mcp_proxy_adapter/api/middleware/base.py +19 -16
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
- mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
- mcp_proxy_adapter/api/middleware/factory.py +50 -52
- mcp_proxy_adapter/api/middleware/logging.py +46 -30
- mcp_proxy_adapter/api/middleware/performance.py +19 -16
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
- mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
- mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
- mcp_proxy_adapter/api/schemas.py +69 -43
- mcp_proxy_adapter/api/tool_integration.py +83 -63
- mcp_proxy_adapter/api/tools.py +60 -50
- mcp_proxy_adapter/commands/__init__.py +15 -6
- mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
- mcp_proxy_adapter/commands/base.py +108 -112
- mcp_proxy_adapter/commands/builtin_commands.py +28 -18
- mcp_proxy_adapter/commands/catalog_manager.py +394 -265
- mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
- mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
- mcp_proxy_adapter/commands/command_registry.py +275 -226
- mcp_proxy_adapter/commands/config_command.py +48 -33
- mcp_proxy_adapter/commands/dependency_container.py +22 -23
- mcp_proxy_adapter/commands/dependency_manager.py +65 -56
- mcp_proxy_adapter/commands/echo_command.py +15 -15
- mcp_proxy_adapter/commands/health_command.py +31 -29
- mcp_proxy_adapter/commands/help_command.py +97 -61
- mcp_proxy_adapter/commands/hooks.py +65 -49
- mcp_proxy_adapter/commands/key_management_command.py +148 -147
- mcp_proxy_adapter/commands/load_command.py +58 -40
- mcp_proxy_adapter/commands/plugins_command.py +80 -54
- mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
- mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
- mcp_proxy_adapter/commands/reload_command.py +43 -37
- mcp_proxy_adapter/commands/result.py +26 -33
- mcp_proxy_adapter/commands/role_test_command.py +26 -26
- mcp_proxy_adapter/commands/roles_management_command.py +176 -173
- mcp_proxy_adapter/commands/security_command.py +134 -122
- mcp_proxy_adapter/commands/settings_command.py +47 -56
- mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
- mcp_proxy_adapter/commands/token_management_command.py +129 -158
- mcp_proxy_adapter/commands/transport_management_command.py +41 -36
- mcp_proxy_adapter/commands/unload_command.py +42 -37
- mcp_proxy_adapter/config.py +36 -35
- mcp_proxy_adapter/core/__init__.py +19 -21
- mcp_proxy_adapter/core/app_factory.py +30 -9
- mcp_proxy_adapter/core/app_runner.py +81 -64
- mcp_proxy_adapter/core/auth_validator.py +176 -182
- mcp_proxy_adapter/core/certificate_utils.py +469 -426
- mcp_proxy_adapter/core/client.py +155 -126
- mcp_proxy_adapter/core/client_manager.py +60 -54
- mcp_proxy_adapter/core/client_security.py +108 -88
- mcp_proxy_adapter/core/config_converter.py +176 -143
- mcp_proxy_adapter/core/config_validator.py +12 -4
- mcp_proxy_adapter/core/crl_utils.py +21 -7
- mcp_proxy_adapter/core/errors.py +64 -20
- mcp_proxy_adapter/core/logging.py +34 -29
- mcp_proxy_adapter/core/mtls_asgi.py +29 -25
- mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
- mcp_proxy_adapter/core/protocol_manager.py +154 -104
- mcp_proxy_adapter/core/proxy_client.py +202 -144
- mcp_proxy_adapter/core/proxy_registration.py +7 -3
- mcp_proxy_adapter/core/role_utils.py +139 -125
- mcp_proxy_adapter/core/security_adapter.py +88 -77
- mcp_proxy_adapter/core/security_factory.py +50 -44
- mcp_proxy_adapter/core/security_integration.py +72 -24
- mcp_proxy_adapter/core/server_adapter.py +68 -64
- mcp_proxy_adapter/core/server_engine.py +71 -53
- mcp_proxy_adapter/core/settings.py +68 -58
- mcp_proxy_adapter/core/ssl_utils.py +69 -56
- mcp_proxy_adapter/core/transport_manager.py +72 -60
- mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
- mcp_proxy_adapter/core/utils.py +4 -2
- mcp_proxy_adapter/custom_openapi.py +107 -99
- mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/commands/__init__.py +1 -1
- mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
- mcp_proxy_adapter/examples/debug_request_state.py +38 -19
- mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
- mcp_proxy_adapter/examples/demo_client.py +48 -36
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
- mcp_proxy_adapter/examples/generate_certificates.py +31 -16
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
- mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
- mcp_proxy_adapter/examples/run_example.py +23 -5
- mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
- mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
- mcp_proxy_adapter/examples/run_security_tests.py +103 -41
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
- mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/security_test_client.py +196 -127
- mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
- mcp_proxy_adapter/examples/test_config.py +19 -4
- mcp_proxy_adapter/examples/test_config_generator.py +23 -7
- mcp_proxy_adapter/examples/test_examples.py +84 -56
- mcp_proxy_adapter/examples/universal_client.py +119 -62
- mcp_proxy_adapter/openapi.py +108 -115
- mcp_proxy_adapter/utils/config_generator.py +429 -274
- mcp_proxy_adapter/version.py +1 -2
- {mcp_proxy_adapter-6.3.4.dist-info ā mcp_proxy_adapter-6.3.5.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.3.5.dist-info/RECORD +143 -0
- mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
- {mcp_proxy_adapter-6.3.4.dist-info ā mcp_proxy_adapter-6.3.5.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info ā mcp_proxy_adapter-6.3.5.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info ā mcp_proxy_adapter-6.3.5.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info ā mcp_proxy_adapter-6.3.5.dist-info}/top_level.txt +0 -0
@@ -128,7 +128,9 @@ def create_and_run_server(
|
|
128
128
|
print(
|
129
129
|
f"š Debug: SSL config after loading: cert_file={ssl_config.get('cert_file')}"
|
130
130
|
)
|
131
|
-
print(
|
131
|
+
print(
|
132
|
+
f"š Debug: SSL config after loading: key_file={ssl_config.get('key_file')}"
|
133
|
+
)
|
132
134
|
|
133
135
|
# Debug: Check if SSL config is correct
|
134
136
|
if ssl_config.get("enabled", False):
|
@@ -137,9 +139,15 @@ def create_and_run_server(
|
|
137
139
|
print(f"š Debug: SSL config is disabled or incorrect")
|
138
140
|
# Try to get SSL config from root level
|
139
141
|
root_ssl = app_config.get("ssl", {})
|
140
|
-
print(
|
141
|
-
|
142
|
-
|
142
|
+
print(
|
143
|
+
f"š Debug: Root SSL config: enabled={root_ssl.get('enabled', False)}"
|
144
|
+
)
|
145
|
+
print(
|
146
|
+
f"š Debug: Root SSL config: cert_file={root_ssl.get('cert_file')}"
|
147
|
+
)
|
148
|
+
print(
|
149
|
+
f"š Debug: Root SSL config: key_file={root_ssl.get('key_file')}"
|
150
|
+
)
|
143
151
|
|
144
152
|
# Validate security framework configuration only if enabled
|
145
153
|
security_config = app_config.get("security", {})
|
@@ -154,7 +162,9 @@ def create_and_run_server(
|
|
154
162
|
)
|
155
163
|
|
156
164
|
# Validate security configuration
|
157
|
-
from mcp_proxy_adapter.core.unified_config_adapter import
|
165
|
+
from mcp_proxy_adapter.core.unified_config_adapter import (
|
166
|
+
UnifiedConfigAdapter,
|
167
|
+
)
|
158
168
|
|
159
169
|
adapter = UnifiedConfigAdapter()
|
160
170
|
validation_result = adapter.validate_configuration(app_config)
|
@@ -243,10 +253,17 @@ def create_and_run_server(
|
|
243
253
|
server_port = app_config.get("server", {}).get("port", 8000) if app_config else 8000
|
244
254
|
print(f"š Port: {server_port}")
|
245
255
|
|
246
|
-
server_config = {
|
256
|
+
server_config = {
|
257
|
+
"host": host,
|
258
|
+
"port": server_port,
|
259
|
+
"log_level": log_level,
|
260
|
+
"reload": False,
|
261
|
+
}
|
247
262
|
|
248
263
|
# Add SSL configuration if present
|
249
|
-
print(
|
264
|
+
print(
|
265
|
+
f"š Debug: app_config keys: {list(app_config.keys()) if app_config else 'None'}"
|
266
|
+
)
|
250
267
|
|
251
268
|
# Check for SSL config in root section first (higher priority)
|
252
269
|
if app_config and "ssl" in app_config:
|
@@ -273,7 +290,9 @@ def create_and_run_server(
|
|
273
290
|
print(f"š Debug: security_config keys: {list(security_config.keys())}")
|
274
291
|
if "ssl" in security_config:
|
275
292
|
print(f"š Debug: SSL config found in security: {security_config['ssl']}")
|
276
|
-
print(
|
293
|
+
print(
|
294
|
+
f"š Debug: SSL enabled: {security_config['ssl'].get('enabled', False)}"
|
295
|
+
)
|
277
296
|
if security_config["ssl"].get("enabled", False):
|
278
297
|
ssl_config = security_config["ssl"]
|
279
298
|
# Add SSL config directly to server_config for Hypercorn
|
@@ -431,7 +450,9 @@ def create_application(
|
|
431
450
|
register_builtin_commands()
|
432
451
|
|
433
452
|
# Create FastAPI application using existing create_app function
|
434
|
-
app = create_app(
|
453
|
+
app = create_app(
|
454
|
+
title=title, description=description, version=version, app_config=config
|
455
|
+
)
|
435
456
|
|
436
457
|
# Add CORS middleware
|
437
458
|
app.add_middleware(
|
@@ -24,11 +24,11 @@ class ApplicationRunner:
|
|
24
24
|
"""
|
25
25
|
Class for running applications with configuration validation.
|
26
26
|
"""
|
27
|
-
|
27
|
+
|
28
28
|
def __init__(self, app: FastAPI, config: Dict[str, Any]):
|
29
29
|
"""
|
30
30
|
Initialize ApplicationRunner.
|
31
|
-
|
31
|
+
|
32
32
|
Args:
|
33
33
|
app: FastAPI application instance
|
34
34
|
config: Application configuration dictionary
|
@@ -36,113 +36,123 @@ class ApplicationRunner:
|
|
36
36
|
self.app = app
|
37
37
|
self.config = config
|
38
38
|
self.errors: List[str] = []
|
39
|
-
|
39
|
+
|
40
40
|
def validate_configuration(self) -> List[str]:
|
41
41
|
"""
|
42
42
|
Validates configuration and returns list of errors.
|
43
|
-
|
43
|
+
|
44
44
|
Returns:
|
45
45
|
List of validation error messages
|
46
46
|
"""
|
47
47
|
self.errors = []
|
48
|
-
|
48
|
+
|
49
49
|
# Validate server configuration
|
50
50
|
self._validate_server_config()
|
51
|
-
|
51
|
+
|
52
52
|
# Validate SSL configuration
|
53
53
|
self._validate_ssl_config()
|
54
|
-
|
54
|
+
|
55
55
|
# Validate security configuration
|
56
56
|
self._validate_security_config()
|
57
|
-
|
57
|
+
|
58
58
|
# Validate file paths
|
59
59
|
self._validate_file_paths()
|
60
|
-
|
60
|
+
|
61
61
|
# Validate port availability
|
62
62
|
self._validate_port_availability()
|
63
|
-
|
63
|
+
|
64
64
|
# Validate configuration compatibility
|
65
65
|
self._validate_compatibility()
|
66
|
-
|
66
|
+
|
67
67
|
return self.errors
|
68
|
-
|
68
|
+
|
69
69
|
def _validate_server_config(self) -> None:
|
70
70
|
"""Validate server configuration."""
|
71
71
|
server_config = self.config.get("server", {})
|
72
|
-
|
72
|
+
|
73
73
|
if not server_config:
|
74
74
|
self.errors.append("Server configuration is missing")
|
75
75
|
return
|
76
|
-
|
76
|
+
|
77
77
|
host = server_config.get("host")
|
78
78
|
port = server_config.get("port")
|
79
|
-
|
79
|
+
|
80
80
|
if not host:
|
81
81
|
self.errors.append("Server host is not specified")
|
82
|
-
|
82
|
+
|
83
83
|
if not port:
|
84
84
|
self.errors.append("Server port is not specified")
|
85
85
|
elif not isinstance(port, int) or port < 1 or port > 65535:
|
86
86
|
self.errors.append(f"Invalid server port: {port}")
|
87
|
-
|
87
|
+
|
88
88
|
def _validate_ssl_config(self) -> None:
|
89
89
|
"""Validate SSL configuration."""
|
90
90
|
ssl_config = self.config.get("ssl", {})
|
91
|
-
|
91
|
+
|
92
92
|
if ssl_config.get("enabled", False):
|
93
93
|
cert_file = ssl_config.get("cert_file")
|
94
94
|
key_file = ssl_config.get("key_file")
|
95
|
-
|
95
|
+
|
96
96
|
if not cert_file:
|
97
97
|
self.errors.append("SSL enabled but certificate file not specified")
|
98
98
|
elif not Path(cert_file).exists():
|
99
99
|
self.errors.append(f"Certificate file not found: {cert_file}")
|
100
|
-
|
100
|
+
|
101
101
|
if not key_file:
|
102
102
|
self.errors.append("SSL enabled but private key file not specified")
|
103
103
|
elif not Path(key_file).exists():
|
104
104
|
self.errors.append(f"Private key file not found: {key_file}")
|
105
|
-
|
105
|
+
|
106
106
|
# Validate mTLS configuration
|
107
107
|
if ssl_config.get("verify_client", False):
|
108
108
|
ca_cert = ssl_config.get("ca_cert")
|
109
109
|
if not ca_cert:
|
110
|
-
self.errors.append(
|
110
|
+
self.errors.append(
|
111
|
+
"Client verification enabled but CA certificate not specified"
|
112
|
+
)
|
111
113
|
elif not Path(ca_cert).exists():
|
112
114
|
self.errors.append(f"CA certificate file not found: {ca_cert}")
|
113
|
-
|
115
|
+
|
114
116
|
def _validate_security_config(self) -> None:
|
115
117
|
"""Validate security configuration."""
|
116
118
|
security_config = self.config.get("security", {})
|
117
|
-
|
119
|
+
|
118
120
|
if security_config.get("enabled", False):
|
119
121
|
auth_config = security_config.get("auth", {})
|
120
122
|
permissions_config = security_config.get("permissions", {})
|
121
|
-
|
123
|
+
|
122
124
|
# Validate authentication configuration
|
123
125
|
if auth_config.get("enabled", False):
|
124
126
|
methods = auth_config.get("methods", [])
|
125
127
|
if not methods:
|
126
|
-
self.errors.append(
|
127
|
-
|
128
|
+
self.errors.append(
|
129
|
+
"Authentication enabled but no methods specified"
|
130
|
+
)
|
131
|
+
|
128
132
|
# Validate API key configuration
|
129
133
|
if "api_key" in methods:
|
130
134
|
# Check if roles file exists for API key auth
|
131
135
|
if permissions_config.get("enabled", False):
|
132
136
|
roles_file = permissions_config.get("roles_file")
|
133
137
|
if not roles_file:
|
134
|
-
self.errors.append(
|
138
|
+
self.errors.append(
|
139
|
+
"Permissions enabled but roles file not specified"
|
140
|
+
)
|
135
141
|
elif not Path(roles_file).exists():
|
136
142
|
self.errors.append(f"Roles file not found: {roles_file}")
|
137
|
-
|
143
|
+
|
138
144
|
# Validate certificate configuration
|
139
145
|
if "certificate" in methods:
|
140
146
|
ssl_config = self.config.get("ssl", {})
|
141
147
|
if not ssl_config.get("enabled", False):
|
142
|
-
self.errors.append(
|
148
|
+
self.errors.append(
|
149
|
+
"Certificate authentication requires SSL to be enabled"
|
150
|
+
)
|
143
151
|
if not ssl_config.get("verify_client", False):
|
144
|
-
self.errors.append(
|
145
|
-
|
152
|
+
self.errors.append(
|
153
|
+
"Certificate authentication requires client verification to be enabled"
|
154
|
+
)
|
155
|
+
|
146
156
|
def _validate_file_paths(self) -> None:
|
147
157
|
"""Validate all file paths in configuration."""
|
148
158
|
# Check SSL certificate files
|
@@ -151,16 +161,22 @@ class ApplicationRunner:
|
|
151
161
|
cert_file = ssl_config.get("cert_file")
|
152
162
|
key_file = ssl_config.get("key_file")
|
153
163
|
ca_cert = ssl_config.get("ca_cert")
|
154
|
-
|
164
|
+
|
155
165
|
if cert_file and not Path(cert_file).is_file():
|
156
|
-
self.errors.append(
|
157
|
-
|
166
|
+
self.errors.append(
|
167
|
+
f"Certificate file is not a regular file: {cert_file}"
|
168
|
+
)
|
169
|
+
|
158
170
|
if key_file and not Path(key_file).is_file():
|
159
|
-
self.errors.append(
|
160
|
-
|
171
|
+
self.errors.append(
|
172
|
+
f"Private key file is not a regular file: {key_file}"
|
173
|
+
)
|
174
|
+
|
161
175
|
if ca_cert and not Path(ca_cert).is_file():
|
162
|
-
self.errors.append(
|
163
|
-
|
176
|
+
self.errors.append(
|
177
|
+
f"CA certificate file is not a regular file: {ca_cert}"
|
178
|
+
)
|
179
|
+
|
164
180
|
# Check roles file
|
165
181
|
security_config = self.config.get("security", {})
|
166
182
|
permissions_config = security_config.get("permissions", {})
|
@@ -168,103 +184,104 @@ class ApplicationRunner:
|
|
168
184
|
roles_file = permissions_config.get("roles_file")
|
169
185
|
if roles_file and not Path(roles_file).is_file():
|
170
186
|
self.errors.append(f"Roles file is not a regular file: {roles_file}")
|
171
|
-
|
187
|
+
|
172
188
|
def _validate_port_availability(self) -> None:
|
173
189
|
"""Validate that the configured port is available."""
|
174
190
|
server_config = self.config.get("server", {})
|
175
191
|
port = server_config.get("port")
|
176
|
-
|
192
|
+
|
177
193
|
if port:
|
178
194
|
try:
|
179
195
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
180
196
|
s.bind(("127.0.0.1", port))
|
181
197
|
except OSError:
|
182
198
|
self.errors.append(f"Port {port} is already in use")
|
183
|
-
|
199
|
+
|
184
200
|
def _validate_compatibility(self) -> None:
|
185
201
|
"""Validate configuration compatibility."""
|
186
202
|
ssl_config = self.config.get("ssl", {})
|
187
203
|
security_config = self.config.get("security", {})
|
188
204
|
protocols_config = self.config.get("protocols", {})
|
189
|
-
|
205
|
+
|
190
206
|
# Check SSL and protocol compatibility
|
191
207
|
if ssl_config.get("enabled", False):
|
192
208
|
allowed_protocols = protocols_config.get("allowed_protocols", [])
|
193
209
|
if "http" in allowed_protocols and "https" not in allowed_protocols:
|
194
210
|
self.errors.append("SSL enabled but HTTPS not in allowed protocols")
|
195
|
-
|
211
|
+
|
196
212
|
# Check security and SSL compatibility
|
197
213
|
if security_config.get("enabled", False):
|
198
214
|
auth_config = security_config.get("auth", {})
|
199
215
|
if auth_config.get("enabled", False):
|
200
216
|
methods = auth_config.get("methods", [])
|
201
217
|
if "certificate" in methods and not ssl_config.get("enabled", False):
|
202
|
-
self.errors.append(
|
203
|
-
|
218
|
+
self.errors.append(
|
219
|
+
"Certificate authentication requires SSL to be enabled"
|
220
|
+
)
|
221
|
+
|
204
222
|
def setup_hooks(self) -> None:
|
205
223
|
"""
|
206
224
|
Setup application hooks.
|
207
225
|
"""
|
226
|
+
|
208
227
|
# Add startup event
|
209
228
|
@self.app.on_event("startup")
|
210
229
|
async def startup_event():
|
211
230
|
logger.info("Application starting up")
|
212
|
-
logger.info(
|
213
|
-
|
231
|
+
logger.info(
|
232
|
+
f"Configuration validation passed with {len(self.errors)} errors"
|
233
|
+
)
|
234
|
+
|
214
235
|
# Add shutdown event
|
215
236
|
@self.app.on_event("shutdown")
|
216
237
|
async def shutdown_event():
|
217
238
|
logger.info("Application shutting down")
|
218
|
-
|
239
|
+
|
219
240
|
def run(self) -> None:
|
220
241
|
"""
|
221
242
|
Run application with full validation.
|
222
243
|
"""
|
223
244
|
# Validate configuration
|
224
245
|
errors = self.validate_configuration()
|
225
|
-
|
246
|
+
|
226
247
|
if errors:
|
227
248
|
print("ERROR: Configuration validation failed:", file=sys.stderr)
|
228
249
|
for error in errors:
|
229
250
|
print(f" - {error}", file=sys.stderr)
|
230
251
|
sys.exit(1)
|
231
|
-
|
252
|
+
|
232
253
|
# Setup hooks
|
233
254
|
self.setup_hooks()
|
234
|
-
|
255
|
+
|
235
256
|
# Get server configuration
|
236
257
|
server_config = self.config.get("server", {})
|
237
258
|
host = server_config.get("host", "127.0.0.1")
|
238
259
|
port = server_config.get("port", 8000)
|
239
|
-
|
260
|
+
|
240
261
|
# Prepare server configuration for hypercorn
|
241
|
-
server_kwargs = {
|
242
|
-
|
243
|
-
"port": port,
|
244
|
-
"log_level": "info"
|
245
|
-
}
|
246
|
-
|
262
|
+
server_kwargs = {"host": host, "port": port, "log_level": "info"}
|
263
|
+
|
247
264
|
# Add SSL configuration if enabled
|
248
265
|
ssl_config = self.config.get("ssl", {})
|
249
266
|
if ssl_config.get("enabled", False):
|
250
267
|
server_kwargs["certfile"] = ssl_config.get("cert_file")
|
251
268
|
server_kwargs["keyfile"] = ssl_config.get("key_file")
|
252
|
-
|
269
|
+
|
253
270
|
# Add mTLS configuration if enabled
|
254
271
|
if ssl_config.get("verify_client", False):
|
255
272
|
server_kwargs["ca_certs"] = ssl_config.get("ca_cert")
|
256
|
-
|
273
|
+
|
257
274
|
try:
|
258
275
|
import hypercorn.asyncio
|
259
276
|
import asyncio
|
260
|
-
|
277
|
+
|
261
278
|
print(f"š Starting server on {host}:{port}")
|
262
279
|
print(" Use Ctrl+C to stop the server")
|
263
280
|
print("=" * 60)
|
264
|
-
|
281
|
+
|
265
282
|
# Run with hypercorn
|
266
283
|
asyncio.run(hypercorn.asyncio.serve(self.app, **server_kwargs))
|
267
|
-
|
284
|
+
|
268
285
|
except KeyboardInterrupt:
|
269
286
|
print("\nš Server stopped by user")
|
270
287
|
except Exception as e:
|