Skip to content

Commit 0b997aa

Browse files
committed
tests: integration: fix http server response overrides
Signed-off-by: Eduardo Silva <eduardo@chronosphere.io>
1 parent 79616f6 commit 0b997aa

2 files changed

Lines changed: 170 additions & 31 deletions

File tree

tests/integration/scenarios/out_http/tests/test_out_http_001.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import logging
33
import os
4+
import time
45

56
import requests
67

@@ -9,11 +10,39 @@
910
configure_oauth_token_response,
1011
data_storage,
1112
http_server_run,
13+
server_instances,
1214
)
1315
from utils.test_service import FluentBitTestService
1416

1517
logger = logging.getLogger(__name__)
1618

19+
def _wait_for_http_server(port, timeout=5):
20+
deadline = time.time() + timeout
21+
22+
while time.time() < deadline:
23+
try:
24+
response = requests.get(f"http://127.0.0.1:{port}/ping", timeout=1)
25+
if response.status_code == 200:
26+
return
27+
except requests.RequestException:
28+
pass
29+
30+
time.sleep(0.1)
31+
32+
raise TimeoutError(f"Timed out waiting for HTTP server on port {port}")
33+
34+
35+
def _wait_for_http_server_port(timeout=5):
36+
deadline = time.time() + timeout
37+
38+
while time.time() < deadline:
39+
if server_instances:
40+
return server_instances[-1].server_port
41+
42+
time.sleep(0.1)
43+
44+
raise TimeoutError("Timed out waiting for HTTP server port assignment")
45+
1746

1847
class Service:
1948
def __init__(self, config_file, *, response_setup=None, use_tls=False):
@@ -281,3 +310,93 @@ def test_out_http_tls_read_idle_timeout_retries_partial_response():
281310

282311
assert len(requests_seen) >= 2
283312
assert "read idle timeout reached" in log_text
313+
314+
315+
def test_http_server_configure_helpers_allow_clearing_nullable_fields():
316+
http_server_run(0, reset_state=True)
317+
port = _wait_for_http_server_port()
318+
_wait_for_http_server(port)
319+
320+
try:
321+
configure_http_response(
322+
stream_fragments=["part"],
323+
hang_after_fragment_index=0,
324+
)
325+
configure_http_response(
326+
stream_fragments=None,
327+
hang_after_fragment_index=None,
328+
)
329+
330+
response = requests.post(
331+
f"http://127.0.0.1:{port}/data",
332+
json={"test": "clear"},
333+
timeout=5,
334+
)
335+
336+
assert response.status_code == 200
337+
assert response.json() == {"status": "received"}
338+
finally:
339+
try:
340+
requests.post(f"http://127.0.0.1:{port}/shutdown", timeout=2)
341+
except requests.RequestException:
342+
pass
343+
344+
345+
def test_http_server_oauth_token_honors_explicit_content_type_and_raw_body():
346+
http_server_run(0, reset_state=True)
347+
port = _wait_for_http_server_port()
348+
_wait_for_http_server(port)
349+
350+
try:
351+
configure_oauth_token_response(
352+
stream_fragments=["partial-token"],
353+
hang_after_fragment_index=0,
354+
)
355+
configure_oauth_token_response(
356+
body="not-json",
357+
content_type="text/plain",
358+
stream_fragments=None,
359+
hang_after_fragment_index=None,
360+
)
361+
362+
response = requests.post(
363+
f"http://127.0.0.1:{port}/oauth/token",
364+
data="grant_type=client_credentials",
365+
timeout=5,
366+
)
367+
368+
assert response.status_code == 200
369+
assert response.text == "not-json"
370+
assert response.headers["Content-Type"].startswith("text/plain")
371+
finally:
372+
try:
373+
requests.post(f"http://127.0.0.1:{port}/shutdown", timeout=2)
374+
except requests.RequestException:
375+
pass
376+
377+
378+
def test_http_server_oauth_token_honors_explicit_json_content_type():
379+
http_server_run(0, reset_state=True)
380+
port = _wait_for_http_server_port()
381+
_wait_for_http_server(port)
382+
383+
try:
384+
configure_oauth_token_response(
385+
body={"access_token": "json-token", "token_type": "Bearer"},
386+
content_type="application/json; charset=utf-8",
387+
)
388+
389+
response = requests.post(
390+
f"http://127.0.0.1:{port}/oauth/token",
391+
data="grant_type=client_credentials",
392+
timeout=5,
393+
)
394+
395+
assert response.status_code == 200
396+
assert response.json()["access_token"] == "json-token"
397+
assert "application/json" in response.headers["Content-Type"]
398+
finally:
399+
try:
400+
requests.post(f"http://127.0.0.1:{port}/shutdown", timeout=2)
401+
except requests.RequestException:
402+
pass

tests/integration/src/server/http_server.py

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
server_thread = None
6464
server_instances = []
6565
shutdown_event = threading.Event()
66+
UNSET = object()
6667

