ruby openssl and client side certificates
July 11th, 2009
I recently needed to deal with ssl connection using client side certificates. The ruby openssl bindings are fairly impenetrable, here's what worked for me (at least in part as a note for myself in the future)
ctx = OpenSSL::SSL::SSLContext.new ctx.cert = OpenSSL::X509::Certificate.new("mycert.cer") ctx.key = OpenSSL::PKey::RSA.new("mykey.pem") ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.connect
If the key you've got is a .p12 file (which is what the key chain utility on the mac exports) then you'll need to convert it like so
openssl pkcs12 -in key.p12 -nocerts -nodes -out key.pem
Fun with ruby http clients
April 13th, 2009
Quite a few people have written about the performance failings of Net::HTTP, but until recently, to be honest, I never really cared a lot. Most of my http request needs have been fairly meagre, often not much more than hitting a url and checking the result code.
I've been playing with couchdb recently, and so my app does a fair amount of http requests. I've been using RelaxDB which uses net/http, so Net::HTTP's performance has started to matter.
Net::HTTP is not the only game in town. I spent some time recently playing with rfuzz, eventmachine and taf2-curb and came to largely the same conclusion as Paul Dix.
Leaning on a mature library such as libcurl gives taf2-curb a huge advantage. While eventmachine was on par speed wise, neither of the 2 http clients it includes are a complete implementation of the HTTP protocol. For example HttpClient will tell the remote server that it speaks HTTP/1.1, yet it does not support chunked encoding (mandatory part of the spec). HttpClient2 does understand chunked encoding, but doesn't let you set headers or a body to the request. Fine for just pinging a url, but not up to the task of working with couchdb. Something to do with couchdb's chunk encoded also seemed to confuse rfuzz.
taf2-curb does the job very nicely. On my dumb benchmark, 1000 requests for a static html page hosted on the same machine (ie we're pretty much only testing overhead) the numbers are:
Benchmark.bmbm(5) do |x| x.report 'net/http' do u = URI.parse('http://docs.local/') 1000.times {Net::HTTP.get u} end x.report 'curb' do 1000.times do c = Curl::Easy.new 'http://docs.local/' c.perform end end end
user system total real
net/http 0.560000 0.270000 0.830000 ( 1.065960)
curb 0.310000 0.170000 0.480000 ( 0.696188)
On the other extreme, these numbers corresponds to ~1 meg of data pulled from couchdb (benchmark code the same apart from the urls, and I did 100 iterations rather than 1000).
user system total real
net/http 17.400000 8.900000 2.630000 ( 32.067821)
curb 0.700000 1.300000 2.000000 ( 29.586022)
curb comes up squarely on top. Another thing of note during this test is cpu usage (as you might expect from the difference in user time). With Net::HTTP the ruby process running this was taking up 60-70% (on a 2.4GHz core duo), with curb it used around 5% of cpu.
The commit to switch RelaxDB from net/http to taf2-curb is here for those interested - really very straightforward stuff. There may well be more to be had by fiddling with libcurl options, I haven't tried yet.
Confused by sync.rb ?
May 18th, 2008
I needed a reader/writer lock the other day and followed the trail to ruby's Sync class (why this gets to call itself Sync/Synchronizer as opposed to all the other synchronisation primitives is beyond me). The documentation isn't exactly enlightening either. I'm sure there's all sorts of clever stuff to do with upgrading a lock you already hold and other stuff like that, but if you're just interested in the boring case all you need is
lock = Sync.new lock.synchronize(Sync::EX) do #do something that requires a writer (exclusive) lock end lock.synchronize(Sync::SH) do #do something that requires a reader (shared) lock end
