pympacds-http 0.1.0__tar.gz

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.
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: pympacds-http
3
+ Version: 0.1.0
4
+ Summary: HTTP client service for pympacds
5
+ Author-email: Oscar Diaz <odiaz@ieee.org>
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: pympacds>=0.2.0
10
+ Requires-Dist: aiohttp>=3.0
11
+
12
+ # pympacds-http
13
+
14
+ HTTP client service for [pympacds](https://github.com/dargor0/pympacds). Sends
15
+ data over HTTP on behalf of other services and exposes it over D-Bus, with
16
+ automatic retry and optional mutual TLS.
17
+
18
+ ## Features
19
+
20
+ - Fire-and-forget D-Bus API: `send`, `download`, and `request` each return a
21
+ random request token immediately; the result is delivered asynchronously via
22
+ the `request_completed(token, result)` signal, so callers correlate a call
23
+ with its outcome by token.
24
+ - A single pooled `aiohttp.ClientSession` (native asyncio, no thread
25
+ offloading), created at startup with no network I/O and closed on shutdown.
26
+ - Automatic retry of transient failures (network error, timeout, HTTP 5xx)
27
+ with exponential backoff (`retries`, `retry_backoff_s`).
28
+ - TLS verification control (`tls_verify`) plus mutual TLS via
29
+ `tls_certfile`/`tls_keyfile` (must be provided together).
30
+ - `download` / `request` with a `download_path` write the response body
31
+ atomically to disk (temp file + rename) for binary/large downloads.
32
+ - Runtime configuration: `[http]` keys are writable via the framework
33
+ `ConfigContract`.
34
+ - Exports the framework `HealthContract` (always) and `ConfigContract`
35
+ (opt-in via `[dbus] contract_config = true`) alongside the HTTP contract.
36
+
37
+ ### Why aiohttp
38
+
39
+ The service uses [aiohttp](https://docs.aiohttp.org/) because it is asyncio
40
+ native: requests run directly on the framework's single event loop with no
41
+ thread offloading. It is isolated in this service's own package, so the core
42
+ `pympacds` framework keeps its zero-dependency property (the core's
43
+ `httpconfprov` middleware continues to use the stdlib `urllib.request`).
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ pip install pympacds-http
49
+ ```
50
+
51
+ ## Configuration
52
+
53
+ The service reads its parameters from the `[http]` INI section (see
54
+ `config/http.ini.example`). Key options:
55
+
56
+ | Key | Default | Description |
57
+ |-----|---------|-------------|
58
+ | `timeout_s` | `10` | Per-request timeout |
59
+ | `retries` | `3` | Number of retry attempts |
60
+ | `retry_backoff_s` | `1` | Initial backoff (exponential) between retries |
61
+ | `tls_verify` | `true` | Verify TLS certificates |
62
+ | `tls_certfile` / `tls_keyfile` | `""` | Client cert/key (PEM) for mutual TLS |
63
+ | `max_redirects` | `5` | Maximum redirects to follow |
64
+ | `default_url` | `""` | Target URL used by `send()` (empty = `send()` fails) |
65
+ | `default_method` | `POST` | Default HTTP method used by `send()` |
66
+ | `default_headers` | `""` | Default headers (JSON object) used by `send()` |
67
+
68
+ Run with:
69
+
70
+ ```bash
71
+ pympacds-http -c /etc/pympacds/http.ini
72
+ ```
73
+
74
+ ## D-Bus API
75
+
76
+ Interface `org.pympacds.HTTP` at object path `/org/pympacds/http`:
77
+
78
+ | Member | Type | Description |
79
+ |--------|------|-------------|
80
+ | `send(payload)` | `send(s) -> s` | Send with default url/method/headers; returns a token |
81
+ | `download(payload, download_path)` | `download(ss) -> s` | Like `send`, but saves the body to `download_path` |
82
+ | `request(method, url, headers, body, download_path)` | `request(sssss) -> s` | Explicit request; empty `download_path` = return body |
83
+ | `request_completed(token, result)` | signal `(ss)` | Emitted on completion; carries the token and JSON result |
84
+
85
+ All sends are **fire-and-forget**: the method returns immediately with a
86
+ randomized token, the request runs asynchronously, and `request_completed` is
87
+ emitted with the full JSON outcome:
88
+
89
+ ```json
90
+ {
91
+ "status_code": 200,
92
+ "url": "https://example.com/api",
93
+ "elapsed_ms": 123,
94
+ "body": "...",
95
+ "download_path": null,
96
+ "bytes_written": null,
97
+ "error": null
98
+ }
99
+ ```
100
+
101
+ The framework `HealthContract` (at `.../health`) and `ConfigContract`
102
+ (`[dbus] contract_config = true`) are also exported.
@@ -0,0 +1,91 @@
1
+ # pympacds-http
2
+
3
+ HTTP client service for [pympacds](https://github.com/dargor0/pympacds). Sends
4
+ data over HTTP on behalf of other services and exposes it over D-Bus, with
5
+ automatic retry and optional mutual TLS.
6
+
7
+ ## Features
8
+
9
+ - Fire-and-forget D-Bus API: `send`, `download`, and `request` each return a
10
+ random request token immediately; the result is delivered asynchronously via
11
+ the `request_completed(token, result)` signal, so callers correlate a call
12
+ with its outcome by token.
13
+ - A single pooled `aiohttp.ClientSession` (native asyncio, no thread
14
+ offloading), created at startup with no network I/O and closed on shutdown.
15
+ - Automatic retry of transient failures (network error, timeout, HTTP 5xx)
16
+ with exponential backoff (`retries`, `retry_backoff_s`).
17
+ - TLS verification control (`tls_verify`) plus mutual TLS via
18
+ `tls_certfile`/`tls_keyfile` (must be provided together).
19
+ - `download` / `request` with a `download_path` write the response body
20
+ atomically to disk (temp file + rename) for binary/large downloads.
21
+ - Runtime configuration: `[http]` keys are writable via the framework
22
+ `ConfigContract`.
23
+ - Exports the framework `HealthContract` (always) and `ConfigContract`
24
+ (opt-in via `[dbus] contract_config = true`) alongside the HTTP contract.
25
+
26
+ ### Why aiohttp
27
+
28
+ The service uses [aiohttp](https://docs.aiohttp.org/) because it is asyncio
29
+ native: requests run directly on the framework's single event loop with no
30
+ thread offloading. It is isolated in this service's own package, so the core
31
+ `pympacds` framework keeps its zero-dependency property (the core's
32
+ `httpconfprov` middleware continues to use the stdlib `urllib.request`).
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install pympacds-http
38
+ ```
39
+
40
+ ## Configuration
41
+
42
+ The service reads its parameters from the `[http]` INI section (see
43
+ `config/http.ini.example`). Key options:
44
+
45
+ | Key | Default | Description |
46
+ |-----|---------|-------------|
47
+ | `timeout_s` | `10` | Per-request timeout |
48
+ | `retries` | `3` | Number of retry attempts |
49
+ | `retry_backoff_s` | `1` | Initial backoff (exponential) between retries |
50
+ | `tls_verify` | `true` | Verify TLS certificates |
51
+ | `tls_certfile` / `tls_keyfile` | `""` | Client cert/key (PEM) for mutual TLS |
52
+ | `max_redirects` | `5` | Maximum redirects to follow |
53
+ | `default_url` | `""` | Target URL used by `send()` (empty = `send()` fails) |
54
+ | `default_method` | `POST` | Default HTTP method used by `send()` |
55
+ | `default_headers` | `""` | Default headers (JSON object) used by `send()` |
56
+
57
+ Run with:
58
+
59
+ ```bash
60
+ pympacds-http -c /etc/pympacds/http.ini
61
+ ```
62
+
63
+ ## D-Bus API
64
+
65
+ Interface `org.pympacds.HTTP` at object path `/org/pympacds/http`:
66
+
67
+ | Member | Type | Description |
68
+ |--------|------|-------------|
69
+ | `send(payload)` | `send(s) -> s` | Send with default url/method/headers; returns a token |
70
+ | `download(payload, download_path)` | `download(ss) -> s` | Like `send`, but saves the body to `download_path` |
71
+ | `request(method, url, headers, body, download_path)` | `request(sssss) -> s` | Explicit request; empty `download_path` = return body |
72
+ | `request_completed(token, result)` | signal `(ss)` | Emitted on completion; carries the token and JSON result |
73
+
74
+ All sends are **fire-and-forget**: the method returns immediately with a
75
+ randomized token, the request runs asynchronously, and `request_completed` is
76
+ emitted with the full JSON outcome:
77
+
78
+ ```json
79
+ {
80
+ "status_code": 200,
81
+ "url": "https://example.com/api",
82
+ "elapsed_ms": 123,
83
+ "body": "...",
84
+ "download_path": null,
85
+ "bytes_written": null,
86
+ "error": null
87
+ }
88
+ ```
89
+
90
+ The framework `HealthContract` (at `.../health`) and `ConfigContract`
91
+ (`[dbus] contract_config = true`) are also exported.
@@ -0,0 +1,69 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pympacds-http"
7
+ version = "0.1.0"
8
+ description = "HTTP client service for pympacds"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ authors = [
13
+ {name="Oscar Diaz", email="odiaz@ieee.org"}
14
+ ]
15
+ dependencies = [
16
+ "pympacds>=0.2.0",
17
+ "aiohttp>=3.0",
18
+ ]
19
+
20
+ [project.scripts]
21
+ pympacds-http = "pympacds_http.service:main"
22
+
23
+ [tool.setuptools.packages.find]
24
+ where = ["src"]
25
+
26
+ [tool.pytest.ini_options]
27
+ asyncio_mode = "auto"
28
+ testpaths = ["tests"]
29
+ pythonpath = ["src", "../../src"]
30
+ addopts = [
31
+ "-v",
32
+ "--tb=short",
33
+ "--cov=pympacds_http",
34
+ "--cov-report=term-missing",
35
+ ]
36
+
37
+ [tool.ruff]
38
+ line-length = 100
39
+ target-version = "py310"
40
+
41
+ [tool.ruff.lint]
42
+ select = ["E", "F", "W", "I", "B", "UP"]
43
+
44
+ # D-Bus type-signature string annotations ("s", "b", "ss", ...) are the
45
+ # dbus-fast/dbus-next convention, not Python type references.
46
+ [tool.ruff.lint.per-file-ignores]
47
+ "src/pympacds_http/contracts.py" = ["F821", "F722"]
48
+
49
+ [tool.mypy]
50
+ python_version = "3.10"
51
+ ignore_missing_imports = true
52
+ mypy_path = ["../../src"]
53
+
54
+ # The core framework has its own (pre-existing) typing debt ("object"-typed
55
+ # D-Bus globals, D-Bus signature strings). Treat it as Any rather than
56
+ # re-checking it from the service.
57
+ [[tool.mypy.overrides]]
58
+ module = "pympacds"
59
+ follow_imports = "silent"
60
+
61
+ [[tool.mypy.overrides]]
62
+ module = "pympacds.*"
63
+ follow_imports = "silent"
64
+
65
+ # D-Bus type-signature annotations ("s", "b", "ss", ...) are the dbus-fast/dbus-next
66
+ # convention, not Python types; mypy tries to resolve them as forward references.
67
+ [[tool.mypy.overrides]]
68
+ module = "pympacds_http.contracts"
69
+ disable_error_code = ["name-defined", "valid-type"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """pympacds-http — HTTP client service for pympacds."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,43 @@
1
+ """HTTP D-Bus contract (REQ-HTTP-004)."""
2
+
3
+ from pympacds.contracts import ServiceContract, dbus_method, dbus_signal
4
+
5
+
6
+ class HttpContract(ServiceContract):
7
+ """HTTP request interface."""
8
+
9
+ iface_name = "HTTP"
10
+ iface_version = "1.0.0"
11
+ iface_provides = ["http"]
12
+ iface_requires = ["network"]
13
+
14
+ def __init__(self, ifname: str, base):
15
+ super().__init__(ifname, base)
16
+ self._require(
17
+ "dbus_http_send",
18
+ "dbus_http_download",
19
+ "dbus_http_request",
20
+ )
21
+
22
+ @dbus_method()
23
+ def send(self, payload: "s") -> "s":
24
+ return self.base.dbus_http_send(payload)
25
+
26
+ @dbus_method()
27
+ def download(self, payload: "s", download_path: "s") -> "s":
28
+ return self.base.dbus_http_download(payload, download_path)
29
+
30
+ @dbus_method()
31
+ def request(
32
+ self,
33
+ method: "s",
34
+ url: "s",
35
+ headers: "s",
36
+ body: "s",
37
+ download_path: "s",
38
+ ) -> "s":
39
+ return self.base.dbus_http_request(method, url, headers, body, download_path)
40
+
41
+ @dbus_signal()
42
+ def request_completed(self, token: "s", result: "s") -> "ss":
43
+ return [token, result]