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

Abierto
#147 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
35/100
Tipo de issue
Nueva funcionalidad
Claridad
Bastante claro
Estado de actividad
Tranquilo
Stack tecnológico
java

Línea de trabajo

Empieza leyendo WinRMClient.uploadFile y la lectura por rangos/streaming del issue #146; después inspecciona ShellFileCopy.digestHex, parseAnyDigest, CERTUTIL_ALGORITHMS y las constantes de reintento por cuota. Usa las pruebas de FakeWsmanServer para cubrir round trips, discrepancias de digest, interrupciones y destinos idénticos, y añade la comprobación en vivo en WinRMLiveTest. Se considera terminado cuando mvn verify site pasa, file-transfers.md y el README documentan las garantías y la recomendación de SMB, y no queda ningún archivo de destino parcial.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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

Lenguaje dominante
Java
Estrellas
11
Forks
4
Merge medio
5 d 5 h
PR fusionados (30 d)
6

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de MetricsHub/winrm-java

Todos los issues de MetricsHub/winrm-java

Issues similares

Más issues de Java

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.