Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Remote file access (2/4): read remote file content — whole file, byte ranges (offset/length), text with charset, streaming

Aperta
#146 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
38/100
Tipo di issue
Funzionalità
Chiarezza
Abbastanza chiara
Stato di attività
Tranquilla
Stack tecnologico
java, powershell

Direzione di ricerca

Inizia dal punto di ingresso client.file(path) e rivedi RemoteFileInfo, ShellFileCopy, il terminale dei comandi in streaming di #111 e l’helper base64 di #145. Usa FakeWsmanServer e WinRMLiveTest come punti di ingresso principali per i test, quindi aggiorna files.md, README e file-transfers.md. Il lavoro è completato quando passano i casi di file intero, intervallo, offset negativo, streaming, digest, errore, limite e host live con mvn verify site.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Second issue of the remote file access family, building on the client.file(path) entry point, the RemoteFileInfo type and the base64-framed PowerShell helper introduced by #145.

Context

Listing tells you a remote file is there; this issue reads its bytes. Same channel as the upload path in ShellFileCopy, run in reverse: the remote side base64-encodes the requested byte range, the client decodes it as the output chunks arrive.

Two properties make base64 the right framing rather than an accident of history:

  • it is binary-safe — arbitrary bytes survive a channel that is fundamentally a text console, with none of the code page hazards of #142; and
  • it is ASCII-only, so the client never has to know or guess the remote console encoding to recover the exact bytes.

The cost is the 4/3 inflation plus the shell's own overhead: this is a mechanism for configuration files, logs and small data files, not a bulk transport. That must be stated in the API docs, exactly as the upload path already states it.

Proposed API

Terminals on the client.file(path) request:

// Whole file, bytes
byte[] content = client.file("C:\\Windows\\Temp\\collect.bin").readBytes();

// Whole file, text with an explicit charset (no guessing, no default)
String text = client.file("C:\\inetpub\\logs\\u_ex260729.log")
    .readText(StandardCharsets.UTF_8);

// A range: from a position, for a length — the point of the exercise for big logs
byte[] tail = client.file("D:\\logs\\huge.log")
    .offset(1_073_741_824L)          // absolute byte position
    .length(65_536)                  // bytes to read; omit for "to the end"
    .readBytes();

// Streaming, so memory stays bounded by the buffer and not by the file
try (InputStream in = client.file("D:\\logs\\huge.log").openStream()) {
    ...
}
try (BufferedReader reader = client.file("D:\\logs\\huge.log")
        .charset(StandardCharsets.UTF_8)
        .openReader()) {
    reader.lines().filter(l -> l.contains("ERROR")).forEach(System.out::println);
}

// Integrity, reusing the digest probe ShellFileCopy already has
String sha256 = client.file("C:\\Windows\\Temp\\collect.ps1").digest("SHA256");

offset(long)/length(long) compose with every read terminal.

Negative offsets mean "from the end" — decided, and part of the scope:

// The tail case: last 8 KiB of a log, one seek, no scan
byte[] tail = client.file("D:\\logs\\huge.log").offset(-8192).readBytes();

Exact semantics to implement and document:

  • offset(-n) resolves server-side to max(0, size - n), so a file shorter than n returns the whole file, never an error and never a short read from a negative position.
  • length(...) still applies after the resolution and still clamps at EOF: offset(-8192).length(1024) returns the first 1 KiB of the last 8 KiB.
  • The size is read inside the same remote invocation as the seek (from the open FileStream), not by a separate info() round trip — otherwise a growing log would be tailed from a stale position.
  • offset(0) is the start of the file; there is no "negative zero" case to special-case.

Requirements

The remote read
  • Open with a share mode that tolerates other writers: [IO.File]::Open($p, 'Open', 'Read', 'ReadWrite'), not [IO.File]::OpenRead. A log being written by a running service is the single most common thing a caller wants to read, and OpenRead fails on it. Files held with a truly exclusive lock (pagefile.sys, live registry hives) still fail — map that to a clear exception naming the sharing violation.
  • Seek + bounded read, so a range costs a seek and not a full scan: position the stream at offset, read at most length bytes, base64 the buffer, write it out. Never read the whole file to serve a range.
  • Chunked, streaming output: the script writes base64 in fixed-size blocks so the client can decode incrementally through the streaming command terminal (#111) instead of buffering the whole payload. Keep each write within the wire framing the shell already handles, and mind MaxEnvelopeSize (153 600) for the command itself, not just the output.
  • -EncodedCommand invocation and pure-ASCII output, exactly as in #145 — no cmd.exe quoting of caller-supplied paths, no dependence on the console code page.
  • certutil -encode fallback for whole-file reads when PowerShell is unavailable or constrained (strip the -----BEGIN CERTIFICATE----- framing it adds). Ranged reads have no certutil equivalent: fail with a message that says so.
Semantics to pin down and document
  • Ranges are byte ranges, not character ranges. A range boundary can split a multi-byte character, so readText(Charset) on a ranged read may yield U+FFFD at the edges. Say this in the Javadoc; do not try to be clever about it.
  • No BOM stripping in readBytes(). For readText/openReader, either strip a leading BOM or don't — pick one, document it, test it.
  • offset past EOF returns empty, not an error; length beyond EOF returns what exists. A file that grows or shrinks between two ranged reads is the caller's problem, but note it: this is not a snapshot.
  • Empty file, zero length, and 0-byte range are valid and return an empty result.
  • Size guard: an unbounded readBytes() on a multi-gigabyte file must not OOM the JVM silently. Enforce a documented default cap (e.g. 64 MiB) with an explicit maxBytes(long) override, and point callers at openStream()/downloadFile for anything larger.
  • Throughput expectation documented with a measured figure from the live host, next to the existing "designed for small script files, not bulk data" wording in file-transfers.md.
  • Timeout semantics follow the existing split: the blocking terminals use the wall-clock deadline; openStream()/openReader() use the inactivity semantics of the streaming terminals.
Tests & docs
  • FakeWsmanServer tests: whole-file read, ranged read (mid-file, at EOF, past EOF, zero length), negative offset (larger than, equal to and smaller than the file size, and combined with length), binary content with every byte value 0–255, a multi-byte character split across two chunks (must decode correctly through the incremental path), the size cap, and the sharing-violation and missing-file failures.
  • Digest agreement test: readBytes() of a file uploaded with uploadFile matches the local bytes, and digest("SHA256") matches the local digest.
  • WinRMLiveTest: read a real file whole, read a range from a large file (assert the range is served by a seek, i.e. duration does not scale with the file size), and read a file currently held open by another process.
  • files.md gains the read section; README gains the snippet; file-transfers.md cross-links the reverse direction.

Acceptance criteria

  • Whole-file and ranged reads return byte-exact content for binary and text files, verified by digest against the local original.
  • A range near the end of a large file is served without transferring the whole file, both with an absolute offset and with offset(-n).
  • A log file open for writing by another process can be read.
  • The default size cap prevents an accidental unbounded read, and openStream() reads a file larger than the cap with bounded memory.
  • mvn verify site green: no checkstyle/PMD/SpotBugs findings, full Javadoc, docs updated.

🤖 Generated with Claude Code

Lingua principale
Java
Stelle
11
Fork
4
Merge medio
5g 5h
PR unite (30g)
6

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di MetricsHub/winrm-java

Tutte le issue di MetricsHub/winrm-java

Issue simili

Altre issue su Java

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.