NAME

Fugu::JSONSocket - newline-delimited JSON over a UNIX socket

SYNOPSIS

use Fugu::JSONSocket;

my $client = Fugu::JSONSocket->new(
    path     => '/tmp/qmp.sock',
    timeout  => 10,
    greeting => 1,
);

$client->connect or die $client->error . "\n";

my $reply = $client->request({ execute => 'query-status' });

$client->disconnect;

DESCRIPTION

Fugu::JSONSocket is a client for a protocol of one JSON object for each line, in both directions. qemu(1) speaks this on its machine-protocol and guest-agent sockets, and so does every other program that wanted a protocol without inventing a framing.

The codec is JSON::PP, which is core Perl. Thus a program that only talks to a local socket needs nothing installed.

Every read has a wall-clock deadline. The Timeout of IO::Socket governs its own connect and accept only, so a bare readline blocks for ever against a peer that stopped answering on an open socket.

Bytes after a newline stay buffered. Thus a reply that shares a segment with the one after it is not lost.

new

new(%args) creates a client. The method does not open the socket. Call connect.

These are the arguments:

path

The UNIX socket. This argument is necessary.

timeout

The deadline for one read, in seconds. The default is 10.

greeting

Read one line at connect. A protocol that announces itself before it takes a command sets this. Then connect reads that line and greeting returns it. The default is off.

connect

connect opens the socket, and reads the greeting when the caller asked for one. A second call on an open connection is a success that does nothing.

disconnect

disconnect closes the socket and drops the buffer. The method is idempotent.

request

request($hashref) encodes, sends, and reads one reply.

send_message

send_message($hashref) encodes and writes one message with its newline. Use this, and then read_message, for a protocol whose replies do not pair one to one with its requests.

read_message

read_message reads one line and decodes it.

read_line

read_line reads one line without its terminator, and does not decode it.

is_connected

is_connected reports if the socket is open.

exists

exists reports if the socket file is there. A peer that has not started is not an error, so a caller can test before it connects.

greeting

greeting returns the decoded greeting, for a connection that asked for one. It is undef otherwise.

path

path returns the socket path.

error

error returns the most recent failure.

RETURN VALUES

connect and send_message return 1 on success and undef on failure.

request and read_message return the decoded value, or undef. read_line returns a string, or undef. Every failure puts its reason in error.

disconnect returns the object. is_connected and exists return 1 or 0.

EXAMPLES

This example builds a command set over the client:

sub query_status ($self)
{
    my $reply = $self->{socket}->request({ execute => 'query-status' });
    return if !defined $reply || exists $reply->{error};
    return $reply->{return};
}

ERRORS

new dies when path is absent or empty. That is a programming error.

No other method dies. A missing socket, a peer that closed the connection, a timeout, and a line that is not JSON all report through the return value and error.

A write error and an end of file both close the connection. A later call reports "not connected" until the caller connects again.

SEE ALSO

Fugu::Imsg, IO::Socket::UNIX, JSON::PP

AUTHORS

Dick Olsson <hi@senzilla.io>

CAVEATS

The module is a client. It does not listen and does not accept.

A reply must be one line. A peer that pretty-prints its JSON across several lines does not work with this module, and no such peer exists among the protocols it was written for.

The timeout bounds one read. A reply that arrives in pieces resets nothing: the deadline covers the whole line.