diff --git a/CHANGELOG.md b/CHANGELOG.md index 654ad3a..99450c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,43 @@ All notable changes to this project are documented in this file. -## [Unreleased] — 2.0.0 +## 3.1.00 + +### Fixed + +- **A long-lived client no longer exhausts the host's operation quota** (#196). Every command + run in a shell holds one of the user's WSMan operations until the shell is deleted, even once + terminated, and the client reused its shell forever: a client polling indefinitely ended up + with every command refused with WSManFault 2150859174 (*the maximum number of concurrent + operations for this user has been exceeded*), after 15 commands on Windows Server 2008 R2 and + 1500 on later versions, until it was recreated. The client now replaces its shell every 10 + commands (the new `WinRMClient.Builder.maxCommandsPerShell(int)` changes that number) and never + reuses a shell holding a command it could not terminate (its terminate `Signal` failed or was + skipped). On Windows Server 2008 R2, whose quota fault carries its WSManFault code, a command + the quota refuses in a shell that already ran commands is also retried once in a new shell; + later versions send that fault with no code, so nothing reliable identifies it. The new shell + gets the same working directory, environment and profile; deleting the old one ends any process + a previous command left running in it, as closing the client always did. + +- **A streaming read resumed after a long pause no longer times out spuriously** (#198). When a + streaming consumer (e.g. a `RemoteProcess` read slowly) paused longer than the inactivity + timeout and the host dropped the idle connection meanwhile, the reconnection inherited the + expired deadline of the previous request: its TCP connect and TLS handshake got a 1 ms budget + and failed with a timeout (*No response from the WinRM service*). Each + reconnection now gets a full inactivity timeout of its own. + +- **A WQL row is no longer dropped when none of its selected properties came back** (#201). + WinRM leaves an empty or `NULL` array out of the response, so `SELECT IPSecPermitTCPPorts FROM + Win32_NetworkAdapterConfiguration` returned no rows at all, where WMI returns one per adapter. + Each instance is now a row, on which `string()` returns `null` for the missing property. + `WqlResult.columns()` now also reads the property names of every row, not just the first one: + with `SELECT * FROM Win32_NetworkAdapterConfiguration`, a first adapter without IP configuration + hid `IPAddress`, `DefaultIPGateway`, ... from the columns of all the rows. + +## 2.0.00 to 3.0.00 + +Highlights of these versions; see [GitHub Releases](https://github.com/metricshub/winrm-java/releases) +for the complete notes of each one. ### Added — HTTP Basic authentication @@ -158,41 +194,12 @@ Consequences: ### Fixed -- **A long-lived client no longer exhausts the host's operation quota** (#196). Every command - run in a shell holds one of the user's WSMan operations until the shell is deleted, even once - terminated, and the client reused its shell forever: a client polling indefinitely ended up - with every command refused with WSManFault 2150859174 (*the maximum number of concurrent - operations for this user has been exceeded*), after 15 commands on Windows Server 2008 R2 and - 1500 on later versions, until it was recreated. The client now replaces its shell every 10 - commands (the new `WinRMClient.Builder.maxCommandsPerShell(int)` changes that number) and never - reuses a shell holding a command it could not terminate (its terminate `Signal` failed or was - skipped). On Windows Server 2008 R2, whose quota fault carries its WSManFault code, a command - the quota refuses in a shell that already ran commands is also retried once in a new shell; - later versions send that fault with no code, so nothing reliable identifies it. The new shell - gets the same working directory, environment and profile; deleting the old one ends any process - a previous command left running in it, as closing the client always did. - -- **A streaming read resumed after a long pause no longer times out spuriously** (#198). When a - streaming consumer (e.g. a `RemoteProcess` read slowly) paused longer than the inactivity - timeout and the host dropped the idle connection meanwhile, the reconnection inherited the - expired deadline of the previous request: its TCP connect and TLS handshake got a 1 ms budget - and failed with a timeout (*No response from the WinRM service*). Each - reconnection now gets a full inactivity timeout of its own. - - **WQL array properties keep all their elements** (#189). A WMI array (`IPAddress`, `DefaultIPGateway`, `Capabilities`, ...) used to yield only its last element, without any error. Its elements are now joined with `|` (`"192.0.2.10|fe80::1"`); the new `WinRMClient.Builder.arraySeparator(String)` changes the separator. The `WqlRow` Javadoc now says what the code does: a WMI `NULL` is an empty string, `null` means "no such property". -- **A WQL row is no longer dropped when none of its selected properties came back** (#201). - WinRM leaves an empty or `NULL` array out of the response, so `SELECT IPSecPermitTCPPorts FROM - Win32_NetworkAdapterConfiguration` returned no rows at all, where WMI returns one per adapter. - Each instance is now a row, on which `string()` returns `null` for the missing property. - `WqlResult.columns()` now also reads the property names of every row, not just the first one: - with `SELECT * FROM Win32_NetworkAdapterConfiguration`, a first adapter without IP configuration - hid `IPAddress`, `DefaultIPGateway`, ... from the columns of all the rows. - - **Streaming terminals now report protocol failures as `WinRMClientException`** (#188). `WqlRequest.stream()`, `CommandRequest.start()`, `RemoteFile.openStream()`/`openReader()`, `RemoteDirectoryListing.stream()` and the closing of a `RemoteProcess` let raw diff --git a/src/site/markdown/commands.md b/src/site/markdown/commands.md index bd7cfc4..b05159b 100644 --- a/src/site/markdown/commands.md +++ b/src/site/markdown/commands.md @@ -46,7 +46,7 @@ Everything between `command(...)` and `execute()` is optional (see | --- | --- | --- | | `timeout(Duration)` | the client's timeout | Wall-clock deadline covering file uploads and the command itself with `execute()`; inactivity timeout with `start()`. | | `charset(Charset)` | `UTF-8` | The charset used to decode the command output (see below). | -| `workingDirectory(String)` | remote default | Working directory of the remote process. The remote shell is created by the client's **first** command (remote file operations and `uploadFile(...)` count too) and reused afterward, so this only takes effect on that first command, and never on a request with `upload(...)`. | +| `workingDirectory(String)` | remote default | Working directory of the remote process. The remote shell is created by the client's **first** command (remote file operations and `uploadFile(...)` count too) and reused afterward (every shell that [replaces it](#shell-reuse) gets the same settings), so this only takes effect on that first command, and never on a request with `upload(...)`. | | `environment(String, String)` | none | Environment variable set in the remote shell, like `winrs -env` — call it once per variable, insertion order is preserved. Shell-scoped like `workingDirectory`: only takes effect on the client's **first** command. | | `upload(Path...)` | none | Local files to copy to the host before running (see below). | | `stdin(String)` / `stdin(Path)` / `stdin(InputStream)` | none | Standard input fed to the command — the remote equivalent of a `< file` redirection (see below). | diff --git a/src/site/markdown/timeouts-and-errors.md b/src/site/markdown/timeouts-and-errors.md index 539b8bb..dd5debf 100644 --- a/src/site/markdown/timeouts-and-errors.md +++ b/src/site/markdown/timeouts-and-errors.md @@ -77,7 +77,12 @@ transparently when the server drops an idle one). What is **never** retried: * credential rejections ([`WinRMAuthenticationException`](apidocs/org/metricshub/winrm/exceptions/WinRMAuthenticationException.html)); * WSMan faults - ([`WinRMFaultException`](apidocs/org/metricshub/winrm/exceptions/WinRMFaultException.html)). + ([`WinRMFaultException`](apidocs/org/metricshub/winrm/exceptions/WinRMFaultException.html)), + except the refusals that prove a command never ran, whatever the policy: a shell the host + reaped and, on Windows Server 2008 R2, the operation quota are retried once in a new shell (see + [Shell reuse](commands.html#shell-reuse)), and [file transfers](file-transfers.html) and + [remote file operations](files.html) then retry a quota refusal up to 4 more times, with + escalating delays (5/10/15/20 s) bounded by their timeout. Retries stay inside each operation's **wall-clock deadline**: when the timeout elapses mid-pause, the operation fails with diff --git a/src/site/markdown/wql.md b/src/site/markdown/wql.md index 87cc705..55384ef 100644 --- a/src/site/markdown/wql.md +++ b/src/site/markdown/wql.md @@ -134,7 +134,8 @@ lookups in it are case-sensitive. * When you select explicit properties (`SELECT Name, State FROM ...`), the columns keep the **order of the query** and the **exact case reported by WMI**. -* With `SELECT * FROM ...`, the columns are in **alphabetical order** (case-insensitive). +* With `SELECT * FROM ...`, the columns are the properties of **every** row (a row may lack an + empty array that others have), in **alphabetical order** (case-insensitive). * If the query returns no rows, the columns are the selected property names in lower case (WMI's own casing cannot be recovered from an empty result set), or an empty list for `SELECT *`.