winrm 2.0.3 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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