Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Remote file access (3/4): downloadFile(remote, local) — the symmetric, digest-verified counterpart of uploadFile

Open
#147 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Quiet
Tech stack
java

Research direction

Start by reading WinRMClient.uploadFile and the ranged/streaming read from issue #146, then inspect ShellFileCopy.digestHex, parseAnyDigest, CERTUTIL_ALGORITHMS, and the quota-retry constants. Use the FakeWsmanServer tests to cover round trips, digest mismatches, interruptions, and identical destinations, and add the live check in WinRMLiveTest. Done means mvn verify site passes, file-transfers.md and the README document the guarantees and SMB recommendation, and no partial destination file remains.

Written by the indexing model from the issue text.

Description

Third issue of the remote file access family: the missing symmetric half of WinRMClient.uploadFile(Path, String). Builds on the ranged/streaming read of #146.

Context

client.uploadFile(Path localFile, String remoteFile) has existed since the smbj removal (#117): it pushes a local file through the WinRM channel itself, digest-verified, skipping the transfer when the destination already has identical content. There is no way back. Every caller that needs to retrieve a remote log or a command's output file has to shell out to type/Get-Content and hope the encoding survives.

downloadFile is the mirror image, and it should mirror the upload's guarantees, not just its direction: verified integrity, no wasted transfer, and no half-written local file left behind on failure.

Proposed API

Symmetric with the existing method, on the client:

client.downloadFile("C:\\Windows\\Temp\\collect.log", Path.of("collect.log"));

And as a terminal on the per-path request, for the fluent form:

long bytes = client.file("C:\\Windows\\Temp\\collect.log")
    .downloadTo(Path.of("collect.log"));

Requirements

  • Digest verification, both ways. Compute the remote digest with the probe ShellFileCopy already has (certutil -hashfile, SHA256 with a SHA1 fallback — see CERTUTIL_ALGORITHMS), compare it with the digest of the received bytes, and fail with the same shape of WindowsRemoteException the upload path raises on a mismatch. Reuse ShellFileCopy.digestHex / parseAnyDigest rather than reimplementing.
  • Skip an identical transfer, matching the upload's behavior: if the local destination already exists with the same digest, do not transfer, and say so (return value or documented no-op).
  • Atomic destination: stream into a temporary file in the destination's directory, fsync, then ATOMIC_MOVE onto the target. A failure or a timeout must never leave a truncated file at the destination path.
  • Bounded memory: built on the streaming read, never readBytes() into a byte[] first. A 500 MB file must download in constant memory (slowly — see below).
  • Downloads are not resumable — decided, and deliberately out of scope: an interrupted download starts over. Resuming would mean tracking verified byte counts across attempts and re-validating that the remote file has not changed, which is more state machine than this transport's speed justifies. Say so in the Javadoc and in file-transfers.md so callers do not expect otherwise.
  • Reuse the quota-rejection retry (isRetryableQuotaRejection, QUOTA_RETRIES, QUOTA_RETRY_DELAY_MILLIS): a long download hits the same WinRM operation quotas the upload does.
  • Directory destination: downloadFile(remote, Path.of("C:\\dir")) where the local path is an existing directory should write dir\<remote file name> — or reject it. Pick one, document it.
  • Honest performance documentation. Base64 through a command shell is roughly an order of magnitude slower than SMB; publish a measured figure from the live host in file-transfers.md and keep the existing "not a bulk transport" caveat prominent. Callers moving gigabytes should use SMB, and the docs should say so.
  • Timeout is the client's wall-clock deadline for the blocking method; a large download will need an explicitly raised timeout, and the exception message must make the cause obvious (bytes transferred / total when it fired).

Tests & docs

  • Round-trip test: uploadFile then downloadFile returns byte-identical content, for a binary file with every byte value and for a file with a non-ASCII name.
  • FakeWsmanServer tests: digest mismatch → failure with no file at the destination; interruption mid-transfer → no partial file left behind and no half-written temporary file; identical-digest destination → no transfer performed.
  • WinRMLiveTest: download a real file from anaxagore and verify its digest.
  • file-transfers.md extended with the download direction, the integrity and atomicity guarantees, the measured throughput, and the SMB recommendation for bulk data; README snippet next to uploadFile.

Acceptance criteria

  • downloadFile retrieves byte-exact content, digest-verified against the remote host.
  • No partial file is ever visible at the destination path — verified by a test that interrupts mid-transfer.
  • An identical local file is not re-downloaded.
  • A file substantially larger than the JVM heap downloads successfully.
  • mvn verify site green: no checkstyle/PMD/SpotBugs findings, full Javadoc, docs updated.

🤖 Generated with Claude Code

Dominant language
Java
Stars
11
Forks
4
Avg merge
5d 5h
Merged PRs (30d)
6

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from MetricsHub/winrm-java

All issues in MetricsHub/winrm-java

Similar issues

More Java issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.