]> git.saurik.com Git - redis.git/blob - client-libraries/cpp/redisclient.cpp
5e3468ccc65cedb0bb698697d3de6c364359dbd5
[redis.git] / client-libraries / cpp / redisclient.cpp
1 /* redisclient.cpp -- a C++ client library for redis.
2 *
3 * Copyright (c) 2009, Brian Hammond <brian at fictorial dot com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Redis nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "redisclient.h"
32 #include "anet.h"
33
34 #include <sstream>
35
36 #ifndef NDEBUG
37 #include <algorithm>
38 #include <iostream>
39 #include <ctime>
40 #endif
41
42 #include <cstring>
43 #include <cassert>
44
45 #include <sys/errno.h>
46 #include <sys/socket.h>
47
48 using namespace std;
49
50 namespace
51 {
52 const string whitespace(" \f\n\r\t\v");
53 const string CRLF("\r\n");
54
55 // Modifies in-place.
56
57 inline string & rtrim(string & str, const string & ws = whitespace)
58 {
59 string::size_type pos = str.find_last_not_of(ws);
60 str.erase(pos + 1);
61 return str;
62 }
63
64 vector<string>::size_type split(const string & str, char delim, vector<string> & elems)
65 {
66 stringstream ss(str);
67 string item;
68 vector<string>::size_type n = 0;
69 while (getline(ss, item, delim))
70 {
71 elems.push_back(item);
72 ++n;
73 }
74 return n;
75 }
76
77 inline void split_lines(const string & str, vector<string> & elems)
78 {
79 split(str, '\n', elems);
80 for (vector<string>::iterator it = elems.begin(); it != elems.end(); ++it)
81 rtrim(*it);
82 }
83
84 #ifndef NDEBUG
85
86 void output_proto_debug(const string & data, bool is_received = true)
87 {
88 string escaped_data(data);
89 size_t pos;
90 while ((pos = escaped_data.find("\n")) != string::npos)
91 escaped_data.replace(pos, 1, "\\n");
92 while ((pos = escaped_data.find("\r")) != string::npos)
93 escaped_data.replace(pos, 1, "\\r");
94
95 cerr
96 << time(NULL) << ": "
97 << (is_received ? "RECV '" : "SEND '")
98 << escaped_data
99 << "'"
100 << endl;
101 }
102
103 #endif
104
105 class makecmd
106 {
107 public:
108 explicit makecmd(const string & initial, bool finalize = false)
109 {
110 buffer_ << initial;
111 if (!finalize)
112 buffer_ << " ";
113 }
114
115 template <typename T>
116 makecmd & operator<<(T const & datum)
117 {
118 buffer_ << datum;
119 return *this;
120 }
121
122 template <typename T>
123 makecmd & operator<<(const vector<T> & data)
124 {
125 size_t n = data.size();
126 for (size_t i = 0; i < n; ++i)
127 {
128 buffer_ << data[i];
129 if (i < n - 1)
130 buffer_ << " ";
131 }
132 return *this;
133 }
134
135 operator std::string ()
136 {
137 buffer_ << CRLF;
138 return buffer_.str();
139 }
140
141 private:
142 ostringstream buffer_;
143 };
144
145 // Reads N bytes from given blocking socket.
146
147 string read_n(int socket, ssize_t n)
148 {
149 char * buffer = new char[n + 1];
150 buffer[n] = '\0';
151
152 char * bp = buffer;
153 ssize_t bytes_read = 0;
154
155 while (bytes_read != n)
156 {
157 ssize_t bytes_received = 0;
158 do bytes_received = recv(socket, bp, n - (bp - buffer), 0);
159 while (bytes_received < 0 && errno == EINTR);
160
161 if (bytes_received == 0)
162 throw redis::connection_error("connection was closed");
163
164 bytes_read += bytes_received;
165 bp += bytes_received;
166 }
167
168 string str(buffer);
169 delete [] buffer;
170 return str;
171 }
172
173 // Reads a single line of character data from the given blocking socket.
174 // Returns the line that was read, not including EOL delimiter(s). Both LF
175 // ('\n') and CRLF ("\r\n") delimiters are supported. If there was an I/O
176 // error reading from the socket, connection_error is raised. If max_size
177 // bytes are read before finding an EOL delimiter, a blank string is
178 // returned.
179
180 string read_line(int socket, ssize_t max_size = 2048)
181 {
182 assert(socket > 0);
183 assert(max_size > 0);
184
185 ostringstream oss;
186
187 enum { buffer_size = 64 };
188 char buffer[buffer_size];
189 memset(buffer, 0, buffer_size);
190
191 ssize_t total_bytes_read = 0;
192 bool found_delimiter = false;
193
194 while (total_bytes_read < max_size && !found_delimiter)
195 {
196 // Peek at what's available.
197
198 ssize_t bytes_received = 0;
199 do bytes_received = recv(socket, buffer, buffer_size, MSG_PEEK);
200 while (bytes_received < 0 && errno == EINTR);
201
202 if (bytes_received == 0)
203 throw redis::connection_error("connection was closed");
204
205 // Some data is available; Length might be < buffer_size.
206 // Look for newline in whatever was read though.
207
208 char * eol = static_cast<char *>(memchr(buffer, '\n', bytes_received));
209
210 // If found, write data from the buffer to the output string.
211 // Else, write the entire buffer and continue reading more data.
212
213 ssize_t to_read = bytes_received;
214
215 if (eol)
216 {
217 to_read = eol - buffer + 1;
218 oss.write(buffer, to_read);
219 found_delimiter = true;
220 }
221 else
222 oss.write(buffer, bytes_received);
223
224 // Now read from the socket to remove the peeked data from the socket's
225 // read buffer. This will not block since we've peeked already and know
226 // there's data waiting. It might fail if we were interrupted however.
227
228 do bytes_received = recv(socket, buffer, to_read, 0);
229 while (bytes_received < 0 && errno == EINTR);
230 }
231
232 // Construct final line string. Remove trailing CRLF-based whitespace.
233
234 string line = oss.str();
235 return rtrim(line, CRLF);
236 }
237
238 template <typename T>
239 T value_from_string(const string & data)
240 {
241 T value;
242
243 istringstream iss(data);
244 iss >> value;
245 if (iss.fail())
246 throw redis::value_error("invalid number");
247
248 return value;
249 }
250
251 const string status_reply_ok("OK");
252 const string prefix_status_reply_error("-ERR ");
253 const char prefix_status_reply_value = '+';
254 const char prefix_single_bulk_reply = '$';
255 const char prefix_multi_bulk_reply = '*';
256 const char prefix_int_reply = ':';
257
258 const string server_info_key_version = "redis_version";
259 const string server_info_key_bgsave_in_progress = "bgsave_in_progress";
260 const string server_info_key_connected_clients = "connected_clients";
261 const string server_info_key_connected_slaves = "connected_slaves";
262 const string server_info_key_used_memory = "used_memory";
263 const string server_info_key_changes_since_last_save = "changes_since_last_save";
264 const string server_info_key_last_save_time = "last_save_time";
265 const string server_info_key_total_connections_received = "total_connections_received";
266 const string server_info_key_total_commands_processed = "total_commands_processed";
267 const string server_info_key_uptime_in_seconds = "uptime_in_seconds";
268 const string server_info_key_uptime_in_days = "uptime_in_days";
269 const string server_info_key_role = "role";
270
271 const string server_info_value_role_master = "master";
272 const string server_info_value_role_slave = "slave";
273 }
274
275 namespace redis
276 {
277 redis_error::redis_error(const string & err) : err_(err)
278 {
279 }
280
281 redis_error::operator std::string ()
282 {
283 return err_;
284 }
285
286 redis_error::operator const std::string () const
287 {
288 return err_;
289 }
290
291 connection_error::connection_error(const string & err) : redis_error(err)
292 {
293 }
294
295 protocol_error::protocol_error(const string & err) : redis_error(err)
296 {
297 }
298
299 key_error::key_error(const string & err) : redis_error(err)
300 {
301 }
302
303 value_error::value_error(const string & err) : redis_error(err)
304 {
305 }
306
307 client::string_type client::missing_value("**nonexistent-key**");
308
309 client::client(const string_type & host, unsigned int port)
310 {
311 char err[ANET_ERR_LEN];
312 socket_ = anetTcpConnect(err, const_cast<char*>(host.c_str()), port);
313 if (socket_ == ANET_ERR)
314 throw connection_error(err);
315 anetTcpNoDelay(NULL, socket_);
316 }
317
318 client::~client()
319 {
320 if (socket_ != ANET_ERR)
321 close(socket_);
322 }
323
324 void client::auth(const client::string_type & pass)
325 {
326 send_(makecmd("AUTH") << pass);
327 recv_ok_reply_();
328 }
329
330 void client::set(const client::string_type & key,
331 const client::string_type & value)
332 {
333 send_(makecmd("SET") << key << ' ' << value.size() << CRLF << value);
334 recv_ok_reply_();
335 }
336
337 client::string_type client::get(const client::string_type & key)
338 {
339 send_(makecmd("GET") << key);
340 return recv_bulk_reply_();
341 }
342
343 client::string_type client::getset(const client::string_type & key,
344 const client::string_type & value)
345 {
346 send_(makecmd("GETSET") << key << ' ' << value.size() << CRLF << value);
347 return recv_bulk_reply_();
348 }
349
350 void client::mget(const client::string_vector & keys, string_vector & out)
351 {
352 send_(makecmd("MGET") << keys);
353 recv_multi_bulk_reply_(out);
354 }
355
356 bool client::setnx(const client::string_type & key,
357 const client::string_type & value)
358 {
359 send_(makecmd("SETNX") << key << ' ' << value.size() << CRLF << value);
360 return recv_int_reply_() == 1;
361 }
362
363 client::int_type client::incr(const client::string_type & key)
364 {
365 send_(makecmd("INCR") << key);
366 return recv_int_reply_();
367 }
368
369 client::int_type client::incrby(const client::string_type & key,
370 client::int_type by)
371 {
372 send_(makecmd("INCRBY") << key << ' ' << by);
373 return recv_int_reply_();
374 }
375
376 client::int_type client::decr(const client::string_type & key)
377 {
378 send_(makecmd("DECR") << key);
379 return recv_int_reply_();
380 }
381
382 client::int_type client::decrby(const client::string_type & key,
383 client::int_type by)
384 {
385 send_(makecmd("DECRBY") << key << ' ' << by);
386 return recv_int_reply_();
387 }
388
389 bool client::exists(const client::string_type & key)
390 {
391 send_(makecmd("EXISTS") << key);
392 return recv_int_reply_() == 1;
393 }
394
395 void client::del(const client::string_type & key)
396 {
397 send_(makecmd("DEL") << key);
398 recv_int_ok_reply_();
399 }
400
401 client::datatype client::type(const client::string_type & key)
402 {
403 send_(makecmd("TYPE") << key);
404 string response = recv_single_line_reply_();
405
406 if (response == "none") return datatype_none;
407 if (response == "string") return datatype_string;
408 if (response == "list") return datatype_list;
409 if (response == "set") return datatype_set;
410
411 return datatype_none;
412 }
413
414 client::int_type client::keys(const client::string_type & pattern,
415 client::string_vector & out)
416 {
417 send_(makecmd("KEYS") << pattern);
418 string resp = recv_bulk_reply_();
419 return split(resp, ' ', out);
420 }
421
422 client::string_type client::randomkey()
423 {
424 send_(makecmd("RANDOMKEY", true));
425 return recv_single_line_reply_();
426 }
427
428 void client::rename(const client::string_type & old_name,
429 const client::string_type & new_name)
430 {
431 send_(makecmd("RENAME") << old_name << ' ' << new_name);
432 recv_ok_reply_();
433 }
434
435 bool client::renamenx(const client::string_type & old_name,
436 const client::string_type & new_name)
437 {
438 send_(makecmd("RENAMENX") << old_name << ' ' << new_name);
439 return recv_int_reply_() == 1;
440 }
441
442 client::int_type client::dbsize()
443 {
444 send_(makecmd("DBSIZE"));
445 return recv_int_reply_();
446 }
447
448 void client::expire(const string_type & key, unsigned int secs)
449 {
450 send_(makecmd("EXPIRE") << key << ' ' << secs);
451 recv_int_ok_reply_();
452 }
453
454 void client::rpush(const client::string_type & key,
455 const client::string_type & value)
456 {
457 send_(makecmd("RPUSH") << key << ' ' << value.length() << CRLF << value);
458 recv_ok_reply_();
459 }
460
461 void client::lpush(const client::string_type & key,
462 const client::string_type & value)
463 {
464 send_(makecmd("LPUSH") << key << ' ' << value.length() << CRLF << value);
465 recv_ok_reply_();
466 }
467
468 client::int_type client::llen(const client::string_type & key)
469 {
470 send_(makecmd("LLEN") << key);
471 return recv_int_reply_();
472 }
473
474 client::int_type client::lrange(const client::string_type & key,
475 client::int_type start,
476 client::int_type end,
477 client::string_vector & out)
478 {
479 send_(makecmd("LRANGE") << key << ' ' << start << ' ' << end);
480 return recv_multi_bulk_reply_(out);
481 }
482
483 void client::ltrim(const client::string_type & key,
484 client::int_type start,
485 client::int_type end)
486 {
487 send_(makecmd("LTRIM") << key << ' ' << start << ' ' << end);
488 recv_ok_reply_();
489 }
490
491 client::string_type client::lindex(const client::string_type & key,
492 client::int_type index)
493 {
494 send_(makecmd("LINDEX") << key << ' ' << index);
495 return recv_bulk_reply_();
496 }
497
498 void client::lset(const client::string_type & key,
499 client::int_type index,
500 const client::string_type & value)
501 {
502 send_(makecmd("LSET") << key << ' ' << index << ' ' << value.length() << CRLF << value);
503 recv_ok_reply_();
504 }
505
506 client::int_type client::lrem(const client::string_type & key,
507 client::int_type count,
508 const client::string_type & value)
509 {
510 send_(makecmd("LREM") << key << ' ' << count << ' ' << value.length() << CRLF << value);
511 return recv_int_reply_();
512 }
513
514 client::string_type client::lpop(const client::string_type & key)
515 {
516 send_(makecmd("LPOP") << key);
517 return recv_bulk_reply_();
518 }
519
520 client::string_type client::rpop(const client::string_type & key)
521 {
522 send_(makecmd("RPOP") << key);
523 return recv_bulk_reply_();
524 }
525
526 void client::sadd(const client::string_type & key,
527 const client::string_type & value)
528 {
529 send_(makecmd("SADD") << key << ' ' << value.length() << CRLF << value);
530 recv_int_ok_reply_();
531 }
532
533 void client::srem(const client::string_type & key,
534 const client::string_type & value)
535 {
536 send_(makecmd("SREM") << key << ' ' << value.length() << CRLF << value);
537 recv_int_ok_reply_();
538 }
539
540 void client::smove(const client::string_type & srckey,
541 const client::string_type & dstkey,
542 const client::string_type & value)
543 {
544 send_(makecmd("SMOVE") << srckey << ' ' << dstkey << ' ' << value.length() << CRLF << value);
545 recv_int_ok_reply_();
546 }
547
548 client::int_type client::scard(const client::string_type & key)
549 {
550 send_(makecmd("SCARD") << key);
551 return recv_int_reply_();
552 }
553
554 bool client::sismember(const client::string_type & key,
555 const client::string_type & value)
556 {
557 send_(makecmd("SISMEMBER") << key << ' ' << value.length() << CRLF << value);
558 return recv_int_reply_() == 1;
559 }
560
561 client::int_type client::sinter(const client::string_vector & keys, client::string_set & out)
562 {
563 send_(makecmd("SINTER") << keys);
564 return recv_multi_bulk_reply_(out);
565 }
566
567 client::int_type client::sinterstore(const client::string_type & dstkey,
568 const client::string_vector & keys)
569 {
570 send_(makecmd("SINTERSTORE") << dstkey << ' ' << keys);
571 return recv_int_reply_();
572 }
573
574 client::int_type client::sunion(const client::string_vector & keys,
575 client::string_set & out)
576 {
577 send_(makecmd("SUNION") << keys);
578 return recv_multi_bulk_reply_(out);
579 }
580
581 client::int_type client::sunionstore(const client::string_type & dstkey,
582 const client::string_vector & keys)
583 {
584 send_(makecmd("SUNIONSTORE") << dstkey << ' ' << keys);
585 return recv_int_reply_();
586 }
587
588 client::int_type client::smembers(const client::string_type & key,
589 client::string_set & out)
590 {
591 send_(makecmd("SMEMBERS") << key);
592 return recv_multi_bulk_reply_(out);
593 }
594
595 void client::select(client::int_type dbindex)
596 {
597 send_(makecmd("SELECT") << dbindex);
598 recv_ok_reply_();
599 }
600
601 void client::move(const client::string_type & key,
602 client::int_type dbindex)
603 {
604 send_(makecmd("MOVE") << key << ' ' << dbindex);
605 recv_int_ok_reply_();
606 }
607
608 void client::flushdb()
609 {
610 send_(makecmd("FLUSHDB", true));
611 recv_ok_reply_();
612 }
613
614 void client::flushall()
615 {
616 send_(makecmd("FLUSHALL", true));
617 recv_ok_reply_();
618 }
619
620 client::int_type client::sort(const client::string_type & key,
621 client::string_vector & out,
622 client::sort_order order,
623 bool lexicographically)
624 {
625 send_(makecmd("SORT") << key
626 << (order == sort_order_ascending ? " ASC" : " DESC")
627 << (lexicographically ? " ALPHA" : ""));
628
629 return recv_multi_bulk_reply_(out);
630 }
631
632 client::int_type client::sort(const client::string_type & key,
633 client::string_vector & out,
634 client::int_type limit_start,
635 client::int_type limit_end,
636 client::sort_order order,
637 bool lexicographically)
638 {
639 send_(makecmd("SORT") << key
640 << " LIMIT " << limit_start << ' ' << limit_end
641 << (order == sort_order_ascending ? " ASC" : " DESC")
642 << (lexicographically ? " ALPHA" : ""));
643
644 return recv_multi_bulk_reply_(out);
645 }
646
647 client::int_type client::sort(const client::string_type & key,
648 client::string_vector & out,
649 const client::string_type & by_pattern,
650 client::int_type limit_start,
651 client::int_type limit_end,
652 const client::string_vector & get_patterns,
653 client::sort_order order,
654 bool lexicographically)
655 {
656 makecmd m("SORT");
657
658 m << key
659 << " BY " << by_pattern
660 << " LIMIT " << limit_start << ' ' << limit_end;
661
662 client::string_vector::const_iterator it = get_patterns.begin();
663 for ( ; it != get_patterns.end(); ++it)
664 m << " GET " << *it;
665
666 m << (order == sort_order_ascending ? " ASC" : " DESC")
667 << (lexicographically ? " ALPHA" : "");
668
669 send_(m);
670
671 return recv_multi_bulk_reply_(out);
672 }
673
674 void client::save()
675 {
676 send_(makecmd("SAVE", true));
677 recv_ok_reply_();
678 }
679
680 void client::bgsave()
681 {
682 send_(makecmd("BGSAVE", true));
683 recv_ok_reply_();
684 }
685
686 time_t client::lastsave()
687 {
688 send_(makecmd("LASTSAVE", true));
689 return recv_int_reply_();
690 }
691
692 void client::shutdown()
693 {
694 send_(makecmd("SHUTDOWN", true));
695
696 // we expected to get a connection_error as redis closes the connection on shutdown command.
697
698 try
699 {
700 recv_ok_reply_();
701 }
702 catch (connection_error & e)
703 {
704 }
705 }
706
707 void client::info(server_info & out)
708 {
709 send_(makecmd("INFO", true));
710 string response = recv_bulk_reply_();
711
712 if (response.empty())
713 throw protocol_error("empty");
714
715 string_vector lines;
716 split_lines(response, lines);
717 if (lines.empty())
718 throw protocol_error("empty line for info");
719
720 for (string_vector::const_iterator it = lines.begin();
721 it != lines.end(); ++it)
722 {
723 const string & line = *it;
724 string_vector line_parts;
725 split(line, ':', line_parts);
726 if (line_parts.size() != 2)
727 throw protocol_error("unexpected line format for info");
728
729 const string & key = line_parts[0];
730 const string & val = line_parts[1];
731
732 if (key == server_info_key_version)
733 out.version = val;
734 else if (key == server_info_key_bgsave_in_progress)
735 out.bgsave_in_progress = value_from_string<unsigned long>(val) == 1;
736 else if (key == server_info_key_connected_clients)
737 out.connected_clients = value_from_string<unsigned long>(val);
738 else if (key == server_info_key_connected_slaves)
739 out.connected_slaves = value_from_string<unsigned long>(val);
740 else if (key == server_info_key_used_memory)
741 out.used_memory = value_from_string<unsigned long>(val);
742 else if (key == server_info_key_changes_since_last_save)
743 out.changes_since_last_save = value_from_string<unsigned long>(val);
744 else if (key == server_info_key_last_save_time)
745 out.last_save_time = value_from_string<unsigned long>(val);
746 else if (key == server_info_key_total_connections_received)
747 out.total_connections_received = value_from_string<unsigned long>(val);
748 else if (key == server_info_key_total_commands_processed)
749 out.total_commands_processed = value_from_string<unsigned long>(val);
750 else if (key == server_info_key_uptime_in_seconds)
751 out.uptime_in_seconds = value_from_string<unsigned long>(val);
752 else if (key == server_info_key_uptime_in_days)
753 out.uptime_in_days = value_from_string<unsigned long>(val);
754 else if (key == server_info_key_role)
755 out.role = val == server_info_value_role_master ? role_master : role_slave;
756 else
757 throw protocol_error(string("unexpected info key '") + key + "'");
758 }
759 }
760
761 //
762 // Private methods
763 //
764
765 void client::send_(const string & msg)
766 {
767 #ifndef NDEBUG
768 output_proto_debug(msg, false);
769 #endif
770
771 if (anetWrite(socket_, const_cast<char *>(msg.data()), msg.size()) == -1)
772 throw connection_error(strerror(errno));
773 }
774
775 string client::recv_single_line_reply_()
776 {
777 string line = read_line(socket_);
778
779 #ifndef NDEBUG
780 output_proto_debug(line);
781 #endif
782
783 if (line.empty())
784 throw protocol_error("empty single line reply");
785
786 if (line.find(prefix_status_reply_error) == 0)
787 {
788 string error_msg = line.substr(prefix_status_reply_error.length());
789 if (error_msg.empty())
790 error_msg = "unknown error";
791 throw protocol_error(error_msg);
792 }
793
794 if (line[0] != prefix_status_reply_value)
795 throw protocol_error("unexpected prefix for status reply");
796
797 return line.substr(1);
798 }
799
800 void client::recv_ok_reply_()
801 {
802 if (recv_single_line_reply_() != status_reply_ok)
803 throw protocol_error("expected OK response");
804 }
805
806 client::int_type client::recv_bulk_reply_(char prefix)
807 {
808 string line = read_line(socket_);
809
810 #ifndef NDEBUG
811 output_proto_debug(line);
812 #endif
813
814 if (line[0] != prefix)
815 throw protocol_error("unexpected prefix for bulk reply");
816
817 return value_from_string<client::int_type>(line.substr(1));
818 }
819
820 string client::recv_bulk_reply_()
821 {
822 int_type length = recv_bulk_reply_(prefix_single_bulk_reply);
823
824 if (length == -1)
825 return client::missing_value;
826
827 int_type real_length = length + 2; // CRLF
828
829 string data = read_n(socket_, real_length);
830
831 #ifndef NDEBUG
832 output_proto_debug(data.substr(0, data.length()-2));
833 #endif
834
835 if (data.empty())
836 throw protocol_error("invalid bulk reply data; empty");
837
838 if (data.length() != static_cast<string::size_type>(real_length))
839 throw protocol_error("invalid bulk reply data; data of unexpected length");
840
841 data.erase(data.size() - 2);
842
843 return data;
844 }
845
846 client::int_type client::recv_multi_bulk_reply_(string_vector & out)
847 {
848 int_type length = recv_bulk_reply_(prefix_multi_bulk_reply);
849
850 if (length == -1)
851 throw key_error("no such key");
852
853 for (int_type i = 0; i < length; ++i)
854 out.push_back(recv_bulk_reply_());
855
856 return length;
857 }
858
859 client::int_type client::recv_multi_bulk_reply_(string_set & out)
860 {
861 int_type length = recv_bulk_reply_(prefix_multi_bulk_reply);
862
863 if (length == -1)
864 throw key_error("no such key");
865
866 for (int_type i = 0; i < length; ++i)
867 out.insert(recv_bulk_reply_());
868
869 return length;
870 }
871
872 client::int_type client::recv_int_reply_()
873 {
874 string line = read_line(socket_);
875
876 #ifndef NDEBUG
877 output_proto_debug(line);
878 #endif
879
880 if (line.empty())
881 throw protocol_error("invalid integer reply; empty");
882
883 if (line[0] != prefix_int_reply)
884 throw protocol_error("unexpected prefix for integer reply");
885
886 return value_from_string<client::int_type>(line.substr(1));
887 }
888
889 void client::recv_int_ok_reply_()
890 {
891 if (recv_int_reply_() != 1)
892 throw protocol_error("expecting int reply of 1");
893 }
894 }