appmesh 1.3.4__py3-none-any.whl → 1.3.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- appmesh/appmesh_client.py +45 -28
- {appmesh-1.3.4.dist-info → appmesh-1.3.5.dist-info}/METADATA +1 -1
- appmesh-1.3.5.dist-info/RECORD +6 -0
- {appmesh-1.3.4.dist-info → appmesh-1.3.5.dist-info}/WHEEL +1 -1
- appmesh-1.3.4.dist-info/RECORD +0 -6
- {appmesh-1.3.4.dist-info → appmesh-1.3.5.dist-info}/top_level.txt +0 -0
appmesh/appmesh_client.py
CHANGED
@@ -29,9 +29,12 @@ DEFAULT_SSL_CA_PEM_FILE = "/opt/appmesh/ssl/ca.pem"
|
|
29
29
|
DEFAULT_SSL_CLIENT_PEM_FILE = "/opt/appmesh/ssl/client.pem"
|
30
30
|
DEFAULT_SSL_CLIENT_PEM_KEY_FILE = "/opt/appmesh/ssl/client-key.pem"
|
31
31
|
|
32
|
+
# TLS-optimized chunk size (slightly less than maximum TLS record size)
|
33
|
+
# leaves some room for TLS overhead (like headers) within the 16 KB limit.
|
34
|
+
TCP_CHUNK_BLOCK_SIZE = 16 * 1024 - 256 # target to 16KB
|
35
|
+
TCP_MESSAGE_HEADER_LENGTH = 4
|
32
36
|
REST_TEXT_MESSAGE_JSON_KEY = "message"
|
33
37
|
MESSAGE_ENCODING_UTF8 = "utf-8"
|
34
|
-
TCP_MESSAGE_HEADER_LENGTH = 4
|
35
38
|
|
36
39
|
HTTP_USER_AGENT = "appmesh/python"
|
37
40
|
HTTP_USER_AGENT_TCP = "appmesh/python/tcp"
|
@@ -450,7 +453,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
450
453
|
the server's TLS certificate, or a string, in which case it must be a path to a CA bundle to use. Defaults to ``True``.
|
451
454
|
rest_ssl_client_cert (tuple, optional): SSL client certificate and key pair. If String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
|
452
455
|
rest_timeout (tuple, optional): HTTP timeout, Defaults to 60 seconds for connect timeout and 300 seconds for read timeout
|
453
|
-
jwt_token (str, optional): JWT token, provide correct token is same with login() &
|
456
|
+
jwt_token (str, optional): JWT token, provide correct token is same with login() & authentication().
|
454
457
|
"""
|
455
458
|
|
456
459
|
self.server_url = rest_url
|
@@ -1221,20 +1224,20 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1221
1224
|
########################################
|
1222
1225
|
# File management
|
1223
1226
|
########################################
|
1224
|
-
def file_download(self,
|
1227
|
+
def file_download(self, remote_file: str, local_file: str, apply_file_attributes: bool = True) -> None:
|
1225
1228
|
"""Copy a remote file to local. Optionally, the local file will have the same permission as the remote file.
|
1226
1229
|
|
1227
1230
|
Args:
|
1228
|
-
|
1231
|
+
remote_file (str): the remote file path.
|
1229
1232
|
local_file (str): the local file path to be downloaded.
|
1230
1233
|
apply_file_attributes (bool): whether to apply file attributes (permissions, owner, group) to the local file.
|
1231
1234
|
"""
|
1232
|
-
resp = self._request_http(AppMeshClient.Method.GET, path="/appmesh/file/download", header={"File-Path":
|
1235
|
+
resp = self._request_http(AppMeshClient.Method.GET, path="/appmesh/file/download", header={"File-Path": remote_file})
|
1233
1236
|
resp.raise_for_status()
|
1234
1237
|
|
1235
1238
|
# Write the file content locally
|
1236
1239
|
with open(local_file, "wb") as fp:
|
1237
|
-
for chunk in resp.iter_content(chunk_size=
|
1240
|
+
for chunk in resp.iter_content(chunk_size=8 * 1024): # 8 KB
|
1238
1241
|
if chunk:
|
1239
1242
|
fp.write(chunk)
|
1240
1243
|
|
@@ -1250,7 +1253,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1250
1253
|
except PermissionError:
|
1251
1254
|
print(f"Warning: Unable to change owner/group of {local_file}. Operation requires elevated privileges.")
|
1252
1255
|
|
1253
|
-
def file_upload(self, local_file: str,
|
1256
|
+
def file_upload(self, local_file: str, remote_file: str, apply_file_attributes: bool = True) -> None:
|
1254
1257
|
"""Upload a local file to the remote server. Optionally, the remote file will have the same permission as the local file.
|
1255
1258
|
|
1256
1259
|
Dependency:
|
@@ -1259,7 +1262,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1259
1262
|
|
1260
1263
|
Args:
|
1261
1264
|
local_file (str): the local file path.
|
1262
|
-
|
1265
|
+
remote_file (str): the target remote file to be uploaded.
|
1263
1266
|
apply_file_attributes (bool): whether to upload file attributes (permissions, owner, group) along with the file.
|
1264
1267
|
"""
|
1265
1268
|
if not os.path.exists(local_file):
|
@@ -1268,13 +1271,13 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1268
1271
|
from requests_toolbelt import MultipartEncoder
|
1269
1272
|
|
1270
1273
|
with open(file=local_file, mode="rb") as fp:
|
1271
|
-
encoder = MultipartEncoder(fields={"filename": os.path.basename(
|
1272
|
-
header = {"File-Path":
|
1274
|
+
encoder = MultipartEncoder(fields={"filename": os.path.basename(remote_file), "file": ("filename", fp, "application/octet-stream")})
|
1275
|
+
header = {"File-Path": remote_file, "Content-Type": encoder.content_type}
|
1273
1276
|
|
1274
1277
|
# Include file attributes (permissions, owner, group) if requested
|
1275
1278
|
if apply_file_attributes:
|
1276
1279
|
file_stat = os.stat(local_file)
|
1277
|
-
header["File-Mode"] = str(file_stat.st_mode)
|
1280
|
+
header["File-Mode"] = str(file_stat.st_mode & 0o777) # Mask to keep only permission bits
|
1278
1281
|
header["File-User"] = str(file_stat.st_uid)
|
1279
1282
|
header["File-Group"] = str(file_stat.st_gid)
|
1280
1283
|
|
@@ -1486,13 +1489,13 @@ class AppMeshClientTCP(AppMeshClient):
|
|
1486
1489
|
rest_ssl_verify (str, optional): (optional) SSL CA certification. Either a boolean, in which case it controls whether we verify
|
1487
1490
|
the server's TLS certificate, or a string, in which case it must be a path to a CA bundle to use. Defaults to ``True``.
|
1488
1491
|
rest_ssl_client_cert (tuple, optional): SSL client certificate and key pair . If String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
|
1489
|
-
jwt_token (str, optional): JWT token, provide correct token is same with login() &
|
1492
|
+
jwt_token (str, optional): JWT token, provide correct token is same with login() & authentication().
|
1490
1493
|
|
1491
1494
|
tcp_address (tuple, optional): TCP connect address.
|
1492
1495
|
"""
|
1493
|
-
super().__init__(rest_ssl_verify=rest_ssl_verify, rest_ssl_client_cert=rest_ssl_client_cert, jwt_token=jwt_token)
|
1494
1496
|
self.tcp_address = tcp_address
|
1495
1497
|
self.__socket_client = None
|
1498
|
+
super().__init__(rest_ssl_verify=rest_ssl_verify, rest_ssl_client_cert=rest_ssl_client_cert, jwt_token=jwt_token)
|
1496
1499
|
|
1497
1500
|
def __del__(self) -> None:
|
1498
1501
|
"""De-construction"""
|
@@ -1501,15 +1504,31 @@ class AppMeshClientTCP(AppMeshClient):
|
|
1501
1504
|
def __connect_socket(self) -> None:
|
1502
1505
|
"""Establish tcp connection"""
|
1503
1506
|
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
|
1507
|
+
# Set minimum TLS version
|
1504
1508
|
if hasattr(context, "minimum_version"):
|
1505
1509
|
context.minimum_version = ssl.TLSVersion.TLSv1_2
|
1506
1510
|
else:
|
1507
1511
|
context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
context.
|
1512
|
+
# Configure SSL verification
|
1513
|
+
if not self.ssl_verify:
|
1514
|
+
context.verify_mode = ssl.CERT_NONE
|
1515
|
+
else:
|
1516
|
+
context.verify_mode = ssl.CERT_REQUIRED # Require certificate verification
|
1517
|
+
context.load_default_certs() # Load system's default CA certificates
|
1518
|
+
if isinstance(self.ssl_verify, str):
|
1519
|
+
if os.path.isfile(self.ssl_verify):
|
1520
|
+
# Add custom CA certificate file
|
1521
|
+
try:
|
1522
|
+
context.load_verify_locations(cafile=self.ssl_verify)
|
1523
|
+
except ssl.SSLError:
|
1524
|
+
# If loading fails, try using just the default CAs
|
1525
|
+
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
|
1526
|
+
elif os.path.isdir(self.ssl_verify):
|
1527
|
+
# Load CA certificates from directory
|
1528
|
+
context.load_verify_locations(capath=self.ssl_verify)
|
1529
|
+
else:
|
1530
|
+
raise ValueError(f"ssl_verify path '{self.ssl_verify}' is neither a file nor a directory")
|
1531
|
+
|
1513
1532
|
if self.ssl_client_cert is not None:
|
1514
1533
|
# Load client-side certificate and private key
|
1515
1534
|
context.load_cert_chain(certfile=self.ssl_client_cert[0], keyfile=self.ssl_client_cert[1])
|
@@ -1657,15 +1676,15 @@ class AppMeshClientTCP(AppMeshClient):
|
|
1657
1676
|
########################################
|
1658
1677
|
# File management
|
1659
1678
|
########################################
|
1660
|
-
def file_download(self,
|
1679
|
+
def file_download(self, remote_file: str, local_file: str, apply_file_attributes: bool = True) -> None:
|
1661
1680
|
"""Copy a remote file to local, the local file will have the same permission as the remote file
|
1662
1681
|
|
1663
1682
|
Args:
|
1664
|
-
|
1683
|
+
remote_file (str): the remote file path.
|
1665
1684
|
local_file (str): the local file path to be downloaded.
|
1666
1685
|
apply_file_attributes (bool): whether to apply file attributes (permissions, owner, group) to the local file.
|
1667
1686
|
"""
|
1668
|
-
header = {"File-Path":
|
1687
|
+
header = {"File-Path": remote_file}
|
1669
1688
|
header[HTTP_HEADER_KEY_X_RECV_FILE_SOCKET] = "true"
|
1670
1689
|
resp = self._request_http(AppMeshClient.Method.GET, path="/appmesh/file/download", header=header)
|
1671
1690
|
|
@@ -1695,7 +1714,7 @@ class AppMeshClientTCP(AppMeshClient):
|
|
1695
1714
|
except PermissionError:
|
1696
1715
|
print(f"Warning: Unable to change owner/group of {local_file}. Operation requires elevated privileges.")
|
1697
1716
|
|
1698
|
-
def file_upload(self, local_file: str,
|
1717
|
+
def file_upload(self, local_file: str, remote_file: str, apply_file_attributes: bool = True) -> None:
|
1699
1718
|
"""Upload a local file to the remote server, the remote file will have the same permission as the local file
|
1700
1719
|
|
1701
1720
|
Dependency:
|
@@ -1704,19 +1723,19 @@ class AppMeshClientTCP(AppMeshClient):
|
|
1704
1723
|
|
1705
1724
|
Args:
|
1706
1725
|
local_file (str): the local file path.
|
1707
|
-
|
1726
|
+
remote_file (str): the target remote file to be uploaded.
|
1708
1727
|
apply_file_attributes (bool): whether to upload file attributes (permissions, owner, group) along with the file.
|
1709
1728
|
"""
|
1710
1729
|
if not os.path.exists(local_file):
|
1711
1730
|
raise FileNotFoundError(f"Local file not found: {local_file}")
|
1712
1731
|
|
1713
1732
|
with open(file=local_file, mode="rb") as fp:
|
1714
|
-
header = {"File-Path":
|
1733
|
+
header = {"File-Path": remote_file, "Content-Type": "text/plain"}
|
1715
1734
|
header[HTTP_HEADER_KEY_X_SEND_FILE_SOCKET] = "true"
|
1716
1735
|
|
1717
1736
|
if apply_file_attributes:
|
1718
1737
|
file_stat = os.stat(local_file)
|
1719
|
-
header["File-Mode"] = str(file_stat.st_mode)
|
1738
|
+
header["File-Mode"] = str(file_stat.st_mode & 0o777) # Mask to keep only permission bits
|
1720
1739
|
header["File-User"] = str(file_stat.st_uid)
|
1721
1740
|
header["File-Group"] = str(file_stat.st_gid)
|
1722
1741
|
|
@@ -1727,9 +1746,7 @@ class AppMeshClientTCP(AppMeshClient):
|
|
1727
1746
|
if HTTP_HEADER_KEY_X_SEND_FILE_SOCKET not in resp.headers:
|
1728
1747
|
raise ValueError(f"Server did not respond with socket transfer option: {HTTP_HEADER_KEY_X_SEND_FILE_SOCKET}")
|
1729
1748
|
|
1730
|
-
|
1731
|
-
# leaves some room for TLS overhead (like headers) within the 16 KB limit.
|
1732
|
-
chunk_size = 16 * 1024 - 512 # target to 16KB
|
1749
|
+
chunk_size = TCP_CHUNK_BLOCK_SIZE
|
1733
1750
|
while True:
|
1734
1751
|
chunk_data = fp.read(chunk_size)
|
1735
1752
|
if not chunk_data:
|
@@ -0,0 +1,6 @@
|
|
1
|
+
appmesh/__init__.py,sha256=xRdXeFHEieRauuJZElbEBASgXG0ZzU1a5_0isAhM7Gw,11
|
2
|
+
appmesh/appmesh_client.py,sha256=bduPEDpI36XQyK_U9y3EvpZvO9CbpDeAB5402yf9qhU,69329
|
3
|
+
appmesh-1.3.5.dist-info/METADATA,sha256=VSkQis_Azjvw2FwjrK8qjOvYKw2WRNB37J2tYm_KEbw,11191
|
4
|
+
appmesh-1.3.5.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
5
|
+
appmesh-1.3.5.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
6
|
+
appmesh-1.3.5.dist-info/RECORD,,
|
appmesh-1.3.4.dist-info/RECORD
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
appmesh/__init__.py,sha256=xRdXeFHEieRauuJZElbEBASgXG0ZzU1a5_0isAhM7Gw,11
|
2
|
-
appmesh/appmesh_client.py,sha256=6lnfsR6SAhF66jWBPIVPbC43LPhyAL3amBHxSx30Cu4,68327
|
3
|
-
appmesh-1.3.4.dist-info/METADATA,sha256=TZjYqpu2P33ZcN7ue8uTAMobwavbud7_zr9Eo2si7NY,11191
|
4
|
-
appmesh-1.3.4.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
5
|
-
appmesh-1.3.4.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
6
|
-
appmesh-1.3.4.dist-info/RECORD,,
|
File without changes
|