NAME

Respite::Client - Generic class for running remote services

$Id: Client.pod,v 1.5 2014/02/26 19:30:38 dfackrell Exp $

SYNOPSIS

use Respite::Client;
use Data::Debug;

my $client = Respite::Client->new({
    service => 'foo',        # typically all that is required
    host    => 'localhost',
    pass    => '-',
    port    => 50000,        # default 443
    no_ssl  => 1,            # default is ssl
    #no_sign => 1,           # turn off request signing
    #no_trace => 1,          # turn off sending caller info
    #no_brand => 1,          # respite does not require brand
    flat    => 1,            # return flat hashref of results
    ns      => '',           # optional prefixed namespace
});

debug $client->run_method(foo => {hey => 1});

OPTIONS

service_name (or service)

This will be the name of the service used for this client. It will be used for looking up connection info in %config::config. It will first look under ${name}_service and then $name (you can pass foo_service or foo in for the service and it will do the same thing).

All of the remaining options will attempt to look in this remote info from the config.

host

Defaults to remote info host.

port

Defaults to remote info port, and then to 443.

path

Defaults to service_name with the _service removed from the end.

flat

By default, the results are blessed into the class returned by _result_class (default Respite::Client::Result). If flat is set to true it will just return raw data from the request.

If an error is returned and is true, the run_method response will die with the value set to the error.

The flat argument is intended to make working with API::Client more seamless and close to RPC than the default API::Client behavior of returning a response object.

The value can be set per request by passing _flat as an arg to run_method.

ns

No default. If set, the value is added to the beginning of any requested methods, with an underscore. For example, if ns is 'test' and we request the 'something' method, the resulting method name will be 'test_something'.

utf8_encoded

Default false. When false, data passed should be properly utf8 decoded (or have no utf8 data at all). When true, data passed is assumed to be utf8 encoded meaning that it will need to be decoded before calling json->encode.

Additionally, the true value can be a hashref of methods that need this treatment. This is useful if you know some of your methods have utf8 data, while others do not.

(Note: Conversely when the non-json transport is finalized, it will need to call decode_utf8 to make sure data is ready for the transport.)

PROTOCOL

The Respite::Client service is a JSON over HTTPS service though it does also support normal x-uri-encoded forms including multipart.

The request should be a POST request with a content type of x-application/json.

The request should be hashref (map or associative array) of properly encoded JSON.

Several meta parameters may passed as part of the request.

_i  Remote IP   - IP of end user requestion action (defaults to REMOTE_ADDR)
_w  Remote user - Who is the initiator of the action, may be an admin user (defaults to REMOTE_USER)
_t  Token       - An auth compatible token (required for some admin tasks)
_c  Caller      - What remote code called this method (automatically added)

The response will be a hashref of properly encoded JSON (a JSON object). (Note: Some Respite methods may choose to return other content. This will be noted in the meta information).

Though not required, it is suggested that all Respites return a hash element named "error" if an error situation arrises. Optionally the Respite service could return a http 500 or 400 class error and Respite::Client will translate that into a hashref with "error" set. This allows all calling code to easily and consistently check for error conditions.

DEBUG_Respite

If the environment variable DEBUG_Respite is set, each time a request is made, the URL and headers will be output to STDERR via warn.

export DEBUG_Respite=1

# bare request
PROV=company_brand perl -Ilib -e 'use config; use Respite::Client; $c=Respite::Client->new({service => "service_name",no_sign=>1,no_trace=>1}); use Data::Debug; debug $c->hello'
DEBUG_Respite: Connected to https://localhost:50901/
POST /service_name/hello/company_brand HTTP/1.0
Host: localhost
Content-length: 28
Content-type: x-application/json

{"_i":"cmdline","_w":"paul"}


# with caller trace
PROV=company_brand perl -Ilib -e 'use config; use Respite::Client; $c=Respite::Client->new({service => "service_name",no_sign=>1}); use Data::Debug; debug $c->hello'

DEBUG_Respite: Connected to https://localhost:50901/
POST /service_name/hello/company_brand HTTP/1.0
Host: localhost
Content-length: 70
Content-type: x-application/json

{"_c":"main; -e; 1; Respite::Client::AUTOLOAD","_i":"cmdline","_w":"paul"}


# with method signing
PROV=company_brand perl -Ilib -e 'use config; use Respite::Client; $c=Respite::Client->new({service => "service_name"}); use Data::Debug; debug $c->hello'
DEBUG_Respite: Connected to https://localhost:50901/
POST /service_name/hello/company_brand HTTP/1.0
X-Respite-Auth: 28b771585f523c91ad555d7a956f0940:1368823114
Host: localhost
Content-length: 70
Content-type: x-application/json

{"_c":"main; -e; 1; Respite::Client::AUTOLOAD","_i":"cmdline","_w":"paul"}

AUTHENTICATION

At the top level, the incoming IP will be validated against a per-brand whitelist. Additionally, each IP can have its own password specified. It is up to the server to enforce this. If a password is found during request it will be used to sign the generated arguments. Signing can be turned off via the no_sign argument. Some Respites may choose to allow a basic MD5 sum of the password rather than requiring siging the request. In these cases the md5_pass configuration item should be set and the MD5 hex sum of the password should be sent in place of the signed sum.

Various administrative methods require authentication beyond the brand password. In these cases an token must be passed along. The Respite::CommandLine library will request a token for you should authentication be required.

METHOD SIGNING

By default, the Respite::Client will send along a signed token in the X-Respite-Auth HTTP header. This can be turned off by setting no_sign for the service.

The following is the pseudo code for how a method token is generated

path    := 'service_name'        # typically the service name
method  := 'hello'
brand   := 'company_brand'
pass    := 'jdDU&9dk1S' # client specific config
time    := 1368823114   # system time
request := '{"_c":"main; -e; 1; Respite::Client::AUTOLOAD","_i":"cmdline","_w":"paul"}'

sum1    := md5(request)
url     := concat('/', path, '/', method, '/', brand)
secret  := concat(pass, ':', time, ':', url, ':', sum1)
sum2    := md5(secret)
token   := concat(sum2, ':', time)

token   == '28b771585f523c91ad555d7a956f0940:1368823114'

This token should be sent in the X-Respite-Auth HTTP request header.

If md5_pass is set and the server is set to allow md5_pass, then the X-Respite-Auth HTTP request header would be generated as follows

pass    := 'jdDU&9dk1S' # client specific config
token   := md5(pass)

token   == b39fc4d2b6ffb1a3cbe50598f14a9dbe

Additionally, since the request itself is not signed when md5_pass is enabled, the server also supports passing the token along as part of the JSON request arguments under the x_api_auth key name.