Skip to content

Commit a67aa8e

Browse files
committed
Solving the iter_lines problem
1 parent 64917ff commit a67aa8e

File tree

1 file changed

+43
-2
lines changed

1 file changed

+43
-2
lines changed

locust/contrib/fasthttp.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,16 +317,25 @@ def get(self, url: str, **kwargs: Unpack[RESTKwargs]) -> ResponseContextManager:
317317
return self.request("GET", url, **kwargs)
318318

319319
def iter_lines(self, url: str, method: str = "GET", **kwargs) -> Generator[str]:
320-
"""Sends a iter_lines request"""
320+
"""Sends a iter_lines request for streaming responses"""
321321
response = self.request(method, url, stream=True, **kwargs)
322322
response.raise_for_status()
323+
323324
buffer = ""
324325
for chunk in response.iter_content(chunk_size=1024, decode_content=True):
325-
buffer += chunk.decode("utf-8")
326+
# Ensure that chunk is a string.
327+
if isinstance(chunk, bytes):
328+
chunk = chunk.decode('utf-8', errors='replace')
329+
330+
buffer += chunk
326331
while "\n" in buffer:
327332
line, buffer = buffer.split("\n", 1)
328333
yield line
329334

335+
# Return to the last line that may be incomplete.
336+
if buffer:
337+
yield buffer
338+
330339
def head(self, url: str, **kwargs: Unpack[RESTKwargs]) -> ResponseContextManager:
331340
"""Sends a HEAD request"""
332341
return self.request("HEAD", url, **kwargs)
@@ -569,6 +578,38 @@ def raise_for_status(self):
569578
if error := getattr(self, "error", None):
570579
raise error
571580

581+
def iter_content(self, chunk_size=1024, decode_content=True):
582+
"""
583+
Simulates the `requests.Response.iter_content` method
584+
585+
Used for streaming response content
586+
587+
:param `chunk_size`: The size of the chunk read each time
588+
589+
:param `decode_content`: Whether to decode the content (from bytes to string)
590+
591+
:return: A generator that produces one chunk at a time
592+
"""
593+
if not self._response:
594+
raise LocustError("Cannot iterate content on a response without _response attribute")
595+
596+
while True:
597+
try:
598+
chunk = self._response.read(chunk_size)
599+
if not chunk:
600+
break
601+
602+
if decode_content and isinstance(chunk, bytes):
603+
try:
604+
chunk = chunk.decode('utf-8')
605+
except UnicodeDecodeError:
606+
# If decoding fails, preserve the data in byte format.
607+
pass
608+
609+
yield chunk
610+
except (HTTPConnectionClosed, ConnectionError):
611+
break
612+
572613
@property
573614
def status_code(self) -> int:
574615
"""

0 commit comments

Comments
 (0)