| 1 |
# SOAP4R - SOAP handler servlet for WEBrick |
|---|
| 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 '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 |
attr_accessor :authenticator |
|---|
| 45 |
|
|---|
| 46 |
def initialize(router = nil) |
|---|
| 47 |
@router = router || ::SOAP::RPC::Router.new(self.class.name) |
|---|
| 48 |
@options = {} |
|---|
| 49 |
@authenticator = nil |
|---|
| 50 |
@config = {} |
|---|
| 51 |
end |
|---|
| 52 |
|
|---|
| 53 |
# for backward compatibility |
|---|
| 54 |
def app_scope_router |
|---|
| 55 |
@router |
|---|
| 56 |
end |
|---|
| 57 |
|
|---|
| 58 |
# for backward compatibility |
|---|
| 59 |
def add_servant(obj, namespace) |
|---|
| 60 |
@router.add_rpc_servant(obj, namespace) |
|---|
| 61 |
end |
|---|
| 62 |
|
|---|
| 63 |
def allow_content_encoding_gzip=(allow) |
|---|
| 64 |
@options[:allow_content_encoding_gzip] = allow |
|---|
| 65 |
end |
|---|
| 66 |
|
|---|
| 67 |
### |
|---|
| 68 |
## Servlet interfaces for WEBrick. |
|---|
| 69 |
# |
|---|
| 70 |
def get_instance(config, *options) |
|---|
| 71 |
@config = config |
|---|
| 72 |
self |
|---|
| 73 |
end |
|---|
| 74 |
|
|---|
| 75 |
def require_path_info? |
|---|
| 76 |
false |
|---|
| 77 |
end |
|---|
| 78 |
|
|---|
| 79 |
def do_GET(req, res) |
|---|
| 80 |
res.header['Allow'] = 'POST' |
|---|
| 81 |
raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed" |
|---|
| 82 |
end |
|---|
| 83 |
|
|---|
| 84 |
def do_POST(req, res) |
|---|
| 85 |
logger.debug { "SOAP request: " + req.body } if logger |
|---|
| 86 |
if @authenticator |
|---|
| 87 |
@authenticator.authenticate(req, res) |
|---|
| 88 |
# you can check authenticated user with SOAP::RPC::SOAPlet.user |
|---|
| 89 |
end |
|---|
| 90 |
begin |
|---|
| 91 |
conn_data = ::SOAP::StreamHandler::ConnectionData.new |
|---|
| 92 |
setup_req(conn_data, req) |
|---|
| 93 |
@router.external_ces = @options[:external_ces] |
|---|
| 94 |
Mapping.protect_threadvars(:SOAPlet) do |
|---|
| 95 |
SOAPlet.user = req.user |
|---|
| 96 |
SOAPlet.cookies = req.cookies |
|---|
| 97 |
conn_data = @router.route(conn_data) |
|---|
| 98 |
setup_res(conn_data, req, res) |
|---|
| 99 |
end |
|---|
| 100 |
rescue Exception => e |
|---|
| 101 |
conn_data = @router.create_fault_response(e) |
|---|
| 102 |
res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR |
|---|
| 103 |
res.body = conn_data.send_string |
|---|
| 104 |
res['content-type'] = conn_data.send_contenttype || "text/xml" |
|---|
| 105 |
end |
|---|
| 106 |
if res.body.is_a?(IO) |
|---|
| 107 |
res.chunked = true |
|---|
| 108 |
logger.debug { "SOAP response: (chunked response not logged)" } if logger |
|---|
| 109 |
else |
|---|
| 110 |
logger.debug { "SOAP response: " + res.body } if logger |
|---|
| 111 |
end |
|---|
| 112 |
end |
|---|
| 113 |
|
|---|
| 114 |
def self.cookies |
|---|
| 115 |
get_variable(:Cookies) |
|---|
| 116 |
end |
|---|
| 117 |
|
|---|
| 118 |
def self.cookies=(cookies) |
|---|
| 119 |
set_variable(:Cookies, cookies) |
|---|
| 120 |
end |
|---|
| 121 |
|
|---|
| 122 |
def self.user |
|---|
| 123 |
get_variable(:User) |
|---|
| 124 |
end |
|---|
| 125 |
|
|---|
| 126 |
def self.user=(user) |
|---|
| 127 |
set_variable(:User, user) |
|---|
| 128 |
end |
|---|
| 129 |
|
|---|
| 130 |
private |
|---|
| 131 |
|
|---|
| 132 |
def self.get_variable(name) |
|---|
| 133 |
if var = Thread.current[:SOAPlet] |
|---|
| 134 |
var[name] |
|---|
| 135 |
else |
|---|
| 136 |
nil |
|---|
| 137 |
end |
|---|
| 138 |
end |
|---|
| 139 |
|
|---|
| 140 |
def self.set_variable(name, value) |
|---|
| 141 |
var = Thread.current[:SOAPlet] ||= {} |
|---|
| 142 |
var[name] = value |
|---|
| 143 |
end |
|---|
| 144 |
|
|---|
| 145 |
def logger |
|---|
| 146 |
@config[:Logger] |
|---|
| 147 |
end |
|---|
| 148 |
|
|---|
| 149 |
def setup_req(conn_data, req) |
|---|
| 150 |
conn_data.receive_string = req.body |
|---|
| 151 |
conn_data.receive_contenttype = req['content-type'] |
|---|
| 152 |
conn_data.soapaction = parse_soapaction(req.meta_vars['HTTP_SOAPACTION']) |
|---|
| 153 |
end |
|---|
| 154 |
|
|---|
| 155 |
def setup_res(conn_data, req, res) |
|---|
| 156 |
res['content-type'] = conn_data.send_contenttype |
|---|
| 157 |
cookies = SOAPlet.cookies |
|---|
| 158 |
unless cookies.empty? |
|---|
| 159 |
res['set-cookie'] = cookies.collect { |cookie| cookie.to_s } |
|---|
| 160 |
end |
|---|
| 161 |
if conn_data.is_nocontent |
|---|
| 162 |
res.status = WEBrick::HTTPStatus::RC_ACCEPTED |
|---|
| 163 |
res.body = '' |
|---|
| 164 |
return |
|---|
| 165 |
end |
|---|
| 166 |
if conn_data.is_fault |
|---|
| 167 |
res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR |
|---|
| 168 |
end |
|---|
| 169 |
if outstring = encode_gzip(req, conn_data.send_string) |
|---|
| 170 |
res['content-encoding'] = 'gzip' |
|---|
| 171 |
res['content-length'] = outstring.size |
|---|
| 172 |
res.body = outstring |
|---|
| 173 |
else |
|---|
| 174 |
res.body = conn_data.send_string |
|---|
| 175 |
end |
|---|
| 176 |
end |
|---|
| 177 |
|
|---|
| 178 |
def parse_soapaction(soapaction) |
|---|
| 179 |
if !soapaction.nil? and !soapaction.empty? |
|---|
| 180 |
if /^"(.+)"$/ =~ soapaction |
|---|
| 181 |
return $1 |
|---|
| 182 |
end |
|---|
| 183 |
end |
|---|
| 184 |
nil |
|---|
| 185 |
end |
|---|
| 186 |
|
|---|
| 187 |
def encode_gzip(req, outstring) |
|---|
| 188 |
unless encode_gzip?(req) |
|---|
| 189 |
return nil |
|---|
| 190 |
end |
|---|
| 191 |
begin |
|---|
| 192 |
ostream = StringIO.new |
|---|
| 193 |
gz = Zlib::GzipWriter.new(ostream) |
|---|
| 194 |
gz.write(outstring) |
|---|
| 195 |
ostream.string |
|---|
| 196 |
ensure |
|---|
| 197 |
gz.close |
|---|
| 198 |
end |
|---|
| 199 |
end |
|---|
| 200 |
|
|---|
| 201 |
def encode_gzip?(req) |
|---|
| 202 |
@options[:allow_content_encoding_gzip] and defined?(::Zlib) and |
|---|
| 203 |
req['accept-encoding'] and |
|---|
| 204 |
req['accept-encoding'].split(/,\s*/).include?('gzip') |
|---|
| 205 |
end |
|---|
| 206 |
end |
|---|
| 207 |
|
|---|
| 208 |
|
|---|
| 209 |
end |
|---|
| 210 |
end |
|---|