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