// internally, this is very similar to RawResult, except it is designed to be usable // outside of the IO-processing pipeline: the buffers are standalone, etc internal static RedisResult TryCreate(PhysicalConnection connection, RawResult result) { try { switch (result.Type) { case ResultType.Integer: case ResultType.SimpleString: case ResultType.BulkString: return new SingleRedisResult(result.AsRedisValue()); case ResultType.MultiBulk: var items = result.GetItems(); var arr = new RedisResult[items.Length]; for (int i = 0; i < arr.Length; i++) { var next = TryCreate(connection, items[i]); if (next == null) return null; // means we didn't understand arr[i] = next; } return new ArrayRedisResult(arr); case ResultType.Error: return new ErrorRedisResult(result.GetString()); default: return null; } } catch(Exception ex) { connection?.OnInternalError(ex); return null; // will be logged as a protocol fail by the processor } }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { var tran = message as TransactionMessage; if (tran != null) { var bridge = connection.Bridge; var wrapped = tran.InnerOperations; switch (result.Type) { case ResultType.SimpleString: if (tran.IsAborted && result.IsEqual(RedisLiterals.BytesOK)) { connection.Multiplexer.Trace("Acknowledging UNWATCH (aborted electively)"); SetResult(message, false); return(true); } break; case ResultType.MultiBulk: if (!tran.IsAborted) { var arr = result.GetItems(); if (arr == null) { connection.Multiplexer.Trace("Server aborted due to failed WATCH"); foreach (var op in wrapped) { op.Wrapped.Cancel(); bridge.CompleteSyncOrAsync(op.Wrapped); } SetResult(message, false); return(true); } else if (wrapped.Length == arr.Length) { connection.Multiplexer.Trace("Server committed; processing nested replies"); for (int i = 0; i < arr.Length; i++) { if (wrapped[i].Wrapped.ComputeResult(connection, arr[i])) { bridge.CompleteSyncOrAsync(wrapped[i].Wrapped); } } SetResult(message, true); return(true); } } break; } // even if we didn't fully understand the result, we still need to do something with // the pending tasks foreach (var op in wrapped) { op.Wrapped.Fail(ConnectionFailureType.ProtocolFailure, null); bridge.CompleteSyncOrAsync(op.Wrapped); } } return(false); }
public bool TryParse(RawResult result, out T[] pairs) { switch (result.Type) { case ResultType.MultiBulk: var arr = result.GetItems(); if (arr == null) { pairs = null; } else { int count = arr.Length / 2; if (count == 0) { pairs = nix; } else { pairs = new T[count]; int offset = 0; for (int i = 0; i < pairs.Length; i++) { pairs[i] = Parse(arr[offset++], arr[offset++]); } } } return(true); default: pairs = null; return(false); } }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { switch (result.Type) { case ResultType.MultiBulk: var arr = result.GetItems(); RedisChannel[] final; if (arr.Length == 0) { final = RedisChannel.EmptyArray; } else { final = new RedisChannel[arr.Length]; byte[] channelPrefix = connection.ChannelPrefix; for (int i = 0; i < final.Length; i++) { final[i] = result.AsRedisChannel(channelPrefix); } } SetResult(message, final); return(true); } return(false); }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { if (result.Type == ResultType.MultiBulk) { var items = result.GetItems(); long count; if (items.Length >= 3 && items[2].TryGetInt64(out count)) { connection.SubscriptionCount = count; return(true); } } return(false); }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { switch (result.Type) { case ResultType.MultiBulk: var arr = result.GetItems(); long i64; if (arr.Length == 2 && arr[1].Type == ResultType.MultiBulk && arr[0].TryGetInt64(out i64)) { var keysResult = new ScanResult(i64, arr[1].GetItemsAsKeys()); SetResult(message, keysResult); return(true); } break; } return(false); }
// internally, this is very similar to RawResult, except it is designed to be usable // outside of the IO-processing pipeline: the buffers are standalone, etc internal static RedisResult TryCreate(PhysicalConnection connection, RawResult result) { try { switch (result.Type) { case ResultType.Integer: case ResultType.SimpleString: case ResultType.BulkString: return(new SingleRedisResult(result.AsRedisValue(), result.Type)); case ResultType.MultiBulk: if (result.IsNull) { return(NullArray); } var items = result.GetItems(); if (items.Length == 0) { return(EmptyArray); } var arr = new RedisResult[items.Length]; for (int i = 0; i < arr.Length; i++) { var next = TryCreate(connection, items[i]); if (next == null) { return(null); // means we didn't understand } arr[i] = next; } return(new ArrayRedisResult(arr)); case ResultType.Error: return(new ErrorRedisResult(result.GetString())); default: return(null); } } catch (Exception ex) { connection?.OnInternalError(ex); return(null); // will be logged as a protocol fail by the processor } }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { long unixTime, micros; switch (result.Type) { case ResultType.Integer: if (result.TryGetInt64(out unixTime)) { var time = RedisBase.UnixEpoch.AddSeconds(unixTime); SetResult(message, time); return(true); } break; case ResultType.MultiBulk: var arr = result.GetItems(); switch (arr.Length) { case 1: if (arr[0].TryGetInt64(out unixTime)) { var time = RedisBase.UnixEpoch.AddSeconds(unixTime); SetResult(message, time); return(true); } break; case 2: if (arr[0].TryGetInt64(out unixTime) && arr[1].TryGetInt64(out micros)) { var time = RedisBase.UnixEpoch.AddSeconds(unixTime).AddTicks(micros * 10); // datetime ticks are 100ns SetResult(message, time); return(true); } break; } break; } return(false); }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { bool happy; switch (message.Command) { case RedisCommand.ECHO: happy = result.Type == ResultType.BulkString && (!establishConnection || result.IsEqual(connection.Multiplexer.UniqueId)); break; case RedisCommand.PING: happy = result.Type == ResultType.SimpleString && result.IsEqual(RedisLiterals.BytesPONG); break; case RedisCommand.TIME: happy = result.Type == ResultType.MultiBulk && result.GetItems().Length == 2; break; case RedisCommand.EXISTS: happy = result.Type == ResultType.Integer; break; default: happy = true; break; } if (happy) { if (establishConnection) { connection.Bridge.OnFullyEstablished(connection); } SetResult(message, happy); return(true); } else { connection.RecordConnectionFailed(ConnectionFailureType.ProtocolFailure); return(false); } }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { switch (result.Type) { case ResultType.MultiBulk: var parts = result.GetItems(); CommandTrace[] arr = new CommandTrace[parts.Length]; for (int i = 0; i < parts.Length; i++) { var subParts = parts[i].GetItems(); if (!subParts[0].TryGetInt64(out long uniqueid) || !subParts[1].TryGetInt64(out long time) || !subParts[2].TryGetInt64(out long duration)) { return(false); } arr[i] = new CommandTrace(uniqueid, time, duration, subParts[3].GetItemsAsValues()); } SetResult(message, arr); return(true); } return(false); }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { if (result.IsNull) { SetResult(message, false); // lots of ops return (nil) when they mean "no" return(true); } switch (result.Type) { case ResultType.SimpleString: if (result.IsEqual(RedisLiterals.BytesOK)) { SetResult(message, true); } else { SetResult(message, result.GetBoolean()); } return(true); case ResultType.Integer: case ResultType.BulkString: SetResult(message, result.GetBoolean()); return(true); case ResultType.MultiBulk: var items = result.GetItems(); if (items.Length == 1) { // treat an array of 1 like a single reply (for example, SCRIPT EXISTS) SetResult(message, items[0].GetBoolean()); return(true); } break; } return(false); }
void MatchResult(RawResult result) { // check to see if it could be an out-of-band pubsub message if (connectionType == ConnectionType.Subscription && result.Type == ResultType.MultiBulk) { // out of band message does not match to a queued message var items = result.GetItems(); if (items.Length >= 3 && items[0].IsEqual(message)) { // special-case the configuration change broadcasts (we don't keep that in the usual pub/sub registry) var configChanged = Multiplexer.ConfigurationChangedChannel; if (configChanged != null && items[1].IsEqual(configChanged)) { EndPoint blame = null; try { if (!items[2].IsEqual(RedisLiterals.ByteWildcard)) { blame = Format.TryParseEndPoint(items[2].GetString()); } } catch { /* no biggie */ } Multiplexer.Trace("Configuration changed: " + Format.ToString(blame), physicalName); Multiplexer.ReconfigureIfNeeded(blame, true, "broadcast"); } // invoke the handlers var channel = items[1].AsRedisChannel(ChannelPrefix, RedisChannel.PatternMode.Literal); Multiplexer.Trace("MESSAGE: " + channel, physicalName); if (!channel.IsNull) { Multiplexer.OnMessage(channel, channel, items[2].AsRedisValue()); } return; // AND STOP PROCESSING! } else if (items.Length >= 4 && items[0].IsEqual(pmessage)) { var channel = items[2].AsRedisChannel(ChannelPrefix, RedisChannel.PatternMode.Literal); Multiplexer.Trace("PMESSAGE: " + channel, physicalName); if (!channel.IsNull) { var sub = items[1].AsRedisChannel(ChannelPrefix, RedisChannel.PatternMode.Pattern); Multiplexer.OnMessage(sub, channel, items[3].AsRedisValue()); } return; // AND STOP PROCESSING! } // if it didn't look like "[p]message", then we still need to process the pending queue } Multiplexer.Trace("Matching result...", physicalName); Message msg; lock (outstanding) { Multiplexer.Trace(outstanding.Count == 0, "Nothing to respond to!", physicalName); msg = outstanding.Dequeue(); } Multiplexer.Trace("Response to: " + msg.ToString(), physicalName); if (msg.ComputeResult(this, result)) { Bridge.CompleteSyncOrAsync(msg); } }
void MatchResult(RawResult result) { // check to see if it could be an out-of-band pubsub message if (connectionType == ConnectionType.Subscription && result.Type == ResultType.MultiBulk) { // out of band message does not match to a queued message var items = result.GetItems(); if (items.Length >= 3 && items[0].IsEqual(message)) { // special-case the configuration change broadcasts (we don't keep that in the usual pub/sub registry) var configChanged = multiplexer.ConfigurationChangedChannel; if (configChanged != null && items[1].IsEqual(configChanged)) { EndPoint blame = null; try { if (!items[2].IsEqual(RedisLiterals.ByteWildcard)) { blame = Format.TryParseEndPoint(items[2].GetString()); } } catch { /* no biggie */ } multiplexer.Trace("Configuration changed: " + Format.ToString(blame), physicalName); multiplexer.ReconfigureIfNeeded(blame, true, "broadcast"); } // invoke the handlers var channel = items[1].AsRedisChannel(ChannelPrefix, RedisChannel.PatternMode.Literal); multiplexer.Trace("MESSAGE: " + channel, physicalName); if (!channel.IsNull) { multiplexer.OnMessage(channel, channel, items[2].AsRedisValue()); } return; // AND STOP PROCESSING! } else if (items.Length >= 4 && items[0].IsEqual(pmessage)) { var channel = items[2].AsRedisChannel(ChannelPrefix, RedisChannel.PatternMode.Literal); multiplexer.Trace("PMESSAGE: " + channel, physicalName); if (!channel.IsNull) { var sub = items[1].AsRedisChannel(ChannelPrefix, RedisChannel.PatternMode.Pattern); multiplexer.OnMessage(sub, channel, items[3].AsRedisValue()); } return; // AND STOP PROCESSING! } // if it didn't look like "[p]message", then we still need to process the pending queue } multiplexer.Trace("Matching result...", physicalName); Message msg; lock (outstanding) { multiplexer.Trace(outstanding.Count == 0, "Nothing to respond to!", physicalName); msg = outstanding.Dequeue(); } multiplexer.Trace("Response to: " + msg.ToString(), physicalName); if (msg.ComputeResult(this, result)) { bridge.CompleteSyncOrAsync(msg); } }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { var server = connection.Bridge.ServerEndPoint; switch (result.Type) { case ResultType.BulkString: if (message != null && message.Command == RedisCommand.INFO) { string info = result.GetString(), line; if (string.IsNullOrWhiteSpace(info)) { SetResult(message, true); return(true); } using (var reader = new StringReader(info)) { while ((line = reader.ReadLine()) != null) { if (string.IsNullOrWhiteSpace(line) || line.StartsWith("# ")) { continue; } string val; if ((val = Extract(line, "role:")) != null) { switch (val) { case "master": server.IsSlave = false; server.Multiplexer.Trace("Auto-configured role: master"); break; case "slave": server.IsSlave = true; server.Multiplexer.Trace("Auto-configured role: slave"); break; } } else if ((val = Extract(line, "redis_version:")) != null) { Version version; if (Version.TryParse(val, out version)) { server.Version = version; server.Multiplexer.Trace("Auto-configured version: " + version); } } else if ((val = Extract(line, "redis_mode:")) != null) { switch (val) { case "standalone": server.ServerType = ServerType.Standalone; server.Multiplexer.Trace("Auto-configured server-type: standalone"); break; case "cluster": server.ServerType = ServerType.Cluster; server.Multiplexer.Trace("Auto-configured server-type: cluster"); break; case "sentinel": server.ServerType = ServerType.Sentinel; server.Multiplexer.Trace("Auto-configured server-type: sentinel"); break; } } } } } SetResult(message, true); return(true); case ResultType.MultiBulk: if (message != null && message.Command == RedisCommand.CONFIG) { var arr = result.GetItems(); int count = arr.Length / 2; byte[] timeout = (byte[])RedisLiterals.timeout, databases = (byte[])RedisLiterals.databases, slave_read_only = (byte[])RedisLiterals.slave_read_only, yes = (byte[])RedisLiterals.yes, no = (byte[])RedisLiterals.no; long i64; for (int i = 0; i < count; i++) { var key = arr[i * 2]; if (key.IsEqual(timeout) && arr[(i * 2) + 1].TryGetInt64(out i64)) { // note the configuration is in seconds int timeoutSeconds = checked ((int)i64), targetSeconds; if (timeoutSeconds > 0) { if (timeoutSeconds >= 60) { targetSeconds = timeoutSeconds - 20; // time to spare... } else { targetSeconds = (timeoutSeconds * 3) / 4; } server.Multiplexer.Trace("Auto-configured timeout: " + targetSeconds + "s"); server.WriteEverySeconds = targetSeconds; } } else if (key.IsEqual(databases) && arr[(i * 2) + 1].TryGetInt64(out i64)) { int dbCount = checked ((int)i64); server.Multiplexer.Trace("Auto-configured databases: " + dbCount); server.Databases = dbCount; } else if (key.IsEqual(slave_read_only)) { var val = arr[(i * 2) + 1]; if (val.IsEqual(yes)) { server.SlaveReadOnly = true; server.Multiplexer.Trace("Auto-configured slave-read-only: true"); } else if (val.IsEqual(no)) { server.SlaveReadOnly = false; server.Multiplexer.Trace("Auto-configured slave-read-only: false"); } } } } SetResult(message, true); return(true); } return(false); }