Skip to content

Commit 9b818de

Browse files
HarshCasperwhummerclaude
authored
fix(paradedb): prevent extension from intercepting all HTTP requests (#124)
* fix(paradedb): prevent extension from intercepting all HTTP requests * test(paradedb): add test for mixed TCP and HTTP traffic Adds a test to verify that the ParadeDB extension correctly handles mixed protocol traffic - PostgreSQL wire protocol (TCP) for ParadeDB queries alongside regular HTTP-based AWS API requests (S3, STS). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * tmp commit to trigger error * undo tmp change --------- Co-authored-by: Waldemar Hummer <waldemar.hummer@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 902c605 commit 9b818de

2 files changed

Lines changed: 96 additions & 0 deletions

File tree

paradedb/localstack_paradedb/extension.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from localstack_extensions.utils.docker import ProxiedDockerContainerExtension
55
from localstack import config
6+
from localstack.extensions.api import http
67

78
# Environment variables for configuration
89
ENV_POSTGRES_USER = "PARADEDB_POSTGRES_USER"
@@ -56,6 +57,23 @@ def _tcp_health_check():
5657
tcp_ports=[postgres_port], # Enable TCP proxying through gateway
5758
)
5859

60+
# TODO: this should be migrated into the base class directly ..!
61+
def update_gateway_routes(self, router: http.Router[http.RouteHandler]):
62+
"""
63+
Override to set up only TCP routing without HTTP proxy.
64+
65+
ParadeDB uses the native PostgreSQL wire protocol (not HTTP), so we
66+
only need TCP protocol routing - not HTTP proxying. Adding an HTTP
67+
proxy without a host restriction would cause all HTTP requests to be
68+
forwarded to the PostgreSQL container, breaking other services.
69+
"""
70+
# Start the container
71+
self.start_container()
72+
73+
# Set up only TCP protocol routing (skip HTTP proxy from base class)
74+
if self.tcp_ports:
75+
self._setup_tcp_protocol_routing()
76+
5977
def tcp_connection_matcher(self, data: bytes) -> bool:
6078
"""
6179
Identify PostgreSQL/ParadeDB connections by protocol handshake.

paradedb/tests/test_extension.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import boto3
12
import psycopg2
23
from localstack.utils.strings import short_uid
34

@@ -104,3 +105,80 @@ def test_paradedb_quickstart():
104105
conn.commit()
105106
cursor.close()
106107
conn.close()
108+
109+
110+
def test_mixed_tcp_and_http_traffic():
111+
"""
112+
Test that mixed TCP (ParadeDB) and HTTP (AWS) traffic works correctly.
113+
114+
This verifies that the ParadeDB extension only intercepts PostgreSQL wire
115+
protocol connections and doesn't interfere with regular HTTP-based AWS
116+
API requests to LocalStack.
117+
"""
118+
# First, verify ParadeDB TCP connection works
119+
conn = get_connection()
120+
cursor = conn.cursor()
121+
cursor.execute("SELECT 1 as test_value;")
122+
result = cursor.fetchone()
123+
assert result[0] == 1, "ParadeDB TCP connection should work"
124+
cursor.close()
125+
conn.close()
126+
127+
# Now verify AWS HTTP requests still work (S3 and STS)
128+
# These should NOT be intercepted by the ParadeDB extension
129+
endpoint_url = f"http://localhost:{PORT}"
130+
131+
# Test S3 HTTP requests
132+
s3_client = boto3.client(
133+
"s3",
134+
endpoint_url=endpoint_url,
135+
aws_access_key_id="test",
136+
aws_secret_access_key="test",
137+
region_name="us-east-1",
138+
)
139+
140+
bucket_name = f"test-bucket-{short_uid()}"
141+
s3_client.create_bucket(Bucket=bucket_name)
142+
143+
# List buckets to verify HTTP API is working
144+
buckets = s3_client.list_buckets()
145+
bucket_names = [b["Name"] for b in buckets["Buckets"]]
146+
assert bucket_name in bucket_names, "S3 HTTP API should work alongside ParadeDB TCP"
147+
148+
# Put and get an object
149+
test_key = "test-object.txt"
150+
test_content = b"Hello from mixed TCP/HTTP test!"
151+
s3_client.put_object(Bucket=bucket_name, Key=test_key, Body=test_content)
152+
response = s3_client.get_object(Bucket=bucket_name, Key=test_key)
153+
retrieved_content = response["Body"].read()
154+
assert retrieved_content == test_content, "S3 object operations should work"
155+
156+
# Clean up S3
157+
s3_client.delete_object(Bucket=bucket_name, Key=test_key)
158+
s3_client.delete_bucket(Bucket=bucket_name)
159+
160+
# Test STS HTTP requests
161+
sts_client = boto3.client(
162+
"sts",
163+
endpoint_url=endpoint_url,
164+
aws_access_key_id="test",
165+
aws_secret_access_key="test",
166+
region_name="us-east-1",
167+
)
168+
169+
caller_identity = sts_client.get_caller_identity()
170+
assert "Account" in caller_identity, (
171+
"STS HTTP API should work alongside ParadeDB TCP"
172+
)
173+
assert "Arn" in caller_identity, "STS should return valid caller identity"
174+
175+
# Finally, verify ParadeDB still works after HTTP requests
176+
conn = get_connection()
177+
cursor = conn.cursor()
178+
cursor.execute("SELECT 'tcp_works_after_http' as verification;")
179+
result = cursor.fetchone()
180+
assert result[0] == "tcp_works_after_http", (
181+
"ParadeDB should still work after HTTP requests"
182+
)
183+
cursor.close()
184+
conn.close()

0 commit comments

Comments
 (0)