winrm 2.2.3 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +13 -1
  3. data/.travis.yml +10 -11
  4. data/Gemfile +2 -3
  5. data/README.md +1 -1
  6. data/Rakefile +3 -4
  7. data/appveyor.yml +1 -2
  8. data/bin/rwinrm +90 -97
  9. data/changelog.md +5 -0
  10. data/lib/winrm.rb +3 -5
  11. data/lib/winrm/connection.rb +84 -86
  12. data/lib/winrm/connection_opts.rb +90 -91
  13. data/lib/winrm/exceptions.rb +14 -2
  14. data/lib/winrm/http/response_handler.rb +127 -96
  15. data/lib/winrm/http/transport.rb +462 -427
  16. data/lib/winrm/http/transport_factory.rb +1 -5
  17. data/lib/winrm/output.rb +1 -2
  18. data/lib/winrm/psrp/fragment.rb +0 -2
  19. data/lib/winrm/psrp/message.rb +1 -3
  20. data/lib/winrm/psrp/message_data.rb +0 -2
  21. data/lib/winrm/psrp/message_data/base.rb +0 -2
  22. data/lib/winrm/psrp/message_data/error_record.rb +0 -2
  23. data/lib/winrm/psrp/message_data/pipeline_host_call.rb +0 -2
  24. data/lib/winrm/psrp/message_data/pipeline_output.rb +48 -54
  25. data/lib/winrm/psrp/message_data/pipeline_state.rb +0 -2
  26. data/lib/winrm/psrp/message_data/runspacepool_host_call.rb +0 -2
  27. data/lib/winrm/psrp/message_data/runspacepool_state.rb +0 -2
  28. data/lib/winrm/psrp/message_data/session_capability.rb +0 -2
  29. data/lib/winrm/psrp/message_defragmenter.rb +2 -2
  30. data/lib/winrm/psrp/message_factory.rb +2 -3
  31. data/lib/winrm/psrp/message_fragmenter.rb +1 -3
  32. data/lib/winrm/psrp/powershell_output_decoder.rb +0 -2
  33. data/lib/winrm/psrp/receive_response_reader.rb +3 -5
  34. data/lib/winrm/psrp/uuid.rb +1 -2
  35. data/lib/winrm/shells/base.rb +6 -4
  36. data/lib/winrm/shells/cmd.rb +63 -65
  37. data/lib/winrm/shells/power_shell.rb +207 -202
  38. data/lib/winrm/shells/retryable.rb +44 -45
  39. data/lib/winrm/shells/shell_factory.rb +0 -2
  40. data/lib/winrm/version.rb +1 -3
  41. data/lib/winrm/wsmv/base.rb +0 -2
  42. data/lib/winrm/wsmv/cleanup_command.rb +1 -2
  43. data/lib/winrm/wsmv/close_shell.rb +1 -2
  44. data/lib/winrm/wsmv/command.rb +2 -3
  45. data/lib/winrm/wsmv/command_output.rb +2 -3
  46. data/lib/winrm/wsmv/command_output_decoder.rb +1 -2
  47. data/lib/winrm/wsmv/configuration.rb +0 -2
  48. data/lib/winrm/wsmv/create_pipeline.rb +0 -2
  49. data/lib/winrm/wsmv/create_shell.rb +2 -6
  50. data/lib/winrm/wsmv/header.rb +213 -215
  51. data/lib/winrm/wsmv/init_runspace_pool.rb +96 -95
  52. data/lib/winrm/wsmv/iso8601_duration.rb +0 -2
  53. data/lib/winrm/wsmv/keep_alive.rb +0 -2
  54. data/lib/winrm/wsmv/receive_response_reader.rb +128 -126
  55. data/lib/winrm/wsmv/send_data.rb +0 -2
  56. data/lib/winrm/wsmv/soap.rb +0 -2
  57. data/lib/winrm/wsmv/wql_pull.rb +54 -56
  58. data/lib/winrm/wsmv/wql_query.rb +98 -99
  59. data/lib/winrm/wsmv/write_stdin.rb +0 -2
  60. data/tests/integration/auth_timeout_spec.rb +0 -1
  61. data/tests/integration/cmd_spec.rb +2 -3
  62. data/tests/integration/issue_59_spec.rb +0 -1
  63. data/tests/integration/powershell_spec.rb +4 -5
  64. data/tests/integration/spec_helper.rb +3 -6
  65. data/tests/integration/transport_spec.rb +0 -1
  66. data/tests/integration/wql_spec.rb +33 -34
  67. data/tests/matchers.rb +2 -3
  68. data/tests/spec/configuration_spec.rb +0 -1
  69. data/tests/spec/connection_spec.rb +0 -2
  70. data/tests/spec/exception_spec.rb +0 -1
  71. data/tests/spec/http/transport_factory_spec.rb +1 -3
  72. data/tests/spec/http/transport_spec.rb +0 -1
  73. data/tests/spec/output_spec.rb +4 -3
  74. data/tests/spec/psrp/fragment_spec.rb +0 -2
  75. data/tests/spec/psrp/message_data/base_spec.rb +0 -2
  76. data/tests/spec/psrp/message_data/error_record_spec.rb +0 -2
  77. data/tests/spec/psrp/message_data/pipeline_host_call_spec.rb +0 -2
  78. data/tests/spec/psrp/message_data/pipeline_output_spec.rb +0 -2
  79. data/tests/spec/psrp/message_data/pipeline_state_spec.rb +0 -2
  80. data/tests/spec/psrp/message_data/runspace_pool_host_call_spec.rb +0 -2
  81. data/tests/spec/psrp/message_data/runspacepool_state_spec.rb +0 -2
  82. data/tests/spec/psrp/message_data/session_capability_spec.rb +0 -2
  83. data/tests/spec/psrp/message_data_spec.rb +0 -2
  84. data/tests/spec/psrp/message_defragmenter_spec.rb +0 -2
  85. data/tests/spec/psrp/message_fragmenter_spec.rb +0 -2
  86. data/tests/spec/psrp/powershell_output_decoder_spec.rb +0 -2
  87. data/tests/spec/psrp/psrp_message_spec.rb +10 -7
  88. data/tests/spec/psrp/recieve_response_reader_spec.rb +0 -2
  89. data/tests/spec/psrp/uuid_spec.rb +2 -2
  90. data/tests/spec/response_handler_spec.rb +69 -61
  91. data/tests/spec/shells/base_spec.rb +7 -5
  92. data/tests/spec/shells/cmd_spec.rb +4 -4
  93. data/tests/spec/shells/powershell_spec.rb +221 -175
  94. data/tests/spec/spec_helper.rb +0 -1
  95. data/tests/spec/stubs/responses/get_omi_command_output_response.xml.erb +23 -0
  96. data/tests/spec/stubs/responses/get_omi_command_output_response_not_done.xml.erb +24 -0
  97. data/tests/spec/stubs/responses/get_omi_config_response.xml +45 -0
  98. data/tests/spec/stubs/responses/get_omi_powershell_keepalive_response.xml.erb +33 -0
  99. data/tests/spec/stubs/responses/open_shell_omi.xml +43 -0
  100. data/tests/spec/stubs/responses/soap_fault_omi.xml +31 -0
  101. data/tests/spec/wsmv/cleanup_command_spec.rb +0 -2
  102. data/tests/spec/wsmv/close_shell_spec.rb +0 -2
  103. data/tests/spec/wsmv/command_output_decoder_spec.rb +0 -2
  104. data/tests/spec/wsmv/command_output_spec.rb +1 -3
  105. data/tests/spec/wsmv/command_spec.rb +0 -2
  106. data/tests/spec/wsmv/configuration_spec.rb +0 -2
  107. data/tests/spec/wsmv/create_pipeline_spec.rb +2 -3
  108. data/tests/spec/wsmv/create_shell_spec.rb +6 -5
  109. data/tests/spec/wsmv/init_runspace_pool_spec.rb +38 -36
  110. data/tests/spec/wsmv/keep_alive_spec.rb +4 -4
  111. data/tests/spec/wsmv/receive_response_reader_spec.rb +124 -123
  112. data/tests/spec/wsmv/send_data_spec.rb +4 -4
  113. data/tests/spec/wsmv/wql_query_spec.rb +11 -13
  114. data/tests/spec/wsmv/write_stdin_spec.rb +0 -2
  115. data/winrm.gemspec +11 -12
  116. metadata +73 -67
