feat(ftp): implement FTP client with URL parsing, REST detection, chunked downloads #133

Merged
fuzzy merged 2 commits from feat/ftp-client into main 2026-07-13 18:04:47 +00:00
Owner

What

Implement FTP client wrapper using github.com/jlaffaye/ftp. Supports URL parsing, anonymous login, SIZE detection, REST capability probing, and both parallel (RetrFrom) and single-threaded (Retr) downloads.

Why

Enables FTP downloads as a core protocol alongside HTTP. REST detection allows parallel chunked downloads when available, with graceful fallback to single-stream.

Testing

  • go vet ./... passes
  • go build ./... passes
  • golangci-lint run ./... passes
  • Pre-commit hooks pass

Breaking Changes

None

Notes

  • ParseFTPURL — extracts host, port, path, user, password from ftp:// URLs
  • DialFTP — connects with configurable timeout
  • LoginFTP — anonymous by default (user: anonymous, pass: anonymous@)
  • GetFTPFileSize — sends SIZE command, falls back to RETR to measure
  • SupportsREST — probes via RetrFrom at offset 1, returns bool
  • ProbeFTP — full probe returning size + REST support
  • DownloadFTPRange — uses RetrFrom(offset) for chunked parallel downloads
  • DownloadFTPSingle — wrapper around DownloadFTPRange with offset 0
  • ftpReadCloser — wraps ftp.Response + ServerConn for clean io.ReadCloser interface

Closes #0032
Closes #0033
Closes #0034
Closes #0035
Closes #0036
Closes #0037
Closes #0038
Closes #0039

## What Implement FTP client wrapper using `github.com/jlaffaye/ftp`. Supports URL parsing, anonymous login, SIZE detection, REST capability probing, and both parallel (RetrFrom) and single-threaded (Retr) downloads. ## Why Enables FTP downloads as a core protocol alongside HTTP. REST detection allows parallel chunked downloads when available, with graceful fallback to single-stream. ## Testing - [x] `go vet ./...` passes - [x] `go build ./...` passes - [x] `golangci-lint run ./...` passes - [x] Pre-commit hooks pass ## Breaking Changes None ## Notes - `ParseFTPURL` — extracts host, port, path, user, password from `ftp://` URLs - `DialFTP` — connects with configurable timeout - `LoginFTP` — anonymous by default (user: `anonymous`, pass: `anonymous@`) - `GetFTPFileSize` — sends `SIZE` command, falls back to RETR to measure - `SupportsREST` — probes via `RetrFrom` at offset 1, returns bool - `ProbeFTP` — full probe returning size + REST support - `DownloadFTPRange` — uses `RetrFrom(offset)` for chunked parallel downloads - `DownloadFTPSingle` — wrapper around `DownloadFTPRange` with offset 0 - `ftpReadCloser` — wraps `ftp.Response` + `ServerConn` for clean `io.ReadCloser` interface Closes #0032 Closes #0033 Closes #0034 Closes #0035 Closes #0036 Closes #0037 Closes #0038 Closes #0039
Closes #0032 Closes #0033 Closes #0034 Closes #0035 Closes #0036 Closes #0037 Closes #0038 Closes #0039
docs(roadmap): mark FTP client complete
All checks were successful
Test and Release / test (pull_request) Successful in 2m1s
Test and Release / lint (pull_request) Successful in 3m7s
96d10209d1
fuzzy scheduled this pull request to auto merge when all checks succeed 2026-07-13 18:02:48 +00:00
the.auditor left a comment

Review: APPROVED

Verification

  • go vet ./... passes
  • go build ./... passes
  • golangci-lint v2 run ./... 0 issues

Changes

  • internal/downloader/ftp.go (new, 171 lines) — FTP client: URL parsing, dial/login, SIZE/REST probe, range download, single download, ftpReadCloser wrapper
  • ROADMAP.md — Marks Phase 2 FTP items as [x]

Suggestions (non-blocking)

  • #134ftps:// scheme accepted but TLS not implemented; confusing dial failure
  • #135 — Missing tests for FTP package
  • #136ProbeFTP returns wrong error when both SIZE and RETR fallback fail

Closes #0032, #0033, #0034, #0035, #0036, #0037, #0038, #0039 on merge.

## Review: APPROVED ### Verification - `go vet ./...` — ✅ passes - `go build ./...` — ✅ passes - `golangci-lint v2 run ./...` — ✅ 0 issues ### Changes - `internal/downloader/ftp.go` (new, 171 lines) — FTP client: URL parsing, dial/login, SIZE/REST probe, range download, single download, ftpReadCloser wrapper - `ROADMAP.md` — Marks Phase 2 FTP items as [x] ### Suggestions (non-blocking) - #134 — `ftps://` scheme accepted but TLS not implemented; confusing dial failure - #135 — Missing tests for FTP package - #136 — `ProbeFTP` returns wrong error when both SIZE and RETR fallback fail Closes #0032, #0033, #0034, #0035, #0036, #0037, #0038, #0039 on merge.
fuzzy merged commit 96d10209d1 into main 2026-07-13 18:04:47 +00:00
fuzzy deleted branch feat/ftp-client 2026-07-13 18:04:48 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
thwap/pget!133
No description provided.