Nut
Miniature Reactive HTTP Server
Presentation
This library is an implementation of an HTTP Server using the Reactor Pattern as provided by RxIO. This allows easy development of fast, non-blocking web services.
Installation
Gemfile
gem 'nut'
Terminal
gem install -V nut
Usage
Introduction
Nut is a miniature pure-Ruby implementation of a reactive HTTP Server. Relying on RxIO, Nut is simple to use, fast and lightweight.
Request Handler
Nut uses a request_handler module to service client requests. This module is to be implemented by the user. The request_handler module requires only one method: handle, which takes two Hashes as arguments: request and response.
# Request Handler Module
module ExampleRequestHandler
def handle request, response
# All processing done here...
end
end
# Create Nut Server around Request Handler
n = Nut::Service.new ExampleRequestHandler
The Request Hash
The request argument to the handle method is a Hash containing information representing a client request.
The following fields are available:
- :peer - RxIO Peer Information Hash such as { name: 'client1.example.com', addr: '51.254.97.136', port: 42432 }
- :verb - HTTP Verb such as :get / :post
- :uri - Request URI (without host and params)
- :body - Complete Request Body (without headers)
- :ctype - Content Type
- :params - Request Params Hash such as { foo: 'bar', test_file: { filename: 'raccoon.txt', data: '...' } }
- :headers - Request Headers Hash
The Response Hash
The response argument to the handle method is a Hash that should be filled by the user to represent a response. The following Hash keys are valid:
- :code - HTTP Response Code (Numeric format) (200, 404, etc...) - If unspecified, defaults to 200 (unless a redirect is requested)
- :body - Response Body
- :type - Response Content-Type - If unspecified, will be automatically inferred from :body through Typor
- :redirect - String (target of redirect) / Hash { to: 'http://url', permanent: true } (for a 301 Moved instead of 302 Found)
Rendering
Rendering any output is achieved by appending data to response[:body], as shown in the following example:
# Request Handler Module
module ExampleRequestHandler
def handle request, response
# Spit out some HTML
response[:body] << "<html><body>Hello world!</body></html>"
end
end
# Create Nut Server around Request Handler
n = Nut::Service.new ExampleRequestHandler
Content-Type
Nut will try to determine the appropriate content-type based on the response body. However, this can easily be bypassed by setting response[:type] to anything (other than nil). If response[:type] is a String, it will be used directly as the Content-Type.
# Request Handler Module
module ExampleRequestHandler
def handle request, response
# Respond with JSON
response[:body] = { success: true, foo: :bar }.to_json
response[:type] = 'application/json'
end
end
# Create Nut Server around Request Handler
n = Nut::Service.new ExampleRequestHandler
If response[:type] is a Symbol, it will be matched against a set of shortcuts to determine the Content-Type. The available shortcuts are:
- :json
- :text
- :html
# Request Handler Module
module ExampleRequestHandler
def handle request, response
# Respond with JSON
response[:body] = { success: true, foo: :bar }.to_json
response[:type] = :json
end
end
# Create Nut Server around Request Handler
n = Nut::Service.new ExampleRequestHandler
Redirecting
Redirecting is achieved by setting response[:redirect] to either a String or a Hash. If response[:redirect] is a String, Nut will respond with a 302 Found "temporary" redirect.
# Request Handler Module
module ExampleRequestHandler
def handle request, response
# Temporarily Redirect to 'http://www.example.com'
response[:redirect] = 'http://www.example.com'
end
end
# Create Nut Server around Request Handler
n = Nut::Service.new ExampleRequestHandler
If response[:redirect] is a Hash, Nut will respond with either a 302 Found or a 301 Moved "permanent" redirect depending on the :permanent field.
# Request Handler Module
module ExampleRequestHandler
def handle request, response
# Permanently Redirect to 'http://www.eresse.net'
response[:redirect] = { to: 'http://www.eresse.net', permanent: true }
end
end
# Create Nut Server around Request Handler
n = Nut::Service.new ExampleRequestHandler
Best Practice
While anything is possible, the recommended pattern for using Nut is to extend the Nut Service class, and embed the Request Handler Module inside the new Service Class.
class ExampleService < Nut::Service
def initialize
super RequestHandler
end
module RequestHandler
def handle request, response
response[:body] << "You requested [#{request[:uri]}] as [#{request[:verb]}]"
end
end
end
Service Interface
The usual run / stop RxIO synchronous service interface is available, as well as the Runify-ed version ( startup / shutdown).
Running the server
# Create Nut Server
n = Nut::Service.new 'localhost', 23280, ExampleHandler
# Start
n.run
# Blocks until stop is called on the server...
Running in the background
# Create Nut Server
n = Nut::Service.new 'localhost', 23280, ExampleHandler
# Start
n.startup
# Server is running in the background...
# Stop
n.shutdown
# Server has stopped
License
The gem is available as open source under the terms of the MIT License.