File Coverage

lib/DR/Tnt/Proto.pm
Criterion Covered Total %
statement 131 161 81.3
branch 45 80 56.2
condition 5 16 31.2
subroutine 24 29 82.7
pod 0 14 0.0
total 205 300 68.3


line stmt bran cond sub pod time code
1 8     8   159480 use utf8;
  8         33  
  8         46  
2 8     8   257 use strict;
  8         16  
  8         180  
3 8     8   43 use warnings;
  8         17  
  8         390  
4              
5             package DR::Tnt::Proto;
6 8     8   49 use base qw(Exporter);
  8         53  
  8         1439  
7             our @EXPORT_OK = qw(
8             call_lua
9             response
10             insert
11             replace
12             del
13             update
14             select
15             auth
16             handshake
17             ping
18             );
19 8     8   57 use Carp;
  8         47  
  8         640  
20             $Carp::Internal{ (__PACKAGE__) }++;
21 8     8   3166 use DR::Tnt::Msgpack;
  8         75  
  8         574  
22 8     8   4558 use Digest::SHA;
  8         28037  
  8         467  
23 8     8   121 use Scalar::Util 'looks_like_number';
  8         22  
  8         450  
24 8     8   54 use Digest::SHA 'sha1';
  8         17  
  8         592  
25 8     8   3827 use MIME::Base64;
  8         5457  
  8         462  
26 8     8   4981 use Data::Dumper;
  8         58837  
  8         2430  
