Service Drivers
Service drivers interface with devices that communicate using the HTTP(S) protocol. Currently HTTP versions 1.0 and 1.1 are supported.
They are similar to Device Drivers with two major differences:
  1. 1.
    It’s not recommended to use the common received function
  2. 2.
    They do not have a send function
This is because HTTP is much more contextual than many protocols. It’ll often only return success whereas many custom device protocol responses can be interpreted without knowledge of the original request.

Sending a Request

Each request should either set the on_receive callback option or provide a block for response processing.
Method
Arguments
Description
request
verb, path, options = {}, &blk
allows you to pass in a custom verb
get
path, options = {}, &blk
post
path, options = {}, &blk
put
path, options = {}, &blk
delete
path, options = {}, &blk
1
# Example usage:
2
3
def query_position
4
# Get request will look like:
5
# http://domain.or.ip/api/status_of?coordinates=detailed
6
get('/api/status_of', {
7
query: {
8
coordinates: :detailed
9
}
10
}) do |data, resolve, command|
11
check_response(data) do |resp|
12
# Update status (made available to interfaces)
13
self[:position] = resp['coords']
14
end
15
end
16
end
17
18
def check_response(data)
19
# Check response status
20
# (might have been 500 or 404, depends on what you are expecting)
21
if data.status == 200
22
begin
23
# We're assuming a JSON response and we are passing that data
24
# back to the calling function and assuming success at this point
25
yield ::JSON.parse(data.body) if block_given?
26
return :success
27
rescue => e
28
logger.print_error e
29
end
30
end
31
32
# Fail if there are any issues
33
# Obviously this behaviour depends on the service etc
34
:abort
35
end
Copied!

Request Options

Option
Example
Effect
query
query: "me=bob&other=rain" or query: { me: :bob, other: :rain }
URI?me=bob&other=rain
body
body: "data=hello&other=world" or body: { data: :hello, other: :world }
when body is a string it will be sent as is. When a hash, it will be form encoded.
headers
headers: { Name: 'value'
}
Some headers are transformed further. See bellow
file
file: 'path/to/file.ext'
Will send the file as the body
keepalive
keepalive: false
Will close the connection once the request has completed
ntlm
ntlm: { user: 'u', password: 'p', domain: 'd' }
Will perform a request with an endpoint that requires NTLM auth
digest
digest: { user: 'u', password: 'p', domain: 'd' }
Will perform a request with an endpoint that requires digest auth
proxy
proxy: { host: '1.2.3.4', port: 80, username: 'bob', password: 'hunter2' }
Use a proxy server for the request.
NOTE:: Both NTLM and Digest auth are challenge response protocols and won’t work with HTTP 1.0 or keep alive false

Basic Authentication

Basic auth is supported natively along with NTLM and digest authentication techniques. All that is required is to set the authorization header like so:
1
options = {
2
headers: {
3
authorization: [username, password]
4
}
5
}
Copied!
For more advanced methods of authentication see Utilities and Helpers.

Handling a Response

The response object is passed to your received block and looks like this:
1
get '/' do |data|
2
# Response body as a string
3
data.body
4
5
# HTTP version the server is using (a string)
6
data.http_version
7
8
# The status code returned as an integer
9
data.status
10
11
# Was the connection kept alive for possible further requests
12
data.keep_alive
13
14
# What cookies have been stored at this path (as a Hash)
15
data.cookies
16
data.cookies['user_id']
17
18
# The data object itself is a hash of all the headers
19
data['Content-Type'] # => 'text/html'
20
end
Copied!

Cookies

Cookies are handled in the background in the same way a browser would handle cookies.
There is a helper method that can be used to clear cookies: clear_cookies
You can set cookies by setting the cookie header field. Supports both strings and hashes.

Proxy Support

When building drivers that need to communicate with external endpoints, its good practice to provide proxy support. This can be achieved by passing the proxy parameter with requests. To provide user support of these, these can be loaded from settings and set as part of the defaults for every request.
1
def on_update
2
proxy = setting(:proxy)
3
if proxy
4
config({
5
proxy: {
6
host: proxy[:host],
7
port: proxy[:port]
8
}
9
})
10
end
11
end
Copied!