2
# File: 06-11-14-mongrel_xmlrpc.rb
3
# Author: Manuel Holtgrewe <purestorm at ggnore.net>
5
# Copyright (c) 2006 Manuel Holtgrewe, 2007 Luke Kanies
7
# This file is based heavily on a file retrieved from
8
# http://ttt.ggnore.net/2006/11/15/xmlrpc-with-mongrel-and-ruby-off-rails/
12
require 'xmlrpc/server'
13
require 'puppet/network/xmlrpc/server'
14
require 'puppet/network/http_server'
15
require 'puppet/network/client_request'
16
require 'puppet/network/handler'
20
# This handler can be hooked into Mongrel to accept HTTP requests. After
21
# checking whether the request itself is sane, the handler forwards it
22
# to an internal instance of XMLRPC::BasicServer to process it.
24
# You can access the server by calling the Handler's "xmlrpc_server"
25
# attribute accessor method and add XMLRPC handlers there. For example:
28
# handler = XmlRpcHandler.new
29
# handler.xmlrpc_server.add_handler("my.add") { |a, b| a.to_i + b.to_i }
31
module Puppet::Network
32
class HTTPServer::Mongrel < ::Mongrel::HttpHandler
33
attr_reader :xmlrpc_server
35
def initialize(handlers)
37
$mongrel_debug_client = true
38
Puppet.debug 'Mongrel client debugging enabled. [$mongrel_debug_client = true].'
40
# Create a new instance of BasicServer. We are supposed to subclass it
41
# but that does not make sense since we would not introduce any new
42
# behaviour and we have to subclass Mongrel::HttpHandler so our handler
44
@xmlrpc_server = Puppet::Network::XMLRPCServer.new
45
handlers.each do |name|
46
unless handler = Puppet::Network::Handler.handler(name)
47
raise ArgumentError, "Invalid handler #{name}"
49
@xmlrpc_server.add_handler(handler.interface, handler.new({}))
53
# This method produces the same results as XMLRPC::CGIServer.serve
54
# from Ruby's stdlib XMLRPC implementation.
55
def process(request, response)
56
# Make sure this has been a POST as required for XMLRPC.
57
request_method = request.params[Mongrel::Const::REQUEST_METHOD] || Mongrel::Const::GET
58
if request_method != "POST"
59
response.start(405) { |head, out| out.write("Method Not Allowed") }
63
# Make sure the user has sent text/xml data.
64
request_mime = request.params["CONTENT_TYPE"] || "text/plain"
65
if parse_content_type(request_mime).first != "text/xml"
66
response.start(400) { |head, out| out.write("Bad Request") }
70
# Make sure there is data in the body at all.
71
length = request.params[Mongrel::Const::CONTENT_LENGTH].to_i
73
response.start(411) { |head, out| out.write("Length Required") }
77
# Check the body to be valid.
78
if request.body.nil? or request.body.size != length
79
response.start(400) { |head, out| out.write("Bad Request") }
83
info = client_info(request)
85
# All checks above passed through
86
response.start(200) do |head, out|
87
head["Content-Type"] = "text/xml; charset=utf-8"
89
out.write(@xmlrpc_server.process(request.body, info))
99
def client_info(request)
100
params = request.params
101
ip = params["HTTP_X_FORWARDED_FOR"] ? params["HTTP_X_FORWARDED_FOR"].split(',').last.strip : params["REMOTE_ADDR"]
102
# JJM #906 The following dn.match regular expression is forgiving
103
# enough to match the two Distinguished Name string contents
104
# coming from Apache, Pound or other reverse SSL proxies.
105
if dn = params[Puppet[:ssl_client_header]] and dn_matchdata = dn.match(/^.*?CN\s*=\s*(.*)/)
106
client = dn_matchdata[1].to_str
107
valid = (params[Puppet[:ssl_client_verify_header]] == 'SUCCESS')
110
client = Resolv.getname(ip)
112
Puppet.err "Could not resolve #{ip}: #{detail}"
118
info = Puppet::Network::ClientRequest.new(client, ip, valid)
123
# Taken from XMLRPC::ParseContentType
124
def parse_content_type(str)
125
a, *b = str.split(";")