|
50 | 50 |
|
51 | 51 | if not sys.implementation.name == "circuitpython":
|
52 | 52 | from types import TracebackType
|
53 |
| - from typing import Any, Dict, Optional, Type |
| 53 | + from typing import IO, Any, Dict, Optional, Type |
54 | 54 |
|
55 | 55 | from circuitpython_typing.socket import (
|
56 | 56 | SocketpoolModuleType,
|
@@ -387,19 +387,7 @@ def _build_boundary_data(self, files: dict): # pylint: disable=too-many-locals
|
387 | 387 | boundary_objects.append("\r\n")
|
388 | 388 |
|
389 | 389 | if hasattr(file_handle, "read"):
|
390 |
| - is_binary = False |
391 |
| - try: |
392 |
| - content = file_handle.read(1) |
393 |
| - is_binary = isinstance(content, bytes) |
394 |
| - except UnicodeError: |
395 |
| - is_binary = False |
396 |
| - |
397 |
| - if not is_binary: |
398 |
| - raise ValueError("Files must be opened in binary mode") |
399 |
| - |
400 |
| - file_handle.seek(0, SEEK_END) |
401 |
| - content_length += file_handle.tell() |
402 |
| - file_handle.seek(0) |
| 390 | + content_length += self._get_file_length(file_handle) |
403 | 391 |
|
404 | 392 | boundary_objects.append(file_handle)
|
405 | 393 | boundary_objects.append("\r\n")
|
@@ -428,6 +416,25 @@ def _check_headers(headers: Dict[str, str]):
|
428 | 416 | f"Header part ({value}) from {key} must be of type str or bytes, not {type(value)}"
|
429 | 417 | )
|
430 | 418 |
|
| 419 | + @staticmethod |
| 420 | + def _get_file_length(file_handle: IO): |
| 421 | + is_binary = False |
| 422 | + try: |
| 423 | + file_handle.seek(0) |
| 424 | + # read at least 4 bytes incase we are reading a b64 stream |
| 425 | + content = file_handle.read(4) |
| 426 | + is_binary = isinstance(content, bytes) |
| 427 | + except UnicodeError: |
| 428 | + is_binary = False |
| 429 | + |
| 430 | + if not is_binary: |
| 431 | + raise ValueError("Files must be opened in binary mode") |
| 432 | + |
| 433 | + file_handle.seek(0, SEEK_END) |
| 434 | + content_length = file_handle.tell() |
| 435 | + file_handle.seek(0) |
| 436 | + return content_length |
| 437 | + |
431 | 438 | @staticmethod
|
432 | 439 | def _send(socket: SocketType, data: bytes):
|
433 | 440 | total_sent = 0
|
@@ -458,13 +465,16 @@ def _send_boundary_objects(self, socket: SocketType, boundary_objects: Any):
|
458 | 465 | if isinstance(boundary_object, str):
|
459 | 466 | self._send_as_bytes(socket, boundary_object)
|
460 | 467 | else:
|
461 |
| - chunk_size = 32 |
462 |
| - b = bytearray(chunk_size) |
463 |
| - while True: |
464 |
| - size = boundary_object.readinto(b) |
465 |
| - if size == 0: |
466 |
| - break |
467 |
| - self._send(socket, b[:size]) |
| 468 | + self._send_file(socket, boundary_object) |
| 469 | + |
| 470 | + def _send_file(self, socket: SocketType, file_handle: IO): |
| 471 | + chunk_size = 36 |
| 472 | + b = bytearray(chunk_size) |
| 473 | + while True: |
| 474 | + size = file_handle.readinto(b) |
| 475 | + if size == 0: |
| 476 | + break |
| 477 | + self._send(socket, b[:size]) |
468 | 478 |
|
469 | 479 | def _send_header(self, socket, header, value):
|
470 | 480 | if value is None:
|
@@ -517,12 +527,16 @@ def _send_request( # pylint: disable=too-many-arguments
|
517 | 527 |
|
518 | 528 | # If files are send, build data to send and calculate length
|
519 | 529 | content_length = 0
|
| 530 | + data_is_file = False |
520 | 531 | boundary_objects = None
|
521 | 532 | if files and isinstance(files, dict):
|
522 | 533 | boundary_string, content_length, boundary_objects = (
|
523 | 534 | self._build_boundary_data(files)
|
524 | 535 | )
|
525 | 536 | content_type_header = f"multipart/form-data; boundary={boundary_string}"
|
| 537 | + elif data and hasattr(data, "read"): |
| 538 | + data_is_file = True |
| 539 | + content_length = self._get_file_length(data) |
526 | 540 | else:
|
527 | 541 | if data is None:
|
528 | 542 | data = b""
|
@@ -551,7 +565,9 @@ def _send_request( # pylint: disable=too-many-arguments
|
551 | 565 | self._send(socket, b"\r\n")
|
552 | 566 |
|
553 | 567 | # Send data
|
554 |
| - if data: |
| 568 | + if data_is_file: |
| 569 | + self._send_file(socket, data) |
| 570 | + elif data: |
555 | 571 | self._send(socket, bytes(data))
|
556 | 572 | elif boundary_objects:
|
557 | 573 | self._send_boundary_objects(socket, boundary_objects)
|
|
0 commit comments