Skip to content

Instantly share code, notes, and snippets.

@mwisnicki
Last active August 29, 2015 14:17
Show Gist options
  • Save mwisnicki/f91f9e50813ee53b3bfa to your computer and use it in GitHub Desktop.
Save mwisnicki/f91f9e50813ee53b3bfa to your computer and use it in GitHub Desktop.
Ruby XML-RPC over Unix Domain Socket (SCGI)
# vim: ai ts=2
require 'xmlrpc/client'
module XMLRPC
class LocalClient < Client
def initialize(path, timeout)
super("unix:", path, 0, nil, nil, nil, nil, false, timeout)
end
private
def net_http(host, port, proxy_host, proxy_port)
LocalHTTP.new @path, port, proxy_host, proxy_port
end
class LocalHTTP < Net::HTTP
def connect
D "opening unix socket #{@address}"
s = timeout(@open_timeout) { UNIXSocket.open(@address) }
D "opened"
@socket = ::Net::BufferedIO.new(s)
@socket.read_timeout = @read_timeout
@socket.continue_timeout = @continue_timeout
@socket.debug_output = @debug_output
@socket.instance_variable_set('@scgi_http_hack', true)
on_connect
end
def request_post(path, data, initheader = nil, &block)
request SCGIPost.new(path, initheader), data, &block
end
private
module SCGISupport
def encode_netstring(s)
sprintf('%d:%s', s.length, s)
end
def make_headers(headers)
headers.flatten.join("\0") + "\0"
end
def scgi_headers(data, headers=[])
headers=make_headers([
['CONTENT_LENGTH', data.length.to_s],
['SCGI', '1']
] + headers)
encode_netstring(headers)
end
end
class SCGIPost < Net::HTTP::Post
private
include SCGISupport
def write_header(sock, ver, path)
end
def send_request_with_body(sock, ver, path, body)
self.content_length = body.bytesize
delete 'Transfer-Encoding'
supply_default_content_type
write_header sock, ver, path
wait_for_continue sock, ver if sock.continue_timeout
sock.write scgi_headers(body)+","+body
end
end
end
end
class ::Net::HTTPResponse
class << self
alias_method :read_new_noscgi, :read_new
def read_new(sock)
if is_scgi(sock) then
read_new_scgi(sock)
else
read_new_noscgi(sock)
end
end
private
def read_new_scgi(sock)
httpv = "1.0"
code = nil
msg = nil
headers = []
each_response_header(sock) do |k,v|
if k == 'Status' then
code, msg = read_status_header(v)
else
headers << [k,v]
end
end
res = response_class(code).new(httpv, code, msg)
headers.each do |k,v|
res.add_field k, v
end
res
end
def read_status_header(str)
m = /\A(\d\d\d)\s*(.*)\z/in.match(str) or
raise Net::HTTPBadResponse, "wrong status header: #{str.dump}"
m.captures
end
def is_scgi(sock)
sock.instance_variable_get('@scgi_http_hack')
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment