Skip to content

Fix API client retries and return the job ID - #263

Open
whartond wants to merge 6 commits into
secureworks:masterfrom
whartond:fix-api-client-retries
Open

whartond wants to merge 6 commits into
secureworks:masterfrom
whartond:fix-api-client-retries

Conversation

@whartond

@whartond whartond commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Fixes the Python API client in api/dalton.py:

  • Every request ran RETRIES (3) times even when it succeeded, so submit_job() queued three jobs. A retryable error slept 60 seconds and then raised without retrying.
  • Each request is now tried up to three times. GETs are retried on 429/5xx, connection errors and timeouts. Job submissions are retried only when the job can't have been queued: a 429, or a failure to connect to Dalton or the proxy (not a connection dropped after the request went out, which urllib3 also reports as a proxy error). Retries after a 429/5xx wait for the server's Retry-After (in seconds, at most 300) or 60 seconds; retries after a connection failure wait 10 seconds, and (with urllib3 2) a host name that doesn't resolve isn't retried. Uploads are read once, from the file's current position, so every attempt sends the same bytes; files can be a dict or a list, with tuple or list values.
  • GETs time out after 60 seconds. Dalton processes the pcap before it answers a submission, so submissions wait up to an hour for the reply by default (submit_timeout=; None waits forever). A new headers= option adds headers, such as an API key, to every request.
  • submit_job() sends optionCustomRuleset. Without it Dalton ignored custom_ruleset, so the custom rules were never loaded. The README's field name is fixed too.
  • submit_job() returns the job ID (comma-separated IDs if the pcap was split) instead of None, and raises DaltonAPIError with Dalton's error message when it answers with its error page.
  • The example script takes the pcap as an argument (its mock pcap was never committed), reads it once, and prints the job IDs.
  • requests is added to the testing extras (the client needs it and the new tests import it).

Tests: tests/test_api_client.py (33 tests, against a stubbed requests plus real local sockets for the connection-failure checks; also run against urllib3 1.26 and on Python 3.8). Also checked live against Dalton with Suricata 8.0.6 and 7.0.17: the same pcap bytes submitted to both engines, custom rules loaded and fired, an API key sent with headers=, and an unknown sensor raising DaltonAPIError with Dalton's message.

Every request ran RETRIES times, even on success, so submit_job queued
three jobs; retryable errors slept and then raised without retrying.
Requests are now made once, retried on 429/5xx (job submissions only on
429/503, so a job can't be queued twice), rewind uploaded files before a
retry, and time out. submit_job returns the job ID and raises
DaltonAPIError on Dalton's error page.
…nd custom rules

- job submissions wait for Dalton's reply by default (submit_timeout sets a limit)
- GETs are retried on connection errors and timeouts; submissions only when the
  connection failed before anything was sent
- honour Retry-After; accept files as a list; reuse one Session
- set optionCustomRuleset, without which Dalton ignored the custom rules
- the example takes the pcap as an argument (its mock pcap was never committed)
- fresh connection per request (no Session), with a headers= option
- read uploads once from their current position instead of rewinding
- connect timeout taken from a (connect, read) timeout tuple
- retry proxy connection failures; cap Retry-After at 300s
- error messages show Dalton's message only; RETRIES=0 still makes one attempt
- a POST dropped by a proxy after it was sent is not retried
- unresolvable hosts aren't retried; connection failures wait 10s, not 60s
- GETs are retried when reading the body fails
- ignore non-ASCII digits in Retry-After
- files type hint, example docstring, test thread timeouts
- retry a job submission only on 429; a proxy's 503 may follow a queued job
- import on Python 3.8/3.9; read files given as lists; accept files=None
- one-hour default for the submission reply instead of no limit
@whartond
whartond marked this pull request as ready for review November 14, 2026 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant