winrm 2.0.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +10 -10
  3. data/.rubocop.yml +26 -26
  4. data/.travis.yml +11 -11
  5. data/Gemfile +3 -3
  6. data/README.md +260 -233
  7. data/Rakefile +34 -34
  8. data/Vagrantfile +6 -6
  9. data/WinrmAppveyor.psm1 +31 -31
  10. data/appveyor.yml +51 -51
  11. data/changelog.md +104 -101
  12. data/lib/winrm.rb +39 -39
  13. data/lib/winrm/connection.rb +83 -82
  14. data/lib/winrm/connection_opts.rb +91 -91
  15. data/lib/winrm/exceptions.rb +76 -76
  16. data/lib/winrm/http/response_handler.rb +96 -96
  17. data/lib/winrm/http/transport.rb +424 -424
  18. data/lib/winrm/http/transport_factory.rb +68 -68
  19. data/lib/winrm/output.rb +59 -59
  20. data/lib/winrm/psrp/create_pipeline.xml.erb +167 -167
  21. data/lib/winrm/psrp/fragment.rb +70 -70
  22. data/lib/winrm/psrp/init_runspace_pool.xml.erb +224 -224
  23. data/lib/winrm/psrp/message.rb +130 -130
  24. data/lib/winrm/psrp/message_data.rb +42 -42
  25. data/lib/winrm/psrp/message_data/base.rb +49 -49
  26. data/lib/winrm/psrp/message_data/error_record.rb +68 -68
  27. data/lib/winrm/psrp/message_data/pipeline_host_call.rb +32 -32
  28. data/lib/winrm/psrp/message_data/pipeline_output.rb +49 -49
  29. data/lib/winrm/psrp/message_data/pipeline_state.rb +40 -40
  30. data/lib/winrm/psrp/message_data/runspacepool_host_call.rb +32 -32
  31. data/lib/winrm/psrp/message_data/runspacepool_state.rb +39 -39
  32. data/lib/winrm/psrp/message_data/session_capability.rb +36 -36
  33. data/lib/winrm/psrp/message_defragmenter.rb +62 -62
  34. data/lib/winrm/psrp/message_factory.rb +75 -75
  35. data/lib/winrm/psrp/message_fragmenter.rb +60 -60
  36. data/lib/winrm/psrp/powershell_output_decoder.rb +139 -139
  37. data/lib/winrm/psrp/receive_response_reader.rb +97 -97
  38. data/lib/winrm/psrp/session_capability.xml.erb +7 -7
  39. data/lib/winrm/psrp/uuid.rb +40 -40
  40. data/lib/winrm/shells/base.rb +180 -175
  41. data/lib/winrm/shells/cmd.rb +65 -65
  42. data/lib/winrm/shells/power_shell.rb +202 -202
  43. data/lib/winrm/shells/retryable.rb +45 -45
  44. data/lib/winrm/shells/shell_factory.rb +58 -50
  45. data/lib/winrm/version.rb +7 -7
  46. data/lib/winrm/wsmv/base.rb +59 -59
  47. data/lib/winrm/wsmv/cleanup_command.rb +61 -61
  48. data/lib/winrm/wsmv/close_shell.rb +50 -50
  49. data/lib/winrm/wsmv/command.rb +101 -101
  50. data/lib/winrm/wsmv/command_output.rb +76 -76
  51. data/lib/winrm/wsmv/command_output_decoder.rb +55 -55
  52. data/lib/winrm/wsmv/configuration.rb +46 -46
  53. data/lib/winrm/wsmv/create_pipeline.rb +66 -66
  54. data/lib/winrm/wsmv/create_shell.rb +119 -119
  55. data/lib/winrm/wsmv/header.rb +203 -203
  56. data/lib/winrm/wsmv/init_runspace_pool.rb +95 -95
  57. data/lib/winrm/wsmv/iso8601_duration.rb +60 -60
  58. data/lib/winrm/wsmv/keep_alive.rb +68 -68
  59. data/lib/winrm/wsmv/receive_response_reader.rb +126 -126
  60. data/lib/winrm/wsmv/send_data.rb +68 -68
  61. data/lib/winrm/wsmv/soap.rb +51 -51
  62. data/lib/winrm/wsmv/wql_query.rb +79 -79
  63. data/lib/winrm/wsmv/write_stdin.rb +88 -88
  64. data/tests/integration/auth_timeout_spec.rb +18 -18
  65. data/tests/integration/cmd_spec.rb +131 -110
  66. data/tests/integration/config-example.yml +16 -16
  67. data/tests/integration/issue_59_spec.rb +26 -26
  68. data/tests/integration/powershell_spec.rb +165 -165
  69. data/tests/integration/spec_helper.rb +65 -65
  70. data/tests/integration/transport_spec.rb +99 -99
  71. data/tests/integration/wql_spec.rb +16 -16
  72. data/tests/matchers.rb +60 -60
  73. data/tests/spec/configuration_spec.rb +184 -184
  74. data/tests/spec/connection_spec.rb +39 -39
  75. data/tests/spec/exception_spec.rb +50 -50
  76. data/tests/spec/http/transport_factory_spec.rb +68 -68
  77. data/tests/spec/http/transport_spec.rb +44 -44
  78. data/tests/spec/output_spec.rb +127 -127
  79. data/tests/spec/psrp/fragment_spec.rb +62 -62
  80. data/tests/spec/psrp/message_data/base_spec.rb +13 -13
  81. data/tests/spec/psrp/message_data/error_record_spec.rb +41 -41
  82. data/tests/spec/psrp/message_data/pipeline_host_call_spec.rb +25 -25
  83. data/tests/spec/psrp/message_data/pipeline_output_spec.rb +32 -32
  84. data/tests/spec/psrp/message_data/pipeline_state_spec.rb +40 -40
  85. data/tests/spec/psrp/message_data/runspace_pool_host_call_spec.rb +25 -25
  86. data/tests/spec/psrp/message_data/runspacepool_state_spec.rb +16 -16
  87. data/tests/spec/psrp/message_data/session_capability_spec.rb +30 -30
  88. data/tests/spec/psrp/message_data_spec.rb +35 -35
  89. data/tests/spec/psrp/message_defragmenter_spec.rb +47 -47
  90. data/tests/spec/psrp/message_fragmenter_spec.rb +105 -105
  91. data/tests/spec/psrp/powershell_output_decoder_spec.rb +100 -100
  92. data/tests/spec/psrp/psrp_message_spec.rb +70 -70
  93. data/tests/spec/psrp/recieve_response_reader_spec.rb +172 -172
  94. data/tests/spec/psrp/uuid_spec.rb +28 -28
  95. data/tests/spec/response_handler_spec.rb +61 -61
  96. data/tests/spec/shells/base_spec.rb +202 -202
  97. data/tests/spec/shells/cmd_spec.rb +75 -75
  98. data/tests/spec/shells/powershell_spec.rb +175 -175
  99. data/tests/spec/spec_helper.rb +47 -47
  100. data/tests/spec/stubs/clixml/error_record.xml.erb +84 -84
  101. data/tests/spec/stubs/clixml/pipeline_state.xml.erb +88 -88
  102. data/tests/spec/stubs/responses/get_command_output_response.xml.erb +13 -13
  103. data/tests/spec/stubs/responses/get_command_output_response_not_done.xml.erb +10 -10
  104. data/tests/spec/stubs/responses/get_powershell_keepalive_response.xml.erb +10 -10
  105. data/tests/spec/stubs/responses/get_powershell_output_response.xml.erb +12 -12
  106. data/tests/spec/stubs/responses/get_powershell_output_response_not_done.xml.erb +9 -9
  107. data/tests/spec/stubs/responses/open_shell_v1.xml +19 -19
  108. data/tests/spec/stubs/responses/open_shell_v2.xml +20 -20
  109. data/tests/spec/stubs/responses/soap_fault_v1.xml +36 -36
  110. data/tests/spec/stubs/responses/soap_fault_v2.xml +42 -42
  111. data/tests/spec/stubs/responses/wmi_error_v2.xml +41 -41
  112. data/tests/spec/wsmv/cleanup_command_spec.rb +22 -22
  113. data/tests/spec/wsmv/close_shell_spec.rb +17 -17
  114. data/tests/spec/wsmv/command_output_decoder_spec.rb +37 -37
  115. data/tests/spec/wsmv/command_output_spec.rb +45 -45
  116. data/tests/spec/wsmv/command_spec.rb +19 -19
  117. data/tests/spec/wsmv/configuration_spec.rb +17 -17
  118. data/tests/spec/wsmv/create_pipeline_spec.rb +31 -31
  119. data/tests/spec/wsmv/create_shell_spec.rb +38 -38
  120. data/tests/spec/wsmv/init_runspace_pool_spec.rb +36 -36
  121. data/tests/spec/wsmv/keep_alive_spec.rb +21 -21
  122. data/tests/spec/wsmv/receive_response_reader_spec.rb +123 -123
  123. data/tests/spec/wsmv/send_data_spec.rb +30 -30
  124. data/tests/spec/wsmv/wql_query_spec.rb +13 -13
  125. data/tests/spec/wsmv/write_stdin_spec.rb +22 -22
  126. data/winrm.gemspec +42 -42
  127. metadata +2 -2
