@@ -39,6 +39,89 @@ extern zend_class_entry *redis_ce;
3939extern zend_class_entry * redis_exception_ce ;
4040extern zend_class_entry * spl_ce_RuntimeException ;
4141
42+ /* Helper to reselect the proper DB number when we reconnect */
43+ static int reselect_db (RedisSock * redis_sock TSRMLS_DC ) {
44+ char * cmd , * response ;
45+ int cmd_len , response_len ;
46+
47+ cmd_len = redis_cmd_format_static (& cmd , "SELECT" , "d" , redis_sock -> dbNumber );
48+
49+ if (redis_sock_write (redis_sock , cmd , cmd_len TSRMLS_CC ) < 0 ) {
50+ efree (cmd );
51+ return -1 ;
52+ }
53+
54+ efree (cmd );
55+
56+ if ((response = redis_sock_read (redis_sock , & response_len TSRMLS_CC )) == NULL ) {
57+ return -1 ;
58+ }
59+
60+ if (strncmp (response , "+OK" , 3 )) {
61+ efree (response );
62+ return -1 ;
63+ }
64+
65+ efree (response );
66+ return 0 ;
67+ }
68+
69+ /* Helper to resend AUTH <password> in the case of a reconnect */
70+ static int resend_auth (RedisSock * redis_sock TSRMLS_DC ) {
71+ char * cmd , * response ;
72+ int cmd_len , response_len ;
73+
74+ cmd_len = redis_cmd_format_static (& cmd , "AUTH" , "s" , redis_sock -> auth ,
75+ strlen (redis_sock -> auth ));
76+
77+ if (redis_sock_write (redis_sock , cmd , cmd_len TSRMLS_DC ) < 0 ) {
78+ efree (cmd );
79+ return -1 ;
80+ }
81+
82+ efree (cmd );
83+
84+ response = redis_sock_read (redis_sock , & response_len TSRMLS_CC );
85+ if (response == NULL ) {
86+ return -1 ;
87+ }
88+
89+ if (strncmp (response , "+OK" , 3 )) {
90+ efree (response );
91+ return -1 ;
92+ }
93+
94+ efree (response );
95+ return 0 ;
96+ }
97+
98+ /* Helper function that will throw an exception for a small number of ERR codes
99+ * returned by Redis. Typically we just return FALSE to the caller in the event
100+ * of an ERROR reply, but for the following error types:
101+ * 1) MASTERDOWN
102+ * 2) AUTH
103+ * 3) LOADING
104+ */
105+ static void redis_error_throw (char * err , size_t err_len TSRMLS_DC ) {
106+ /* Handle stale data error (slave syncing with master) */
107+ if (err_len == sizeof (REDIS_ERR_SYNC_MSG ) - 1 &&
108+ !memcmp (err ,REDIS_ERR_SYNC_KW ,sizeof (REDIS_ERR_SYNC_KW )- 1 ))
109+ {
110+ zend_throw_exception (redis_exception_ce ,
111+ "SYNC with master in progress or master down!" , 0 TSRMLS_CC );
112+ } else if (err_len == sizeof (REDIS_ERR_LOADING_MSG ) - 1 &&
113+ !memcmp (err ,REDIS_ERR_LOADING_KW ,sizeof (REDIS_ERR_LOADING_KW )- 1 ))
114+ {
115+ zend_throw_exception (redis_exception_ce ,
116+ "Redis is LOADING the dataset" , 0 TSRMLS_CC );
117+ } else if (err_len == sizeof (REDIS_ERR_AUTH_MSG ) - 1 &&
118+ !memcmp (err ,REDIS_ERR_AUTH_KW ,sizeof (REDIS_ERR_AUTH_KW )- 1 ))
119+ {
120+ zend_throw_exception (redis_exception_ce ,
121+ "Failed to AUTH connection" , 0 TSRMLS_CC );
122+ }
123+ }
124+
42125PHP_REDIS_API void redis_stream_close (RedisSock * redis_sock TSRMLS_DC ) {
43126 if (!redis_sock -> persistent ) {
44127 php_stream_close (redis_sock -> stream );
@@ -58,64 +141,60 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
58141
59142 eof = php_stream_eof (redis_sock -> stream );
60143 for (; eof ; count ++ ) {
61- if ((MULTI == redis_sock -> mode ) || redis_sock -> watching || count == 10 ) { /* too many failures */
62- if (redis_sock -> stream ) { /* close stream if still here */
144+ /* Only try up to a certain point */
145+ if ((MULTI == redis_sock -> mode ) || redis_sock -> watching || count == 10 ) {
146+ if (redis_sock -> stream ) { /* close stream if still here */
63147 redis_stream_close (redis_sock TSRMLS_CC );
64148 redis_sock -> stream = NULL ;
65- redis_sock -> mode = ATOMIC ;
149+ redis_sock -> mode = ATOMIC ;
66150 redis_sock -> status = REDIS_SOCK_STATUS_FAILED ;
67151 redis_sock -> watching = 0 ;
68- }
152+ }
153+
69154 zend_throw_exception (redis_exception_ce , "Connection lost" , 0 TSRMLS_CC );
70- return -1 ;
71- }
72- if (redis_sock -> stream ) { /* close existing stream before reconnecting */
155+ return -1 ;
156+ }
157+
158+ /* Close existing stream before reconnecting */
159+ if (redis_sock -> stream ) {
73160 redis_stream_close (redis_sock TSRMLS_CC );
74161 redis_sock -> stream = NULL ;
75- redis_sock -> mode = ATOMIC ;
162+ redis_sock -> mode = ATOMIC ;
76163 redis_sock -> watching = 0 ;
77- }
78- /* Wait for a while before trying to reconnect */
79- if (redis_sock -> retry_interval ) {
80- // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time
81- long retry_interval = (count ? redis_sock -> retry_interval : (php_rand (TSRMLS_C ) % redis_sock -> retry_interval ));
82- usleep (retry_interval );
83- }
164+ }
165+
166+ /* Wait for a while before trying to reconnect */
167+ if (redis_sock -> retry_interval ) {
168+ // Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time
169+ long retry_interval = (count ? redis_sock -> retry_interval : (php_rand (TSRMLS_C ) % redis_sock -> retry_interval ));
170+ usleep (retry_interval );
171+ }
172+
84173 redis_sock_connect (redis_sock TSRMLS_CC ); /* reconnect */
85174 if (redis_sock -> stream ) { /* check for EOF again. */
86175 eof = php_stream_eof (redis_sock -> stream );
87176 }
88177 }
89178
90- /* Reselect the DB. */
91- if (count && redis_sock -> dbNumber ) {
92- char * cmd , * response ;
93- int cmd_len , response_len ;
94-
95- cmd_len = redis_cmd_format_static (& cmd , "SELECT" , "d" , redis_sock -> dbNumber );
96-
97- if (redis_sock_write (redis_sock , cmd , cmd_len TSRMLS_CC ) < 0 ) {
98- efree (cmd );
179+ /* We've reconnected if we have a count */
180+ if (count ) {
181+ /* If we're using a password, attempt a reauthorization */
182+ if (redis_sock -> auth && resend_auth (redis_sock ) != 0 ) {
99183 return -1 ;
100184 }
101- efree (cmd );
102185
103- if ((response = redis_sock_read (redis_sock , & response_len TSRMLS_CC )) == NULL ) {
186+ /* If we're using a non zero db, reselect it */
187+ if (redis_sock -> dbNumber && reselect_db (redis_sock ) != 0 ) {
104188 return -1 ;
105189 }
106-
107- if (strncmp (response , "+OK" , 3 )) {
108- efree (response );
109- return -1 ;
110- }
111- efree (response );
112190 }
113191
192+ /* Success */
114193 return 0 ;
115194}
116195
117196
118- PHP_REDIS_API int
197+ PHP_REDIS_API int
119198redis_sock_read_scan_reply (INTERNAL_FUNCTION_PARAMETERS , RedisSock * redis_sock ,
120199 REDIS_SCAN_TYPE type , long * iter )
121200{
@@ -259,14 +338,14 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D
259338
260339 switch (inbuf [0 ]) {
261340 case '-' :
262- err_len = strlen (inbuf + 1 ) - 2 ;
341+ /* Set the last error */
342+ err_len = strlen (inbuf + 1 ) - 2 ;
263343 redis_sock_set_err (redis_sock , inbuf + 1 , err_len );
264- /* stale data */
265- if (memcmp (inbuf + 1 , "-ERR SYNC " , 10 ) == 0 ) {
266- zend_throw_exception (redis_exception_ce , "SYNC with master in progress" , 0 TSRMLS_CC );
267- }
268- return NULL ;
269344
345+ /* Filter our ERROR through the few that should actually throw */
346+ redis_error_throw (inbuf + 1 , err_len );
347+
348+ return NULL ;
270349 case '$' :
271350 * buf_len = atoi (inbuf + 1 );
272351 resp = redis_sock_read_bulk_reply (redis_sock , * buf_len TSRMLS_CC );
@@ -432,7 +511,7 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) {
432511/**
433512 * This command behave somehow like printf, except that strings need 2 arguments:
434513 * Their data and their size (strlen).
435- * Supported formats are: % d, %i, %s, %l
514+ * Supported formats are:d, %i, %s, %l
436515 */
437516int
438517redis_cmd_format (char * * ret , char * format , ...) {
@@ -1090,7 +1169,7 @@ PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *
10901169}
10911170
10921171/* Response for DEBUG object which is a formatted single line reply */
1093- PHP_REDIS_API void redis_debug_response (INTERNAL_FUNCTION_PARAMETERS , RedisSock * redis_sock ,
1172+ PHP_REDIS_API void redis_debug_response (INTERNAL_FUNCTION_PARAMETERS , RedisSock * redis_sock ,
10941173 zval * z_tab , void * ctx )
10951174{
10961175 char * resp , * p , * p2 , * p3 , * p4 ;
@@ -1116,7 +1195,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock
11161195 while ((p2 = strchr (p , ':' ))!= NULL ) {
11171196 /* Null terminate at the ':' */
11181197 * p2 ++ = '\0' ;
1119-
1198+
11201199 /* Null terminate at the space if we have one */
11211200 if ((p3 = strchr (p2 , ' ' ))!= NULL ) {
11221201 * p3 ++ = '\0' ;
@@ -1138,7 +1217,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock
11381217 } else {
11391218 add_assoc_string (z_result , p , p2 , 1 );
11401219 }
1141-
1220+
11421221 p = p3 ;
11431222 }
11441223
@@ -1789,7 +1868,6 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_siz
17891868 * line_size -= 2 ;
17901869 buf [* line_size ]= '\0' ;
17911870
1792-
17931871 /* Success! */
17941872 return 0 ;
17951873}
@@ -1839,11 +1917,11 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval
18391917 return -1 ;
18401918 }
18411919
1842- /* If this is an error response, check if it is a SYNC error, and throw in that case */
1920+ /* If this is an error response, filter specific errors that should throw
1921+ * an exception, and set our error field in our RedisSock object. */
18431922 if (reply_type == TYPE_ERR ) {
1844- if (memcmp (inbuf , "ERR SYNC" , 9 ) == 0 ) {
1845- zend_throw_exception (redis_exception_ce , "SYNC with master in progress" , 0 TSRMLS_CC );
1846- }
1923+ /* Handle throwable errors */
1924+ redis_error_throw (inbuf , line_size TSRMLS_CC );
18471925
18481926 /* Set our last error */
18491927 redis_sock_set_err (redis_sock , inbuf , line_size );
0 commit comments