Let callers supply their own dialer - #71
Conversation
Adds Config.DialFunc. When set, it opens every TCP connection the client makes — control connections and data connections alike — in place of the package's own dialer. It exists for callers who need the connection itself rather than only what travels over it: counting bytes for transfer statistics, routing through a proxy or a tunnel, or returning something that is not really a socket. Today there is no way to reach the connection, so those callers cannot use this package at all. Both dial sites now funnel through one Config.dial helper, which is what makes the data connections reachable too. That is the part worth having: a caller who supplied a dialer to count bytes and only saw the control connection would miss every transfer, which is nearly all the bytes there are, and would have no way to tell. Two things become the caller's responsibility, documented on the field because neither is guessable: - Config.Timeout no longer applies to the dial. It still governs reads and writes on the returned connection. - In TLSImplicit mode the returned connection is wrapped in TLS by this package, so DialFunc should return a plain connection. The handshake happens at dial time rather than on first read, matching what tls.DialWithDialer does. Leaving DialFunc nil keeps the previous behaviour exactly, including the timeout. ## Running the tests without the test servers The new tests need no server: both call sites go through Config.dial, so exercising that covers them by construction. But the package's TestMain stops with log.Fatal when ./build_test_server.sh has not been run, so on a machine that cannot build pure-ftpd and proftpd no test runs at all — including the ones that do not care. GOFTP_SKIP_SERVERS now skips the four tests that need a live server and runs the rest. Skipping is on the tests themselves rather than the runner, so they report as skipped with the reason rather than failing on a connection refused, which reads like a bug in the package instead of a missing prerequisite.
b9955dd to
818a879
Compare
|
I've since found #55, which proposes the same thing — same field name, same two dial sites — and predates this by some years. Credit to @machship-mm; I didn't see it before opening this. If you'd rather take that one, this can close. Two differences, in case they're useful either way: Implicit TLS. #55 replaces the TLS dial when One dial path. Both sites go through a single This branch also has tests — the ones for Either way, the point @rpetti made in #50 is the one worth preserving: a hook that only covers the control connection doesn't help, because FTP negotiates data connections separately. Both #55 and this cover both. |
Adds
Config.DialFunc. When set, it opens every TCP connection the client makes — control connections and data connections alike — in place of the package's own dialer.Why
Some callers need the connection itself, not only what travels over it:
There is currently no way to reach the connection, so those callers cannot use this package at all. That is what brought me here — I need per-mount byte accounting for an FTP-backed filesystem, and every other option meant giving up this library.
What it does
Both dial sites now funnel through one
Config.dialhelper, and that is the part worth having. A caller who supplied a dialer to count bytes and only saw the control connection would miss every transfer — nearly all the bytes there are — and would have no way to tell.Two things become the caller's responsibility, documented on the field because neither is guessable:
Config.Timeoutno longer applies to the dial. It still governs reads and writes on the returned connection.TLSImplicitmode the returned connection is wrapped in TLS by this package, soDialFuncshould return a plain connection. The handshake happens at dial time rather than on first read, matching whattls.DialWithDialerdoes.Leaving
DialFuncnil keeps the previous behaviour exactly, including the timeout.Tests
The new tests need no server: both call sites go through
Config.dial, so exercising that covers them by construction.They did not run at first, though.
TestMainstops withlog.Fatalwhen./build_test_server.shhas not been run, so on a machine that cannot build pure-ftpd and proftpd no test runs at all — including the ones that do not care.GOFTP_SKIP_SERVERSnow skips the four tests that need a live server and runs the rest. The skip is on the tests themselves rather than in the runner, so they report as skipped with a reason instead of failing on a connection refused — which reads like a bug in the package rather than a missing prerequisite.Happy to drop that second part into its own PR if you would rather keep this to the feature.