Files
bambuddy/backend
maziggy a9b15e727f Stop retrying a printer whose FTPS handshake fails, and name the cause (#2780)
Two printers went on printing while every archive they produced held nothing
    but a filename. Bambuddy opened port 990, the printer accepted the connection
    and answered with something that was not TLS, and connect() logged a warning
    and returned False -- indistinguishable, to every caller, from "the file is
    not at this path". So the 3MF lookup walked all six filename variants across
    five directories with four retries each, the cover endpoint ran its own
    sixteen-path sweep, and the timelapse scan added four more, all against a
    sixteen-path sweep, and the timelapse scan added four more, all against a
    printer that could not have answered any of them. One reporter's log carried
    1813 identical handshake failures, another's 3511.

    The evidence says this is the printer's own file service getting stuck, not a
    model, firmware or TLS-configuration problem. In #2780's bundle the same two
    printers ran clean from 22 July to 4 August and failed again from the 5th; a
    second bundle shows an X2D serving files for five days, flipping on 19 July,
    then failing every connection for eight days with zero successes. The same
    models and firmware appear in roughly twenty other bundles with no occurrences
    at all. Both bundles show it happening with cap_tls_v1_2 in effect -- the X2D
    and H2C entries in ftp_profiles were added on analogy with P2S to fix exactly
    this symptom, and the reporter's own debug line proves they do not.

    An ssl.SSLError from connect() now opens a five-minute cool-off for that
    printer. Subsequent connects return False without touching the network, so a
    wedged printer is contacted twice an hour instead of hundreds of times a
    minute, and the single warning that is logged names the remedy. The cool-off
    is dropped on expiry rather than kept, so the map holds one key per currently
    wedged printer. ftps_handshake_blocked() lets the sweeps stop: the 3MF lookup
    abandons the remaining paths and skips the directory-walk fallback, the cover
    endpoint returns 503 naming the file service instead of a 404 that reads as
    "this print has no thumbnail", and the timelapse scan separates 503 (cannot
    reach the printer) from 404 (no timelapse directory) -- one 500 used to cover
    both, which is what the reporter hit when reproducing.

    The Connection Diagnostic completed a bare TCP connect to 990, which is why it
    reported the port green throughout: the port is open, it is what is behind it
    that is broken. It now completes a real implicit-TLS handshake using the
    model's own ftp_profiles cap, so a pass means the FTP client would also get
    through. An open port that cannot negotiate reports warn with reason no_tls,
    selecting a new message in all 13 locales that points at a printer restart
    rather than at the firewall. No login is attempted, so this stays valid in the
    pre-save Add Printer flow.

    The cool-off tests run against a real socket that accepts on 990 and replies
    with a plaintext FTP banner, reproducing WRONG_VERSION_NUMBER rather than
    mocking ssl. The autouse fixture clearing _mode_cache now clears the cool-off
    map too -- every test here talks to 127.0.0.1, so one left behind would make
    the next test's connect() a no-op.
2026-08-15 14:41:08 +02:00
..