Skip to content

und3f/protocol-redis

Repository files navigation

NAME

Protocol::Redis - Redis protocol parser/encoder with asynchronous capabilities.

SYNOPSIS

use Protocol::Redis;
my $redis = Protocol::Redis->new(api => 1);

$redis->parse("+foo\r\n");

# get parsed message
my $message = $redis->get_message;
print "parsed message: ", $message->{data}, "\n";

# asynchronous parsing interface
$redis->on_message(sub {
    my ($redis, $message) = @_;
    print "parsed message: ", $message->{data}, "\n";
});

# parse pipelined message
$redis->parse("+bar\r\n-error\r\n");

# create message
print "Get key message:\n",
  $redis->encode({type => '*', data => [
     {type => '$', data => 'string'},
     {type => '+', data => 'OK'}
]});

# RESP3 supported with api 3 specified
my $redis = Protocol::Redis->new(api => 3);

print $redis->encode({type => '%', data => {
    null   => {type => '_', data => undef},
    bignum => {type => '(', data => '3492890328409238509324850943850943825024385'},
    string => {type => '$', data => 'this is a string'},
    # format prepended to verbatim strings (defaults to txt)
    verbatim => {type => '=', data => '*verbatim* string', format => 'mkd'},
    # set is unordered but specified as array
    booleans => {type => '~', data => [
        {type => '#', data => 1},
        {type => '#', data => 0},
    ]},
    # map is hashlike but can be specified as an
    # (even-sized) array to encode non-string keys
    special_map => {type => '%', data => [
        {type => ':', data => 42} => {type => '$', data => 'The answer'},
        {type => '_'} => {type => '$', data => 'No data'},
    ]},
}, attributes => {
    coordinates => {type => '*', data => [
        {type => ',', data => '36.001516'},
        {type => ',', data => '-78.943319'},
    ]},
});

# "|1\r\n\$11\r\ncoordinates\r\n*2\r\n,36.001516\r\n,-78.943319\r\n" .
# "%6\r\n" .
# "\$6\r\nbignum\r\n(3492890328409238509324850943850943825024385\r\n" .
# "\$8\r\nbooleans\r\n~2\r\n#t\r\n#f\r\n" .
# "\$4\r\nnull\r\n_\r\n" .
# "\$11\r\nspecial_map\r\n%2\r\n:42\r\n\$10\r\nThe answer\r\n_\r\n\$7\r\nNo data\r\n" .
# "\$6\r\nstring\r\n\$16\r\nthis is a string\r\n" .
# "\$8\r\nverbatim\r\n=21\r\nmkd:*verbatim* string\r\n"

# sets represented in the protocol the same as arrays
# remapping into a hash may be useful to access string set elements
$redis->parse("~3\r\n+x\r\n+y\r\n+z\r\n");
my %set = map {($_->{data} => 1)} @{$redis->get_message->{data}};
die unless exists $set{x};
print join ',', keys %set; # x,y,z in unspecified order

# verbatim strings are prefixed by a format
# this will be returned as the format key
$redis->parse("=16\r\nmkd:* one\n* two\n\r\n");
my $verbatim = $redis->get_message;
die unless $verbatim->{format} eq 'mkd';
print $verbatim->{data};
# * one
# * two

# attributes are maps that apply to the following value
$redis->parse("|1\r\n+hits\r\n:6\r\n\$4\r\nterm\r\n");
my $term = $redis->get_message;
print "$term->{data}: $term->{attributes}{hits}{data}\n";

DESCRIPTION

Redis protocol parser/encoder with asynchronous capabilities and pipelining support.

APIv1

Protocol::Redis APIv1 uses "Unified Request Protocol" for message encoding/parsing and supports methods described further. Client libraries should specify API version during Protocol::Redis construction.

API version 1 corresponds to the protocol now known as RESP2 and can also thusly be specified as API version 2. It supports the RESP2 data types +-:$*.

APIv3

API version 3 supports the same methods as API version 1, corresponding to the RESP3 protocol. RESP3 contains support for several additional data types (null, boolean, double, big number, blob error, verbatim string, map, and set), data attributes, streamed strings and aggregate data, and explicitly specified push data so that asynchronous and synchronous responses can share a connection. A client must request RESP3 support from the server with the HELLO command to use these features.

APIv3 supports the RESP2 data types +-:$* as well as the RESP3-specific data types _,#!=(%~|>, with the following implementation notes:

  • Verbatim String

    The Verbatim String type, specified with the initial byte =, is treated the same as the Blob String type $ except that the first three bytes specify the format, followed by a colon :, and the remaining bytes are the string data. When parsing, the format will be returned as a separate key and not included in the string data. When encoding, a format can be specified and otherwise defaults to txt, and will be prepended to the string data.

  • Big Number

    The Big Number type, specified with the initial byte (, is parsed to a Math::BigInt object, which can be used in numeric or string operations without losing precision.

  • Map

    The Map type, specified with the initial byte %, is represented in Perl as a hash. The keys of a Map are allowed to be any data type, but for simplicity they are coerced to strings as required by Perl hashes, which the specification allows. When encoding a Map, a hash reference is normally passed, but an array reference of alternating keys and values may also be passed to allow specifying non-string keys. If passed as an array, the values will be encoded in the order specified, but the Map type is defined as unordered.

  • Attribute

    The Attribute type, specified with the initial byte |, is much like the Map type, but instead of acting as a value in the message, it is applied as the attributes key of the following value. Like Map, its keys are coerced to strings as it is represented as a Perl hash.

  • Set

    The Set type, specified with the initial byte ~, is represented as an array, since a Set can contain values of any data type, which native Perl hashes cannot represent as keys, and the specification does not require enforcing element uniqueness. If desired, the higher level client and server should handle deduplication of Set elements, and should also be aware that the type is defined as unordered and the values are likely to be tested for existence rather than position.

  • Push

    The Push type, specified with the initial byte >, is treated no differently from an Array, but a client supporting RESP3 must be prepared to handle a Push value at any time rather than in response to a command. An asynchronous client would generally execute a predefined callback when a Push value is received; a synchronous client must also take this into consideration for how and when it reads messages.

new

my $redis = Protocol::Redis->new(api => 1);

Construct Protocol::Redis object with specific API version support. If specified API version not supported constructor should raise an exception. Client libraries should always specify API version.

parse

$redis->parse("*2\r\n$4ping\r\n\r\n");

Parse Redis protocol chunk.

get_message

while (my $message = $redis->get_message) {
    ...
}

Get parsed message or undef.

on_message

$redis->on_message(sub {
    my ($redis, $message) = @_;

}

Calls callback on each parsed message.

encode

my $string = $redis->encode({type => '+', data => 'test'});
$string = $redis->encode(
    {type => '*', data => [
        {type => '$', data => 'test'}]});

Encode data into redis message.

api

my $api_version = $redis->api;

Get API version.

SUPPORT

IRC

#redis on irc.perl.org

DEVELOPMENT

Repository

http://github.com/und3f/protocol-redis

AUTHOR

Serhii Zasenko, undef@cpan.org.

CREDITS

In alphabetical order

    Dan Book (Grinnz)

    David Leadbeater (dgl)

    Jan Henning Thorsen (jhthorsen)

    Viacheslav Tykhanovskyi (vti)

    Yaroslav Korshak (yko)

COPYRIGHT AND LICENSE

Copyright (C) 2011-2024, Serhii Zasenko.

This program is free software, you can redistribute it and/or modify it under the same terms as Perl 5.10.

About

Redis protocol messages parser/encoder

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages