/tools/Ruby/lib/ruby/1.8/soap/streamHandler.rb

http://github.com/agross/netopenspace · Ruby · 229 lines · 189 code · 34 blank · 6 comment · 21 complexity · 0af91e9db62d09e951e0dd1efb2c8a68 MD5 · raw file

  1. # SOAP4R - Stream handler.
  2. # Copyright (C) 2000, 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
  3. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
  4. # redistribute it and/or modify it under the same terms of Ruby's license;
  5. # either the dual license version in 2003, or any later version.
  6. require 'soap/soap'
  7. require 'soap/httpconfigloader'
  8. begin
  9. require 'stringio'
  10. require 'zlib'
  11. rescue LoadError
  12. warn("Loading stringio or zlib failed. No gzipped response support.") if $DEBUG
  13. end
  14. module SOAP
  15. class StreamHandler
  16. RUBY_VERSION_STRING = "ruby #{ RUBY_VERSION } (#{ RUBY_RELEASE_DATE }) [#{ RUBY_PLATFORM }]"
  17. class ConnectionData
  18. attr_accessor :send_string
  19. attr_accessor :send_contenttype
  20. attr_accessor :receive_string
  21. attr_accessor :receive_contenttype
  22. attr_accessor :is_fault
  23. attr_accessor :soapaction
  24. def initialize(send_string = nil)
  25. @send_string = send_string
  26. @send_contenttype = nil
  27. @receive_string = nil
  28. @receive_contenttype = nil
  29. @is_fault = false
  30. @soapaction = nil
  31. end
  32. end
  33. def self.parse_media_type(str)
  34. if /^#{ MediaType }(?:\s*;\s*charset=([^"]+|"[^"]+"))?$/i !~ str
  35. return nil
  36. end
  37. charset = $1
  38. charset.gsub!(/"/, '') if charset
  39. charset || 'us-ascii'
  40. end
  41. def self.create_media_type(charset)
  42. "#{ MediaType }; charset=#{ charset }"
  43. end
  44. end
  45. class HTTPStreamHandler < StreamHandler
  46. include SOAP
  47. begin
  48. require 'http-access2'
  49. if HTTPAccess2::VERSION < "2.0"
  50. raise LoadError.new("http-access/2.0 or later is required.")
  51. end
  52. Client = HTTPAccess2::Client
  53. RETRYABLE = true
  54. rescue LoadError
  55. warn("Loading http-access2 failed. Net/http is used.") if $DEBUG
  56. require 'soap/netHttpClient'
  57. Client = SOAP::NetHttpClient
  58. RETRYABLE = false
  59. end
  60. public
  61. attr_reader :client
  62. attr_accessor :wiredump_file_base
  63. MAX_RETRY_COUNT = 10 # [times]
  64. def initialize(options)
  65. super()
  66. @client = Client.new(nil, "SOAP4R/#{ Version }")
  67. @wiredump_file_base = nil
  68. @charset = @wiredump_dev = nil
  69. @options = options
  70. set_options
  71. @client.debug_dev = @wiredump_dev
  72. @cookie_store = nil
  73. @accept_encoding_gzip = false
  74. end
  75. def test_loopback_response
  76. @client.test_loopback_response
  77. end
  78. def accept_encoding_gzip=(allow)
  79. @accept_encoding_gzip = allow
  80. end
  81. def inspect
  82. "#<#{self.class}>"
  83. end
  84. def send(endpoint_url, conn_data, soapaction = nil, charset = @charset)
  85. conn_data.soapaction ||= soapaction # for backward conpatibility
  86. send_post(endpoint_url, conn_data, charset)
  87. end
  88. def reset(endpoint_url = nil)
  89. if endpoint_url.nil?
  90. @client.reset_all
  91. else
  92. @client.reset(endpoint_url)
  93. end
  94. @client.save_cookie_store if @cookie_store
  95. end
  96. private
  97. def set_options
  98. HTTPConfigLoader.set_options(@client, @options)
  99. @charset = @options["charset"] || XSD::Charset.xml_encoding_label
  100. @options.add_hook("charset") do |key, value|
  101. @charset = value
  102. end
  103. @wiredump_dev = @options["wiredump_dev"]
  104. @options.add_hook("wiredump_dev") do |key, value|
  105. @wiredump_dev = value
  106. @client.debug_dev = @wiredump_dev
  107. end
  108. set_cookie_store_file(@options["cookie_store_file"])
  109. @options.add_hook("cookie_store_file") do |key, value|
  110. set_cookie_store_file(value)
  111. end
  112. ssl_config = @options["ssl_config"]
  113. basic_auth = @options["basic_auth"]
  114. @options.lock(true)
  115. ssl_config.unlock
  116. basic_auth.unlock
  117. end
  118. def set_cookie_store_file(value)
  119. value = nil if value and value.empty?
  120. @cookie_store = value
  121. @client.set_cookie_store(@cookie_store) if @cookie_store
  122. end
  123. def send_post(endpoint_url, conn_data, charset)
  124. conn_data.send_contenttype ||= StreamHandler.create_media_type(charset)
  125. if @wiredump_file_base
  126. filename = @wiredump_file_base + '_request.xml'
  127. f = File.open(filename, "w")
  128. f << conn_data.send_string
  129. f.close
  130. end
  131. extra = {}
  132. extra['Content-Type'] = conn_data.send_contenttype
  133. extra['SOAPAction'] = "\"#{ conn_data.soapaction }\""
  134. extra['Accept-Encoding'] = 'gzip' if send_accept_encoding_gzip?
  135. send_string = conn_data.send_string
  136. @wiredump_dev << "Wire dump:\n\n" if @wiredump_dev
  137. begin
  138. retry_count = 0
  139. while true
  140. res = @client.post(endpoint_url, send_string, extra)
  141. if RETRYABLE and HTTP::Status.redirect?(res.status)
  142. retry_count += 1
  143. if retry_count >= MAX_RETRY_COUNT
  144. raise HTTPStreamError.new("redirect count exceeded")
  145. end
  146. endpoint_url = res.header["location"][0]
  147. puts "redirected to #{endpoint_url}" if $DEBUG
  148. else
  149. break
  150. end
  151. end
  152. rescue
  153. @client.reset(endpoint_url)
  154. raise
  155. end
  156. @wiredump_dev << "\n\n" if @wiredump_dev
  157. receive_string = res.content
  158. if @wiredump_file_base
  159. filename = @wiredump_file_base + '_response.xml'
  160. f = File.open(filename, "w")
  161. f << receive_string
  162. f.close
  163. end
  164. case res.status
  165. when 405
  166. raise PostUnavailableError.new("#{ res.status }: #{ res.reason }")
  167. when 200, 500
  168. # Nothing to do.
  169. else
  170. raise HTTPStreamError.new("#{ res.status }: #{ res.reason }")
  171. end
  172. if res.respond_to?(:header) and !res.header['content-encoding'].empty? and
  173. res.header['content-encoding'][0].downcase == 'gzip'
  174. receive_string = decode_gzip(receive_string)
  175. end
  176. conn_data.receive_string = receive_string
  177. conn_data.receive_contenttype = res.contenttype
  178. conn_data
  179. end
  180. def send_accept_encoding_gzip?
  181. @accept_encoding_gzip and defined?(::Zlib)
  182. end
  183. def decode_gzip(instring)
  184. unless send_accept_encoding_gzip?
  185. raise HTTPStreamError.new("Gzipped response content.")
  186. end
  187. begin
  188. gz = Zlib::GzipReader.new(StringIO.new(instring))
  189. gz.read
  190. ensure
  191. gz.close
  192. end
  193. end
  194. end
  195. end