appmesh 1.6.9__py3-none-any.whl → 1.6.11__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/__init__.py +12 -0
- appmesh/app_output.py +2 -2
- appmesh/client_http.py +71 -23
- appmesh/client_tcp.py +7 -2
- appmesh/server_http.py +4 -1
- appmesh/server_tcp.py +4 -1
- appmesh/tcp_messages.py +3 -0
- appmesh/tcp_transport.py +1 -0
- {appmesh-1.6.9.dist-info → appmesh-1.6.11.dist-info}/METADATA +1 -1
- appmesh-1.6.11.dist-info/RECORD +16 -0
- appmesh-1.6.9.dist-info/RECORD +0 -16
- {appmesh-1.6.9.dist-info → appmesh-1.6.11.dist-info}/WHEEL +0 -0
- {appmesh-1.6.9.dist-info → appmesh-1.6.11.dist-info}/top_level.txt +0 -0
appmesh/__init__.py
CHANGED
@@ -11,6 +11,7 @@ Example:
|
|
11
11
|
|
12
12
|
import sys
|
13
13
|
from types import ModuleType
|
14
|
+
from typing import TYPE_CHECKING
|
14
15
|
|
15
16
|
__all__ = ["App", "AppMeshClient", "AppMeshClientTCP", "AppMeshClientOAuth", "AppMeshServer", "AppMeshServerTCP"]
|
16
17
|
|
@@ -24,6 +25,17 @@ _LAZY_IMPORTS = {
|
|
24
25
|
}
|
25
26
|
|
26
27
|
|
28
|
+
if TYPE_CHECKING:
|
29
|
+
# Provide explicit imports for static analyzers and type checkers
|
30
|
+
# These imports are only executed during type checking and won't affect runtime.
|
31
|
+
from .app import App # noqa: F401
|
32
|
+
from .client_http import AppMeshClient # noqa: F401
|
33
|
+
from .client_tcp import AppMeshClientTCP # noqa: F401
|
34
|
+
from .client_http_oauth import AppMeshClientOAuth # noqa: F401
|
35
|
+
from .server_http import AppMeshServer # noqa: F401
|
36
|
+
from .server_tcp import AppMeshServerTCP # noqa: F401
|
37
|
+
|
38
|
+
|
27
39
|
def _lazy_import(name):
|
28
40
|
"""Helper function for lazy importing."""
|
29
41
|
if name in _LAZY_IMPORTS:
|
appmesh/app_output.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# app_output.py
|
2
|
+
# pylint: disable=line-too-long
|
2
3
|
"""Application output information"""
|
3
4
|
|
5
|
+
# Standard library imports
|
4
6
|
from http import HTTPStatus
|
5
7
|
from typing import Optional
|
6
8
|
|
7
|
-
# pylint: disable=line-too-long
|
8
|
-
|
9
9
|
|
10
10
|
class AppOutput(object):
|
11
11
|
"""
|
appmesh/client_http.py
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# client_http.py
|
2
2
|
# pylint: disable=broad-exception-raised,line-too-long,broad-exception-caught,too-many-lines,import-outside-toplevel
|
3
|
+
|
4
|
+
# Standard library imports
|
3
5
|
import abc
|
4
6
|
import base64
|
7
|
+
import http.cookiejar as cookiejar
|
5
8
|
import json
|
6
9
|
import locale
|
7
10
|
import logging
|
@@ -9,19 +12,21 @@ import os
|
|
9
12
|
import sys
|
10
13
|
import threading
|
11
14
|
import time
|
12
|
-
import requests
|
13
|
-
import http.cookiejar as cookiejar
|
14
15
|
from datetime import datetime
|
15
16
|
from enum import Enum, unique
|
16
17
|
from http import HTTPStatus
|
17
|
-
import threading
|
18
18
|
from typing import Optional, Tuple, Union
|
19
19
|
from urllib import parse
|
20
|
+
|
21
|
+
# Third-party imports
|
20
22
|
import aniso8601
|
21
23
|
import jwt
|
24
|
+
import requests
|
25
|
+
|
26
|
+
# Local imports
|
22
27
|
from .app import App
|
23
|
-
from .app_run import AppRun
|
24
28
|
from .app_output import AppOutput
|
29
|
+
from .app_run import AppRun
|
25
30
|
|
26
31
|
|
27
32
|
class AppMeshClient(metaclass=abc.ABCMeta):
|
@@ -129,6 +134,10 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
129
134
|
HTTP_HEADER_KEY_USER_AGENT = "User-Agent"
|
130
135
|
HTTP_HEADER_KEY_X_TARGET_HOST = "X-Target-Host"
|
131
136
|
HTTP_HEADER_KEY_X_FILE_PATH = "X-File-Path"
|
137
|
+
HTTP_HEADER_JWT_set_cookie = "X-Set-Cookie"
|
138
|
+
HTTP_HEADER_NAME_CSRF_TOKEN = "X-CSRF-Token"
|
139
|
+
COOKIE_TOKEN = "appmesh_auth_token"
|
140
|
+
COOKIE_CSRF_TOKEN = "appmesh_csrf_token"
|
132
141
|
|
133
142
|
@unique
|
134
143
|
class Method(Enum):
|
@@ -216,7 +225,8 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
216
225
|
rest_timeout (tuple, optional): HTTP connection timeouts for API requests, as `(connect_timeout, read_timeout)`.
|
217
226
|
The default is `(60, 300)`, where `60` seconds is the maximum time to establish a connection and `300` seconds for the maximum read duration.
|
218
227
|
|
219
|
-
rest_cookie_file (str, optional): Path to a file for storing session cookies.
|
228
|
+
rest_cookie_file (str, optional): Path to a file for storing session cookies.
|
229
|
+
If provided, cookies will be saved to and loaded from this file to maintain session state across client instances instead of keep jwt_token.
|
220
230
|
|
221
231
|
jwt_token (str, optional): JWT token for API authentication, used in headers to authorize requests where required.
|
222
232
|
auto_refresh_token (bool, optional): Enable automatic token refresh before expiration.
|
@@ -231,25 +241,16 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
231
241
|
self.rest_timeout = rest_timeout
|
232
242
|
self._forward_to = None
|
233
243
|
|
234
|
-
# Session and cookie management
|
235
|
-
self._lock = threading.Lock()
|
236
|
-
self.session = requests.Session()
|
237
|
-
self.cookie_file = rest_cookie_file
|
238
|
-
if self.cookie_file:
|
239
|
-
self.session.cookies = cookiejar.MozillaCookieJar(self.cookie_file)
|
240
|
-
if os.path.exists(self.cookie_file):
|
241
|
-
self.session.cookies.load(ignore_discard=True, ignore_expires=True)
|
242
|
-
else:
|
243
|
-
os.makedirs(os.path.dirname(self.cookie_file), exist_ok=True)
|
244
|
-
self.session.cookies.save(ignore_discard=True, ignore_expires=True)
|
245
|
-
if os.name == "posix":
|
246
|
-
os.chmod(self.cookie_file, 0o600) # User read/write only
|
247
|
-
|
248
244
|
# Token auto-refresh
|
249
245
|
self._token_refresh_timer = None
|
250
246
|
self._auto_refresh_token = auto_refresh_token
|
251
247
|
self.jwt_token = jwt_token # Set property last after all dependencies are initialized to setup refresh timer
|
252
248
|
|
249
|
+
# Session and cookie management
|
250
|
+
self._lock = threading.Lock()
|
251
|
+
self.session = requests.Session()
|
252
|
+
self.cookie_file = self._load_cookies(rest_cookie_file)
|
253
|
+
|
253
254
|
@staticmethod
|
254
255
|
def _ensure_logging_configured():
|
255
256
|
"""Ensure logging is configured. If no handlers are configured, add a default console handler."""
|
@@ -259,6 +260,45 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
259
260
|
def _get_access_token(self) -> str:
|
260
261
|
return self.jwt_token
|
261
262
|
|
263
|
+
def _load_cookies(self, cookie_file: Optional[str]) -> str:
|
264
|
+
"""Load cookies from the cookie file and return the file path ."""
|
265
|
+
if not cookie_file:
|
266
|
+
return ""
|
267
|
+
|
268
|
+
self.session.cookies = cookiejar.MozillaCookieJar(cookie_file)
|
269
|
+
if os.path.exists(cookie_file):
|
270
|
+
self.session.cookies.load(ignore_discard=True, ignore_expires=True)
|
271
|
+
self.jwt_token = self._get_cookie_value(self.session.cookies, self.COOKIE_TOKEN)
|
272
|
+
else:
|
273
|
+
os.makedirs(os.path.dirname(cookie_file), exist_ok=True)
|
274
|
+
self.session.cookies.save(ignore_discard=True, ignore_expires=True)
|
275
|
+
if os.name == "posix":
|
276
|
+
os.chmod(cookie_file, 0o600) # User read/write only
|
277
|
+
return cookie_file
|
278
|
+
|
279
|
+
@staticmethod
|
280
|
+
def _get_cookie_value(cookies, name, check_expiry=True) -> Optional[str]:
|
281
|
+
"""Get cookie value by name, checking expiry if requested."""
|
282
|
+
# If it's a RequestsCookieJar, use .get() but check expiry manually if requested
|
283
|
+
if hasattr(cookies, "get") and not isinstance(cookies, list):
|
284
|
+
cookie = cookies.get(name)
|
285
|
+
if cookie is None:
|
286
|
+
return None
|
287
|
+
if check_expiry and getattr(cookie, "expires", None):
|
288
|
+
if cookie.expires < time.time():
|
289
|
+
return None # expired
|
290
|
+
return cookie.value if hasattr(cookie, "value") else cookie
|
291
|
+
|
292
|
+
# Otherwise, assume it's a MozillaCookieJar — iterate manually
|
293
|
+
for c in cookies:
|
294
|
+
if c.name == name:
|
295
|
+
if check_expiry and getattr(c, "expires", None):
|
296
|
+
if c.expires < time.time():
|
297
|
+
return None # expired
|
298
|
+
return c.value
|
299
|
+
|
300
|
+
return None
|
301
|
+
|
262
302
|
def _check_and_refresh_token(self):
|
263
303
|
"""Check and refresh token if needed, then schedule next check.
|
264
304
|
|
@@ -407,6 +447,8 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
407
447
|
- Refresh tokens before expiration
|
408
448
|
- Validate token format before setting
|
409
449
|
"""
|
450
|
+
if self._jwt_token == token:
|
451
|
+
return # No change
|
410
452
|
self._jwt_token = token
|
411
453
|
|
412
454
|
# handle refresh
|
@@ -418,7 +460,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
418
460
|
|
419
461
|
# handle session
|
420
462
|
with self._lock:
|
421
|
-
if self.cookie_file:
|
463
|
+
if hasattr(self, "cookie_file") and self.cookie_file:
|
422
464
|
self.session.cookies.save(ignore_discard=True, ignore_expires=True)
|
423
465
|
|
424
466
|
@property
|
@@ -499,6 +541,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
499
541
|
self.HTTP_HEADER_KEY_AUTH: "Basic " + base64.b64encode(f"{user_name}:{user_pwd}".encode()).decode(),
|
500
542
|
"X-Expire-Seconds": str(self._parse_duration(timeout_seconds)),
|
501
543
|
**({"X-Audience": audience} if audience else {}),
|
544
|
+
**({self.HTTP_HEADER_JWT_set_cookie: "true"} if self.cookie_file else {}),
|
502
545
|
# **({"X-Totp-Code": totp_code} if totp_code else {}),
|
503
546
|
},
|
504
547
|
)
|
@@ -539,6 +582,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
539
582
|
"totp_challenge": challenge,
|
540
583
|
"expire_seconds": self._parse_duration(timeout),
|
541
584
|
},
|
585
|
+
header={self.HTTP_HEADER_JWT_set_cookie: "true"} if self.cookie_file else {},
|
542
586
|
)
|
543
587
|
if resp.status_code == HTTPStatus.OK and "access_token" in resp.json():
|
544
588
|
self.jwt_token = resp.json()["access_token"]
|
@@ -1422,9 +1466,13 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1422
1466
|
|
1423
1467
|
# Prepare headers
|
1424
1468
|
header = {} if header is None else header
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1469
|
+
|
1470
|
+
# JWT or Cookie token
|
1471
|
+
if self.cookie_file and self._get_cookie_value(self.session.cookies, self.COOKIE_CSRF_TOKEN):
|
1472
|
+
header[self.HTTP_HEADER_NAME_CSRF_TOKEN] = self._get_cookie_value(self.session.cookies, self.COOKIE_CSRF_TOKEN)
|
1473
|
+
elif self._get_access_token():
|
1474
|
+
header[self.HTTP_HEADER_KEY_AUTH] = f"Bearer {self._get_access_token()}"
|
1475
|
+
|
1428
1476
|
if self.forward_to and len(self.forward_to) > 0:
|
1429
1477
|
if ":" in self.forward_to:
|
1430
1478
|
header[self.HTTP_HEADER_KEY_X_TARGET_HOST] = self.forward_to
|
appmesh/client_tcp.py
CHANGED
@@ -1,15 +1,20 @@
|
|
1
1
|
# client_tcp.py
|
2
2
|
# pylint: disable=line-too-long,broad-exception-raised,broad-exception-caught,import-outside-toplevel,protected-access
|
3
3
|
|
4
|
+
# Standard library imports
|
4
5
|
import json
|
5
6
|
import os
|
6
|
-
import sys
|
7
7
|
import socket
|
8
|
+
import sys
|
8
9
|
import uuid
|
10
|
+
|
11
|
+
# Third-party imports
|
9
12
|
import requests
|
13
|
+
|
14
|
+
# Local imports
|
10
15
|
from .client_http import AppMeshClient
|
11
|
-
from .tcp_transport import TCPTransport
|
12
16
|
from .tcp_messages import RequestMessage, ResponseMessage
|
17
|
+
from .tcp_transport import TCPTransport
|
13
18
|
|
14
19
|
|
15
20
|
class AppMeshClientTCP(AppMeshClient):
|
appmesh/server_http.py
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
# server_http.py
|
2
2
|
# pylint: disable=line-too-long,broad-exception-raised,broad-exception-caught,import-outside-toplevel,protected-access
|
3
3
|
|
4
|
+
# Standard library imports
|
4
5
|
import abc
|
5
6
|
import logging
|
6
7
|
import os
|
7
8
|
import time
|
8
|
-
from typing import Optional, Tuple, Union
|
9
9
|
from http import HTTPStatus
|
10
|
+
from typing import Optional, Tuple, Union
|
11
|
+
|
12
|
+
# Local imports
|
10
13
|
from .client_http import AppMeshClient
|
11
14
|
|
12
15
|
logger = logging.getLogger(__name__)
|
appmesh/server_tcp.py
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
# server_tcp.py
|
2
2
|
# pylint: disable=line-too-long,broad-exception-raised,broad-exception-caught,import-outside-toplevel,protected-access
|
3
3
|
|
4
|
-
|
4
|
+
# Standard library imports
|
5
5
|
import logging
|
6
|
+
import os
|
6
7
|
from typing import Optional, Tuple
|
8
|
+
|
9
|
+
# Local imports
|
7
10
|
from .client_http import AppMeshClient
|
8
11
|
from .client_tcp import AppMeshClientTCP
|
9
12
|
from .server_http import AppMeshServer
|
appmesh/tcp_messages.py
CHANGED
appmesh/tcp_transport.py
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
appmesh/__init__.py,sha256=uJ5LOadwZW2nOXkSuPOy2S3WH9Bx0BUn5wUvPBeFzoc,2217
|
2
|
+
appmesh/app.py,sha256=crD4DRFZJuHtZMfSsz7C-EwvjPmGZbFXYXvA_wCdvdI,10734
|
3
|
+
appmesh/app_output.py,sha256=N8BihWhuUgPUyqZabZMKhQylk8nTHXKk6kMIdidEkGM,1061
|
4
|
+
appmesh/app_run.py,sha256=aYq852a29OThIi32Xtx5s0sTXZ97T0lHD5WXH8yfPoc,2018
|
5
|
+
appmesh/appmesh_client.py,sha256=ywB2222PtJUffdfdxZcBfdhZs1KYyc7JvzMxwuK2qyI,378
|
6
|
+
appmesh/client_http.py,sha256=FXCldfTZVU_5RSuSknlH1K2UiC6gOeotj-4NDQzdEhY,60752
|
7
|
+
appmesh/client_http_oauth.py,sha256=1d51o0JX_xtB8d2bEuM7_XJHcwMnhcjkbIq7GE1Zxm8,6120
|
8
|
+
appmesh/client_tcp.py,sha256=RX3T3OG4iOAju8ZPOnTjI_y97Y23_kWoNDjmKjtrBeU,11524
|
9
|
+
appmesh/server_http.py,sha256=wfyiIa2zC-uJR2ZNTGMjYWheqAfSRl0aAv5b_GYcwpE,4343
|
10
|
+
appmesh/server_tcp.py,sha256=J65kmN7DJftyW1LlF--S3keQ6VGmqXb778E79I1R0_k,1488
|
11
|
+
appmesh/tcp_messages.py,sha256=YB_AiMOSzN9j8oSrF0V9gD184Ugbsff-ZnezExwmvtk,1928
|
12
|
+
appmesh/tcp_transport.py,sha256=FCfTBHb9FLwMUIxZejOuH_NocHQFGaNDge57IUTJvgs,8973
|
13
|
+
appmesh-1.6.11.dist-info/METADATA,sha256=gHUSXnQdxvG61jPEUWcJBSJlrmR_PIHZ_qyyaYwTuIc,11764
|
14
|
+
appmesh-1.6.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
15
|
+
appmesh-1.6.11.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
16
|
+
appmesh-1.6.11.dist-info/RECORD,,
|
appmesh-1.6.9.dist-info/RECORD
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
appmesh/__init__.py,sha256=TY1y5B5cE57uhraEzCFOZRWuo9SY1R-fYNRan8hCZOM,1670
|
2
|
-
appmesh/app.py,sha256=crD4DRFZJuHtZMfSsz7C-EwvjPmGZbFXYXvA_wCdvdI,10734
|
3
|
-
appmesh/app_output.py,sha256=vfn322AyixblI8DbXds08h6L_ybObiaRSifsA1-Xcoo,1035
|
4
|
-
appmesh/app_run.py,sha256=aYq852a29OThIi32Xtx5s0sTXZ97T0lHD5WXH8yfPoc,2018
|
5
|
-
appmesh/appmesh_client.py,sha256=ywB2222PtJUffdfdxZcBfdhZs1KYyc7JvzMxwuK2qyI,378
|
6
|
-
appmesh/client_http.py,sha256=OiYM4CXn21S4e_jx71X5_31krLes6UuHHueEYk6UIu4,58690
|
7
|
-
appmesh/client_http_oauth.py,sha256=1d51o0JX_xtB8d2bEuM7_XJHcwMnhcjkbIq7GE1Zxm8,6120
|
8
|
-
appmesh/client_tcp.py,sha256=aq6UUzytZA4ibE9WQMMWdo1uW8sHETEhJjsbM6IYSno,11457
|
9
|
-
appmesh/server_http.py,sha256=rBIYO9rbR-r3x1Jcry440Sp--IM-OWKRaOhNpGdkxh8,4299
|
10
|
-
appmesh/server_tcp.py,sha256=-CU5tw97WJmDcUNsNPWqpdZ0wxRzRD6kUP3XyNZUTHc,1444
|
11
|
-
appmesh/tcp_messages.py,sha256=H9S_iCy0IuufY2v50_SUgRvcyQmJsySG65tBe_xb3Ko,1878
|
12
|
-
appmesh/tcp_transport.py,sha256=0hRSp5fpL9wKB05JIyIRIuyBC8w1IdokryhMDHqtN4M,8946
|
13
|
-
appmesh-1.6.9.dist-info/METADATA,sha256=aT2BPT5BM_5FkkFKhR-h_NBWMQMeVBP3-Wo2QwA-j6Y,11763
|
14
|
-
appmesh-1.6.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
15
|
-
appmesh-1.6.9.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
16
|
-
appmesh-1.6.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|