Skip to content

Bind pure-Go net-ftp into rbgo (require "net/ftp") - #204

Closed
tannevaled wants to merge 1 commit into
mainfrom
feat/bind-net-ftp
Closed

tannevaled wants to merge 1 commit into
mainfrom
feat/bind-net-ftp

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Summary

Binds github.com/go-ruby-net-ftp/net-ftp — the interpreter-independent FTP protocol codec (command construction, reply classification, PASV/EPSV address extraction, MLSx parsing) — into a real Net::FTP client. rbgo owns the sockets (Go net), the library owns every observable byte, mirroring the socket-transport pattern of the Net::HTTP binding. require "net/ftp" now works with no .rb file.

Surface

  • Class methods: Net::FTP.new(host=nil, options), Net::FTP.open(...) { |ftp| } (yields and closes, even on raise).
  • Connect / login: connect, login (USER/PASS/ACCT ladder, anonymous default password), welcome, last_response, last_response_code/lastresp.
  • Modes: passive/passive=, binary/binary=, use_pasv_ip/use_pasv_ip= (both PASV and EPSV, PORT and EPRT).
  • Directory / metadata: pwd/getdir, chdir (CDUP→CWD fallback on 500), mkdir, rmdir, delete, rename, size, mtime, mdtm, system, status, help, site, noop.
  • Listings: nlst, list/ls/dir, mlsd, mlst → Net::FTP::MLSxEntry (pathname, facts Hash of Integer/Time/String, file?/directory?, perm predicates).
  • Transfers: retrlines, retrbinary, storlines, storbinary; file helpers get/getbinaryfile/gettextfile, put/putbinaryfile/puttextfile.
  • Teardown: abort, quit, close, closed?; generic sendcmd/voidcmd.
  • Errors: the Net::FTPError family (FTPReplyError/FTPTempError/FTPPermError/FTPProtoError/FTPConnectionError), re-raised from the codec by class name.

Wiring

  • registerNetFTP() runs after registerNetHTTP (the Net module) and after registerSocket/registerOpenSSL.
  • "net/ftp" added to providedFeatures; ftpObj / ftpMLSxEntry added to classOf.

Verification

  • 100% statement coverage of the new files (go tool cover -func filtered on net_ftp reports no line below 100%).
  • Tests drive an in-process loopback FTP server — no real network, connections force-closed and joined on cleanup, no leaked goroutines — exercising every command, passive/active with PASV/EPSV/PORT/EPRT, and each error branch. Direct unit tests cover the transport error branches (dial/accept/write/listen failures) and the addressing helpers.
  • Full internal/vm suite green; -race clean; gofmt/go vet clean.
  • Builds on all six 64-bit arches (amd64, arm64, riscv64, loong64, ppc64le, s390x); CGO=0.
  • Smoke-tested in the real rbgo binary: require "net/ftp" returns true, Net::FTP / Net::FTP::MLSxEntry / the error tree resolve.

🤖 Generated with Claude Code

Wire github.com/go-ruby-net-ftp/net-ftp — the interpreter-independent FTP
protocol codec (command construction, reply classification, PASV/EPSV
address extraction, MLSx parsing) — into a real Net::FTP client that drives
Go net sockets, mirroring the socket-transport pattern of the Net::HTTP
binding.

- net_ftp.go: Net::FTP class + the Net::FTPError family (Reply/Temp/Perm/
  Proto/Connection) + Net::FTP::MLSxEntry. Surface: new/open (block-closing),
  connect/login (USER/PASS/ACCT ladder, anonymous default), passive/binary/
  use_pasv_ip accessors, pwd/chdir(CDUP->CWD fallback)/mkdir/rmdir/delete/
  rename, size/mtime/mdtm/system/status/help/site/noop, nlst/list/mlsd/mlst,
  retrlines/retrbinary/storlines/storbinary, get*/put* file helpers,
  abort/quit/close, sendcmd/voidcmd.
- net_ftp_bind.go: the transport engine — control-connection reply reading via
  the codec LineReader, passive (PASV/EPSV dial) and active (PORT/EPRT listen)
  data connections, TYPE negotiation, and codec-error -> Ruby-class mapping.
  The transfer path closes the data socket before raising on an error reply so
  no fd/goroutine leaks.
- registerNetFTP() runs after registerNetHTTP (Net module) and registerSocket/
  registerOpenSSL; "net/ftp" added to providedFeatures; ftpObj / ftpMLSxEntry
  wired into classOf.

Tests drive an in-process loopback FTP server (no real network, connections
force-closed and joined -> no leaked goroutines), covering every command,
passive/active + PASV/EPSV/PORT/EPRT, and each error branch; new files at 100%
statement coverage. CGO=0, builds on all six 64-bit arches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled

Copy link
Copy Markdown
Contributor Author

Superseded by the net-wave consolidation PR (single merge onto main; shared builtins/require/object_model). Work preserved verbatim; branch kept.

@tannevaled tannevaled closed this Jul 6, 2026
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