Ruby 1.9.2-p290 on Solaris 11 Express

By Morten Møller Riis

January 20 2012 12:05 CET

Compiling Ruby on Solaris 11 Express is easy but there are a few things you should know.

First of, Ruby 1.9.3-p0 won’t build on Solaris 11 Express. This will probably be fixed in the next preview.

Instead you’ll have to make do with 1.9.2-p290 for now.

You’ll need the gcc compiler and math headers and of course the source:

               pkg install gcc-3 header-math
               cd
               wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p290.tar.gz
               tar xvfz ruby-1.9.2-p290.tar.gz
               cd ruby-1.9.2-p290.tar.gz
               ./configure
               make
               make install   
            	

It’ll install in /usr/local/bin which by default isn’t included in path.

Here’s a one-liner for that.

               echo "export PATH=$PATH:/usr/local/bin" >> ~/.profile
            	

Then either login/logout or source .profile.

To update to newest rubygems just do a gem update --system.

RSpec and ZeroMQ

By Morten Møller Riis

December 19 2011 14:30 CET

When you have something like this using ZeroMQ:

client.rb

               class Client
                 def self.listen
                   socket = ZMQ::Context.new.socket(ZMQ::UPSTREAM)
                   socket.bind("tcp://127.0.0.1:1234")
            
                   while(true)
                     msg = socket.recv()
                     if msg == "quit"
                       socket.close
                       return
                     end
                     ...
                   end
             

And you would like to write a spec for it you will run into the problem that #recv() is a blocking method.

How best to go about this? I would prefer some kind of spawning a seperate process but that get’s kinda hairy.

Instead I have opted to simply mock it and write the following two methods:

spec_helper.rb

               def queue_zmq_message(msg)
                 @zmq_queue ||= []
                 @zmq_queue << msg
               end
            
               def mock_zmq
                 @zmq_queue ||= []
                 zmq_socket = mock('zmq_socket')
                 zmq_socket.stub(:bind)
                 zmq_socket.stub(:recv).and_return do
                   queue_zmq_message("quit") if @zmq_queue.empty?
                   @zmq_queue.shift
                 end
            
                 zmq_context = mock('zmq_context')
                 zmq_context.stub(:socket).and_return(zmq_socket)
                 ZMQ::Context.stub(:new).and_return(zmq_context)
                 zmq_socket.stub(:close)
               end
             

This allows me to do something like this in my specs:

client_spec.rb

               before do
                mock_zmq
               end
               
               it "should do something when a message is received" do      
                 queue_zmq_message "some message"
                 # Expections here
                 Client.listen
                 # ...or expections here
               end
             

This is a bit simplified from the project I am using it in now. But you should get the point. The program accepts some sort of quit signal as well to break us out of the main loop.

Postfix non-interactive Install

By Morten Møller Riis

November 15 2011 17:30 CET

Debian and Ubuntu should be able to handle dpkg installs non-interactively by specifying a variable for DEBIAN_FRONTEND=noninteractive.

However, postfix seems not to respect this and starts whiptail to ask you whether the machine is “an internet site” etc.

The trick is to prepopulate /etc/mailname and /etc/postfix/main.cf

After this you should be able to do this:

            	DEBIAN_FRONTEND='noninteractive' apt-get install -y -q --force-yes postfix
             

Live long and prosper :)

Nginx as an IMAP/POP3 proxy Part 2

By Morten Møller Riis

November 11 2011 11:11 CET

A month or so ago I started discovering some problems with my previous implementation.

It seems that either my Perl script or the nginx embedded Perl module suffers from memory leaks.

Now, the easy way to fix this would be to run a /etc/init.d/nginx restart every so often. However, that would of course suck.

So I started looking into alternative ways, using FastCGI to serve the authentication script.

The normal fcgiwrapper in Debian was way to slow though. Handling only about 30 requests/sec.

Enter FCGI-Daemon by Dmitry Smirnov. It works by keeping the processes alive not respawning Perl on every request.

With this I was able to achieve 2500-3000 request/sec. More than enough to handle IMAP/POP3 authentications.

I’ve included an updated authentication script for use with this.

