Bingo!

h3.layout: post
title: Bingo!
created_at: 2006-09-15 13:09:00 +02:00
filter:
– erb
– textile
-
-

A few days ago we launched Bingodisk, which is a 100GB WebDAV powered disk in the sky, with a public folder. Useful for storing just about anything and serving it up (or not) to the public.

Building Bingo! is a lot of fun, the bingodisk you’ll get is sitting on a ‘Thumper’ which is just a lovely piece of monster storage hardware and the actual frontend application is a Rails application that talks to the Thumpers via a distributed interface I wrote.

The really nice thing is that it uses a WebDAV interface which means that it’s possibly to mount it as a disk in almost every modern operating system (as usual, we had to jump through hoops to get it working properly in windows, but it does). WebDAV also supports things such as resource locking, and easily moving and/or copying resources.
Now, WebDAV is a set of HTTP extensions, which means that it’s easy to talk to from an application. Let’s see how we could do that from a Ruby script using Net::HTTP. Unfortunately Net::HTTP doesn’t support Digest authentication out of the box (Basic auth won’t work with Bingo), but we can fairly easily add that, based on a snippet I found by Eric Hodel.

First you’ll need to get this file. It adds a digest_auth method to Net::HTTP.

Finding properties

To get a list of the resources available we’ll use the PROPFIND HTTP method, which will return a (rather large) chunk of XML, containing locking info, resource name and meta such as size and mtime. Here’s a script that lists the files at a given path:


  # list.rb
  require 'net_digest_auth'
  require 'rexml/document'
h3.nclude REXML

  abort("Usage #{$0} <username> <password>") unless ARGV.size==2

  ALLPROPS = <<EOS
  <?xml version="1.0" encoding="utf-8" ?>
  <D:propfind xmlns:D="DAV:">
    <D:allprop/>
  </D:propfind>
  EOS

  url = URI.parse("http://johan.bingodisk.com/bingo/")
  Net::HTTP.start(url.host) do |http|
    res = http.head(url.request_uri)
    req = Net::HTTP::Propfind.new('/bingo/tmp/', {'Depth' => '1'})
    req.digest_auth(ARGV[0], ARGV[1], res)
    response = http.request(req, ALLPROPS)
  
    puts "#{response.code} #{response.message}\n"
    puts
  
    Document.new(response.body).elements.each("//D:response") do |r| 
      puts r.elements["D:href"].text
    end
  end

Apologies for the textile parsing error with the ARGV index

And the output:


  $ ruby list.rb johan@bingodisk.com secret
  207 Multi-Status

  /bingo/tmp/
  /bingo/tmp/mch.jpg
  /bingo/tmp/TextMateBook-beta.pdf

So what this does is that it first requests HEAD to get the things needed for the digest auth, then we create a new Net::HTTP::Propfind request instance and use the digest_auth_ method to set the user and password from the arguments given.
Then fire off the request with a snippet (the ALLPROPS constant) of XML telling the DAV server we want to get all the props.

We’ll get back the “207 Multi-Status” HTTP request code and the XML describing the properties of the resources, on which it does a XPath query using REXML to get the filenames (the D:href element).

PUTting resources on the disk

Now let’s upload something, as expected we’ll want to use the PUT method, here’s a script that takes the username, password and a path for a file to upload into /bingo/public/code/:


  # upload.rb
  require 'net_digest_auth'

  abort("Usage: #{$0} <username> <password> <path/to/file/to/upload>") unless ARGV.size==3

  if File.exists?(ARGV[2])
    url = URI.parse("http://johan.bingodisk.com/bingo/")
    Net::HTTP.start(url.host) do |http|
      res = http.head(url.request_uri)
      req = Net::HTTP::Put.new("/bingo/public/code/#{File.basename(ARGV[2])}")
      req.digest_auth(ARGV[0], ARGV[1], res)
      response = http.request(req, File.read(ARGV[2]))
      puts response.code + " " + response.message
    end
  else
    puts "No such file #{ARGV[2].inspect}"
  end

By running the script we’ll upload the net_digest_auth.rb file you:

</code>
  $ ruby upload.rb johan@bingodisk.com secret net_digest_auth.rb 
  201 Created

Nice and easy.

WebDAV might feel a bit more “bulky” than a straight up RESTful interface, but it’s really not that bad and the fact that it’s so well-supported in existing client programs is just friggin’ sweet.


Comments:

  1. Thijs van der Vossen Says:


    You could also use http://rubyforge.org/projects/httpauth/ for easy HTTP Digest Authentication.

About

This is the personal blog of Johan Sørensen, here you’ll various various musings about technology and other semi-related stuff. Read more…

Follow me on Twitter.

Atom feed