@@ -1,60 +1,60 @@
1
- # encoding: UTF-8
2
- #
3
- # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
4
- #
5
- # Licensed under the Apache License, Version 2.0 (the "License");
6
- # you may not use this file except in compliance with the License.
7
- # You may obtain a copy of the License at
8
- #
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
- #
11
- # Unless required by applicable law or agreed to in writing, software
12
- # distributed under the License is distributed on an "AS IS" BASIS,
13
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- # See the License for the specific language governing permissions and
15
- # limitations under the License.
16
-
17
- # rubocop:disable Metrics/MethodLength
18
-
19
- # Format an ISO8601 Duration
20
- module WinRM
21
- module WSMV
22
- # Converts seconds to ISO8601 duration format
23
- module Iso8601Duration
24
- # Convert the number of seconds to an ISO8601 duration format
25
- # @see http://tools.ietf.org/html/rfc2445#section-4.3.6
26
- # @param [Fixnum] seconds The amount of seconds for this duration
27
- def self.sec_to_dur(seconds)
28
- seconds = seconds.to_i
29
- iso_str = 'P'
30
- if seconds > 604_800 # more than a week
31
- weeks = seconds / 604_800
32
- seconds -= (604_800 * weeks)
33
- iso_str << "#{weeks}W"
34
- end
35
- if seconds > 86_400 # more than a day
36
- days = seconds / 86_400
37
- seconds -= (86_400 * days)
38
- iso_str << "#{days}D"
39
- end
40
- if seconds > 0
41
- iso_str << 'T'
42
- if seconds > 3600 # more than an hour
43
- hours = seconds / 3600
44
- seconds -= (3600 * hours)
45
- iso_str << "#{hours}H"
46
- end
47
- if seconds > 60 # more than a minute
48
- minutes = seconds / 60
49
- seconds -= (60 * minutes)
50
- iso_str << "#{minutes}M"
51
- end
52
- iso_str << "#{seconds}S"
53
- end
54
-
55
- iso_str
56
- end
57
- end
58
- end
59
- end
60
- # rubocop:enable Metrics/MethodLength
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ # rubocop:disable Metrics/MethodLength
18
+
19
+ # Format an ISO8601 Duration
20
+ module WinRM
21
+ module WSMV
22
+ # Converts seconds to ISO8601 duration format
23
+ module Iso8601Duration
24
+ # Convert the number of seconds to an ISO8601 duration format
25
+ # @see http://tools.ietf.org/html/rfc2445#section-4.3.6
26
+ # @param [Fixnum] seconds The amount of seconds for this duration
27
+ def self.sec_to_dur(seconds)
28
+ seconds = seconds.to_i
29
+ iso_str = 'P'
30
+ if seconds > 604_800 # more than a week
31
+ weeks = seconds / 604_800
32
+ seconds -= (604_800 * weeks)
33
+ iso_str << "#{weeks}W"
34
+ end
35
+ if seconds > 86_400 # more than a day
36
+ days = seconds / 86_400
37
+ seconds -= (86_400 * days)
38
+ iso_str << "#{days}D"
39
+ end
40
+ if seconds > 0
41
+ iso_str << 'T'
42
+ if seconds > 3600 # more than an hour
43
+ hours = seconds / 3600
44
+ seconds -= (3600 * hours)
45
+ iso_str << "#{hours}H"
46
+ end
47
+ if seconds > 60 # more than a minute
48
+ minutes = seconds / 60
49
+ seconds -= (60 * minutes)
50
+ iso_str << "#{minutes}M"
51
+ end
52
+ iso_str << "#{seconds}S"
53
+ end
54
+
55
+ iso_str
56
+ end
57
+ end
58
+ end
59
+ end
60
+ # rubocop:enable Metrics/MethodLength
@@ -1,68 +1,68 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Copyright 2016 Matt Wrock <matt@mattwrock.com>
4
- #
5
- # Licensed under the Apache License, Version 2.0 (the "License");
6
- # you may not use this file except in compliance with the License.
7
- # You may obtain a copy of the License at
8
- #
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
- #
11
- # Unless required by applicable law or agreed to in writing, software
12
- # distributed under the License is distributed on an "AS IS" BASIS,
13
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- # See the License for the specific language governing permissions and
15
- # limitations under the License.
16
-
17
- require_relative 'base'
18
-
19
- module WinRM
20
- module WSMV
21
- # WSMV keep alive message
22
- class KeepAlive < Base
23
- attr_accessor :shell_id
24
-
25
- def initialize(session_opts, shell_id)
26
- @session_opts = session_opts
27
- @shell_id = shell_id
28
- end
29
-
30
- protected
31
-
32
- def create_header(header)
33
- header << Gyoku.xml(keep_alive_headers)
34
- end
35
-
36
- def create_body(body)
37
- body.tag!("#{NS_WIN_SHELL}:Receive") { |s| s << Gyoku.xml(keep_alive_body) }
38
- end
39
-
40
- private
41
-
42
- def keep_alive_body
43
- { "#{NS_WIN_SHELL}:DesiredStream" => 'stdout' }
44
- end
45
-
46
- def keep_alive_headers
47
- merge_headers(shared_headers(@session_opts),
48
- resource_uri_shell(RESOURCE_URI_POWERSHELL),
49
- action_receive,
50
- header_opts,
51
- selector_shell_id(shell_id))
52
- end
53
-
54
- def header_opts
55
- {
56
- "#{NS_WSMAN_DMTF}:OptionSet" => {
57
- "#{NS_WSMAN_DMTF}:Option" => 'TRUE',
58
- :attributes! => {
59
- "#{NS_WSMAN_DMTF}:Option" => {
60
- 'Name' => 'WSMAN_CMDSHELL_OPTION_KEEPALIVE'
61
- }
62
- }
63
- }
64
- }
65
- end
66
- end
67
- end
68
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2016 Matt Wrock <matt@mattwrock.com>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require_relative 'base'
18
+
19
+ module WinRM
20
+ module WSMV
21
+ # WSMV keep alive message
22
+ class KeepAlive < Base
23
+ attr_accessor :shell_id
24
+
25
+ def initialize(session_opts, shell_id)
26
+ @session_opts = session_opts
27
+ @shell_id = shell_id
28
+ end
29
+
30
+ protected
31
+
32
+ def create_header(header)
33
+ header << Gyoku.xml(keep_alive_headers)
34
+ end
35
+
36
+ def create_body(body)
37
+ body.tag!("#{NS_WIN_SHELL}:Receive") { |s| s << Gyoku.xml(keep_alive_body) }
38
+ end
39
+
40
+ private
41
+
42
+ def keep_alive_body
43
+ { "#{NS_WIN_SHELL}:DesiredStream" => 'stdout' }
44
+ end
45
+
46
+ def keep_alive_headers
47
+ merge_headers(shared_headers(@session_opts),
48
+ resource_uri_shell(RESOURCE_URI_POWERSHELL),
49
+ action_receive,
50
+ header_opts,
51
+ selector_shell_id(shell_id))
52
+ end
53
+
54
+ def header_opts
55
+ {
56
+ "#{NS_WSMAN_DMTF}:OptionSet" => {
57
+ "#{NS_WSMAN_DMTF}:Option" => 'TRUE',
58
+ :attributes! => {
59
+ "#{NS_WSMAN_DMTF}:Option" => {
60
+ 'Name' => 'WSMAN_CMDSHELL_OPTION_KEEPALIVE'
61
+ }
62
+ }
63
+ }
64
+ }
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,126 +1,126 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Copyright 2016 Shawn Neal <sneal@sneal.net>
4
- #
5
- # Licensed under the Apache License, Version 2.0 (the "License");
6
- # you may not use this file except in compliance with the License.
7
- # You may obtain a copy of the License at
8
- #
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
- #
11
- # Unless required by applicable law or agreed to in writing, software
12
- # distributed under the License is distributed on an "AS IS" BASIS,
13
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- # See the License for the specific language governing permissions and
15
- # limitations under the License.
16
-
17
- require_relative 'soap'
18
- require_relative 'header'
19
- require_relative 'command_output_decoder'
20
- require_relative '../output'
21
-
22
- module WinRM
23
- module WSMV
24
- # Class for reading wsmv Receive_Response messages
25
- class ReceiveResponseReader
26
- include WinRM::WSMV::SOAP
27
- include WinRM::WSMV::Header
28
-
29
- # Creates a new ReceiveResponseReader
30
- # @param transport [HttpTransport] The WinRM SOAP transport
31
- # @param logger [Logger] The logger to log diagnostic messages to
32
- def initialize(transport, logger)
33
- @transport = transport
34
- @logger = logger
35
- @output_decoder = CommandOutputDecoder.new
36
- end
37
-
38
- attr_reader :logger
39
-
40
- # Reads streams and returns decoded output
41
- # @param wsmv_message [WinRM::WSMV::Base] A wsmv message to send to endpoint
42
- # @yieldparam [string] standard out response text
43
- # @yieldparam [string] standard error response text
44
- # @yieldreturn [WinRM::Output] The command output
45
- def read_output(wsmv_message)
46
- with_output do |output|
47
- read_response(wsmv_message, true) do |stream, doc|
48
- handled_out = handle_stream(stream, output, doc)
49
- yield handled_out if handled_out && block_given?
50
- end
51
- end
52
- end
53
-
54
- # Reads streams sent in one or more receive response messages
55
- # @param wsmv_message [WinRM::WSMV::Base] A wsmv message to send to endpoint
56
- # @param wait_for_done_state whether to poll for a CommandState of Done
57
- # @yieldparam [Hash] Hash representation of stream with type and text
58
- # @yieldparam [REXML::Document] Complete SOAP envelope returned to wsmv_message
59
- def read_response(wsmv_message, wait_for_done_state = false)
60
- resp_doc = nil
61
- until command_done?(resp_doc, wait_for_done_state)
62
- logger.debug('[WinRM] Waiting for output...')
63
- resp_doc = send_get_output_message(wsmv_message.build)
64
- logger.debug('[WinRM] Processing output')
65
- read_streams(resp_doc) do |stream|
66
- yield stream, resp_doc
67
- end
68
- end
69
- end
70
-
71
- protected
72
-
73
- def with_output
74
- output = WinRM::Output.new
75
- yield output
76
- output.exitcode ||= 0
77
- output
78
- end
79
-
80
- private
81
-
82
- def handle_stream(stream, output, resp_doc)
83
- decoded_text = @output_decoder.decode(stream[:text])
84
- return unless decoded_text
85
-
86
- out = { stream[:type] => decoded_text }
87
- output << out
88
- if (code = REXML::XPath.first(resp_doc, "//#{NS_WIN_SHELL}:ExitCode"))
89
- output.exitcode = code.text.to_i
90
- end
91
- [out[:stdout], out[:stderr]]
92
- end
93
-
94
- def send_get_output_message(message)
95
- @transport.send_request(message)
96
- rescue WinRMWSManFault => e
97
- # If no output is available before the wsman:OperationTimeout expires,
98
- # the server MUST return a WSManFault with the Code attribute equal to
99
- # 2150858793. When the client receives this fault, it SHOULD issue
100
- # another Receive request.
101
- # http://msdn.microsoft.com/en-us/library/cc251676.aspx
102
- raise unless e.fault_code == '2150858793'
103
-
104
- logger.debug('[WinRM] retrying receive request after timeout')
105
- retry
106
- end
107
-
108
- def command_done?(resp_doc, wait_for_done_state)
109
- return false unless resp_doc
110
- return true unless wait_for_done_state
111
-
112
- REXML::XPath.match(
113
- resp_doc,
114
- "//*[@State='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/" \
115
- "CommandState/Done']").any?
116
- end
117
-
118
- def read_streams(response_document)
119
- REXML::XPath.match(response_document, "//#{NS_WIN_SHELL}:Stream").each do |stream|
120
- next if stream.text.nil? || stream.text.empty?
121
- yield type: stream.attributes['Name'].to_sym, text: stream.text
122
- end
123
- end
124
- end
125
- end
126
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2016 Shawn Neal <sneal@sneal.net>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require_relative 'soap'
18
+ require_relative 'header'
19
+ require_relative 'command_output_decoder'
20
+ require_relative '../output'
21
+
22
+ module WinRM
23
+ module WSMV
24
+ # Class for reading wsmv Receive_Response messages
25
+ class ReceiveResponseReader
26
+ include WinRM::WSMV::SOAP
27
+ include WinRM::WSMV::Header
28
+
29
+ # Creates a new ReceiveResponseReader
30
+ # @param transport [HttpTransport] The WinRM SOAP transport
31
+ # @param logger [Logger] The logger to log diagnostic messages to
32
+ def initialize(transport, logger)
33
+ @transport = transport
34
+ @logger = logger
35
+ @output_decoder = CommandOutputDecoder.new
36
+ end
37
+
38
+ attr_reader :logger
39
+
40
+ # Reads streams and returns decoded output
41
+ # @param wsmv_message [WinRM::WSMV::Base] A wsmv message to send to endpoint
42
+ # @yieldparam [string] standard out response text
43
+ # @yieldparam [string] standard error response text
44
+ # @yieldreturn [WinRM::Output] The command output
45
+ def read_output(wsmv_message)
46
+ with_output do |output|
47
+ read_response(wsmv_message, true) do |stream, doc|
48
+ handled_out = handle_stream(stream, output, doc)
49
+ yield handled_out if handled_out && block_given?
50
+ end
51
+ end
52
+ end
53
+
54
+ # Reads streams sent in one or more receive response messages
55
+ # @param wsmv_message [WinRM::WSMV::Base] A wsmv message to send to endpoint
56
+ # @param wait_for_done_state whether to poll for a CommandState of Done
57
+ # @yieldparam [Hash] Hash representation of stream with type and text
58
+ # @yieldparam [REXML::Document] Complete SOAP envelope returned to wsmv_message
59
+ def read_response(wsmv_message, wait_for_done_state = false)
60
+ resp_doc = nil
61
+ until command_done?(resp_doc, wait_for_done_state)
62
+ logger.debug('[WinRM] Waiting for output...')
63
+ resp_doc = send_get_output_message(wsmv_message.build)
64
+ logger.debug('[WinRM] Processing output')
65
+ read_streams(resp_doc) do |stream|
66
+ yield stream, resp_doc
67
+ end
68
+ end
69
+ end
70
+
71
+ protected
72
+
73
+ def with_output
74
+ output = WinRM::Output.new
75
+ yield output
76
+ output.exitcode ||= 0
77
+ output
78
+ end
79
+
80
+ private
81
+
82
+ def handle_stream(stream, output, resp_doc)
83
+ decoded_text = @output_decoder.decode(stream[:text])
84
+ return unless decoded_text
85
+
86
+ out = { stream[:type] => decoded_text }
87
+ output << out
88
+ if (code = REXML::XPath.first(resp_doc, "//#{NS_WIN_SHELL}:ExitCode"))
89
+ output.exitcode = code.text.to_i
90
+ end
91
+ [out[:stdout], out[:stderr]]
92
+ end
93
+
94
+ def send_get_output_message(message)
95
+ @transport.send_request(message)
96
+ rescue WinRMWSManFault => e
97
+ # If no output is available before the wsman:OperationTimeout expires,
98
+ # the server MUST return a WSManFault with the Code attribute equal to
99
+ # 2150858793. When the client receives this fault, it SHOULD issue
100
+ # another Receive request.
101
+ # http://msdn.microsoft.com/en-us/library/cc251676.aspx
102
+ raise unless e.fault_code == '2150858793'
103
+
104
+ logger.debug('[WinRM] retrying receive request after timeout')
105
+ retry
106
+ end
107
+
108
+ def command_done?(resp_doc, wait_for_done_state)
109
+ return false unless resp_doc
110
+ return true unless wait_for_done_state
111
+
112
+ REXML::XPath.match(
113
+ resp_doc,
114
+ "//*[@State='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/" \
115
+ "CommandState/Done']").any?
116
+ end
117
+
118
+ def read_streams(response_document)
119
+ REXML::XPath.match(response_document, "//#{NS_WIN_SHELL}:Stream").each do |stream|
120
+ next if stream.text.nil? || stream.text.empty?
121
+ yield type: stream.attributes['Name'].to_sym, text: stream.text
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end