@@ -39,6 +39,89 @@ extern zend_class_entry *redis_ce;
39
39
extern zend_class_entry * redis_exception_ce ;
40
40
extern zend_class_entry * spl_ce_RuntimeException ;
41
41
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
+
42
125
PHP_REDIS_API void redis_stream_close (RedisSock * redis_sock TSRMLS_DC ) {
43
126
if (!redis_sock -> persistent ) {
44
127
php_stream_close (redis_sock -> stream );
@@ -58,64 +141,60 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
58
141
59
142
eof = php_stream_eof (redis_sock -> stream );
60
143
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 */
63
147
redis_stream_close (redis_sock TSRMLS_CC );
64
148
redis_sock -> stream = NULL ;
65
- redis_sock -> mode = ATOMIC ;
149
+ redis_sock -> mode = ATOMIC ;
66
150
redis_sock -> status = REDIS_SOCK_STATUS_FAILED ;
67
151
redis_sock -> watching = 0 ;
68
- }
152
+ }
153
+
69
154
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 ) {
73
160
redis_stream_close (redis_sock TSRMLS_CC );
74
161
redis_sock -> stream = NULL ;
75
- redis_sock -> mode = ATOMIC ;
162
+ redis_sock -> mode = ATOMIC ;
76
163
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
+
84
173
redis_sock_connect (redis_sock TSRMLS_CC ); /* reconnect */
85
174
if (redis_sock -> stream ) { /* check for EOF again. */
86
175
eof = php_stream_eof (redis_sock -> stream );
87
176
}
88
177
}
89
178
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 ) {
99
183
return -1 ;
100
184
}
101
- efree (cmd );
102
185
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 ) {
104
188
return -1 ;
105
189
}
106
-
107
- if (strncmp (response , "+OK" , 3 )) {
108
- efree (response );
109
- return -1 ;
110
- }
111
- efree (response );
112
190
}
113
191
192
+ /* Success */
114
193
return 0 ;
115
194
}
116
195
117
196
118
- PHP_REDIS_API int
197
+ PHP_REDIS_API int
119
198
redis_sock_read_scan_reply (INTERNAL_FUNCTION_PARAMETERS , RedisSock * redis_sock ,
120
199
REDIS_SCAN_TYPE type , long * iter )
121
200
{
@@ -259,14 +338,14 @@ PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len TSRMLS_D
259
338
260
339
switch (inbuf [0 ]) {
261
340
case '-' :
262
- err_len = strlen (inbuf + 1 ) - 2 ;
341
+ /* Set the last error */
342
+ err_len = strlen (inbuf + 1 ) - 2 ;
263
343
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 ;
269
344
345
+ /* Filter our ERROR through the few that should actually throw */
346
+ redis_error_throw (inbuf + 1 , err_len );
347
+
348
+ return NULL ;
270
349
case '$' :
271
350
* buf_len = atoi (inbuf + 1 );
272
351
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, ...) {
432
511
/**
433
512
* This command behave somehow like printf, except that strings need 2 arguments:
434
513
* Their data and their size (strlen).
435
- * Supported formats are: % d, %i, %s, %l
514
+ * Supported formats are:d, %i, %s, %l
436
515
*/
437
516
int
438
517
redis_cmd_format (char * * ret , char * format , ...) {
@@ -1090,7 +1169,7 @@ PHP_REDIS_API void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *
1090
1169
}
1091
1170
1092
1171
/* 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 ,
1094
1173
zval * z_tab , void * ctx )
1095
1174
{
1096
1175
char * resp , * p , * p2 , * p3 , * p4 ;
@@ -1116,7 +1195,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock
1116
1195
while ((p2 = strchr (p , ':' ))!= NULL ) {
1117
1196
/* Null terminate at the ':' */
1118
1197
* p2 ++ = '\0' ;
1119
-
1198
+
1120
1199
/* Null terminate at the space if we have one */
1121
1200
if ((p3 = strchr (p2 , ' ' ))!= NULL ) {
1122
1201
* p3 ++ = '\0' ;
@@ -1138,7 +1217,7 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock
1138
1217
} else {
1139
1218
add_assoc_string (z_result , p , p2 , 1 );
1140
1219
}
1141
-
1220
+
1142
1221
p = p3 ;
1143
1222
}
1144
1223
@@ -1789,7 +1868,6 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_siz
1789
1868
* line_size -= 2 ;
1790
1869
buf [* line_size ]= '\0' ;
1791
1870
1792
-
1793
1871
/* Success! */
1794
1872
return 0 ;
1795
1873
}
@@ -1839,11 +1917,11 @@ redis_read_variant_line(RedisSock *redis_sock, REDIS_REPLY_TYPE reply_type, zval
1839
1917
return -1 ;
1840
1918
}
1841
1919
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. */
1843
1922
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 );
1847
1925
1848
1926
/* Set our last error */
1849
1927
redis_sock_set_err (redis_sock , inbuf , line_size );
0 commit comments