1 | # SOAP4R - SOAP handler servlet for WEBrick
|
---|
2 | # Copyright (C) 2001-2005 NAKAMURA, Hiroshi <[email protected]>.
|
---|
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 'webrick/httpservlet/abstract'
|
---|
10 | require 'webrick/httpstatus'
|
---|
11 | require 'soap/rpc/router'
|
---|
12 | require 'soap/streamHandler'
|
---|
13 | begin
|
---|
14 | require 'stringio'
|
---|
15 | require 'zlib'
|
---|
16 | rescue LoadError
|
---|
17 | warn("Loading stringio or zlib failed. No gzipped response supported.") if $DEBUG
|
---|
18 | end
|
---|
19 |
|
---|
20 |
|
---|
21 | warn("Overriding WEBrick::Log#debug") if $DEBUG
|
---|
22 | require 'webrick/log'
|
---|
23 | module WEBrick
|
---|
24 | class Log < BasicLog
|
---|
25 | alias __debug debug
|
---|
26 | def debug(msg = nil)
|
---|
27 | if block_given? and msg.nil?
|
---|
28 | __debug(yield)
|
---|
29 | else
|
---|
30 | __debug(msg)
|
---|
31 | end
|
---|
32 | end
|
---|
33 | end
|
---|
34 | end
|
---|
35 |
|
---|
36 |
|
---|
37 | module SOAP
|
---|
38 | module RPC
|
---|
39 |
|
---|
40 |
|
---|
41 | class SOAPlet < WEBrick::HTTPServlet::AbstractServlet
|
---|
42 | public
|
---|
43 | attr_reader :options
|
---|
44 |
|
---|
45 | def initialize(router = nil)
|
---|
46 | @router = router || ::SOAP::RPC::Router.new(self.class.name)
|
---|
47 | @options = {}
|
---|
48 | @config = {}
|
---|
49 | end
|
---|
50 |
|
---|
51 | # for backward compatibility
|
---|
52 | def app_scope_router
|
---|
53 | @router
|
---|
54 | end
|
---|
55 |
|
---|
56 | # for backward compatibility
|
---|
57 | def add_servant(obj, namespace)
|
---|
58 | @router.add_rpc_servant(obj, namespace)
|
---|
59 | end
|
---|
60 |
|
---|
61 | def allow_content_encoding_gzip=(allow)
|
---|
62 | @options[:allow_content_encoding_gzip] = allow
|
---|
63 | end
|
---|
64 |
|
---|
65 | ###
|
---|
66 | ## Servlet interfaces for WEBrick.
|
---|
67 | #
|
---|
68 | def get_instance(config, *options)
|
---|
69 | @config = config
|
---|
70 | self
|
---|
71 | end
|
---|
72 |
|
---|
73 | def require_path_info?
|
---|
74 | false
|
---|
75 | end
|
---|
76 |
|
---|
77 | def do_GET(req, res)
|
---|
78 | res.header['Allow'] = 'POST'
|
---|
79 | raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed"
|
---|
80 | end
|
---|
81 |
|
---|
82 | def do_POST(req, res)
|
---|
83 | logger.debug { "SOAP request: " + req.body } if logger
|
---|
84 | begin
|
---|
85 | conn_data = ::SOAP::StreamHandler::ConnectionData.new
|
---|
86 | setup_req(conn_data, req)
|
---|
87 | @router.external_ces = @options[:external_ces]
|
---|
88 | conn_data = @router.route(conn_data)
|
---|
89 | setup_res(conn_data, req, res)
|
---|
90 | rescue Exception => e
|
---|
91 | conn_data = @router.create_fault_response(e)
|
---|
92 | res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
|
---|
93 | res.body = conn_data.send_string
|
---|
94 | res['content-type'] = conn_data.send_contenttype || "text/xml"
|
---|
95 | end
|
---|
96 | if res.body.is_a?(IO)
|
---|
97 | res.chunked = true
|
---|
98 | logger.debug { "SOAP response: (chunked response not logged)" } if logger
|
---|
99 | else
|
---|
100 | logger.debug { "SOAP response: " + res.body } if logger
|
---|
101 | end
|
---|
102 | end
|
---|
103 |
|
---|
104 | private
|
---|
105 |
|
---|
106 | def logger
|
---|
107 | @config[:Logger]
|
---|
108 | end
|
---|
109 |
|
---|
110 | def setup_req(conn_data, req)
|
---|
111 | conn_data.receive_string = req.body
|
---|
112 | conn_data.receive_contenttype = req['content-type']
|
---|
113 | conn_data.soapaction = parse_soapaction(req.meta_vars['HTTP_SOAPACTION'])
|
---|
114 | end
|
---|
115 |
|
---|
116 | def setup_res(conn_data, req, res)
|
---|
117 | res['content-type'] = conn_data.send_contenttype
|
---|
118 | if conn_data.is_fault
|
---|
119 | res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
|
---|
120 | end
|
---|
121 | if outstring = encode_gzip(req, conn_data.send_string)
|
---|
122 | res['content-encoding'] = 'gzip'
|
---|
123 | res['content-length'] = outstring.size
|
---|
124 | res.body = outstring
|
---|
125 | else
|
---|
126 | res.body = conn_data.send_string
|
---|
127 | end
|
---|
128 | end
|
---|
129 |
|
---|
130 | def parse_soapaction(soapaction)
|
---|
131 | if !soapaction.nil? and !soapaction.empty?
|
---|
132 | if /^"(.+)"$/ =~ soapaction
|
---|
133 | return $1
|
---|
134 | end
|
---|
135 | end
|
---|
136 | nil
|
---|
137 | end
|
---|
138 |
|
---|
139 | def encode_gzip(req, outstring)
|
---|
140 | unless encode_gzip?(req)
|
---|
141 | return nil
|
---|
142 | end
|
---|
143 | begin
|
---|
144 | ostream = StringIO.new
|
---|
145 | gz = Zlib::GzipWriter.new(ostream)
|
---|
146 | gz.write(outstring)
|
---|
147 | ostream.string
|
---|
148 | ensure
|
---|
149 | gz.close
|
---|
150 | end
|
---|
151 | end
|
---|
152 |
|
---|
153 | def encode_gzip?(req)
|
---|
154 | @options[:allow_content_encoding_gzip] and defined?(::Zlib) and
|
---|
155 | req['accept-encoding'] and
|
---|
156 | req['accept-encoding'].split(/,\s*/).include?('gzip')
|
---|
157 | end
|
---|
158 | end
|
---|
159 |
|
---|
160 |
|
---|
161 | end
|
---|
162 | end
|
---|