auth.pl

               #!/usr/bin/perl
            
               use Digest::HMAC_MD5 qw/ hmac_md5_hex /;
               use DBI;
               use URI::Escape;
               use CGI;
            
               print "Content-type: text/html\n";
            
               my $q = CGI->new;
               my $auth_shared_secret = $q->http("X-NGX-Auth-Key");
            
               # Shared secret to ensure that the request comes from nginx
               if ( $auth_shared_secret ne "your secret" ) {
                 print "Auth-Status: Authentication failed.\n\n";
                 print STDERR "Wrong X-NGC-Auth-Key $auth_shared_secret";
                 exit(0);
               }
            
               my $dsn = "DBI:mysql:database=postfix;host=1.2.3.4";
               our $dbh =
                 DBI->connect_cached( $dsn, 'mailproxy', 'p@ssw0rd',
                   { AutoCommit => 1, mysql_auto_reconnect => 1 } );
            
               our $auth_ok;
               our $protocol_ports = {};
               $protocol_ports->{'pop3'} = 110;
               $protocol_ports->{'imap'} = 143;
               $protocol_ports->{'smtp'} = 25;
            
               if ( !defined $dbh || !$dbh->ping() ) {
                   ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
                     localtime(time);
                   $dbh =
                     DBI->connect_cached( $dsn, 'mailproxy', 'p@ssw0rd',
                       { AutoCommit => 1, mysql_auto_reconnect => 1 } );
                   printf STDERR
               "%4d/%02d/%02d %02d:%02d:%02d [notice] : MySQL server connection lost. Reconnecting.\n",
                     $year + 1900, $mon + 1, $mday, $hour, $min, $sec;
               }
            
               my $auth_method = $q->http("Auth-Method");
               my $username    = uri_unescape( $q->http("Auth-User") );
               my $password    = uri_unescape( $q->http("Auth-Pass") );
               my $salt        = $q->http("Auth-Salt");
            
               our $sth = $dbh->prepare("select clear from users where email=? limit 1");
               $sth->execute($username);
               my $hash          = $sth->fetchrow_hashref();
               my $real_password = $hash->{'clear'};
            
               # Authorize user
               if (
                   ( $auth_method eq "plain" && $password eq $real_password )
                   or (   $auth_method eq "cram-md5"
                       && $password eq hmac_md5_hex( $salt, $real_password ) )
                 )
               {
            
                   # Auth OK, find mail server
                   our $sth = $dbh->prepare(
                       "select destination_mailstore from transport where domain=? limit 1");
                   my $domain = $q->http("Auth-User");
            
                   # remove @ and everything before
                   $domain =~ s/^.*@//;
                   $sth->execute($domain);
                   my $hash       = $sth->fetchrow_hashref();
                   my $mailserver = $hash->{'destination_mailstore'};
                   $mailserver =~ s/smtp://;
            
                   print "Auth-User: $username\n";
                   print "Auth-Pass: $real_password\n";
                   print "Auth-Status: OK\n";
                   print "Auth-Server: $mailserver\n";
                   $auth_port = $protocol_ports->{ $q->http("Auth-Protocol") };
                   print "Auth-Port: $auth_port\n";
               }
               else {
                   print "Auth-Status: Authentication failed.\n";
               }
            
               print "\n";
               

I’ve also made a GIST of this here.

Generating PDFs in Background

By Morten Møller Riis

November 10 2011 15:00 CET

Would you like to be able to generate PDFs in your Rails app using delayed_job or similar? Then read on :)

I have used PDFKit for some time. But locking your Rails process whilest waiting for wkhtmltopdf to finish might not always be desireable.

In Rails 3.x it is very easy and clean to render controller actions outside of the controller (props to AmberBit for a nice tutorial). So you can do something like this:

app/controllers/invoices_controller.rb

               # This is not a real controller for webpages.
               # It is used to render HTML output for PDFKit.
               # Use it like this:
               # InvoicesController.new.show(<invoice id>)
            
               class InvoicesController < AbstractController::Base
                 include AbstractController::Rendering
                 include AbstractController::Layouts
                 include AbstractController::Helpers
                 include AbstractController::Translation
                 include AbstractController::AssetPaths  
                 helper InvoicesHelper
                 self.view_paths = "app/views"
            
                 def show(id)
                   @invoice = Invoice.find(id)
                   render layout: "basic", template: "invoices/show"
                 end 
            
               end

I’ve included its respective helper. This is because I need a method that gives the full path for an image in my template. It is a known problem with PDFKit.

app/helpers/invoices_helper.rb

               module InvoicesHelper
            
                 def pdf_image_tag(filename, options = {})
                   path = Rails.root.join("app/assets/images/#{filename}")
                   options[:src] = path.to_s
                   attributes = options.map{ |k,v| "#{k}='#{v}'" }.join(" ")
                   raw("<img #{attributes} />")
                 end
            
               end

When this is setup you can easily create a delayed_job that can render the action like this:

app/jobs/render_pdf_invoice.rb

               require 'pdfkit'
            
               class RenderPDFInvoice < Struct.new(:id)
                 def perform
                   begin
                     html = InvoicesController.new.show(id)
                     kit = PDFKit.new(html, page_size: "A4")
                     kit.to_file("#{Rails.root}/invoices/#{id}.pdf")
                   rescue
                     puts "could not find that"
                   end
                 end
               end

Now, to render PDFs in the background I just:

               Delayed::Job.enqueue RenderPDFInvoice.new(1337)

And when that’s done a nice PDF will be at invoices/1337.pdf :)