How to Mock an Ambassador Edge Proxy in Python
Build a lightweight mock Ambassador edge proxy with Python's http.server that responds to health and user endpoint requests for local development and testing.
Python code
34 linesimport http.server
import json
import urllib.parse
import threading
class AmbassadorProxyHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
parsed = urllib.parse.urlparse(self.path)
if parsed.path == "/health":
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps({"status": "healthy"}).encode())
elif parsed.path == "/users":
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps({"users": [{"id": 1, "name": "Alice"}]}).encode())
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b'{"error": "not found"}')
def log_message(self, format, *args):
pass
def run_mock_proxy(port=8080, host="127.0.0.1"):
server = http.server.ThreadingHTTPServer((host, port), AmbassadorProxyHandler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
return f"Mock Ambassador proxy running at http://{host}:{port}"
if __name__ == "__main__":
print(run_mock_proxy())
Output
Mock Ambassador proxy running at http://127.0.0.1:8080
# GET /health returns:
{"status": "healthy"}
# GET /users returns:
{"users": [{"id": 1, "name": "Alice"}]}
# Any other path returns:
{"error": "not found"}
How it works
The ThreadingHTTPServer in the stdlib handles each request in its own thread, which lets the mock proxy stay responsive under concurrent calls from a service mesh or gateway. The handler extends BaseHTTPRequestHandler to intercept GET requests and route them to simple JSON responses mimicking Ambassador's health or service endpoints. Using urllib.parse.urlparse splits the request path from query parameters so routing logic stays clean. The daemon thread allows the server to start and stop cleanly alongside your test suite.
Common mistakes
- Starting the server on port 0 to get a random free port, then trying to use port 8080 in tests — keep track of the actual assigned port.
- Forgetting to override `log_message` — the default logging prints every request and clutters test output.
- Shutting down with `server.shutdown()` while requests are in flight — use `server.server_close()` after a clean stop.
- Binding to 127.0.0.1 when the service under test runs in Docker or a VM — use 0.0.0.0 to expose the mock externally.
Variations
- Use `http.server.HTTPServer` instead of `ThreadingHTTPServer` if your test suite never issues concurrent requests.
- Implement `do_POST` and `do_DELETE` to mock writable Ambassador endpoints like route updates or auth tokens.
Real-world use cases
- Stand in for an Ambassador gateway during local microservice development so you can test routing logic without standing up a full mesh.
- Provide deterministic health and user responses in CI pipelines, letting integration tests assert service-to-service behavior without external dependencies.
- Mock Ambassador's auth or rate-limit responses when load-testing downstream services, isolating them from real gateway behavior.
Sponsored
More from Microservices patterns
- BFF aggregation pattern: combine multiple service responses in Python easy
- Backward Compatible Schema Evolution in Python medium
- Bulkhead Thread Pool per Service Mock in Python medium
- CQRS with Separate Read and Write Repositories in Python medium
- Cache-Aside Pattern in Python: Per-Service Mock easy
- Consumer Driven Contract Pact Mock in Python medium
Keep learning
Related tutorials and quizzes for this topic.