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"
45 #include <sys/errno.h>
46 #include <sys/socket.h>
52 const string
whitespace(" \f\n\r\t\v");
53 const string
CRLF("\r\n");
57 inline string
& rtrim(string
& str
, const string
& ws
= whitespace
)
59 string::size_type pos
= str
.find_last_not_of(ws
);
64 vector
<string
>::size_type
split(const string
& str
, char delim
, vector
<string
> & elems
)
68 vector
<string
>::size_type n
= 0;
69 while (getline(ss
, item
, delim
))
71 elems
.push_back(item
);
77 inline void split_lines(const string
& str
, vector
<string
> & elems
)
79 split(str
, '\n', elems
);
80 for (vector
<string
>::iterator it
= elems
.begin(); it
!= elems
.end(); ++it
)
86 void output_proto_debug(const string
& data
, bool is_received
= true)
88 string
escaped_data(data
);
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");
97 << (is_received
? "RECV '" : "SEND '")
108 explicit makecmd(const string
& initial
, bool finalize
= false)
115 template <typename T
>
116 makecmd
& operator<<(T
const & datum
)
122 template <typename T
>
123 makecmd
& operator<<(const vector
<T
> & data
)
125 size_t n
= data
.size();
126 for (size_t i
= 0; i
< n
; ++i
)
135 operator std::string ()
138 return buffer_
.str();
142 ostringstream buffer_
;
145 // Reads N bytes from given blocking socket.
147 string
read_n(int socket
, ssize_t n
)
149 char * buffer
= new char[n
+ 1];
153 ssize_t bytes_read
= 0;
155 while (bytes_read
!= n
)
157 ssize_t bytes_received
= 0;
158 do bytes_received
= recv(socket
, bp
, n
- (bp
- buffer
), 0);
159 while (bytes_received
< 0 && errno
== EINTR
);
161 if (bytes_received
== 0)
162 throw redis::connection_error("connection was closed");
164 bytes_read
+= bytes_received
;
165 bp
+= bytes_received
;
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
180 string
read_line(int socket
, ssize_t max_size
= 2048)
183 assert(max_size
> 0);
187 enum { buffer_size
= 64 };
188 char buffer
[buffer_size
];
189 memset(buffer
, 0, buffer_size
);
191 ssize_t total_bytes_read
= 0;
192 bool found_delimiter
= false;
194 while (total_bytes_read
< max_size
&& !found_delimiter
)
196 // Peek at what's available.
198 ssize_t bytes_received
= 0;
199 do bytes_received
= recv(socket
, buffer
, buffer_size
, MSG_PEEK
);
200 while (bytes_received
< 0 && errno
== EINTR
);
202 if (bytes_received
== 0)
203 throw redis::connection_error("connection was closed");
205 // Some data is available; Length might be < buffer_size.
206 // Look for newline in whatever was read though.
208 char * eol
= static_cast<char *>(memchr(buffer
, '\n', bytes_received
));
210 // If found, write data from the buffer to the output string.
211 // Else, write the entire buffer and continue reading more data.
213 ssize_t to_read
= bytes_received
;
217 to_read
= eol
- buffer
+ 1;
218 oss
.write(buffer
, to_read
);
219 found_delimiter
= true;
222 oss
.write(buffer
, bytes_received
);
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.
228 do bytes_received
= recv(socket
, buffer
, to_read
, 0);
229 while (bytes_received
< 0 && errno
== EINTR
);
232 // Construct final line string. Remove trailing CRLF-based whitespace.
234 string line
= oss
.str();
235 return rtrim(line
, CRLF
);
238 template <typename T
>
239 T
value_from_string(const string
& data
)
243 istringstream
iss(data
);
246 throw redis::value_error("invalid number");
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
= ':';
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";
271 const string server_info_value_role_master
= "master";
272 const string server_info_value_role_slave
= "slave";
277 redis_error::redis_error(const string
& err
) : err_(err
)
281 redis_error::operator std::string ()
286 redis_error::operator const std::string () const
291 connection_error::connection_error(const string
& err
) : redis_error(err
)
295 protocol_error::protocol_error(const string
& err
) : redis_error(err
)
299 key_error::key_error(const string
& err
) : redis_error(err
)
303 value_error::value_error(const string
& err
) : redis_error(err
)
307 client::string_type
client::missing_value("**nonexistent-key**");
309 client::client(const string_type
& host
, unsigned int port
)
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_
);
320 if (socket_
!= ANET_ERR
)
324 void client::auth(const client::string_type
& pass
)
326 send_(makecmd("AUTH") << pass
);
330 void client::set(const client::string_type
& key
,
331 const client::string_type
& value
)
333 send_(makecmd("SET") << key
<< ' ' << value
.size() << CRLF
<< value
);
337 client::string_type
client::get(const client::string_type
& key
)
339 send_(makecmd("GET") << key
);
340 return recv_bulk_reply_();
343 client::string_type
client::getset(const client::string_type
& key
,
344 const client::string_type
& value
)
346 send_(makecmd("GETSET") << key
<< ' ' << value
.size() << CRLF
<< value
);
347 return recv_bulk_reply_();
350 void client::mget(const client::string_vector
& keys
, string_vector
& out
)
352 send_(makecmd("MGET") << keys
);
353 recv_multi_bulk_reply_(out
);
356 bool client::setnx(const client::string_type
& key
,
357 const client::string_type
& value
)
359 send_(makecmd("SETNX") << key
<< ' ' << value
.size() << CRLF
<< value
);
360 return recv_int_reply_() == 1;
363 client::int_type
client::incr(const client::string_type
& key
)
365 send_(makecmd("INCR") << key
);
366 return recv_int_reply_();
369 client::int_type
client::incrby(const client::string_type
& key
,
372 send_(makecmd("INCRBY") << key
<< ' ' << by
);
373 return recv_int_reply_();
376 client::int_type
client::decr(const client::string_type
& key
)
378 send_(makecmd("DECR") << key
);
379 return recv_int_reply_();
382 client::int_type
client::decrby(const client::string_type
& key
,
385 send_(makecmd("DECRBY") << key
<< ' ' << by
);
386 return recv_int_reply_();
389 bool client::exists(const client::string_type
& key
)
391 send_(makecmd("EXISTS") << key
);
392 return recv_int_reply_() == 1;
395 void client::del(const client::string_type
& key
)
397 send_(makecmd("DEL") << key
);
398 recv_int_ok_reply_();
401 client::datatype
client::type(const client::string_type
& key
)
403 send_(makecmd("TYPE") << key
);
404 string response
= recv_single_line_reply_();
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
;
411 return datatype_none
;
414 client::int_type
client::keys(const client::string_type
& pattern
,
415 client::string_vector
& out
)
417 send_(makecmd("KEYS") << pattern
);
418 string resp
= recv_bulk_reply_();
419 return split(resp
, ' ', out
);
422 client::string_type
client::randomkey()
424 send_(makecmd("RANDOMKEY", true));
425 return recv_single_line_reply_();
428 void client::rename(const client::string_type
& old_name
,
429 const client::string_type
& new_name
)
431 send_(makecmd("RENAME") << old_name
<< ' ' << new_name
);
435 bool client::renamenx(const client::string_type
& old_name
,
436 const client::string_type
& new_name
)
438 send_(makecmd("RENAMENX") << old_name
<< ' ' << new_name
);
439 return recv_int_reply_() == 1;
442 client::int_type
client::dbsize()
444 send_(makecmd("DBSIZE"));
445 return recv_int_reply_();
448 void client::expire(const string_type
& key
, unsigned int secs
)
450 send_(makecmd("EXPIRE") << key
<< ' ' << secs
);
451 recv_int_ok_reply_();
454 void client::rpush(const client::string_type
& key
,
455 const client::string_type
& value
)
457 send_(makecmd("RPUSH") << key
<< ' ' << value
.length() << CRLF
<< value
);
461 void client::lpush(const client::string_type
& key
,
462 const client::string_type
& value
)
464 send_(makecmd("LPUSH") << key
<< ' ' << value
.length() << CRLF
<< value
);
468 client::int_type
client::llen(const client::string_type
& key
)
470 send_(makecmd("LLEN") << key
);
471 return recv_int_reply_();
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
)
479 send_(makecmd("LRANGE") << key
<< ' ' << start
<< ' ' << end
);
480 return recv_multi_bulk_reply_(out
);
483 void client::ltrim(const client::string_type
& key
,
484 client::int_type start
,
485 client::int_type end
)
487 send_(makecmd("LTRIM") << key
<< ' ' << start
<< ' ' << end
);
491 client::string_type
client::lindex(const client::string_type
& key
,
492 client::int_type index
)
494 send_(makecmd("LINDEX") << key
<< ' ' << index
);
495 return recv_bulk_reply_();
498 void client::lset(const client::string_type
& key
,
499 client::int_type index
,
500 const client::string_type
& value
)
502 send_(makecmd("LSET") << key
<< ' ' << index
<< ' ' << value
.length() << CRLF
<< value
);
506 client::int_type
client::lrem(const client::string_type
& key
,
507 client::int_type count
,
508 const client::string_type
& value
)
510 send_(makecmd("LREM") << key
<< ' ' << count
<< ' ' << value
.length() << CRLF
<< value
);
511 return recv_int_reply_();
514 client::string_type
client::lpop(const client::string_type
& key
)
516 send_(makecmd("LPOP") << key
);
517 return recv_bulk_reply_();
520 client::string_type
client::rpop(const client::string_type
& key
)
522 send_(makecmd("RPOP") << key
);
523 return recv_bulk_reply_();
526 void client::sadd(const client::string_type
& key
,
527 const client::string_type
& value
)
529 send_(makecmd("SADD") << key
<< ' ' << value
.length() << CRLF
<< value
);
530 recv_int_ok_reply_();
533 void client::srem(const client::string_type
& key
,
534 const client::string_type
& value
)
536 send_(makecmd("SREM") << key
<< ' ' << value
.length() << CRLF
<< value
);
537 recv_int_ok_reply_();
540 void client::smove(const client::string_type
& srckey
,
541 const client::string_type
& dstkey
,
542 const client::string_type
& value
)
544 send_(makecmd("SMOVE") << srckey
<< ' ' << dstkey
<< ' ' << value
.length() << CRLF
<< value
);
545 recv_int_ok_reply_();
548 client::int_type
client::scard(const client::string_type
& key
)
550 send_(makecmd("SCARD") << key
);
551 return recv_int_reply_();
554 bool client::sismember(const client::string_type
& key
,
555 const client::string_type
& value
)
557 send_(makecmd("SISMEMBER") << key
<< ' ' << value
.length() << CRLF
<< value
);
558 return recv_int_reply_() == 1;
561 client::int_type
client::sinter(const client::string_vector
& keys
, client::string_set
& out
)
563 send_(makecmd("SINTER") << keys
);
564 return recv_multi_bulk_reply_(out
);
567 client::int_type
client::sinterstore(const client::string_type
& dstkey
,
568 const client::string_vector
& keys
)
570 send_(makecmd("SINTERSTORE") << dstkey
<< ' ' << keys
);
571 return recv_int_reply_();
574 client::int_type
client::sunion(const client::string_vector
& keys
,
575 client::string_set
& out
)
577 send_(makecmd("SUNION") << keys
);
578 return recv_multi_bulk_reply_(out
);
581 client::int_type
client::sunionstore(const client::string_type
& dstkey
,
582 const client::string_vector
& keys
)
584 send_(makecmd("SUNIONSTORE") << dstkey
<< ' ' << keys
);
585 return recv_int_reply_();
588 client::int_type
client::smembers(const client::string_type
& key
,
589 client::string_set
& out
)
591 send_(makecmd("SMEMBERS") << key
);
592 return recv_multi_bulk_reply_(out
);
595 void client::select(client::int_type dbindex
)
597 send_(makecmd("SELECT") << dbindex
);
601 void client::move(const client::string_type
& key
,
602 client::int_type dbindex
)
604 send_(makecmd("MOVE") << key
<< ' ' << dbindex
);
605 recv_int_ok_reply_();
608 void client::flushdb()
610 send_(makecmd("FLUSHDB", true));
614 void client::flushall()
616 send_(makecmd("FLUSHALL", true));
620 client::int_type
client::sort(const client::string_type
& key
,
621 client::string_vector
& out
,
622 client::sort_order order
,
623 bool lexicographically
)
625 send_(makecmd("SORT") << key
626 << (order
== sort_order_ascending
? " ASC" : " DESC")
627 << (lexicographically
? " ALPHA" : ""));
629 return recv_multi_bulk_reply_(out
);
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
)
639 send_(makecmd("SORT") << key
640 << " LIMIT " << limit_start
<< ' ' << limit_end
641 << (order
== sort_order_ascending
? " ASC" : " DESC")
642 << (lexicographically
? " ALPHA" : ""));
644 return recv_multi_bulk_reply_(out
);
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
)
659 << " BY " << by_pattern
660 << " LIMIT " << limit_start
<< ' ' << limit_end
;
662 client::string_vector::const_iterator it
= get_patterns
.begin();
663 for ( ; it
!= get_patterns
.end(); ++it
)
666 m
<< (order
== sort_order_ascending
? " ASC" : " DESC")
667 << (lexicographically
? " ALPHA" : "");
671 return recv_multi_bulk_reply_(out
);
676 send_(makecmd("SAVE", true));
680 void client::bgsave()
682 send_(makecmd("BGSAVE", true));
686 time_t client::lastsave()
688 send_(makecmd("LASTSAVE", true));
689 return recv_int_reply_();
692 void client::shutdown()
694 send_(makecmd("SHUTDOWN", true));
696 // we expected to get a connection_error as redis closes the connection on shutdown command.
702 catch (connection_error
& e
)
707 void client::info(server_info
& out
)
709 send_(makecmd("INFO", true));
710 string response
= recv_bulk_reply_();
712 if (response
.empty())
713 throw protocol_error("empty");
716 split_lines(response
, lines
);
718 throw protocol_error("empty line for info");
720 for (string_vector::const_iterator it
= lines
.begin();
721 it
!= lines
.end(); ++it
)
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");
729 const string
& key
= line_parts
[0];
730 const string
& val
= line_parts
[1];
732 if (key
== server_info_key_version
)
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
;
757 throw protocol_error(string("unexpected info key '") + key
+ "'");
765 void client::send_(const string
& msg
)
768 output_proto_debug(msg
, false);
771 if (anetWrite(socket_
, const_cast<char *>(msg
.data()), msg
.size()) == -1)
772 throw connection_error(strerror(errno
));
775 string
client::recv_single_line_reply_()
777 string line
= read_line(socket_
);
780 output_proto_debug(line
);
784 throw protocol_error("empty single line reply");
786 if (line
.find(prefix_status_reply_error
) == 0)
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
);
794 if (line
[0] != prefix_status_reply_value
)
795 throw protocol_error("unexpected prefix for status reply");
797 return line
.substr(1);
800 void client::recv_ok_reply_()
802 if (recv_single_line_reply_() != status_reply_ok
)
803 throw protocol_error("expected OK response");
806 client::int_type
client::recv_bulk_reply_(char prefix
)
808 string line
= read_line(socket_
);
811 output_proto_debug(line
);
814 if (line
[0] != prefix
)
815 throw protocol_error("unexpected prefix for bulk reply");
817 return value_from_string
<client::int_type
>(line
.substr(1));
820 string
client::recv_bulk_reply_()
822 int_type length
= recv_bulk_reply_(prefix_single_bulk_reply
);
825 return client::missing_value
;
827 int_type real_length
= length
+ 2; // CRLF
829 string data
= read_n(socket_
, real_length
);
832 output_proto_debug(data
.substr(0, data
.length()-2));
836 throw protocol_error("invalid bulk reply data; empty");
838 if (data
.length() != static_cast<string::size_type
>(real_length
))
839 throw protocol_error("invalid bulk reply data; data of unexpected length");
841 data
.erase(data
.size() - 2);
846 client::int_type
client::recv_multi_bulk_reply_(string_vector
& out
)
848 int_type length
= recv_bulk_reply_(prefix_multi_bulk_reply
);
851 throw key_error("no such key");
853 for (int_type i
= 0; i
< length
; ++i
)
854 out
.push_back(recv_bulk_reply_());
859 client::int_type
client::recv_multi_bulk_reply_(string_set
& out
)
861 int_type length
= recv_bulk_reply_(prefix_multi_bulk_reply
);
864 throw key_error("no such key");
866 for (int_type i
= 0; i
< length
; ++i
)
867 out
.insert(recv_bulk_reply_());
872 client::int_type
client::recv_int_reply_()
874 string line
= read_line(socket_
);
877 output_proto_debug(line
);
881 throw protocol_error("invalid integer reply; empty");
883 if (line
[0] != prefix_int_reply
)
884 throw protocol_error("unexpected prefix for integer reply");
886 return value_from_string
<client::int_type
>(line
.substr(1));
889 void client::recv_int_ok_reply_()
891 if (recv_int_reply_() != 1)
892 throw protocol_error("expecting int reply of 1");