Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/StackExchange.Redis/ExceptionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ internal static Exception CommandDisabled(string command)
internal static Exception TooManyArgs(string command, int argCount)
=> new RedisCommandException($"This operation would involve too many arguments ({argCount + 1} vs the redis limit of {MessageWriter.REDIS_MAX_ARGS}): {command}");

internal static Exception CommandHasWhitespace(string command)
=> new RedisCommandException($"The command '{command}' contains whitespace and would be sent as a single unknown token; pass each word as a separate argument, for example Execute(\"ACL\", \"SETUSER\", \"x\") rather than Execute(\"ACL SETUSER x\").");

internal static Exception ConnectionFailure(bool includeDetail, ConnectionFailureType failureType, string message, ServerEndPoint? server)
{
var ex = new RedisConnectionException(failureType, message);
Expand Down
5 changes: 5 additions & 0 deletions src/StackExchange.Redis/RedisDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5549,6 +5549,11 @@ public ExecuteMessage(CommandMap? map, int db, CommandFlags flags, string comman
throw ExceptionFactory.TooManyArgs(command, args.Count);
}

// a redis command token never contains space, so a command like
// "ACL SETUSER x" is always a caller mistake (it gets sent as one unknown token
// and the server replies with an opaque error); fail fast with actionable guidance
if (command.IndexOf(' ') >= 0) throw ExceptionFactory.CommandHasWhitespace(command);

map ??= CommandMap.Default;
_unknownCommand = "";
if (Command is RedisCommand.UNKNOWN)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,26 @@ public async Task EchoRoundTripTest(MapMode mode, string payload, string request
}
}

[Theory(Timeout = 1000)]
[InlineData("ACL SETUSER x")]
[InlineData("get key")]
public void CommandWithWhitespaceThrows(string command)
{
object[] args = [];
var ex = Assert.Throws<RedisCommandException>(
() => new RedisDatabase.ExecuteMessage(CommandMap.Default, -1, CommandFlags.None, command, args));
Assert.Contains("whitespace", ex.Message);
}

[Fact(Timeout = 1000)]
public void SingleTokenCommandDoesNotThrow()
{
// the correct token-per-argument form must still be accepted unchanged
object[] args = ["SETUSER", "x"];
var msg = new RedisDatabase.ExecuteMessage(CommandMap.Default, -1, CommandFlags.None, "ACL", args);
Assert.Equal("ACL", msg.CommandString);
}

private static CommandMap? GetMap(MapMode mode) => mode switch
{
MapMode.Null => null,
Expand Down
Loading