@@ -1,95 +1,96 @@
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 message to create a remote shell
22
- class InitRunspacePool < Base
23
- attr_accessor :shell_id
24
-
25
- def initialize(session_opts, shell_id, payload)
26
- @session_opts = session_opts
27
- @shell_id = shell_id
28
- @payload = payload
29
- end
30
-
31
- protected
32
-
33
- def create_header(header)
34
- header << Gyoku.xml(shell_headers)
35
- end
36
-
37
- def create_body(body)
38
- body.tag!("#{NS_WIN_SHELL}:Shell", 'ShellId' => shell_id) { |s| s << Gyoku.xml(shell_body) }
39
- end
40
-
41
- private
42
-
43
- def shell_body
44
- body = {
45
- "#{NS_WIN_SHELL}:InputStreams" => 'stdin pr',
46
- "#{NS_WIN_SHELL}:OutputStreams" => 'stdout'
47
- }
48
- body['creationXml'] = encode_bytes(@payload)
49
- body[:attributes!] = {
50
- 'creationXml' => {
51
- 'xmlns' => 'http://schemas.microsoft.com/powershell'
52
- }
53
- }
54
- body
55
- end
56
-
57
- def shell_headers
58
- merge_headers(shared_headers(@session_opts),
59
- resource_uri_shell(RESOURCE_URI_POWERSHELL),
60
- action_create,
61
- header_opts)
62
- end
63
-
64
- def action_create
65
- {
66
- "#{NS_ADDRESSING}:Action" => 'http://schemas.xmlsoap.org/ws/2004/09/transfer/Create',
67
- :attributes! => {
68
- "#{NS_ADDRESSING}:Action" => {
69
- 'mustUnderstand' => true
70
- }
71
- }
72
- }
73
- end
74
-
75
- def header_opts
76
- {
77
- "#{NS_WSMAN_DMTF}:OptionSet" => {
78
- "#{NS_WSMAN_DMTF}:Option" => 2.3,
79
- :attributes! => {
80
- "#{NS_WSMAN_DMTF}:Option" => {
81
- 'Name' => 'protocolversion',
82
- 'MustComply' => 'true'
83
- }
84
- }
85
- },
86
- :attributes! => {
87
- "#{NS_WSMAN_DMTF}:OptionSet" => {
88
- 'env:mustUnderstand' => 'true'
89
- }
90
- }
91
- }
92
- end
93
- end
94
- end
95
- end
1
+ # Copyright 2016 Matt Wrock <matt@mattwrock.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require_relative 'base'
16
+
17
+ module WinRM
18
+ module WSMV
19
+ # WSMV message to create a remote shell
20
+ class InitRunspacePool < Base
21
+ attr_accessor :shell_id
22
+
23
+ def initialize(session_opts, shell_id, payload)
24
+ @session_opts = session_opts
25
+ @shell_id = shell_id
26
+ @payload = payload
27
+ end
28
+
29
+ protected
30
+
31
+ def create_header(header)
32
+ header << Gyoku.xml(shell_headers)
33
+ end
34
+
35
+ def create_body(body)
36
+ # OMI server requires a Name for the Shell
37
+ body.tag!("#{NS_WIN_SHELL}:Shell", 'ShellId' => shell_id, 'Name' => 'Runspace') do |s|
38
+ s << Gyoku.xml(shell_body)
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def shell_body
45
+ body = {
46
+ "#{NS_WIN_SHELL}:InputStreams" => 'stdin pr',
47
+ "#{NS_WIN_SHELL}:OutputStreams" => 'stdout'
48
+ }
49
+ body['creationXml'] = encode_bytes(@payload)
50
+ body[:attributes!] = {
51
+ 'creationXml' => {
52
+ 'xmlns' => 'http://schemas.microsoft.com/powershell'
53
+ }
54
+ }
55
+ body
56
+ end
57
+
58
+ def shell_headers
59
+ merge_headers(shared_headers(@session_opts),
60
+ resource_uri_shell(RESOURCE_URI_POWERSHELL),
61
+ action_create,
62
+ header_opts)
63
+ end
64
+
65
+ def action_create
66
+ {
67
+ "#{NS_ADDRESSING}:Action" => 'http://schemas.xmlsoap.org/ws/2004/09/transfer/Create',
68
+ :attributes! => {
69
+ "#{NS_ADDRESSING}:Action" => {
70
+ 'mustUnderstand' => true
71
+ }
72
+ }
73
+ }
74
+ end
75
+
76
+ def header_opts
77
+ {
78
+ "#{NS_WSMAN_DMTF}:OptionSet" => {
79
+ "#{NS_WSMAN_DMTF}:Option" => 2.3,
80
+ :attributes! => {
81
+ "#{NS_WSMAN_DMTF}:Option" => {
82
+ 'Name' => 'protocolversion',
83
+ 'MustComply' => 'true'
84
+ }
85
+ }
86
+ },
87
+ :attributes! => {
88
+ "#{NS_WSMAN_DMTF}:OptionSet" => {
89
+ 'env:mustUnderstand' => 'true'
90
+ }
91
+ }
92
+ }
93
+ end
94
+ end
95
+ end
96
+ end
@@ -1,5 +1,3 @@
1
- # encoding: UTF-8
2
- #
3
1
  # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
