1 /* redisclient.cpp -- a C++ client library for redis.
3 * Copyright (c) 2009, Brian Hammond <brian at fictorial dot com>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
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.
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.
31 #include "redisclient.h"
46 #include <sys/errno.h>
47 #include <sys/socket.h>
53 const string
whitespace(" \f\n\r\t\v");
54 const string
CRLF("\r\n");
58 inline string
& rtrim(string
& str
, const string
& ws
= whitespace
)
60 string::size_type pos
= str
.find_last_not_of(ws
);
65 vector
<string
>::size_type
split(const string
& str
, char delim
, vector
<string
> & elems
)
69 vector
<string
>::size_type n
= 0;
70 while (getline(ss
, item
, delim
))
72 elems
.push_back(item
);
78 inline void split_lines(const string
& str
, vector
<string
> & elems
)
80 split(str
, '\n', elems
);
81 for (vector
<string
>::iterator it
= elems
.begin(); it
!= elems
.end(); ++it
)
87 void output_proto_debug(const string
& data
, bool is_received
= true)
89 string
escaped_data(data
);
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");
98 << (is_received
? "RECV '" : "SEND '")
109 explicit makecmd(const string
& initial
, bool finalize
= false)
116 template <typename T
>
117 makecmd
& operator<<(T
const & datum
)
123 template <typename T
>
124 makecmd
& operator<<(const vector
<T
> & data
)
126 size_t n
= data
.size();
127 for (size_t i
= 0; i
< n
; ++i
)
136 operator std::string ()
139 return buffer_
.str();
143 ostringstream buffer_
;
146 // Reads N bytes from given blocking socket.
148 string
read_n(int socket
, ssize_t n
)
150 char * buffer
= new char[n
+ 1];
154 ssize_t bytes_read
= 0;
156 while (bytes_read
!= n
)
158 ssize_t bytes_received
= 0;
159 do bytes_received
= recv(socket
, bp
, n
- (bp
- buffer
), 0);
160 while (bytes_received
< 0 && errno
== EINTR
);
162 if (bytes_received
== 0)
163 throw redis::connection_error("connection was closed");
165 bytes_read
+= bytes_received
;
166 bp
+= bytes_received
;
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
181 string
read_line(int socket
, ssize_t max_size
= 2048)
184 assert(max_size
> 0);
188 enum { buffer_size
= 64 };
189 char buffer
[buffer_size
];
190 memset(buffer
, 0, buffer_size
);
192 ssize_t total_bytes_read
= 0;
193 bool found_delimiter
= false;
195 while (total_bytes_read
< max_size
&& !found_delimiter
)
197 // Peek at what's available.
199 ssize_t bytes_received
= 0;
200 do bytes_received
= recv(socket
, buffer
, buffer_size
, MSG_PEEK
);
201 while (bytes_received
< 0 && errno
== EINTR
);
203 if (bytes_received
== 0)
204 throw redis::connection_error("connection was closed");
206 // Some data is available; Length might be < buffer_size.
207 // Look for newline in whatever was read though.
209 char * eol
= static_cast<char *>(memchr(buffer
, '\n', bytes_received
));
211 // If found, write data from the buffer to the output string.
212 // Else, write the entire buffer and continue reading more data.
214 ssize_t to_read
= bytes_received
;
218 to_read
= eol
- buffer
+ 1;
219 oss
.write(buffer
, to_read
);
220 found_delimiter
= true;
223 oss
.write(buffer
, bytes_received
);
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.
229 do bytes_received
= recv(socket
, buffer
, to_read
, 0);
230 while (bytes_received
< 0 && errno
== EINTR
);
233 // Construct final line string. Remove trailing CRLF-based whitespace.
235 string line
= oss
.str();
236 return rtrim(line
, CRLF
);
239 unsigned long unsigned_number_from_string(const string
& data
)
243 unsigned long value
= strtoul(data
.c_str(), NULL
, 10);
245 if (value
== ULONG_MAX
&& errno
== ERANGE
)
246 throw redis::value_error("invalid number; out of range of long");
248 if (value
== 0 && errno
== EINVAL
)
249 throw redis::value_error("invalid number; unrecognized format");
254 redis::client::int_type
number_from_string(const string
& data
)
258 redis::client::int_type value
= strtol(data
.c_str(), NULL
, 10);
260 if ((value
== LONG_MAX
|| value
== LONG_MIN
) && errno
== ERANGE
)
261 throw redis::value_error("invalid number; out of range of long");
263 if (value
== 0 && errno
== EINVAL
)
264 throw redis::value_error("invalid number; unrecognized format");
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
= ':';
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";
291 redis_error::redis_error(const string
& err
) : err_(err
)
295 redis_error::operator std::string ()
300 redis_error::operator const std::string () const
305 connection_error::connection_error(const string
& err
) : redis_error(err
)
309 protocol_error::protocol_error(const string
& err
) : redis_error(err
)
313 key_error::key_error(const string
& err
) : redis_error(err
)
317 value_error::value_error(const string
& err
) : redis_error(err
)
321 client::string_type
client::missing_value("**nonexistent-key**");
323 client::client(const string_type
& host
, unsigned int port
)
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_
);
334 if (socket_
!= ANET_ERR
)
338 void client::auth(const client::string_type
& pass
)
340 send_(makecmd("AUTH") << pass
);
344 void client::set(const client::string_type
& key
,
345 const client::string_type
& value
)
347 send_(makecmd("SET") << key
<< ' ' << value
.size() << CRLF
<< value
);
351 client::string_type
client::get(const client::string_type
& key
)
353 send_(makecmd("GET") << key
);
354 return recv_bulk_reply_();
357 client::string_type
client::getset(const client::string_type
& key
,
358 const client::string_type
& value
)
360 send_(makecmd("GETSET") << key
<< ' ' << value
.size() << CRLF
<< value
);
361 return recv_bulk_reply_();
364 void client::mget(const client::string_vector
& keys
, string_vector
& out
)
366 send_(makecmd("MGET") << keys
);
367 recv_multi_bulk_reply_(out
);
370 bool client::setnx(const client::string_type
& key
,
371 const client::string_type
& value
)
373 send_(makecmd("SETNX") << key
<< ' ' << value
.size() << CRLF
<< value
);
374 return recv_int_reply_() == 1;
377 client::int_type
client::incr(const client::string_type
& key
)
379 send_(makecmd("INCR") << key
);
380 return recv_int_reply_();
383 client::int_type
client::incrby(const client::string_type
& key
,
386 send_(makecmd("INCRBY") << key
<< ' ' << by
);
387 return recv_int_reply_();
390 client::int_type
client::decr(const client::string_type
& key
)
392 send_(makecmd("DECR") << key
);
393 return recv_int_reply_();
396 client::int_type
client::decrby(const client::string_type
& key
,
399 send_(makecmd("DECRBY") << key
<< ' ' << by
);
400 return recv_int_reply_();
403 bool client::exists(const client::string_type
& key
)
405 send_(makecmd("EXISTS") << key
);
406 return recv_int_reply_() == 1;
409 void client::del(const client::string_type
& key
)
411 send_(makecmd("DEL") << key
);
412 recv_int_ok_reply_();
415 client::datatype
client::type(const client::string_type
& key
)
417 send_(makecmd("TYPE") << key
);
418 string response
= recv_single_line_reply_();
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
;
425 return datatype_none
;
428 client::int_type
client::keys(const client::string_type
& pattern
,
429 client::string_vector
& out
)
431 send_(makecmd("KEYS") << pattern
);
432 string resp
= recv_bulk_reply_();
433 return split(resp
, ' ', out
);
436 client::string_type
client::randomkey()
438 send_(makecmd("RANDOMKEY", true));
439 return recv_single_line_reply_();
442 void client::rename(const client::string_type
& old_name
,
443 const client::string_type
& new_name
)
445 send_(makecmd("RENAME") << old_name
<< ' ' << new_name
);
449 bool client::renamenx(const client::string_type
& old_name
,
450 const client::string_type
& new_name
)
452 send_(makecmd("RENAMENX") << old_name
<< ' ' << new_name
);
453 return recv_int_reply_() == 1;
456 client::int_type
client::dbsize()
458 send_(makecmd("DBSIZE"));
459 return recv_int_reply_();
462 void client::expire(const string_type
& key
, unsigned int secs
)
464 send_(makecmd("EXPIRE") << key
<< ' ' << secs
);
465 recv_int_ok_reply_();
468 void client::rpush(const client::string_type
& key
,
469 const client::string_type
& value
)
471 send_(makecmd("RPUSH") << key
<< ' ' << value
.length() << CRLF
<< value
);
475 void client::lpush(const client::string_type
& key
,
476 const client::string_type
& value
)
478 send_(makecmd("LPUSH") << key
<< ' ' << value
.length() << CRLF
<< value
);
482 client::int_type
client::llen(const client::string_type
& key
)
484 send_(makecmd("LLEN") << key
);
485 return recv_int_reply_();
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
)
493 send_(makecmd("LRANGE") << key
<< ' ' << start
<< ' ' << end
);
494 return recv_multi_bulk_reply_(out
);
497 void client::ltrim(const client::string_type
& key
,
498 client::int_type start
,
499 client::int_type end
)
501 send_(makecmd("LTRIM") << key
<< ' ' << start
<< ' ' << end
);
505 client::string_type
client::lindex(const client::string_type
& key
,
506 client::int_type index
)
508 send_(makecmd("LINDEX") << key
<< ' ' << index
);
509 return recv_bulk_reply_();
512 void client::lset(const client::string_type
& key
,
513 client::int_type index
,
514 const client::string_type
& value
)
516 send_(makecmd("LSET") << key
<< ' ' << index
<< ' ' << value
.length() << CRLF
<< value
);
520 client::int_type
client::lrem(const client::string_type
& key
,
521 client::int_type count
,
522 const client::string_type
& value
)
524 send_(makecmd("LREM") << key
<< ' ' << count
<< ' ' << value
.length() << CRLF
<< value
);
525 return recv_int_reply_();
528 client::string_type
client::lpop(const client::string_type
& key
)
530 send_(makecmd("LPOP") << key
);
531 return recv_bulk_reply_();
534 client::string_type
client::rpop(const client::string_type
& key
)
536 send_(makecmd("RPOP") << key
);
537 return recv_bulk_reply_();
540 void client::sadd(const client::string_type
& key
,
541 const client::string_type
& value
)
543 send_(makecmd("SADD") << key
<< ' ' << value
.length() << CRLF
<< value
);
544 recv_int_ok_reply_();
547 void client::srem(const client::string_type
& key
,
548 const client::string_type
& value
)
550 send_(makecmd("SREM") << key
<< ' ' << value
.length() << CRLF
<< value
);
551 recv_int_ok_reply_();
554 void client::smove(const client::string_type
& srckey
,
555 const client::string_type
& dstkey
,
556 const client::string_type
& value
)
558 send_(makecmd("SMOVE") << srckey
<< ' ' << dstkey
<< ' ' << value
.length() << CRLF
<< value
);
559 recv_int_ok_reply_();
562 client::int_type
client::scard(const client::string_type
& key
)
564 send_(makecmd("SCARD") << key
);
565 return recv_int_reply_();
568 bool client::sismember(const client::string_type
& key
,
569 const client::string_type
& value
)
571 send_(makecmd("SISMEMBER") << key
<< ' ' << value
.length() << CRLF
<< value
);
572 return recv_int_reply_() == 1;
575 client::int_type
client::sinter(const client::string_vector
& keys
, client::string_set
& out
)
577 send_(makecmd("SINTER") << keys
);
578 return recv_multi_bulk_reply_(out
);
581 void client::sinterstore(const client::string_type
& dstkey
,
582 const client::string_vector
& keys
)
584 send_(makecmd("SINTERSTORE") << dstkey
<< ' ' << keys
);
588 client::int_type
client::sunion(const client::string_vector
& keys
,
589 client::string_set
& out
)
591 send_(makecmd("SUNION") << keys
);
592 return recv_multi_bulk_reply_(out
);
595 void client::sunionstore(const client::string_type
& dstkey
,
596 const client::string_vector
& keys
)
598 send_(makecmd("SUNIONSTORE") << dstkey
<< ' ' << keys
);
602 client::int_type
client::smembers(const client::string_type
& key
,
603 client::string_set
& out
)
605 send_(makecmd("SMEMBERS") << key
);
606 return recv_multi_bulk_reply_(out
);
609 void client::select(client::int_type dbindex
)
611 send_(makecmd("SELECT") << dbindex
);
615 void client::move(const client::string_type
& key
,
616 client::int_type dbindex
)
618 send_(makecmd("MOVE") << key
<< ' ' << dbindex
);
619 recv_int_ok_reply_();
622 void client::flushdb()
624 send_(makecmd("FLUSHDB", true));
628 void client::flushall()
630 send_(makecmd("FLUSHALL", true));
634 client::int_type
client::sort(const client::string_type
& key
,
635 client::string_vector
& out
,
636 client::sort_order order
,
637 bool lexicographically
)
639 send_(makecmd("SORT") << key
640 << (order
== sort_order_ascending
? " ASC" : " DESC")
641 << (lexicographically
? " ALPHA" : ""));
643 return recv_multi_bulk_reply_(out
);
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
)
653 send_(makecmd("SORT") << key
654 << " LIMIT " << limit_start
<< ' ' << limit_end
655 << (order
== sort_order_ascending
? " ASC" : " DESC")
656 << (lexicographically
? " ALPHA" : ""));
658 return recv_multi_bulk_reply_(out
);
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
)
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" : ""));
677 return recv_multi_bulk_reply_(out
);
682 send_(makecmd("SAVE", true));
686 void client::bgsave()
688 send_(makecmd("BGSAVE", true));
692 time_t client::lastsave()
694 send_(makecmd("LASTSAVE", true));
695 return recv_int_reply_();
698 void client::shutdown()
700 send_(makecmd("SHUTDOWN", true));
702 // we expected to get a connection_error as redis closes the connection on shutdown command.
708 catch (connection_error
& e
)
713 void client::info(server_info
& out
)
715 send_(makecmd("INFO", true));
716 string response
= recv_bulk_reply_();
718 if (response
.empty())
719 throw protocol_error("empty");
722 split_lines(response
, lines
);
724 throw protocol_error("empty line for info");
726 for (string_vector::const_iterator it
= lines
.begin();
727 it
!= lines
.end(); ++it
)
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");
735 const string
& key
= line_parts
[0];
736 const string
& val
= line_parts
[1];
738 if (key
== server_info_key_version
)
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
);
761 throw protocol_error(string("unexpected info key '") + key
+ "'");
769 void client::send_(const string
& msg
)
772 output_proto_debug(msg
, false);
775 if (anetWrite(socket_
, const_cast<char *>(msg
.data()), msg
.size()) == -1)
776 throw connection_error(strerror(errno
));
779 string
client::recv_single_line_reply_()
781 string line
= read_line(socket_
);
784 output_proto_debug(line
);
788 throw protocol_error("empty single line reply");
790 if (line
.find(prefix_status_reply_error
) == 0)
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
);
798 if (line
[0] != prefix_status_reply_value
)
799 throw protocol_error("unexpected prefix for status reply");
801 return line
.substr(1);
804 void client::recv_ok_reply_()
806 if (recv_single_line_reply_() != status_reply_ok
)
807 throw protocol_error("expected OK response");
810 client::int_type
client::recv_bulk_reply_(char prefix
)
812 string line
= read_line(socket_
);
815 output_proto_debug(line
);
818 if (line
[0] != prefix
)
819 throw protocol_error("unexpected prefix for bulk reply");
821 return number_from_string(line
.substr(1));
824 string
client::recv_bulk_reply_()
826 int_type length
= recv_bulk_reply_(prefix_single_bulk_reply
);
829 return client::missing_value
;
831 int_type real_length
= length
+ 2; // CRLF
833 string data
= read_n(socket_
, real_length
);
836 output_proto_debug(data
.substr(0, data
.length()-2));
840 throw protocol_error("invalid bulk reply data; empty");
842 if (data
.length() != static_cast<string::size_type
>(real_length
))
843 throw protocol_error("invalid bulk reply data; data of unexpected length");
845 data
.erase(data
.size() - 2);
850 client::int_type
client::recv_multi_bulk_reply_(string_vector
& out
)
852 int_type length
= recv_bulk_reply_(prefix_multi_bulk_reply
);
855 throw key_error("no such key");
857 for (int_type i
= 0; i
< length
; ++i
)
858 out
.push_back(recv_bulk_reply_());
863 client::int_type
client::recv_multi_bulk_reply_(string_set
& out
)
865 int_type length
= recv_bulk_reply_(prefix_multi_bulk_reply
);
868 throw key_error("no such key");
870 for (int_type i
= 0; i
< length
; ++i
)
871 out
.insert(recv_bulk_reply_());
876 client::int_type
client::recv_int_reply_()
878 string line
= read_line(socket_
);
881 output_proto_debug(line
);
885 throw protocol_error("invalid integer reply; empty");
887 if (line
[0] != prefix_int_reply
)
888 throw protocol_error("unexpected prefix for integer reply");
890 return number_from_string(line
.substr(1));
893 void client::recv_int_ok_reply_()
895 if (recv_int_reply_() != 1)
896 throw protocol_error("expecting int reply of 1");