You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Phase 3 of the API modernization, building directly on the streaming terminals of #111 (PR #134). The bounded-poll primitive built there (CommandCursor.poll / RemoteCommand.pollChunk) is the enabler: it is exactly the "ask for output, but only for a moment" operation an input pump needs.
Context
The shell we create already declares <rsp:InputStreams>stdin</rsp:InputStreams>, but the client never sends any input: commands that read stdin hang or see an open, never-fed pipe. The WSMan shell protocol has first-class support for this — the Send operation (.../windows/shell/Send) carries base64 <rsp:Stream Name="stdin" CommandId="..."> chunks, with End="true" on the final chunk signalling EOF.
With input, the natural end-game is an interactive session: a shell subcommand on the CLI that behaves like winrs, letting the user drive cmd.exe on the remote host live.
Phase 1 — stdin for commands (library)
Proposed API
Pre-supplied input on the existing builders (works with both execute() and start()):
CommandResultresult = client.command("sort")
.stdin(Path.of("data.txt")) // also stdin(InputStream), stdin(String)
.execute();
Process-style interactive input on the streaming handle, completing the java.lang.Process shape:
try (RemoteProcessp = client.command("some-repl.exe").start()) {
try (BufferedWriterin = p.stdin()) { // OutputStream/Writer, encoded with the command charsetin.write("first request\n");
in.flush(); // flush() emits a WSMan SendSystem.out.println(p.stdout().readLine());
} // close() sends the final chunk with End="true" (EOF)p.waitFor();
}
Requirements
New Send envelope in Envelopes (rsp:Send / stream stdin / optional End="true"), and a send(byte[] data, boolean end) seam on CommandCursor + RemoteCommand.
Same serial connection, no concurrency: a Send is just another request under the handle's connection permit. RemoteProcess stays single-threaded — writes and reads alternate on the caller's thread, like java.lang.Process pipes. Document the classic deadlock (blocking on a read while the remote blocks on input) as the caller's to avoid, exactly like Process.
WINRS_CONSOLEMODE_STDIN becomes configurable: FALSE (pipe semantics) when stdin is supplied programmatically, TRUE kept as today's default otherwise, and TRUE for the interactive shell below.
Chunking: input split to respect MaxEnvelopeSize (153 600), like ShellFileCopy does for file payloads.
Encoding: input encoded with the same charset used to decode output (detected code page or explicit charset(...)); ChunkDecoder has the decode side, encoding needs no state (chunk on byte boundaries after encoding, not before).
FakeWsmanServer learns Send: decrypt and record stdin chunks so tests can assert content, ordering, End flag, and console-mode option on the wire.
Phase 2 — interactive shell subcommand (CLI)
java -jar winrm-java-standalone.jar -h server -u 'DOMAIN\user' -pf pw.txt shell
Starts cmd.exe in the remote shell (console-mode stdin) and bridges it to the local terminal until the remote shell exits.
Design
Single-threaded protocol pump built on the bounded poll: loop = drain queued local input → Send → poll(shortBudget) → write decoded stdout/stderr to the local streams. Latency is the poll cadence (~100–250 ms). A polling session never trips the inactivity timeout while idle — every round trip completes with output or the protocol's "nothing yet" fault.
One local helper thread only, reading local stdin into a queue (blocking System.in reads must not stall the pump). The WinRM connection itself stays strictly serial.
Line-oriented, like winrs: with zero dependencies there is no raw-terminal/PTY mode in pure Java, so input is line-buffered by the local terminal. No ANSI/completion guarantees — document this clearly in the manual.
Signals: local EOF (Ctrl+Z/Ctrl+D) → Send with End="true"; Ctrl+C → WSMan Signal with the ctrl_c code (new constant next to terminate), interrupting the remote child without killing the session.
Exit code: the remote cmd.exe exit code is propagated using the existing CLI exit-code contract.
command subcommand bonus: when local stdin is not a console (piped/redirected), forward it as the command's stdin — winrm-java ... command sort < data.txt just works.
Docs: new sections in the CLI manual (shell subcommand, stdin forwarding, limitations) and in commands.md for the library API; README one-liner.
Acceptance criteria
A command can consume programmatic stdin end-to-end (content, chunking, End flag, console-mode option asserted against FakeWsmanServer).
RemoteProcess.stdin() supports a write → flush → read round trip against a scripted REPL exchange.
The shell pump is testable headlessly: scripted exchange (input line → Send on the wire → scripted output → local stdout), EOF → End="true", Ctrl+C → ctrl_c Signal, exit-code propagation.
Timeout semantics documented: per-round-trip bounds apply; an idle interactive session does not die of inactivity.
Context
The shell we create already declares
<rsp:InputStreams>stdin</rsp:InputStreams>, but the client never sends any input: commands that read stdin hang or see an open, never-fed pipe. The WSMan shell protocol has first-class support for this — theSendoperation (.../windows/shell/Send) carries base64<rsp:Stream Name="stdin" CommandId="...">chunks, withEnd="true"on the final chunk signalling EOF.With input, the natural end-game is an interactive session: a
shellsubcommand on the CLI that behaves likewinrs, letting the user drivecmd.exeon the remote host live.Phase 1 — stdin for commands (library)
Proposed API
Pre-supplied input on the existing builders (works with both
execute()andstart()):Process-style interactive input on the streaming handle, completing the
java.lang.Processshape:Requirements
Sendenvelope inEnvelopes(rsp:Send/ streamstdin/ optionalEnd="true"), and asend(byte[] data, boolean end)seam onCommandCursor+RemoteCommand.Sendis just another request under the handle's connection permit.RemoteProcessstays single-threaded — writes and reads alternate on the caller's thread, likejava.lang.Processpipes. Document the classic deadlock (blocking on a read while the remote blocks on input) as the caller's to avoid, exactly likeProcess.WINRS_CONSOLEMODE_STDINbecomes configurable:FALSE(pipe semantics) when stdin is supplied programmatically,TRUEkept as today's default otherwise, andTRUEfor the interactive shell below.MaxEnvelopeSize(153 600), likeShellFileCopydoes for file payloads.charset(...));ChunkDecoderhas the decode side, encoding needs no state (chunk on byte boundaries after encoding, not before).FakeWsmanServerlearnsSend: decrypt and record stdin chunks so tests can assert content, ordering,Endflag, and console-mode option on the wire.Phase 2 — interactive
shellsubcommand (CLI)Starts
cmd.exein the remote shell (console-mode stdin) and bridges it to the local terminal until the remote shell exits.Design
Send→poll(shortBudget)→ write decoded stdout/stderr to the local streams. Latency is the poll cadence (~100–250 ms). A polling session never trips the inactivity timeout while idle — every round trip completes with output or the protocol's "nothing yet" fault.System.inreads must not stall the pump). The WinRM connection itself stays strictly serial.winrs: with zero dependencies there is no raw-terminal/PTY mode in pure Java, so input is line-buffered by the local terminal. No ANSI/completion guarantees — document this clearly in the manual.SendwithEnd="true"; Ctrl+C → WSManSignalwith thectrl_ccode (new constant next toterminate), interrupting the remote child without killing the session.cmd.exeexit code is propagated using the existing CLI exit-code contract.commandsubcommand bonus: when local stdin is not a console (piped/redirected), forward it as the command's stdin —winrm-java ... command sort < data.txtjust works.shellsubcommand, stdin forwarding, limitations) and in commands.md for the library API; README one-liner.Acceptance criteria
Endflag, console-mode option asserted againstFakeWsmanServer).RemoteProcess.stdin()supports a write → flush → read round trip against a scripted REPL exchange.Send, and client-closed-while-handle-open stay clean (no stray requests, no revived transport — same invariants as Streaming APIs (phase 2): stream()/start() terminals on the fluent WinRMClient #134).shellpump is testable headlessly: scripted exchange (input line → Send on the wire → scripted output → local stdout), EOF →End="true", Ctrl+C →ctrl_cSignal, exit-code propagation.mvn verify sitegreen; cli.md updated.🤖 Generated with Claude Code