mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.6__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 +120 -91
- 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.6.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.3.6.dist-info/RECORD +144 -0
- mcp_proxy_adapter-6.3.6.dist-info/top_level.txt +2 -0
- mcp_proxy_adapter_issue_package/demonstrate_issue.py +178 -0
- mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
- mcp_proxy_adapter-6.3.4.dist-info/top_level.txt +0 -1
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/licenses/LICENSE +0 -0
@@ -33,7 +33,9 @@ try:
|
|
33
33
|
ValidationResult,
|
34
34
|
CertificatePair,
|
35
35
|
)
|
36
|
-
from mcp_security_framework.middleware.fastapi_middleware import
|
36
|
+
from mcp_security_framework.middleware.fastapi_middleware import (
|
37
|
+
FastAPISecurityMiddleware,
|
38
|
+
)
|
37
39
|
|
38
40
|
SECURITY_FRAMEWORK_AVAILABLE = True
|
39
41
|
except ImportError:
|
@@ -73,7 +75,9 @@ class SecurityIntegration:
|
|
73
75
|
# Initialize framework components
|
74
76
|
self.security_manager = SecurityManager(self.security_config)
|
75
77
|
self.permission_manager = PermissionManager(self.security_config.permissions)
|
76
|
-
self.auth_manager = AuthManager(
|
78
|
+
self.auth_manager = AuthManager(
|
79
|
+
self.security_config.auth, self.permission_manager
|
80
|
+
)
|
77
81
|
self.certificate_manager = CertificateManager(self.security_config.certificates)
|
78
82
|
self.rate_limiter = RateLimiter(self.security_config.rate_limit)
|
79
83
|
|
@@ -92,11 +96,17 @@ class SecurityIntegration:
|
|
92
96
|
ca_cert_file=security_section.get("ssl", {}).get("ca_cert_file"),
|
93
97
|
client_cert_file=security_section.get("ssl", {}).get("client_cert_file"),
|
94
98
|
client_key_file=security_section.get("ssl", {}).get("client_key_file"),
|
95
|
-
verify_mode=security_section.get("ssl", {}).get(
|
96
|
-
|
99
|
+
verify_mode=security_section.get("ssl", {}).get(
|
100
|
+
"verify_mode", "CERT_REQUIRED"
|
101
|
+
),
|
102
|
+
min_tls_version=security_section.get("ssl", {}).get(
|
103
|
+
"min_tls_version", "TLSv1.2"
|
104
|
+
),
|
97
105
|
check_hostname=security_section.get("ssl", {}).get("check_hostname", True),
|
98
106
|
check_expiry=security_section.get("ssl", {}).get("check_expiry", True),
|
99
|
-
expiry_warning_days=security_section.get("ssl", {}).get(
|
107
|
+
expiry_warning_days=security_section.get("ssl", {}).get(
|
108
|
+
"expiry_warning_days", 30
|
109
|
+
),
|
100
110
|
)
|
101
111
|
|
102
112
|
# Create auth config
|
@@ -106,9 +116,15 @@ class SecurityIntegration:
|
|
106
116
|
api_keys=security_section.get("auth", {}).get("api_keys", {}),
|
107
117
|
user_roles=security_section.get("auth", {}).get("user_roles", {}),
|
108
118
|
jwt_secret=security_section.get("auth", {}).get("jwt_secret"),
|
109
|
-
jwt_algorithm=security_section.get("auth", {}).get(
|
110
|
-
|
111
|
-
|
119
|
+
jwt_algorithm=security_section.get("auth", {}).get(
|
120
|
+
"jwt_algorithm", "HS256"
|
121
|
+
),
|
122
|
+
jwt_expiry_hours=security_section.get("auth", {}).get(
|
123
|
+
"jwt_expiry_hours", 24
|
124
|
+
),
|
125
|
+
certificate_auth=security_section.get("auth", {}).get(
|
126
|
+
"certificate_auth", False
|
127
|
+
),
|
112
128
|
public_paths=security_section.get("auth", {}).get("public_paths", []),
|
113
129
|
)
|
114
130
|
|
@@ -132,9 +148,15 @@ class SecurityIntegration:
|
|
132
148
|
default_role=permissions_section.get("default_role", "guest"),
|
133
149
|
admin_role=permissions_section.get("admin_role", "admin"),
|
134
150
|
role_hierarchy=permissions_section.get("role_hierarchy", {}),
|
135
|
-
permission_cache_enabled=permissions_section.get(
|
136
|
-
|
137
|
-
|
151
|
+
permission_cache_enabled=permissions_section.get(
|
152
|
+
"permission_cache_enabled", True
|
153
|
+
),
|
154
|
+
permission_cache_ttl=permissions_section.get(
|
155
|
+
"permission_cache_ttl", 300
|
156
|
+
),
|
157
|
+
wildcard_permissions=permissions_section.get(
|
158
|
+
"wildcard_permissions", False
|
159
|
+
),
|
138
160
|
strict_mode=permissions_section.get("strict_mode", True),
|
139
161
|
roles=permissions_section.get("roles"),
|
140
162
|
)
|
@@ -166,7 +188,9 @@ class SecurityIntegration:
|
|
166
188
|
window_size_seconds=security_section.get("rate_limit", {}).get(
|
167
189
|
"window_size_seconds", 60
|
168
190
|
),
|
169
|
-
storage_backend=security_section.get("rate_limit", {}).get(
|
191
|
+
storage_backend=security_section.get("rate_limit", {}).get(
|
192
|
+
"storage_backend", "memory"
|
193
|
+
),
|
170
194
|
exempt_paths=security_section.get("rate_limit", {}).get("exempt_paths", []),
|
171
195
|
exempt_roles=security_section.get("rate_limit", {}).get("exempt_roles", []),
|
172
196
|
)
|
@@ -186,7 +210,9 @@ class SecurityIntegration:
|
|
186
210
|
"default_validity_days", 365
|
187
211
|
),
|
188
212
|
key_size=security_section.get("certificates", {}).get("key_size", 2048),
|
189
|
-
hash_algorithm=security_section.get("certificates", {}).get(
|
213
|
+
hash_algorithm=security_section.get("certificates", {}).get(
|
214
|
+
"hash_algorithm", "sha256"
|
215
|
+
),
|
190
216
|
)
|
191
217
|
|
192
218
|
# Create logging config
|
@@ -196,7 +222,9 @@ class SecurityIntegration:
|
|
196
222
|
format=security_section.get("logging", {}).get(
|
197
223
|
"format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
198
224
|
),
|
199
|
-
console_output=security_section.get("logging", {}).get(
|
225
|
+
console_output=security_section.get("logging", {}).get(
|
226
|
+
"console_output", True
|
227
|
+
),
|
200
228
|
file_path=security_section.get("logging", {}).get("file_path"),
|
201
229
|
)
|
202
230
|
|
@@ -231,17 +259,29 @@ class SecurityIntegration:
|
|
231
259
|
return await self.security_manager.validate_request(request_data)
|
232
260
|
|
233
261
|
# Certificate methods - direct calls to CertificateManager
|
234
|
-
async def create_ca_certificate(
|
262
|
+
async def create_ca_certificate(
|
263
|
+
self, common_name: str, **kwargs
|
264
|
+
) -> CertificatePair:
|
235
265
|
"""Create CA certificate."""
|
236
|
-
return await self.certificate_manager.create_ca_certificate(
|
266
|
+
return await self.certificate_manager.create_ca_certificate(
|
267
|
+
common_name, **kwargs
|
268
|
+
)
|
237
269
|
|
238
|
-
async def create_client_certificate(
|
270
|
+
async def create_client_certificate(
|
271
|
+
self, common_name: str, **kwargs
|
272
|
+
) -> CertificatePair:
|
239
273
|
"""Create client certificate."""
|
240
|
-
return await self.certificate_manager.create_client_certificate(
|
274
|
+
return await self.certificate_manager.create_client_certificate(
|
275
|
+
common_name, **kwargs
|
276
|
+
)
|
241
277
|
|
242
|
-
async def create_server_certificate(
|
278
|
+
async def create_server_certificate(
|
279
|
+
self, common_name: str, **kwargs
|
280
|
+
) -> CertificatePair:
|
243
281
|
"""Create server certificate."""
|
244
|
-
return await self.certificate_manager.create_server_certificate(
|
282
|
+
return await self.certificate_manager.create_server_certificate(
|
283
|
+
common_name, **kwargs
|
284
|
+
)
|
245
285
|
|
246
286
|
async def validate_certificate(self, cert_path: str) -> bool:
|
247
287
|
"""Validate certificate with CRL check if enabled."""
|
@@ -261,12 +301,16 @@ class SecurityIntegration:
|
|
261
301
|
"crl_enabled": cert_config.crl_enabled,
|
262
302
|
"crl_path": getattr(cert_config, "crl_path", None),
|
263
303
|
"crl_url": getattr(cert_config, "crl_url", None),
|
264
|
-
"crl_validity_days": getattr(
|
304
|
+
"crl_validity_days": getattr(
|
305
|
+
cert_config, "crl_validity_days", 30
|
306
|
+
),
|
265
307
|
}
|
266
308
|
|
267
309
|
# Use mcp_security_framework's validate_certificate_chain with CRL
|
268
310
|
if crl_config and crl_config.get("crl_enabled"):
|
269
|
-
from mcp_security_framework.utils.cert_utils import
|
311
|
+
from mcp_security_framework.utils.cert_utils import (
|
312
|
+
validate_certificate_chain,
|
313
|
+
)
|
270
314
|
from .crl_utils import CRLManager
|
271
315
|
|
272
316
|
# Get CRL data
|
@@ -276,7 +320,9 @@ class SecurityIntegration:
|
|
276
320
|
# Validate with CRL
|
277
321
|
if crl_data:
|
278
322
|
return validate_certificate_chain(
|
279
|
-
cert_path,
|
323
|
+
cert_path,
|
324
|
+
self.security_config.certificates.ca_cert_path,
|
325
|
+
crl_data,
|
280
326
|
)
|
281
327
|
|
282
328
|
# Fallback to standard validation
|
@@ -312,7 +358,9 @@ class SecurityIntegration:
|
|
312
358
|
return await self.permission_manager.remove_user_role(user_id, role)
|
313
359
|
|
314
360
|
# Rate limiting methods - direct calls to RateLimiter
|
315
|
-
async def check_rate_limit(
|
361
|
+
async def check_rate_limit(
|
362
|
+
self, identifier: str, limit_type: str = "per_minute"
|
363
|
+
) -> bool:
|
316
364
|
"""Check rate limit."""
|
317
365
|
return await self.rate_limiter.check_rate_limit(identifier, limit_type)
|
318
366
|
|
@@ -20,23 +20,22 @@ logger = logging.getLogger(__name__)
|
|
20
20
|
class ServerConfigAdapter:
|
21
21
|
"""
|
22
22
|
Adapter for converting server configurations between different engines.
|
23
|
-
|
23
|
+
|
24
24
|
This class handles the mapping of configuration parameters between
|
25
25
|
different server engines and provides unified configuration management.
|
26
26
|
"""
|
27
|
-
|
27
|
+
|
28
28
|
@staticmethod
|
29
29
|
def convert_ssl_config_for_engine(
|
30
|
-
ssl_config: Dict[str, Any],
|
31
|
-
target_engine: str
|
30
|
+
ssl_config: Dict[str, Any], target_engine: str
|
32
31
|
) -> Dict[str, Any]:
|
33
32
|
"""
|
34
33
|
Convert SSL configuration for a specific server engine.
|
35
|
-
|
34
|
+
|
36
35
|
Args:
|
37
36
|
ssl_config: Source SSL configuration
|
38
37
|
target_engine: Target engine name (hypercorn)
|
39
|
-
|
38
|
+
|
40
39
|
Returns:
|
41
40
|
Converted SSL configuration for the target engine
|
42
41
|
"""
|
@@ -44,18 +43,18 @@ class ServerConfigAdapter:
|
|
44
43
|
if not engine:
|
45
44
|
logger.error(f"Unknown server engine: {target_engine}")
|
46
45
|
return {}
|
47
|
-
|
46
|
+
|
48
47
|
if target_engine == "hypercorn":
|
49
48
|
return ServerConfigAdapter._convert_to_hypercorn_ssl(ssl_config)
|
50
49
|
else:
|
51
50
|
logger.warning(f"No SSL conversion available for engine: {target_engine}")
|
52
51
|
return {}
|
53
|
-
|
52
|
+
|
54
53
|
@staticmethod
|
55
54
|
def _convert_to_hypercorn_ssl(ssl_config: Dict[str, Any]) -> Dict[str, Any]:
|
56
55
|
"""Convert SSL configuration to hypercorn format."""
|
57
56
|
hypercorn_ssl = {}
|
58
|
-
|
57
|
+
|
59
58
|
# Map SSL parameters
|
60
59
|
if ssl_config.get("cert_file"):
|
61
60
|
hypercorn_ssl["certfile"] = ssl_config["cert_file"]
|
@@ -63,42 +62,39 @@ class ServerConfigAdapter:
|
|
63
62
|
hypercorn_ssl["keyfile"] = ssl_config["key_file"]
|
64
63
|
if ssl_config.get("ca_cert"):
|
65
64
|
hypercorn_ssl["ca_certs"] = ssl_config["ca_cert"]
|
66
|
-
|
65
|
+
|
67
66
|
# Map verification mode
|
68
67
|
if ssl_config.get("verify_client", False):
|
69
68
|
hypercorn_ssl["verify_mode"] = "CERT_REQUIRED"
|
70
|
-
|
69
|
+
|
71
70
|
logger.debug(f"Converted SSL config to hypercorn: {hypercorn_ssl}")
|
72
71
|
return hypercorn_ssl
|
73
|
-
|
72
|
+
|
74
73
|
@staticmethod
|
75
74
|
def get_optimal_engine_for_config(config: Dict[str, Any]) -> Optional[str]:
|
76
75
|
"""
|
77
76
|
Determine the optimal server engine for a given configuration.
|
78
|
-
|
77
|
+
|
79
78
|
Currently only hypercorn is supported.
|
80
|
-
|
79
|
+
|
81
80
|
Args:
|
82
81
|
config: Server configuration
|
83
|
-
|
82
|
+
|
84
83
|
Returns:
|
85
84
|
Optimal engine name (currently always "hypercorn")
|
86
85
|
"""
|
87
86
|
# Currently only hypercorn is supported
|
88
87
|
return "hypercorn"
|
89
|
-
|
88
|
+
|
90
89
|
@staticmethod
|
91
|
-
def validate_engine_compatibility(
|
92
|
-
config: Dict[str, Any],
|
93
|
-
engine_name: str
|
94
|
-
) -> bool:
|
90
|
+
def validate_engine_compatibility(config: Dict[str, Any], engine_name: str) -> bool:
|
95
91
|
"""
|
96
92
|
Validate if a configuration is compatible with a specific engine.
|
97
|
-
|
93
|
+
|
98
94
|
Args:
|
99
95
|
config: Server configuration
|
100
96
|
engine_name: Name of the server engine
|
101
|
-
|
97
|
+
|
102
98
|
Returns:
|
103
99
|
True if compatible, False otherwise
|
104
100
|
"""
|
@@ -106,73 +102,72 @@ class ServerConfigAdapter:
|
|
106
102
|
if not engine:
|
107
103
|
logger.error(f"Unknown engine: {engine_name}")
|
108
104
|
return False
|
109
|
-
|
105
|
+
|
110
106
|
# Check SSL requirements
|
111
107
|
ssl_config = config.get("ssl", {})
|
112
108
|
if not ssl_config:
|
113
109
|
# Try to get SSL config from security section
|
114
110
|
ssl_config = config.get("security", {}).get("ssl", {})
|
115
|
-
|
111
|
+
|
116
112
|
if ssl_config.get("verify_client", False):
|
117
113
|
if not engine.get_supported_features().get("mtls_client_certs", False):
|
118
|
-
logger.error(
|
114
|
+
logger.error(
|
115
|
+
f"Engine {engine_name} doesn't support mTLS client certificates"
|
116
|
+
)
|
119
117
|
return False
|
120
|
-
|
118
|
+
|
121
119
|
# Validate engine-specific configuration
|
122
120
|
return engine.validate_config(config)
|
123
|
-
|
121
|
+
|
124
122
|
@staticmethod
|
125
123
|
def get_engine_capabilities(engine_name: str) -> Dict[str, Any]:
|
126
124
|
"""
|
127
125
|
Get capabilities of a specific server engine.
|
128
|
-
|
126
|
+
|
129
127
|
Args:
|
130
128
|
engine_name: Name of the server engine
|
131
|
-
|
129
|
+
|
132
130
|
Returns:
|
133
131
|
Dictionary of engine capabilities
|
134
132
|
"""
|
135
133
|
engine = ServerEngineFactory.get_engine(engine_name)
|
136
134
|
if not engine:
|
137
135
|
return {}
|
138
|
-
|
136
|
+
|
139
137
|
return {
|
140
138
|
"name": engine.get_name(),
|
141
139
|
"features": engine.get_supported_features(),
|
142
|
-
"config_schema": engine.get_config_schema()
|
140
|
+
"config_schema": engine.get_config_schema(),
|
143
141
|
}
|
144
142
|
|
145
143
|
|
146
144
|
class UnifiedServerRunner:
|
147
145
|
"""
|
148
146
|
Unified server runner that uses hypercorn as the default engine.
|
149
|
-
|
147
|
+
|
150
148
|
This class provides a unified interface for running servers using hypercorn
|
151
149
|
as the underlying engine.
|
152
150
|
"""
|
153
|
-
|
151
|
+
|
154
152
|
def __init__(self, default_engine: str = "hypercorn"):
|
155
153
|
"""
|
156
154
|
Initialize the unified server runner.
|
157
|
-
|
155
|
+
|
158
156
|
Args:
|
159
157
|
default_engine: Default engine to use (currently only hypercorn is supported)
|
160
158
|
"""
|
161
159
|
self.default_engine = default_engine
|
162
160
|
self.available_engines = ServerEngineFactory.get_available_engines()
|
163
|
-
|
161
|
+
|
164
162
|
logger.info(f"Available engines: {list(self.available_engines.keys())}")
|
165
163
|
logger.info(f"Default engine: {default_engine}")
|
166
|
-
|
164
|
+
|
167
165
|
def run_server(
|
168
|
-
self,
|
169
|
-
app: Any,
|
170
|
-
config: Dict[str, Any],
|
171
|
-
engine_name: Optional[str] = None
|
166
|
+
self, app: Any, config: Dict[str, Any], engine_name: Optional[str] = None
|
172
167
|
) -> None:
|
173
168
|
"""
|
174
169
|
Run server with hypercorn engine.
|
175
|
-
|
170
|
+
|
176
171
|
Args:
|
177
172
|
app: ASGI application
|
178
173
|
config: Server configuration
|
@@ -181,29 +176,33 @@ class UnifiedServerRunner:
|
|
181
176
|
# Use hypercorn as the only supported engine
|
182
177
|
selected_engine = "hypercorn"
|
183
178
|
logger.info(f"Using hypercorn engine")
|
184
|
-
|
179
|
+
|
185
180
|
# Validate compatibility
|
186
|
-
if not ServerConfigAdapter.validate_engine_compatibility(
|
187
|
-
|
188
|
-
|
181
|
+
if not ServerConfigAdapter.validate_engine_compatibility(
|
182
|
+
config, selected_engine
|
183
|
+
):
|
184
|
+
raise ValueError(
|
185
|
+
f"Configuration not compatible with engine: {selected_engine}"
|
186
|
+
)
|
187
|
+
|
189
188
|
# Get engine instance
|
190
189
|
engine = ServerEngineFactory.get_engine(selected_engine)
|
191
190
|
if not engine:
|
192
191
|
raise ValueError(f"Engine not available: {selected_engine}")
|
193
|
-
|
192
|
+
|
194
193
|
# Convert configuration if needed
|
195
194
|
converted_config = self._prepare_config_for_engine(config, selected_engine)
|
196
|
-
|
195
|
+
|
197
196
|
# Run server
|
198
197
|
logger.info(f"Starting server with {selected_engine} engine")
|
199
198
|
engine.run_server(app, converted_config)
|
200
|
-
|
199
|
+
|
201
200
|
def _prepare_config_for_engine(
|
202
|
-
self,
|
203
|
-
config: Dict[str, Any],
|
204
|
-
engine_name: str
|
201
|
+
self, config: Dict[str, Any], engine_name: str
|
205
202
|
) -> Dict[str, Any]:
|
206
|
-
logger.info(
|
203
|
+
logger.info(
|
204
|
+
f"🔍 Debug: _prepare_config_for_engine called with config keys: {list(config.keys())}"
|
205
|
+
)
|
207
206
|
logger.info(f"🔍 Debug: SSL config in input: {config.get('ssl', 'NOT_FOUND')}")
|
208
207
|
"""
|
209
208
|
Prepare configuration for a specific engine.
|
@@ -220,12 +219,17 @@ class UnifiedServerRunner:
|
|
220
219
|
"host": config.get("host", "127.0.0.1"),
|
221
220
|
"port": config.get("port", 8000),
|
222
221
|
"log_level": config.get("log_level", "info"),
|
223
|
-
"reload": config.get("reload", False)
|
222
|
+
"reload": config.get("reload", False),
|
224
223
|
}
|
225
|
-
|
224
|
+
|
226
225
|
# Add SSL configuration if present
|
227
226
|
# First check for direct SSL parameters (from app_factory.py)
|
228
|
-
if
|
227
|
+
if (
|
228
|
+
"certfile" in config
|
229
|
+
or "keyfile" in config
|
230
|
+
or "ca_certs" in config
|
231
|
+
or "verify_mode" in config
|
232
|
+
):
|
229
233
|
logger.info(f"🔍 DEBUG: Direct SSL parameters found in config")
|
230
234
|
if "certfile" in config:
|
231
235
|
engine_config["certfile"] = config["certfile"]
|
@@ -241,35 +245,35 @@ class UnifiedServerRunner:
|
|
241
245
|
if not ssl_config:
|
242
246
|
# Try to get SSL config from security section
|
243
247
|
ssl_config = config.get("security", {}).get("ssl", {})
|
244
|
-
|
248
|
+
|
245
249
|
if ssl_config:
|
246
250
|
converted_ssl = ServerConfigAdapter.convert_ssl_config_for_engine(
|
247
251
|
ssl_config, engine_name
|
248
252
|
)
|
249
253
|
engine_config.update(converted_ssl)
|
250
|
-
|
254
|
+
|
251
255
|
# Add engine-specific configuration
|
252
256
|
if "workers" in config:
|
253
257
|
engine_config["workers"] = config["workers"]
|
254
|
-
|
258
|
+
|
255
259
|
return engine_config
|
256
|
-
|
260
|
+
|
257
261
|
def get_engine_info(self, engine_name: str) -> Dict[str, Any]:
|
258
262
|
"""
|
259
263
|
Get information about a specific engine.
|
260
|
-
|
264
|
+
|
261
265
|
Args:
|
262
266
|
engine_name: Name of the engine
|
263
|
-
|
267
|
+
|
264
268
|
Returns:
|
265
269
|
Engine information dictionary
|
266
270
|
"""
|
267
271
|
return ServerConfigAdapter.get_engine_capabilities(engine_name)
|
268
|
-
|
272
|
+
|
269
273
|
def list_available_engines(self) -> Dict[str, Dict[str, Any]]:
|
270
274
|
"""
|
271
275
|
List all available engines with their capabilities.
|
272
|
-
|
276
|
+
|
273
277
|
Returns:
|
274
278
|
Dictionary mapping engine names to their capabilities
|
275
279
|
"""
|
@@ -277,6 +281,6 @@ class UnifiedServerRunner:
|
|
277
281
|
for name, engine in self.available_engines.items():
|
278
282
|
engines_info[name] = {
|
279
283
|
"features": engine.get_supported_features(),
|
280
|
-
"config_schema": engine.get_config_schema()
|
284
|
+
"config_schema": engine.get_config_schema(),
|
281
285
|
}
|
282
286
|
return engines_info
|