Welcome to the "trac"-ing site of soap4r!
[soap4r] [httpclient] [openpgp4u] [pkcs1] [logger] [csv] [vtr]

root/trunk/lib/soap/streamHandler.rb

Revision 2000, 7.7 kB (checked in by nahi, 1 year ago)
  • removed backward compatibility definitions for soap4r-1.4.X. closes #445.
    • removed SOAPlet#{app_scope_router,add_servant} -> use methods in HTTPServer instead.
    • removed SOAP::WSDLDriver#generateEncodeType -> use SOAP::WSDLDriver#generate_explicit_type
    • removed SOAP::SOAPGenerator -> use SOAP::Generator instead.
    • removed compatibility method definitions for ruby-1.6.X.
  • rpc/encoded service + detail element without xsi:type attribute caused NameError? since 1.5.6. (#435)
  • extract attr_proxy definition. added soap/attrproxy.rb and wsdl/xmlSchema/ref.rb.
  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 # SOAP4R - Stream handler.
2 # Copyright (C) 2000-2007  NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
4 # This program is copyrighted free software by NAKAMURA, Hiroshi.  You can
5 # redistribute it and/or modify it under the same terms of Ruby's license;
6 # either the dual license version in 2003, or any later version.
7
8
9 require 'soap/soap'
10 require 'soap/httpconfigloader'
11 require 'soap/filter/filterchain'
12 begin
13   require 'stringio'
14   require 'zlib'
15 rescue LoadError
16   warn("Loading stringio or zlib failed.  No gzipped response support.") if $DEBUG
17 end
18
19
20 module SOAP
21
22
23 class StreamHandler
24   RUBY_VERSION_STRING = "ruby #{ RUBY_VERSION } (#{ RUBY_RELEASE_DATE }) [#{ RUBY_PLATFORM }]"
25
26   attr_reader :filterchain
27
28   class ConnectionData
29     attr_accessor :send_string
30     attr_accessor :send_contenttype
31     attr_accessor :receive_string
32     attr_accessor :receive_contenttype
33     attr_accessor :is_fault
34     attr_accessor :is_nocontent
35     attr_accessor :soapaction
36
37     def initialize(send_string = nil)
38       @send_string = send_string
39       @send_contenttype = nil
40       @receive_string = nil
41       @receive_contenttype = nil
42       @is_fault = false
43       @is_nocontent = false
44       @soapaction = nil
45     end
46   end
47
48   def initialize
49     @filterchain = Filter::FilterChain.new
50   end
51
52   def self.parse_media_type(str)
53     if /^#{ MediaType }(?:\s*;\s*charset=([^"]+|"[^"]+"))?$/i !~ str
54       return nil
55     end
56     charset = $1
57     charset.gsub!(/"/, '') if charset
58     charset || 'us-ascii'
59   end
60
61   def self.create_media_type(charset)
62     "#{ MediaType }; charset=#{ charset }"
63   end
64
65   def send(url, conn_data, soapaction = nil, charset = nil)
66     # send a ConnectionData to specified url.
67     # return value is a ConnectionData with receive_* property filled.
68     # You can fill values of given conn_data and return it.
69   end
70
71   def reset(url = nil)
72     # for initializing connection status if needed.
73     # return value is not expected.
74   end
75
76   def set_wiredump_file_base(wiredump_file_base)
77     # for logging.  return value is not expected.
78     # Override it when you want.
79     raise NotImplementedError
80   end
81
82   def test_loopback_response
83     # for loopback testing.  see HTTPStreamHandler for more detail.
84     # return value is an Array of loopback responses.
85     # Override it when you want.
86     raise NotImplementedError
87   end
88 end
89
90
91 class HTTPStreamHandler < StreamHandler
92   include SOAP
93
94   begin
95     require 'httpclient'
96     Client = HTTPClient
97     RETRYABLE = true
98   rescue LoadError
99     begin
100       require 'http-access2'
101       if HTTPAccess2::VERSION < "2.0"
102         raise LoadError.new("http-access/2.0 or later is required.")
103       end
104       Client = HTTPAccess2::Client
105       RETRYABLE = true
106     rescue LoadError
107       warn("Loading http-access2 failed.  Net/http is used.") if $DEBUG
108       require 'soap/netHttpClient'
109       Client = SOAP::NetHttpClient
110       RETRYABLE = false
111     end
112   end
113
114   class HttpPostRequestFilter
115     def initialize(filterchain)
116       @filterchain = filterchain
117     end
118
119     def filter_request(req)
120       @filterchain.each do |filter|
121         filter.on_http_outbound(req)
122       end
123     end
124
125     def filter_response(req, res)
126       @filterchain.each do |filter|
127         filter.on_http_inbound(req, res)
128       end
129     end
130   end
131
132 public
133  
134   attr_reader :client
135   attr_accessor :wiredump_file_base
136  
137   MAX_RETRY_COUNT = 10          # [times]
138
139   def self.create(options)
140     new(options)
141   end
142
143   def initialize(options)
144     super()
145     @client = Client.new(nil, "SOAP4R/#{ Version }")
146     if @client.respond_to?(:request_filter)
147       @client.request_filter << HttpPostRequestFilter.new(@filterchain)
148     end
149     @wiredump_file_base = nil
150     @charset = @wiredump_dev = nil
151     @options = options
152     set_options
153     @client.debug_dev = @wiredump_dev
154     @cookie_store = nil
155     @accept_encoding_gzip = false
156   end
157
158   def test_loopback_response
159     @client.test_loopback_response
160   end
161
162   def accept_encoding_gzip=(allow)
163     @accept_encoding_gzip = allow
164   end
165
166   def inspect
167     "#<#{self.class}>"
168   end
169
170   def send(url, conn_data, charset = @charset)
171     conn_data = send_post(url, conn_data, charset)
172     @client.save_cookie_store if @cookie_store
173     conn_data
174   end
175
176   def reset(url = nil)
177     if url.nil?
178       @client.reset_all
179     else
180       @client.reset(url)
181     end
182     @client.save_cookie_store if @cookie_store
183   end
184
185 private
186
187   def set_options
188     @options["http"] ||= ::SOAP::Property.new
189     HTTPConfigLoader.set_options(@client, @options["http"])
190     @charset = @options["http.charset"] || XSD::Charset.xml_encoding_label
191     @options.add_hook("http.charset") do |key, value|
192       @charset = value
193     end
194     @wiredump_dev = @options["http.wiredump_dev"]
195     @options.add_hook("http.wiredump_dev") do |key, value|
196       @wiredump_dev = value
197       @client.debug_dev = @wiredump_dev
198     end
199     set_cookie_store_file(@options["http.cookie_store_file"])
200     @options.add_hook("http.cookie_store_file") do |key, value|
201       set_cookie_store_file(value)
202     end
203     ssl_config = @options["http.ssl_config"]
204     basic_auth = @options["http.basic_auth"]
205     auth = @options["http.auth"]
206     @options["http"].lock(true)
207     ssl_config.unlock
208     basic_auth.unlock
209     auth.unlock
210   end
211
212   def set_cookie_store_file(value)
213     value = nil if value and value.empty?
214     @cookie_store = value
215     @client.set_cookie_store(@cookie_store) if @cookie_store
216   end
217
218   def send_post(url, conn_data, charset)
219     conn_data.send_contenttype ||= StreamHandler.create_media_type(charset)
220
221     if @wiredump_file_base
222       filename = @wiredump_file_base + '_request.xml'
223       f = File.open(filename, "w")
224       f << conn_data.send_string
225       f.close
226     end
227
228     extheader = {}
229     extheader['Content-Type'] = conn_data.send_contenttype
230     extheader['SOAPAction'] = "\"#{ conn_data.soapaction }\""
231     extheader['Accept-Encoding'] = 'gzip' if send_accept_encoding_gzip?
232     send_string = conn_data.send_string
233     @wiredump_dev << "Wire dump:\n\n" if @wiredump_dev
234     begin
235       retry_count = 0
236       while true
237         res = @client.post(url, send_string, extheader)
238         if RETRYABLE and HTTP::Status.redirect?(res.status)
239           retry_count += 1
240           if retry_count >= MAX_RETRY_COUNT
241             raise HTTPStreamError.new("redirect count exceeded")
242           end
243           url = res.header["location"][0]
244           puts "redirected to #{url}" if $DEBUG
245         else
246           break
247         end
248       end
249     rescue
250       @client.reset(url)
251       raise
252     end
253     @wiredump_dev << "\n\n" if @wiredump_dev
254     receive_string = res.content
255     if @wiredump_file_base
256       filename = @wiredump_file_base + '_response.xml'
257       f = File.open(filename, "w")
258       f << receive_string
259       f.close
260     end
261     case res.status
262     when 405
263       raise PostUnavailableError.new("#{ res.status }: #{ res.reason }")
264     when 200, 202, 500
265       # Nothing to do.  202 is for oneway service.
266     else
267       raise HTTPStreamError.new("#{ res.status }: #{ res.reason }")
268     end
269
270     # decode gzipped content, if we know it's there from the headers
271     if res.respond_to?(:header) and !res.header['content-encoding'].empty? and
272         res.header['content-encoding'][0].downcase == 'gzip'
273       receive_string = decode_gzip(receive_string)
274     # otherwise check for the gzip header
275     elsif @accept_encoding_gzip && receive_string[0..1] == "\x1f\x8b"
276       receive_string = decode_gzip(receive_string)
277     end
278     conn_data.receive_string = receive_string
279     conn_data.receive_contenttype = res.contenttype
280     conn_data
281   end
282
283   def send_accept_encoding_gzip?
284     @accept_encoding_gzip and defined?(::Zlib)
285   end
286
287   def decode_gzip(instring)
288     unless send_accept_encoding_gzip?
289       raise HTTPStreamError.new("Gzipped response content.")
290     end
291     begin
292       gz = Zlib::GzipReader.new(StringIO.new(instring))
293       gz.read
294     ensure
295       gz.close
296     end
297   end
298 end
299
300
301 end
Note: See TracBrowser for help on using the browser.