mcp-security-framework 0.1.0__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_security_framework/core/auth_manager.py +12 -2
- mcp_security_framework/core/cert_manager.py +247 -16
- mcp_security_framework/core/permission_manager.py +4 -0
- mcp_security_framework/core/rate_limiter.py +10 -0
- mcp_security_framework/core/security_manager.py +2 -0
- mcp_security_framework/examples/comprehensive_example.py +884 -0
- mcp_security_framework/examples/django_example.py +45 -12
- mcp_security_framework/examples/fastapi_example.py +826 -354
- mcp_security_framework/examples/flask_example.py +51 -11
- mcp_security_framework/examples/gateway_example.py +109 -17
- mcp_security_framework/examples/microservice_example.py +112 -16
- mcp_security_framework/examples/standalone_example.py +646 -430
- mcp_security_framework/examples/test_all_examples.py +556 -0
- mcp_security_framework/middleware/auth_middleware.py +1 -1
- mcp_security_framework/middleware/fastapi_auth_middleware.py +82 -14
- mcp_security_framework/middleware/flask_auth_middleware.py +154 -7
- mcp_security_framework/schemas/models.py +1 -0
- mcp_security_framework/utils/cert_utils.py +5 -5
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/METADATA +1 -1
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/RECORD +38 -32
- tests/conftest.py +306 -0
- tests/test_cli/test_cert_cli.py +13 -31
- tests/test_core/test_cert_manager.py +12 -12
- tests/test_examples/test_comprehensive_example.py +560 -0
- tests/test_examples/test_fastapi_example.py +214 -116
- tests/test_examples/test_flask_example.py +250 -131
- tests/test_examples/test_standalone_example.py +44 -99
- tests/test_integration/test_auth_flow.py +4 -4
- tests/test_integration/test_certificate_flow.py +1 -1
- tests/test_integration/test_fastapi_integration.py +39 -45
- tests/test_integration/test_flask_integration.py +4 -2
- tests/test_integration/test_standalone_integration.py +48 -48
- tests/test_middleware/test_fastapi_auth_middleware.py +724 -0
- tests/test_middleware/test_flask_auth_middleware.py +638 -0
- tests/test_middleware/test_security_middleware.py +9 -3
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/WHEEL +0 -0
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/entry_points.txt +0 -0
- {mcp_security_framework-0.1.0.dist-info → mcp_security_framework-1.1.0.dist-info}/top_level.txt +0 -0
@@ -101,10 +101,10 @@ class FlaskExample:
|
|
101
101
|
security_headers=DEFAULT_SECURITY_HEADERS
|
102
102
|
),
|
103
103
|
ssl=SSLConfig(
|
104
|
-
enabled=
|
105
|
-
cert_file=
|
106
|
-
key_file=
|
107
|
-
ca_cert_file=
|
104
|
+
enabled=False, # Disable SSL for testing
|
105
|
+
cert_file=None,
|
106
|
+
key_file=None,
|
107
|
+
ca_cert_file=None,
|
108
108
|
verify_mode="CERT_REQUIRED",
|
109
109
|
min_version="TLSv1.2"
|
110
110
|
),
|
@@ -473,22 +473,27 @@ class FlaskExampleTest:
|
|
473
473
|
readonly_roles = ["readonly"]
|
474
474
|
|
475
475
|
# Admin should have all permissions
|
476
|
-
|
476
|
+
admin_result = example.security_manager.permission_manager.validate_access(
|
477
477
|
admin_roles, ["read", "write", "delete"]
|
478
478
|
)
|
479
|
+
assert admin_result.is_valid
|
479
480
|
|
480
481
|
# User should have read and write permissions
|
481
|
-
|
482
|
+
user_result = example.security_manager.permission_manager.validate_access(
|
482
483
|
user_roles, ["read", "write"]
|
483
484
|
)
|
485
|
+
assert user_result.is_valid
|
484
486
|
|
485
487
|
# Readonly should only have read permission
|
486
|
-
|
488
|
+
readonly_read_result = example.security_manager.permission_manager.validate_access(
|
487
489
|
readonly_roles, ["read"]
|
488
490
|
)
|
489
|
-
assert
|
491
|
+
assert readonly_read_result.is_valid
|
492
|
+
|
493
|
+
readonly_write_result = example.security_manager.permission_manager.validate_access(
|
490
494
|
readonly_roles, ["write"]
|
491
495
|
)
|
496
|
+
assert not readonly_write_result.is_valid
|
492
497
|
|
493
498
|
print("✅ Permission checking test passed")
|
494
499
|
|
@@ -500,7 +505,42 @@ if __name__ == "__main__":
|
|
500
505
|
FlaskExampleTest.test_rate_limiting()
|
501
506
|
FlaskExampleTest.test_permissions()
|
502
507
|
|
503
|
-
# Start server
|
504
|
-
print("\nStarting Flask Example Server...")
|
508
|
+
# Start server in background thread for testing
|
509
|
+
print("\nStarting Flask Example Server in background...")
|
505
510
|
example = FlaskExample()
|
506
|
-
|
511
|
+
|
512
|
+
import threading
|
513
|
+
import time
|
514
|
+
import requests
|
515
|
+
|
516
|
+
# Start server in background thread
|
517
|
+
server_thread = threading.Thread(target=example.run, daemon=True)
|
518
|
+
server_thread.start()
|
519
|
+
|
520
|
+
# Wait for server to start
|
521
|
+
time.sleep(3)
|
522
|
+
|
523
|
+
try:
|
524
|
+
# Test server endpoints
|
525
|
+
print("Testing server endpoints...")
|
526
|
+
|
527
|
+
# Test health endpoint
|
528
|
+
response = requests.get("http://localhost:5000/health", timeout=5)
|
529
|
+
print(f"Health endpoint: {response.status_code}")
|
530
|
+
|
531
|
+
# Test metrics endpoint
|
532
|
+
response = requests.get("http://localhost:5000/metrics", timeout=5)
|
533
|
+
print(f"Metrics endpoint: {response.status_code}")
|
534
|
+
|
535
|
+
# Test protected endpoint with API key
|
536
|
+
headers = {"X-API-Key": "admin_key_123"}
|
537
|
+
response = requests.get("http://localhost:5000/api/v1/users/me", headers=headers, timeout=5)
|
538
|
+
print(f"Protected endpoint: {response.status_code}")
|
539
|
+
|
540
|
+
print("✅ Server testing completed successfully")
|
541
|
+
|
542
|
+
except requests.exceptions.RequestException as e:
|
543
|
+
print(f"⚠️ Server testing failed: {e}")
|
544
|
+
|
545
|
+
# Server will automatically stop when main thread exits
|
546
|
+
print("Flask example completed")
|
@@ -23,7 +23,7 @@ import logging
|
|
23
23
|
import asyncio
|
24
24
|
import aiohttp
|
25
25
|
from typing import Dict, List, Any, Optional
|
26
|
-
from datetime import datetime, timedelta
|
26
|
+
from datetime import datetime, timedelta, timezone
|
27
27
|
|
28
28
|
from mcp_security_framework.core.security_manager import SecurityManager
|
29
29
|
from mcp_security_framework.core.auth_manager import AuthManager
|
@@ -97,14 +97,14 @@ class APIGatewayExample:
|
|
97
97
|
public_paths=["/health", "/metrics", "/status"],
|
98
98
|
security_headers=DEFAULT_SECURITY_HEADERS
|
99
99
|
),
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
100
|
+
ssl=SSLConfig(
|
101
|
+
enabled=False, # Disable SSL for example
|
102
|
+
cert_file=None,
|
103
|
+
key_file=None,
|
104
|
+
ca_cert_file=None,
|
105
|
+
verify_mode="CERT_REQUIRED",
|
106
|
+
min_version="TLSv1.2"
|
107
|
+
),
|
108
108
|
rate_limit={
|
109
109
|
"enabled": True,
|
110
110
|
"default_requests_per_minute": 500, # Gateway-level limits
|
@@ -265,7 +265,7 @@ class APIGatewayExample:
|
|
265
265
|
"service": routing_rule["service"],
|
266
266
|
"success": True,
|
267
267
|
"request_id": request_id,
|
268
|
-
"timestamp": datetime.
|
268
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
269
269
|
})
|
270
270
|
|
271
271
|
return response
|
@@ -496,7 +496,7 @@ class APIGatewayExample:
|
|
496
496
|
"headers": response_headers,
|
497
497
|
"data": response_data,
|
498
498
|
"request_id": request_id,
|
499
|
-
"timestamp": datetime.
|
499
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
500
500
|
}
|
501
501
|
|
502
502
|
except Exception as e:
|
@@ -530,7 +530,7 @@ class APIGatewayExample:
|
|
530
530
|
"status_code": status_code,
|
531
531
|
"request_id": request_id,
|
532
532
|
"details": details,
|
533
|
-
"timestamp": datetime.
|
533
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
534
534
|
}
|
535
535
|
|
536
536
|
def _generate_request_id(self) -> str:
|
@@ -595,7 +595,7 @@ class APIGatewayExample:
|
|
595
595
|
return {
|
596
596
|
"status": "healthy" if overall_healthy else "unhealthy",
|
597
597
|
"gateway": "api-gateway",
|
598
|
-
"timestamp": datetime.
|
598
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
599
599
|
"checks": {
|
600
600
|
"security_manager": security_healthy,
|
601
601
|
"rate_limiter": rate_limit_healthy,
|
@@ -609,7 +609,7 @@ class APIGatewayExample:
|
|
609
609
|
"status": "unhealthy",
|
610
610
|
"gateway": "api-gateway",
|
611
611
|
"error": str(e),
|
612
|
-
"timestamp": datetime.
|
612
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
613
613
|
}
|
614
614
|
|
615
615
|
async def get_metrics(self) -> Dict[str, Any]:
|
@@ -632,7 +632,7 @@ class APIGatewayExample:
|
|
632
632
|
|
633
633
|
return {
|
634
634
|
"gateway": "api-gateway",
|
635
|
-
"timestamp": datetime.
|
635
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
636
636
|
"rate_limiting": rate_limit_stats,
|
637
637
|
"routing": routing_stats,
|
638
638
|
"security": self.get_security_status()
|
@@ -642,7 +642,7 @@ class APIGatewayExample:
|
|
642
642
|
return {
|
643
643
|
"gateway": "api-gateway",
|
644
644
|
"error": str(e),
|
645
|
-
"timestamp": datetime.
|
645
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
646
646
|
}
|
647
647
|
|
648
648
|
def get_security_status(self) -> Dict[str, Any]:
|
@@ -660,7 +660,7 @@ class APIGatewayExample:
|
|
660
660
|
"permissions_enabled": self.config.permissions.enabled,
|
661
661
|
"logging_enabled": self.config.logging.enabled,
|
662
662
|
"auth_methods": self.config.auth.methods,
|
663
|
-
"timestamp": datetime.
|
663
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
664
664
|
}
|
665
665
|
|
666
666
|
|
@@ -801,3 +801,95 @@ async def main():
|
|
801
801
|
|
802
802
|
if __name__ == "__main__":
|
803
803
|
asyncio.run(main())
|
804
|
+
|
805
|
+
# Start HTTP server in background for testing
|
806
|
+
print("\nStarting API Gateway HTTP Server in background...")
|
807
|
+
|
808
|
+
import threading
|
809
|
+
import time
|
810
|
+
import requests
|
811
|
+
from http.server import HTTPServer, BaseHTTPRequestHandler
|
812
|
+
import json
|
813
|
+
|
814
|
+
class GatewayHandler(BaseHTTPRequestHandler):
|
815
|
+
def do_GET(self):
|
816
|
+
if self.path == "/health":
|
817
|
+
self.send_response(200)
|
818
|
+
self.send_header("Content-type", "application/json")
|
819
|
+
self.end_headers()
|
820
|
+
response = {"status": "healthy", "gateway": "api-gateway"}
|
821
|
+
self.wfile.write(json.dumps(response).encode())
|
822
|
+
|
823
|
+
elif self.path == "/metrics":
|
824
|
+
self.send_response(200)
|
825
|
+
self.send_header("Content-type", "application/json")
|
826
|
+
self.end_headers()
|
827
|
+
response = {
|
828
|
+
"gateway": "api-gateway",
|
829
|
+
"requests_total": 100,
|
830
|
+
"requests_per_minute": 10
|
831
|
+
}
|
832
|
+
self.wfile.write(json.dumps(response).encode())
|
833
|
+
|
834
|
+
elif self.path == "/proxy":
|
835
|
+
api_key = self.headers.get("X-API-Key")
|
836
|
+
if not api_key:
|
837
|
+
self.send_response(401)
|
838
|
+
self.send_header("Content-type", "application/json")
|
839
|
+
self.end_headers()
|
840
|
+
response = {"error": "API key required"}
|
841
|
+
self.wfile.write(json.dumps(response).encode())
|
842
|
+
else:
|
843
|
+
self.send_response(200)
|
844
|
+
self.send_header("Content-type", "application/json")
|
845
|
+
self.end_headers()
|
846
|
+
response = {
|
847
|
+
"success": True,
|
848
|
+
"message": "Request proxied successfully",
|
849
|
+
"api_key": api_key
|
850
|
+
}
|
851
|
+
self.wfile.write(json.dumps(response).encode())
|
852
|
+
|
853
|
+
else:
|
854
|
+
self.send_response(404)
|
855
|
+
self.end_headers()
|
856
|
+
|
857
|
+
def log_message(self, format, *args):
|
858
|
+
# Suppress logging
|
859
|
+
pass
|
860
|
+
|
861
|
+
def run_server():
|
862
|
+
"""Run the HTTP server."""
|
863
|
+
server = HTTPServer(("0.0.0.0", 8080), GatewayHandler)
|
864
|
+
server.serve_forever()
|
865
|
+
|
866
|
+
# Start server in background thread
|
867
|
+
server_thread = threading.Thread(target=run_server, daemon=True)
|
868
|
+
server_thread.start()
|
869
|
+
|
870
|
+
# Wait for server to start
|
871
|
+
time.sleep(2)
|
872
|
+
|
873
|
+
try:
|
874
|
+
# Test server endpoints
|
875
|
+
print("Testing API Gateway server endpoints...")
|
876
|
+
|
877
|
+
# Test health endpoint
|
878
|
+
response = requests.get("http://localhost:8080/health", timeout=5)
|
879
|
+
print(f"Health endpoint: {response.status_code}")
|
880
|
+
|
881
|
+
# Test metrics endpoint
|
882
|
+
response = requests.get("http://localhost:8080/metrics", timeout=5)
|
883
|
+
print(f"Metrics endpoint: {response.status_code}")
|
884
|
+
|
885
|
+
# Test proxy endpoint with API key
|
886
|
+
headers = {"X-API-Key": "test_key_123"}
|
887
|
+
response = requests.get("http://localhost:8080/proxy", headers=headers, timeout=5)
|
888
|
+
print(f"Proxy endpoint: {response.status_code}")
|
889
|
+
|
890
|
+
print("✅ API Gateway server testing completed successfully")
|
891
|
+
|
892
|
+
except requests.exceptions.RequestException as e:
|
893
|
+
print(f"⚠️ API Gateway server testing failed: {e}")
|
894
|
+
|
895
|
+
print("API Gateway example completed")
|
@@ -23,7 +23,7 @@ import logging
|
|
23
23
|
import asyncio
|
24
24
|
import aiohttp
|
25
25
|
from typing import Dict, List, Any, Optional
|
26
|
-
from datetime import datetime, timedelta
|
26
|
+
from datetime import datetime, timedelta, timezone
|
27
27
|
|
28
28
|
from mcp_security_framework.core.security_manager import SecurityManager
|
29
29
|
from mcp_security_framework.core.auth_manager import AuthManager
|
@@ -99,10 +99,10 @@ class MicroserviceExample:
|
|
99
99
|
security_headers=DEFAULT_SECURITY_HEADERS
|
100
100
|
),
|
101
101
|
ssl=SSLConfig(
|
102
|
-
enabled=
|
103
|
-
cert_file=
|
104
|
-
key_file=
|
105
|
-
ca_cert_file=
|
102
|
+
enabled=False, # Disable SSL for example
|
103
|
+
cert_file=None,
|
104
|
+
key_file=None,
|
105
|
+
ca_cert_file=None,
|
106
106
|
verify_mode="CERT_REQUIRED",
|
107
107
|
min_version="TLSv1.2"
|
108
108
|
),
|
@@ -263,7 +263,7 @@ class MicroserviceExample:
|
|
263
263
|
"error": "Rate limit exceeded",
|
264
264
|
"error_code": ErrorCodes.RATE_LIMIT_EXCEEDED_ERROR,
|
265
265
|
"request_id": request_id,
|
266
|
-
"timestamp": datetime.
|
266
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
267
267
|
}
|
268
268
|
|
269
269
|
# Step 2: Authentication
|
@@ -275,7 +275,7 @@ class MicroserviceExample:
|
|
275
275
|
"error_code": auth_result.error_code,
|
276
276
|
"error_message": auth_result.error_message,
|
277
277
|
"request_id": request_id,
|
278
|
-
"timestamp": datetime.
|
278
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
279
279
|
}
|
280
280
|
|
281
281
|
# Step 3: Authorization
|
@@ -286,7 +286,7 @@ class MicroserviceExample:
|
|
286
286
|
"error": "Insufficient permissions",
|
287
287
|
"error_code": ErrorCodes.PERMISSION_DENIED_ERROR,
|
288
288
|
"request_id": request_id,
|
289
|
-
"timestamp": datetime.
|
289
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
290
290
|
}
|
291
291
|
|
292
292
|
# Step 4: Process the action
|
@@ -299,7 +299,7 @@ class MicroserviceExample:
|
|
299
299
|
"resource": resource,
|
300
300
|
"success": True,
|
301
301
|
"request_id": request_id,
|
302
|
-
"timestamp": datetime.
|
302
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
303
303
|
})
|
304
304
|
|
305
305
|
return {
|
@@ -311,7 +311,7 @@ class MicroserviceExample:
|
|
311
311
|
"auth_method": auth_result.auth_method
|
312
312
|
},
|
313
313
|
"request_id": request_id,
|
314
|
-
"timestamp": datetime.
|
314
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
315
315
|
}
|
316
316
|
|
317
317
|
except Exception as e:
|
@@ -321,7 +321,7 @@ class MicroserviceExample:
|
|
321
321
|
"error": "Internal server error",
|
322
322
|
"error_code": ErrorCodes.GENERAL_ERROR,
|
323
323
|
"request_id": request_id if 'request_id' in locals() else self._generate_request_id(),
|
324
|
-
"timestamp": datetime.
|
324
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
325
325
|
}
|
326
326
|
|
327
327
|
def authenticate_user(self, credentials: Dict[str, Any]) -> AuthResult:
|
@@ -506,7 +506,7 @@ class MicroserviceExample:
|
|
506
506
|
return {
|
507
507
|
"status": "healthy" if overall_healthy else "unhealthy",
|
508
508
|
"service": self.service_name,
|
509
|
-
"timestamp": datetime.
|
509
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
510
510
|
"checks": {
|
511
511
|
"security_manager": security_healthy,
|
512
512
|
"rate_limiter": rate_limit_healthy,
|
@@ -520,7 +520,7 @@ class MicroserviceExample:
|
|
520
520
|
"status": "unhealthy",
|
521
521
|
"service": self.service_name,
|
522
522
|
"error": str(e),
|
523
|
-
"timestamp": datetime.
|
523
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
524
524
|
}
|
525
525
|
|
526
526
|
async def get_metrics(self) -> Dict[str, Any]:
|
@@ -539,7 +539,7 @@ class MicroserviceExample:
|
|
539
539
|
|
540
540
|
return {
|
541
541
|
"service": self.service_name,
|
542
|
-
"timestamp": datetime.
|
542
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
543
543
|
"rate_limiting": rate_limit_stats,
|
544
544
|
"security": security_status,
|
545
545
|
"service_registry": {
|
@@ -552,7 +552,7 @@ class MicroserviceExample:
|
|
552
552
|
return {
|
553
553
|
"service": self.service_name,
|
554
554
|
"error": str(e),
|
555
|
-
"timestamp": datetime.
|
555
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
556
556
|
}
|
557
557
|
|
558
558
|
def get_security_status(self) -> Dict[str, Any]:
|
@@ -570,7 +570,7 @@ class MicroserviceExample:
|
|
570
570
|
"permissions_enabled": self.config.permissions.enabled,
|
571
571
|
"logging_enabled": self.config.logging.enabled,
|
572
572
|
"auth_methods": self.config.auth.methods,
|
573
|
-
"timestamp": datetime.
|
573
|
+
"timestamp": datetime.now(timezone.utc).isoformat()
|
574
574
|
}
|
575
575
|
|
576
576
|
|
@@ -688,3 +688,99 @@ async def main():
|
|
688
688
|
|
689
689
|
if __name__ == "__main__":
|
690
690
|
asyncio.run(main())
|
691
|
+
|
692
|
+
# Start HTTP server in background for testing
|
693
|
+
print("\nStarting Microservice HTTP Server in background...")
|
694
|
+
|
695
|
+
import threading
|
696
|
+
import time
|
697
|
+
import requests
|
698
|
+
from http.server import HTTPServer, BaseHTTPRequestHandler
|
699
|
+
import json
|
700
|
+
|
701
|
+
class MicroserviceHandler(BaseHTTPRequestHandler):
|
702
|
+
def do_GET(self):
|
703
|
+
if self.path == "/health":
|
704
|
+
self.send_response(200)
|
705
|
+
self.send_header("Content-type", "application/json")
|
706
|
+
self.end_headers()
|
707
|
+
response = {"status": "healthy", "service": "user-service"}
|
708
|
+
self.wfile.write(json.dumps(response).encode())
|
709
|
+
|
710
|
+
elif self.path == "/metrics":
|
711
|
+
self.send_response(200)
|
712
|
+
self.send_header("Content-type", "application/json")
|
713
|
+
self.end_headers()
|
714
|
+
response = {
|
715
|
+
"service": "user-service",
|
716
|
+
"requests_total": 50,
|
717
|
+
"requests_per_minute": 5
|
718
|
+
}
|
719
|
+
self.wfile.write(json.dumps(response).encode())
|
720
|
+
|
721
|
+
elif self.path == "/api/v1/users/123":
|
722
|
+
api_key = self.headers.get("X-API-Key")
|
723
|
+
if not api_key:
|
724
|
+
self.send_response(401)
|
725
|
+
self.send_header("Content-type", "application/json")
|
726
|
+
self.end_headers()
|
727
|
+
response = {"error": "API key required"}
|
728
|
+
self.wfile.write(json.dumps(response).encode())
|
729
|
+
else:
|
730
|
+
self.send_response(200)
|
731
|
+
self.send_header("Content-type", "application/json")
|
732
|
+
self.end_headers()
|
733
|
+
response = {
|
734
|
+
"success": True,
|
735
|
+
"data": {
|
736
|
+
"user_id": "123",
|
737
|
+
"username": "test_user",
|
738
|
+
"email": "test@example.com"
|
739
|
+
},
|
740
|
+
"service": "user-service"
|
741
|
+
}
|
742
|
+
self.wfile.write(json.dumps(response).encode())
|
743
|
+
|
744
|
+
else:
|
745
|
+
self.send_response(404)
|
746
|
+
self.end_headers()
|
747
|
+
|
748
|
+
def log_message(self, format, *args):
|
749
|
+
# Suppress logging
|
750
|
+
pass
|
751
|
+
|
752
|
+
def run_server():
|
753
|
+
"""Run the HTTP server."""
|
754
|
+
server = HTTPServer(("0.0.0.0", 8081), MicroserviceHandler)
|
755
|
+
server.serve_forever()
|
756
|
+
|
757
|
+
# Start server in background thread
|
758
|
+
server_thread = threading.Thread(target=run_server, daemon=True)
|
759
|
+
server_thread.start()
|
760
|
+
|
761
|
+
# Wait for server to start
|
762
|
+
time.sleep(2)
|
763
|
+
|
764
|
+
try:
|
765
|
+
# Test server endpoints
|
766
|
+
print("Testing Microservice server endpoints...")
|
767
|
+
|
768
|
+
# Test health endpoint
|
769
|
+
response = requests.get("http://localhost:8081/health", timeout=5)
|
770
|
+
print(f"Health endpoint: {response.status_code}")
|
771
|
+
|
772
|
+
# Test metrics endpoint
|
773
|
+
response = requests.get("http://localhost:8081/metrics", timeout=5)
|
774
|
+
print(f"Metrics endpoint: {response.status_code}")
|
775
|
+
|
776
|
+
# Test API endpoint with API key
|
777
|
+
headers = {"X-API-Key": "service_key_123"}
|
778
|
+
response = requests.get("http://localhost:8081/api/v1/users/123", headers=headers, timeout=5)
|
779
|
+
print(f"API endpoint: {response.status_code}")
|
780
|
+
|
781
|
+
print("✅ Microservice server testing completed successfully")
|
782
|
+
|
783
|
+
except requests.exceptions.RequestException as e:
|
784
|
+
print(f"⚠️ Microservice server testing failed: {e}")
|
785
|
+
|
786
|
+
print("Microservice example completed")
|