4
2
  #
5
3
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,5 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
1
  # Copyright 2016 Matt Wrock <matt@mattwrock.com>
4
2
  #
5
3
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,126 +1,128 @@
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
+ # Copyright 2016 Shawn Neal <sneal@sneal.net>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require_relative 'soap'
16
+ require_relative 'header'
17
+ require_relative 'command_output_decoder'
18
+ require_relative '../output'
19
+
20
+ module WinRM
21
+ module WSMV
22
+ # Class for reading wsmv Receive_Response messages
23
+ class ReceiveResponseReader
24
+ include WinRM::WSMV::SOAP
25
+ include WinRM::WSMV::Header
26
+
27
+ # Creates a new ReceiveResponseReader
28
+ # @param transport [HttpTransport] The WinRM SOAP transport
29
+ # @param logger [Logger] The logger to log diagnostic messages to
30
+ def initialize(transport, logger)
31
+ @transport = transport
32
+ @logger = logger
33
+ @output_decoder = CommandOutputDecoder.new
34
+ end
35
+
36
+ attr_reader :logger
37
+
38
+ # Reads streams and returns decoded output
39
+ # @param wsmv_message [WinRM::WSMV::Base] A wsmv message to send to endpoint
40
+ # @yieldparam [string] standard out response text
41
+ # @yieldparam [string] standard error response text
42
+ # @yieldreturn [WinRM::Output] The command output
43
+ def read_output(wsmv_message)
44
+ with_output do |output|
45
+ read_response(wsmv_message, true) do |stream, doc|
46
+ handled_out = handle_stream(stream, output, doc)
47
+ yield handled_out if handled_out && block_given?
48
+ end
49
+ end
50
+ end
51
+
52
+ # Reads streams sent in one or more receive response messages
53
+ # @param wsmv_message [WinRM::WSMV::Base] A wsmv message to send to endpoint
54
+ # @param wait_for_done_state whether to poll for a CommandState of Done
55
+ # @yieldparam [Hash] Hash representation of stream with type and text
56
+ # @yieldparam [REXML::Document] Complete SOAP envelope returned to wsmv_message
57
+ def read_response(wsmv_message, wait_for_done_state = false)
58
+ resp_doc = nil
59
+ until command_done?(resp_doc, wait_for_done_state)
60
+ logger.debug('[WinRM] Waiting for output...')
61
+ resp_doc = send_get_output_message(wsmv_message.build)
62
+ logger.debug('[WinRM] Processing output')
63
+ read_streams(resp_doc) do |stream|
64
+ yield stream, resp_doc
65
+ end
66
+ end
67
+ end
68
+
69
+ protected
70
+
71
+ def with_output
72
+ output = WinRM::Output.new
73
+ yield output
74
+ output.exitcode ||= 0
75
+ output
76
+ end
77
+
78
+ private
79
+
80
+ def handle_stream(stream, output, resp_doc)
81
+ decoded_text = @output_decoder.decode(stream[:text])
82
+ return unless decoded_text
83
+
84
+ out = { stream[:type] => decoded_text }
85
+ output << out
86
+ if (code = REXML::XPath.first(resp_doc, "//*[local-name() = 'ExitCode']"))
87
+ output.exitcode = code.text.to_i
88
+ end
89
+ [out[:stdout], out[:stderr]]
90
+ end
91
+
92
+ def send_get_output_message(message)
93
+ @transport.send_request(message)
94
+ rescue WinRMWSManFault => e
95
+ # If no output is available before the wsman:OperationTimeout expires,
96
+ # the server MUST return a WSManFault with the Code attribute equal to
97
+ # 2150858793. When the client receives this fault, it SHOULD issue
98
+ # another Receive request.
99
+ # http://msdn.microsoft.com/en-us/library/cc251676.aspx
100
+ raise unless e.fault_code == '2150858793'
101
+
102
+ logger.debug('[WinRM] retrying receive request after timeout')
103
+ retry
104
+ end
105
+
106
+ def command_done?(resp_doc, wait_for_done_state)
107
+ return false unless resp_doc
108
+ return true unless wait_for_done_state
109
+
110
+ REXML::XPath.match(
111
+ resp_doc,
112
+ "//*[@State='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/" \
113
+ "CommandState/Done']"
114
+ ).any?
115
+ end
116
+
117
+ def read_streams(response_document)
118
+ body_path = "/*[local-name() = 'Envelope']/*[local-name() = 'Body']"
119
+ path = "#{body_path}/*[local-name() = 'ReceiveResponse']/*[local-name() = 'Stream']"
120
+ REXML::XPath.match(response_document, path).each do |stream|
121
+ next if stream.text.nil? || stream.text.empty?
122
+
123
+ yield type: stream.attributes['Name'].to_sym, text: stream.text
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end