41
41
42
42
import errno
43
43
import json as json_module
44
+ import random
44
45
import sys
45
46
46
47
from adafruit_connection_manager import get_connection_manager
47
48
48
49
if not sys .implementation .name == "circuitpython" :
50
+ from io import FileIO
49
51
from types import TracebackType
50
52
from typing import Any , Dict , Optional , Type
51
53
@@ -394,6 +396,13 @@ def _send(socket: SocketType, data: bytes):
394
396
def _send_as_bytes (self , socket : SocketType , data : str ):
395
397
return self ._send (socket , bytes (data , "utf-8" ))
396
398
399
+ def _generate_boundary_str (self ):
400
+ hex_characters = "0123456789abcdef"
401
+ _boundary = ""
402
+ for i in range (32 ):
403
+ _boundary += random .choice (hex_characters )
404
+ return _boundary
405
+
397
406
def _send_header (self , socket , header , value ):
398
407
if value is None :
399
408
return
@@ -415,6 +424,7 @@ def _send_request(
415
424
headers : Dict [str , str ],
416
425
data : Any ,
417
426
json : Any ,
427
+ files : Optional [Dict [str , tuple ]],
418
428
):
419
429
# Check headers
420
430
self ._check_headers (headers )
@@ -425,6 +435,7 @@ def _send_request(
425
435
# If json is sent, set content type header and convert to string
426
436
if json is not None :
427
437
assert data is None
438
+ assert files is None
428
439
content_type_header = "application/json"
429
440
data = json_module .dumps (json )
430
441
@@ -441,13 +452,69 @@ def _send_request(
441
452
if data and isinstance (data , str ):
442
453
data = bytes (data , "utf-8" )
443
454
455
+ if data is None :
456
+ data = b""
457
+
444
458
self ._send_as_bytes (socket , method )
445
459
self ._send (socket , b" /" )
446
460
self ._send_as_bytes (socket , path )
447
461
self ._send (socket , b" HTTP/1.1\r \n " )
448
462
449
463
# create lower-case supplied header list
450
464
supplied_headers = {header .lower () for header in headers }
465
+ boundary_str = None
466
+
467
+ if files is not None and isinstance (files , dict ):
468
+ boundary_str = self ._generate_boundary_str ()
469
+ content_type_header = f"multipart/form-data; boundary={ boundary_str } "
470
+
471
+ for fieldname in files .keys ():
472
+ if not fieldname .endswith ("-name" ):
473
+ if files [fieldname ][0 ] is not None :
474
+ file_content = files [fieldname ][1 ].read ()
475
+
476
+ data += b"--" + boundary_str .encode () + b"\r \n "
477
+ data += (
478
+ b'Content-Disposition: form-data; name="'
479
+ + fieldname .encode ()
480
+ + b'"; filename="'
481
+ + files [fieldname ][0 ].encode ()
482
+ + b'"\r \n '
483
+ )
484
+ if len (files [fieldname ]) >= 3 :
485
+ data += (
486
+ b"Content-Type: "
487
+ + files [fieldname ][2 ].encode ()
488
+ + b"\r \n "
489
+ )
490
+ if len (files [fieldname ]) >= 4 :
491
+ for custom_header_key in files [fieldname ][3 ].keys ():
492
+ data += (
493
+ custom_header_key .encode ()
494
+ + b": "
495
+ + files [fieldname ][3 ][custom_header_key ].encode ()
496
+ + b"\r \n "
497
+ )
498
+ data += b"\r \n "
499
+ data += file_content + b"\r \n "
500
+ else :
501
+ # filename is None
502
+ data += b"--" + boundary_str .encode () + b"\r \n "
503
+ data += (
504
+ b'Content-Disposition: form-data; name="'
505
+ + fieldname .encode ()
506
+ + b'"; \r \n '
507
+ )
508
+ if len (files [fieldname ]) >= 3 :
509
+ data += (
510
+ b"Content-Type: "
511
+ + files [fieldname ][2 ].encode ()
512
+ + b"\r \n "
513
+ )
514
+ data += b"\r \n "
515
+ data += files [fieldname ][1 ].encode () + b"\r \n "
516
+
517
+ data += b"--" + boundary_str .encode () + b"--"
451
518
452
519
# Send headers
453
520
if not "host" in supplied_headers :
@@ -478,6 +545,7 @@ def request(
478
545
stream : bool = False ,
479
546
timeout : float = 60 ,
480
547
allow_redirects : bool = True ,
548
+ files : Optional [Dict [str , tuple ]] = None ,
481
549
) -> Response :
482
550
"""Perform an HTTP request to the given url which we will parse to determine
483
551
whether to use SSL ('https://') or not. We can also send some provided 'data'
@@ -526,7 +594,9 @@ def request(
526
594
)
527
595
ok = True
528
596
try :
529
- self ._send_request (socket , host , method , path , headers , data , json )
597
+ self ._send_request (
598
+ socket , host , method , path , headers , data , json , files
599
+ )
530
600
except OSError as exc :
531
601
last_exc = exc
532
602
ok = False
0 commit comments