6768

6869
def _sleep_interruptible(seconds):
@@ -115,51 +116,51 @@ def reset_http_server_state():
115116
)
116117

117118

118-
def configure_http_response(*, status_code=None, body=None, content_type=None,
119-
delay_seconds=None, stream_fragments=None,
120-
fragment_delay_seconds=None,
121-
hang_before_response=None,
122-
hang_after_fragment_index=None):
123-
if status_code is not None:
119+
def configure_http_response(*, status_code=UNSET, body=UNSET, content_type=UNSET,
120+
delay_seconds=UNSET, stream_fragments=UNSET,
121+
fragment_delay_seconds=UNSET,
122+
hang_before_response=UNSET,
123+
hang_after_fragment_index=UNSET):
124+
if status_code is not UNSET:
124125
response_config["status_code"] = status_code
125-
if body is not None:
126+
if body is not UNSET:
126127
response_config["body"] = body
127-
if content_type is not None:
128+
if content_type is not UNSET:
128129
response_config["content_type"] = content_type
129-
if delay_seconds is not None:
130+
if delay_seconds is not UNSET:
130131
response_config["delay_seconds"] = delay_seconds
131-
if stream_fragments is not None:
132-
response_config["stream_fragments"] = list(stream_fragments)
133-
if fragment_delay_seconds is not None:
132+
if stream_fragments is not UNSET:
133+
response_config["stream_fragments"] = None if stream_fragments is None else list(stream_fragments)
134+
if fragment_delay_seconds is not UNSET:
134135
response_config["fragment_delay_seconds"] = fragment_delay_seconds
135-
if hang_before_response is not None:
136+
if hang_before_response is not UNSET:
136137
response_config["hang_before_response"] = hang_before_response
137-
if hang_after_fragment_index is not None:
138+
if hang_after_fragment_index is not UNSET:
138139
response_config["hang_after_fragment_index"] = hang_after_fragment_index
139140

140141

141-
def configure_oauth_token_response(*, status_code=None, body=None,
142-
content_type=None,
143-
delay_seconds=None,
144-
hang_before_response=None,
145-
stream_fragments=None,
146-
fragment_delay_seconds=None,
147-
hang_after_fragment_index=None):
148-
if status_code is not None:
142+
def configure_oauth_token_response(*, status_code=UNSET, body=UNSET,
143+
content_type=UNSET,
144+
delay_seconds=UNSET,
145+
hang_before_response=UNSET,
146+
stream_fragments=UNSET,
147+
fragment_delay_seconds=UNSET,
148+
hang_after_fragment_index=UNSET):
149+
if status_code is not UNSET:
149150
oauth_token_response["status_code"] = status_code
150-
if body is not None:
151+
if body is not UNSET:
151152
oauth_token_response["body"] = body
152-
if content_type is not None:
153+
if content_type is not UNSET:
153154
oauth_token_response["content_type"] = content_type
154-
if delay_seconds is not None:
155+
if delay_seconds is not UNSET:
155156
oauth_token_response["delay_seconds"] = delay_seconds
156-
if hang_before_response is not None:
157+
if hang_before_response is not UNSET:
157158
oauth_token_response["hang_before_response"] = hang_before_response
158-
if stream_fragments is not None:
159-
oauth_token_response["stream_fragments"] = list(stream_fragments)
160-
if fragment_delay_seconds is not None:
159+
if stream_fragments is not UNSET:
160+
oauth_token_response["stream_fragments"] = None if stream_fragments is None else list(stream_fragments)
161+
if fragment_delay_seconds is not UNSET:
161162
oauth_token_response["fragment_delay_seconds"] = fragment_delay_seconds
162-
if hang_after_fragment_index is not None:
163+
if hang_after_fragment_index is not UNSET:
163164
oauth_token_response["hang_after_fragment_index"] = hang_after_fragment_index
164165

165166

@@ -284,7 +285,26 @@ def oauth_token():
284285
return Response(status=503)
285286
if oauth_token_response["stream_fragments"] is not None:
286287
return _build_streaming_response(oauth_token_response)
287-
return jsonify(oauth_token_response["body"]), oauth_token_response["status_code"]
288+
289+
body = oauth_token_response["body"]
290+
content_type = oauth_token_response.get("content_type")
291+
normalized_content_type = None
292+
293+
if content_type is not None:
294+
normalized_content_type = content_type.split(";", 1)[0].strip().lower()
295+
296+
if isinstance(body, (dict, list)) and (
297+
normalized_content_type is None or
298+
normalized_content_type == "application/json" or
299+
normalized_content_type.endswith("+json")
300+
):
301+
return jsonify(body), oauth_token_response["status_code"]
302+
303+
return Response(
304+
body,
305+
status=oauth_token_response["status_code"],
306+
content_type=content_type,
307+
)
288308

289309

290310
@app.route('/ping', methods=['GET'])

0 commit comments

Comments
 (0)