Libuv FFI bindings for Ruby

Build Status

Libuv is a cross platform asynchronous IO implementation that powers NodeJS. It supports sockets, both UDP and TCP, filesystem watch, TTY, Pipes and other asynchronous primitives like timer, check, prepare and idle.

The Libuv gem contains Libuv and a Ruby wrapper that implements pipelined promises for asynchronous flow control and coroutines / futures for untangling evented code

Usage

Libuv supports multiple reactors that can run on different threads.

For convenience the thread local or default reactor can be accessed via the reactor method You can pass a block to be executed on the reactor and the reactor will run until there is nothing left to do.

  require 'libuv'

  reactor do |reactor|
    reactor.timer {
      puts "5 seconds passed"
    }.start(5000)
  end

  puts "reactor stopped. No more IO to process"

Promises are used to simplify code flow.

  require 'libuv'

  reactor do |reactor|
    reactor.tcp { |data, socket|
      puts "received: #{data}"
      socket.close
    }
    .connect('127.0.0.1', 3000) { |socket|
      socket.start_read
            .write("GET / HTTP/1.1\r\n\r\n")
    }
    .catch { |error|
      puts "error: #{error}"
    }
    .finally {
      puts "socket closed"
    }
  end

Continuations are used if callbacks are not defined

  require 'libuv'

  reactor do |reactor|
    begin
      reactor.tcp { |data, socket|
        puts "received: #{data}"
        socket.close
      }
      .connect('127.0.0.1', 3000)
      .start_read
      .write("GET / HTTP/1.1\r\n\r\n")
    rescue => error
      puts "error: #{error}"
    end
  end

Any promise can be converted into a continuation

  require 'libuv'

  reactor do |reactor|
    # Perform work on the thread pool with promises
    reactor.work {
      10 * 2
    }.then { |result|
      puts "result using a promise #{result}"
    }

    # Use the coroutine helper to obtain the result without a callback
    result = reactor.work {
      10 * 3
    }.value
    puts "no additional callbacks here #{result}"
  end

Check out the yard documentation

Installation

  gem install libuv

or

  git clone https://github.com/cotag/libuv.git
  cd libuv
  bundle install
  rake compile

Prerequisites

  • The installation also requires python 2.x to be installed and available on the PATH
  • setting the environmental variable USE_GLOBAL_LIBUV will prevent compiling the packaged version.
    • if you have a compatible libuv.(so | dylib | dll) on the PATH already

Windows users will additionally require:

  • A copy of Visual Studio 2010 or later. Visual Studio Express works fine.
  • A copy of OpenSSL matching the installed ruby (x86 / x64)
  • If using jRuby then GCC is also required
    • Setup the paths as described on the gcc page
    • Add required environmental variable set LIBRARY_PATH=X:\win-builds-64\lib;X:\win-builds-64\x86_64-w64-mingw32\lib

Features

  • TCP (with TLS support)
  • UDP
  • TTY
  • Pipes
  • Timer
  • Prepare
  • Check
  • Idle
  • Signals
  • Async callbacks
  • Async DNS Resolution
  • Filesystem Events
  • Filesystem manipulation
  • File manipulation
  • Errors (with a catch-all fallback for anything unhandled on the event reactor)
  • Work queue (thread pool)
  • Coroutines / futures (makes use of Fibers)

Server Name Indication

You can host a TLS enabled server with multiple hostnames using SNI.

server = reactor.tcp
server.bind('0.0.0.0', 3000, **{
    hosts: [{
        private_key: '/blah.key',
        cert_chain: '/blah.crt',
        host_name: 'somehost.com',
    },
    {
        private_key: '/blah2.key',
        cert_chain: '/blah2.crt',
        host_name: 'somehost2.com'
    },
    {
        private_key: '/blah3.key',
        cert_chain: '/blah3.crt',
        host_name: 'somehost3.com'
    }]
}) do |client|
    client.start_tls
    client.start_read
end

# at some point later
server.add_host(private_key: '/blah4.key', cert_chain: '/blah4.crt', host_name: 'somehost4.com')
server.remove_host('somehost2.com')

You don't have to specify any hosts at binding time.

Protocols and 3rd party plugins