27              
28             sub parse_greeting {
29 0     0 0 0 my ($str) = @_;
30 0 0 0     0 croak "strlen is not 128 bytes" unless $str and 128 == length $str;
31              
32 0   0     0 my $salt = eval { substr decode_base64(substr $str, 64, 44), 0, 20; } || undef;
33 0         0 my $grstr = substr $str, 0, 64;
34              
35 0         0 my ($title, $v, $pt, $uid) = split /\s+/, $grstr, 5;
36              
37             return {
38 0         0 salt => $salt,
39             gr => $grstr,
40             title => $title,
41             version => $v,
42             uuid => $uid,
43             proto => $pt,
44             }
45             }
46              
47             my (%resolve, %tresolve);
48              
49             my %iter = (
50             EQ => 0,
51             REQ => 1,
52             ALL => 2,
53             LT => 3,
54             LE => 4,
55             GE => 5,
56             GT => 6,
57             BITS_ALL_SET => 7,
58             BITS_ANY_SET => 8,
59             BITS_ALL_NOT_SET => 9
60             );
61              
62             my %riter = reverse %iter;
63              
64             BEGIN {
65 8     8   91 my %types = (
66             IPROTO_SELECT => 1,
67             IPROTO_INSERT => 2,
68             IPROTO_REPLACE => 3,
69             IPROTO_UPDATE => 4,
70             IPROTO_DELETE => 5,
71             IPROTO_CALL => 6,
72             IPROTO_AUTH => 7,
73             IPROTO_EVAL => 8,
74             IPROTO_PING => 64,
75             );
76 8         113 my %attrs = (
77             IPROTO_CODE => 0x00,
78             IPROTO_SYNC => 0x01,
79             IPROTO_SERVER_ID => 0x02,
80             IPROTO_LSN => 0x03,
81             IPROTO_TIMESTAMP => 0x04,
82             IPROTO_SCHEMA_ID => 0x05,
83             IPROTO_SPACE_ID => 0x10,
84             IPROTO_INDEX_ID => 0x11,
85             IPROTO_LIMIT => 0x12,
86             IPROTO_OFFSET => 0x13,
87             IPROTO_ITERATOR => 0x14,
88             IPROTO_KEY => 0x20,
89             IPROTO_TUPLE => 0x21,
90             IPROTO_FUNCTION_NAME => 0x22,
91             IPROTO_USER_NAME => 0x23,
92             IPROTO_EXPRESSION => 0x27,
93             IPROTO_DATA => 0x30,
94             IPROTO_ERROR => 0x31,
95             );
96              
97 8     8   76 use constant;
  8         18  
  8         1374  
98 8         62 while (my ($n, $v) = each %types) {
99 72         1960 constant->import($n => $v);
100 72         314 $n =~ s/^IPROTO_//;
101 72         398 $tresolve{$v} = $n;
102             }
103 8         57 while (my ($n, $v) = each %attrs) {
104 144         3209 constant->import($n => $v);
105 144         595 $n =~ s/^IPROTO_//;
106 144         18815 $resolve{$v} = $n;
107             }
108             }
109              
110              
111             sub raw_response($$) {
112 172     172 0 308 my ($response, $utf8) = @_;
113              
114 172         238 my $len;
115             {
116 172 50       217 return unless defined $response;
  172         357  
117 172 100       420 my $lenheader = length $response > 10 ?
118             substr $response, 0, 10 : $response;
119 172 100       428 return unless my $lenlen = DR::Tnt::Msgpack::msgunpack_check $lenheader;
120              
121 165         392 $len = DR::Tnt::Msgpack::msgunpack $lenheader;
122 165 50       364 croak 'Unexpected msgpack object ' . ref($len) if ref $len;
123 165         269 $len += $lenlen;
124             }
125            
126 165 100       455 return if length $response < $len;
127              
128 7         23 my @r;
129 7         26 my $off = 0;
130              
131 7         21 for (1 .. 3) {
132 21 100       68 my $sp = $off ? substr $response, $off : $response;
133 21         61 my $len_item = DR::Tnt::Msgpack::msgunpack_check $sp;
134 21 50 33     102 croak sprintf('Broken %s section of response', $_)
135             unless $len_item and $len_item + $off <= length $response;
136 21 50       50 if ($utf8) {
137 0         0 push @r => DR::Tnt::Msgpack::msgunpack_utf8 $sp;
138             } else {
139 21         60 push @r => DR::Tnt::Msgpack::msgunpack $sp;
140             }
141 21         37 $off += $len_item;
142              
143 21 50 66     104 if ($_ eq 2 and $off == length $response) {
144 0         0 push @r => {};
145 0         0 last;
146             }
147             }
148              
149 7 50       25 croak 'Broken response header' unless 'HASH' eq ref $r[1];
150 7 50       23 croak 'Broken response body' unless 'HASH' eq ref $r[2];
151              
152 7         35 return [ $r[1], $r[2] ], substr $response, $off;
153             }
154              
155             sub response($;$) {
156              
157 172     172 0 44509 my ($buffer, $utf8) = @_;
158 172         355 my ($resp, $tail) = raw_response($buffer => $utf8);
159 172 100       511 return unless $resp;
160 7         16 my ($h, $b) = @$resp;
161              
162 7         15 my $res = {};
163              
164 7         37 while(my ($k, $v) = each %$h) {
165 14         33 my $name = $resolve{$k};
166 14 50       34 $name = $k unless defined $name;
167 14         61 $res->{$name} = $v;
168             }
169 7         33 while(my ($k, $v) = each %$b) {
170 17         40 my $name = $resolve{$k};
171 17 50       39 $name = $k unless defined $name;
172 17         66 $res->{$name} = $v;
173             }
174              
175 7 50       23 if (defined $res->{CODE}) {
176 7         23 my $n = $tresolve{ $res->{CODE} };
177 7 50       32 $res->{CODE} = $n if defined $n;
178             }
179              
180 7 100       23 if (defined $res->{ITERATOR}) {
181 1         5 my $n = $riter{ $res->{ITERATOR} };
182 1 50       5 $res->{ITERATOR} = $n if defined $n;
183             }
184              
185 7         43 return $res, $tail;
186            
187             }
188              
189             sub request($$) {
190 7     7 0 19 my ($header, $body) = @_;
191 7         28 my $pkt = msgpack($header) . msgpack($body);
192              
193 7         30 return join '',
194             msgpack(length $pkt),
195             $pkt;
196             }
197              
198             sub _mk_header($$$) {
199 7     7   23 my ($code, $sync, $schema_id) = @_;
200            
201              
202             return {
203 7 50       63 IPROTO_SYNC, $sync,
204             IPROTO_CODE, $code,
205             } unless defined $schema_id;
206              
207             return {
208 0         0 IPROTO_SYNC, $sync,
209             IPROTO_CODE, $code,
210             IPROTO_SCHEMA_ID, $schema_id
211             }
212             }
213              
214             sub _call_lua($$$$) {
215 0     0   0 my ($sync, $schema_id, $proc, $tuple) = @_;
216 0         0 request
217             _mk_header(IPROTO_CALL, $sync, $schema_id),
218             {
219             IPROTO_FUNCTION_NAME, $proc,
220             IPROTO_TUPLE, $tuple,
221             }
222             ;
223             }
224              
225             sub call_lua($$$@) {
226 0     0 0 0 my ($sync, $schema_id, $proc, @args) = @_;
227 0         0 return _call_lua($sync, $schema_id, $proc, \@args);
228             }
229              
230              
231             sub eval_lua($$$@) {
232 1     1 0 557 my ($sync, $schema_id, $lua, @args) = @_;
233 1         5 request
234             _mk_header(IPROTO_EVAL, $sync, $schema_id),
235             {
236             IPROTO_EXPRESSION, $lua,
237             IPROTO_TUPLE, \@args,
238             }
239             ;
240             }
241              
242             sub insert($$$$) {
243 1     1 0 557 my ($sync, $schema_id, $space, $tuple) = @_;
244              
245 1 50       5 $tuple = [ $tuple ] unless ref $tuple;
246 1 50       5 croak "Cant convert HashRef to tuple" if 'HASH' eq ref $tuple;
247              
248 1 50       7 if (looks_like_number $space) {
249 1         5 return request
250             _mk_header(IPROTO_INSERT, $sync, $schema_id),
251             {
252             IPROTO_SPACE_ID, $space,
253             IPROTO_TUPLE, $tuple,
254             }
255             ;
256             }
257              
258             # HACK
259 0         0 _call_lua($sync, $schema_id, "box.space.$space:insert", $tuple);
260             }
261              
262             sub replace($$$$) {
263 1     1 0 582 my ($sync, $schema_id, $space, $tuple) = @_;
264              
265 1 50       7 $tuple = [ $tuple ] unless ref $tuple;
266 1 50       5 croak "Cant convert HashRef to tuple" if 'HASH' eq ref $tuple;
267              
268 1 50       7 if (looks_like_number $space) {
269 1         5 return request
270             _mk_header(IPROTO_REPLACE, $sync, $schema_id),
271             {
272             IPROTO_SPACE_ID, $space,
273             IPROTO_TUPLE, $tuple,
274             }
275             ;
276             }
277             # HACK
278 0         0 _call_lua($sync, $schema_id, "box.space.$space:replace", $tuple);
279             }
280             sub del($$$$) {
281 1     1 0 555 my ($sync, $schema_id, $space, $key) = @_;
282              
283 1 50       6 $key = [ $key ] unless ref $key;
284 1 50       7 croak "Cant convert HashRef to key" if 'HASH' eq ref $key;
285              
286 1 50       6 if (looks_like_number $space) {
287 1         4 return request
288             _mk_header(IPROTO_DELETE, $sync, $schema_id),
289             {
290             IPROTO_SPACE_ID, $space,
291             IPROTO_KEY, $key,
292             }
293             ;
294             }
295             # HACK
296 0         0 _call_lua($sync, $schema_id, "box.space.$space:delete", $key);
297             }
298              
299              
300             sub update($$$$$) {
301 1     1 0 563 my ($sync, $schema_id, $space, $key, $ops) = @_;
302 1 50       7 croak 'Oplist must be Arrayref' unless 'ARRAY' eq ref $ops;
303 1 50       6 $key = [ $key ] unless ref $key;
304 1 50       5 croak "Cant convert HashRef to key" if 'HASH' eq ref $key;
305              
306 1 50       6 if (looks_like_number $space) {
307 1         5 return request
308             _mk_header(IPROTO_UPDATE, $sync, $schema_id),
309             {
310             IPROTO_SPACE_ID, $space,
311             IPROTO_KEY, $key,
312             IPROTO_TUPLE, $ops,
313             }
314             ;
315             }
316             # HACK
317 0         0 _call_lua($sync, $schema_id, "box.space.$space:update", [ $key, $ops ]);
318             }
319              
320             sub select($$$$$;$$$) {
321 1     1 0 589 my ($sync, $schema_id, $space, $index, $key, $limit, $offset, $iterator) = @_;
322 1 50       5 $iterator = 'EQ' unless defined $iterator;
323 1   50     4 $offset ||= 0;
324 1 50       4 $limit = 0xFFFF_FFFF unless defined $limit;
325 1 50       5 $key = [ $key ] unless ref $key;
326 1 50       5 croak "Cant convert HashRef to key" if 'HASH' eq ref $key;
327              
328 1 50       7 unless(looks_like_number $iterator) {
329 1         4 my $i = $iter{$iterator};
330 1 50       4 croak "Wrong iterator type: $iterator" unless defined $i;
331 1         4 $iterator = $i;
332             }
333              
334 1 50 33     9 if (looks_like_number $space and looks_like_number $index) {
335 1         4 return request
336             _mk_header(IPROTO_SELECT, $sync, $schema_id),
337             {
338             IPROTO_KEY, $key,
339             IPROTO_SPACE_ID, $space,
340             IPROTO_OFFSET, $offset,
341             IPROTO_INDEX_ID, $index,
342             IPROTO_LIMIT, $limit,
343             IPROTO_ITERATOR, $iterator,
344             }
345             ;
346             }
347              
348             # HACK
349 0         0 _call_lua($sync, $schema_id, "box.space.$space.index.$index:select", [
350             $key,
351             {
352             offset => $offset,
353             limit => $limit,
354             iterator => $iterator
355             }
356             ]
357             );
358             }
359              
360             sub ping($$) {
361 1     1 0 143 my ($sync, $schema_id) = @_;
362 1         5 request
363             _mk_header(IPROTO_PING, $sync, $schema_id),
364             {
365             }
366             ;
367             }
368              
369              
370             sub strxor($$) {
371 0     0 0   my ($x, $y) = @_;
372              
373 0           my @x = unpack 'C*', $x;
374 0           my @y = unpack 'C*', $y;
375 0           $x[$_] ^= $y[$_] for 0 .. $#x;
376 0           return pack 'C*', @x;
377             }
378              
379             sub auth($$$$$) {
380 0     0 0   my ($sync, $schema_id, $user, $password, $salt) = @_;
381              
382 0           my $hpasswd = sha1 $password;
383 0           my $hhpasswd = sha1 $hpasswd;
384 0           my $scramble = sha1 $salt . $hhpasswd;
385              
386 0           my $hash = strxor $hpasswd, $scramble;
387 0           request
388             _mk_header(IPROTO_AUTH, $sync, $schema_id),
389             {
390             IPROTO_USER_NAME, $user,
391             IPROTO_TUPLE, [ 'chap-sha1', $hash ],
392             }
393             ;
394             }
395              
396             1;