Skip to content

Commit 7723420

Browse files
committed
feat: add response handling for various client commands with detailed error checks
1 parent c201cf6 commit 7723420

File tree

1 file changed

+71
-2
lines changed

1 file changed

+71
-2
lines changed

redis/connection.py

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,8 +409,6 @@ def on_connect(self):
409409
# if client caching is enabled, start tracking
410410
if self.client_cache:
411411
self.send_command("CLIENT", "TRACKING", "ON")
412-
self.read_response()
413-
self._parser.set_invalidation_push_handler(self._cache_invalidation_process)
414412

415413
# execute the MULTI block
416414
try:
@@ -447,6 +445,77 @@ def _read_exec_responses(self):
447445
raise ConnectionError(f"EXEC command did not return a list: {response}")
448446
return response
449447

448+
def _handle_responses(self, responses, auth_args):
449+
if not isinstance(responses, list):
450+
raise ConnectionError(f"EXEC command did not return a list: {responses}")
451+
452+
response_iter = iter(responses)
453+
454+
try:
455+
# handle HELLO + AUTH
456+
if auth_args and self.protocol not in [2, "2"]:
457+
response = next(response_iter, None)
458+
if isinstance(response, dict) and (
459+
response.get(b"proto") != self.protocol and response.get("proto") != self.protocol):
460+
raise ConnectionError("Invalid RESP version")
461+
462+
response = next(response_iter, None)
463+
if isinstance(response, bytes) and str_if_bytes(response) != "OK":
464+
raise AuthenticationError("Invalid Username or Password")
465+
elif auth_args:
466+
response = next(response_iter, None)
467+
if isinstance(response, bytes) and str_if_bytes(response) != "OK":
468+
try:
469+
# a username and password were specified but the Redis
470+
# server seems to be < 6.0.0 which expects a single password
471+
# arg. retry auth with just the password.
472+
# https://github.com/andymccurdy/redis-py/issues/1274
473+
self.send_command("AUTH", auth_args[-1], check_health=False)
474+
auth_response = self.read_response()
475+
if isinstance(auth_response, bytes) and str_if_bytes(
476+
auth_response) != "OK":
477+
raise AuthenticationError("Invalid Username or Password")
478+
# add the retry response to the responses list for further processing
479+
responses = [auth_response] + list(response_iter)
480+
response_iter = iter(responses)
481+
except AuthenticationWrongNumberOfArgsError:
482+
raise AuthenticationError("Invalid Username or Password")
483+
484+
# handle CLIENT SETNAME
485+
if self.client_name:
486+
response = next(response_iter, None)
487+
if isinstance(response, bytes) and str_if_bytes(response) != "OK":
488+
raise ConnectionError("Error setting client name")
489+
490+
# handle CLIENT SETINFO LIB-NAME
491+
if self.lib_name:
492+
response = next(response_iter, None)
493+
if isinstance(response, bytes) and str_if_bytes(response) != "OK":
494+
raise ConnectionError("Error setting client library name")
495+
496+
# handle CLIENT SETINFO LIB-VER
497+
if self.lib_version:
498+
response = next(response_iter, None)
499+
if isinstance(response, bytes) and str_if_bytes(response) != "OK":
500+
raise ConnectionError("Error setting client library version")
501+
502+
# handle SELECT
503+
if self.db:
504+
response = next(response_iter, None)
505+
if isinstance(response, bytes) and str_if_bytes(response) != "OK":
506+
raise ConnectionError("Invalid Database")
507+
508+
# handle CLIENT TRACKING ON
509+
if self.client_cache:
510+
response = next(response_iter, None)
511+
if isinstance(response, bytes) and str_if_bytes(response) != "OK":
512+
raise ConnectionError("Error enabling client tracking")
513+
self._parser.set_invalidation_push_handler(
514+
self._cache_invalidation_process)
515+
except (AuthenticationError, ConnectionError):
516+
raise
517+
except Exception as e:
518+
raise ConnectionError("Error during response handling") from e
450519

451520
def disconnect(self, *args):
452521
"Disconnects from the Redis server"

0 commit comments

Comments
 (0)