finsync_redis 3.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.travis/Gemfile +11 -0
- data/.travis.yml +89 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +373 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +410 -0
- data/Rakefile +87 -0
- data/benchmarking/logging.rb +71 -0
- data/benchmarking/pipeline.rb +51 -0
- data/benchmarking/speed.rb +21 -0
- data/benchmarking/suite.rb +24 -0
- data/benchmarking/worker.rb +71 -0
- data/examples/basic.rb +15 -0
- data/examples/consistency.rb +114 -0
- data/examples/dist_redis.rb +43 -0
- data/examples/incr-decr.rb +17 -0
- data/examples/list.rb +26 -0
- data/examples/pubsub.rb +37 -0
- data/examples/sentinel/sentinel.conf +9 -0
- data/examples/sentinel/start +49 -0
- data/examples/sentinel.rb +41 -0
- data/examples/sets.rb +36 -0
- data/examples/unicorn/config.ru +3 -0
- data/examples/unicorn/unicorn.rb +20 -0
- data/lib/redis/client.rb +590 -0
- data/lib/redis/connection/command_helper.rb +44 -0
- data/lib/redis/connection/hiredis.rb +66 -0
- data/lib/redis/connection/registry.rb +12 -0
- data/lib/redis/connection/ruby.rb +429 -0
- data/lib/redis/connection/synchrony.rb +133 -0
- data/lib/redis/connection.rb +9 -0
- data/lib/redis/distributed.rb +873 -0
- data/lib/redis/errors.rb +40 -0
- data/lib/redis/hash_ring.rb +132 -0
- data/lib/redis/pipeline.rb +141 -0
- data/lib/redis/subscribe.rb +91 -0
- data/lib/redis/version.rb +3 -0
- data/lib/redis.rb +2788 -0
- data/redis.gemspec +44 -0
- data/test/bitpos_test.rb +69 -0
- data/test/blocking_commands_test.rb +42 -0
- data/test/client_test.rb +59 -0
- data/test/command_map_test.rb +30 -0
- data/test/commands_on_hashes_test.rb +21 -0
- data/test/commands_on_hyper_log_log_test.rb +21 -0
- data/test/commands_on_lists_test.rb +20 -0
- data/test/commands_on_sets_test.rb +77 -0
- data/test/commands_on_sorted_sets_test.rb +137 -0
- data/test/commands_on_strings_test.rb +101 -0
- data/test/commands_on_value_types_test.rb +133 -0
- data/test/connection_handling_test.rb +277 -0
- data/test/connection_test.rb +57 -0
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +46 -0
- data/test/distributed_commands_on_hashes_test.rb +10 -0
- data/test/distributed_commands_on_hyper_log_log_test.rb +33 -0
- data/test/distributed_commands_on_lists_test.rb +22 -0
- data/test/distributed_commands_on_sets_test.rb +83 -0
- data/test/distributed_commands_on_sorted_sets_test.rb +18 -0
- data/test/distributed_commands_on_strings_test.rb +59 -0
- data/test/distributed_commands_on_value_types_test.rb +95 -0
- data/test/distributed_commands_requiring_clustering_test.rb +164 -0
- data/test/distributed_connection_handling_test.rb +23 -0
- data/test/distributed_internals_test.rb +79 -0
- data/test/distributed_key_tags_test.rb +52 -0
- data/test/distributed_persistence_control_commands_test.rb +26 -0
- data/test/distributed_publish_subscribe_test.rb +92 -0
- data/test/distributed_remote_server_control_commands_test.rb +66 -0
- data/test/distributed_scripting_test.rb +102 -0
- data/test/distributed_sorting_test.rb +20 -0
- data/test/distributed_test.rb +58 -0
- data/test/distributed_transactions_test.rb +32 -0
- data/test/encoding_test.rb +18 -0
- data/test/error_replies_test.rb +59 -0
- data/test/fork_safety_test.rb +65 -0
- data/test/helper.rb +232 -0
- data/test/helper_test.rb +24 -0
- data/test/internals_test.rb +417 -0
- data/test/lint/blocking_commands.rb +150 -0
- data/test/lint/hashes.rb +162 -0
- data/test/lint/hyper_log_log.rb +60 -0
- data/test/lint/lists.rb +143 -0
- data/test/lint/sets.rb +140 -0
- data/test/lint/sorted_sets.rb +316 -0
- data/test/lint/strings.rb +260 -0
- data/test/lint/value_types.rb +122 -0
- data/test/persistence_control_commands_test.rb +26 -0
- data/test/pipelining_commands_test.rb +242 -0
- data/test/publish_subscribe_test.rb +282 -0
- data/test/remote_server_control_commands_test.rb +118 -0
- data/test/scanning_test.rb +413 -0
- data/test/scripting_test.rb +78 -0
- data/test/sentinel_command_test.rb +80 -0
- data/test/sentinel_test.rb +255 -0
- data/test/sorting_test.rb +59 -0
- data/test/ssl_test.rb +73 -0
- data/test/support/connection/hiredis.rb +1 -0
- data/test/support/connection/ruby.rb +1 -0
- data/test/support/connection/synchrony.rb +17 -0
- data/test/support/redis_mock.rb +130 -0
- data/test/support/ssl/gen_certs.sh +31 -0
- data/test/support/ssl/trusted-ca.crt +25 -0
- data/test/support/ssl/trusted-ca.key +27 -0
- data/test/support/ssl/trusted-cert.crt +81 -0
- data/test/support/ssl/trusted-cert.key +28 -0
- data/test/support/ssl/untrusted-ca.crt +26 -0
- data/test/support/ssl/untrusted-ca.key +27 -0
- data/test/support/ssl/untrusted-cert.crt +82 -0
- data/test/support/ssl/untrusted-cert.key +28 -0
- data/test/support/wire/synchrony.rb +24 -0
- data/test/support/wire/thread.rb +5 -0
- data/test/synchrony_driver.rb +88 -0
- data/test/test.conf.erb +9 -0
- data/test/thread_safety_test.rb +62 -0
- data/test/transactions_test.rb +264 -0
- data/test/unknown_commands_test.rb +14 -0
- data/test/url_param_test.rb +138 -0
- metadata +202 -0
@@ -0,0 +1,255 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
+
|
5
|
+
class SentinelTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
include Helper::Client
|
8
|
+
|
9
|
+
def test_sentinel_connection
|
10
|
+
sentinels = [{:host => "127.0.0.1", :port => 26381},
|
11
|
+
{:host => "127.0.0.1", :port => 26382}]
|
12
|
+
|
13
|
+
commands = {
|
14
|
+
:s1 => [],
|
15
|
+
:s2 => [],
|
16
|
+
}
|
17
|
+
|
18
|
+
handler = lambda do |id|
|
19
|
+
{
|
20
|
+
:sentinel => lambda do |command, *args|
|
21
|
+
commands[id] << [command, *args]
|
22
|
+
["127.0.0.1", "6381"]
|
23
|
+
end
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
RedisMock.start(handler.call(:s1)) do |s1_port|
|
28
|
+
RedisMock.start(handler.call(:s2)) do |s2_port|
|
29
|
+
sentinels[0][:port] = s1_port
|
30
|
+
sentinels[1][:port] = s2_port
|
31
|
+
redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master)
|
32
|
+
|
33
|
+
assert redis.ping
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
assert_equal commands[:s1], [%w[get-master-addr-by-name master1]]
|
38
|
+
assert_equal commands[:s2], []
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_sentinel_failover
|
42
|
+
sentinels = [{:host => "127.0.0.1", :port => 26381},
|
43
|
+
{:host => "127.0.0.1", :port => 26382}]
|
44
|
+
|
45
|
+
commands = {
|
46
|
+
:s1 => [],
|
47
|
+
:s2 => [],
|
48
|
+
}
|
49
|
+
|
50
|
+
s1 = {
|
51
|
+
:sentinel => lambda do |command, *args|
|
52
|
+
commands[:s1] << [command, *args]
|
53
|
+
"$-1" # Nil
|
54
|
+
end
|
55
|
+
}
|
56
|
+
|
57
|
+
s2 = {
|
58
|
+
:sentinel => lambda do |command, *args|
|
59
|
+
commands[:s2] << [command, *args]
|
60
|
+
["127.0.0.1", "6381"]
|
61
|
+
end
|
62
|
+
}
|
63
|
+
|
64
|
+
RedisMock.start(s1) do |s1_port|
|
65
|
+
RedisMock.start(s2) do |s2_port|
|
66
|
+
sentinels[0][:port] = s1_port
|
67
|
+
sentinels[1][:port] = s2_port
|
68
|
+
redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master)
|
69
|
+
|
70
|
+
assert redis.ping
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
assert_equal commands[:s1], [%w[get-master-addr-by-name master1]]
|
75
|
+
assert_equal commands[:s2], [%w[get-master-addr-by-name master1]]
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_sentinel_failover_prioritize_healthy_sentinel
|
79
|
+
sentinels = [{:host => "127.0.0.1", :port => 26381},
|
80
|
+
{:host => "127.0.0.1", :port => 26382}]
|
81
|
+
|
82
|
+
commands = {
|
83
|
+
:s1 => [],
|
84
|
+
:s2 => [],
|
85
|
+
}
|
86
|
+
|
87
|
+
s1 = {
|
88
|
+
:sentinel => lambda do |command, *args|
|
89
|
+
commands[:s1] << [command, *args]
|
90
|
+
"$-1" # Nil
|
91
|
+
end
|
92
|
+
}
|
93
|
+
|
94
|
+
s2 = {
|
95
|
+
:sentinel => lambda do |command, *args|
|
96
|
+
commands[:s2] << [command, *args]
|
97
|
+
["127.0.0.1", "6381"]
|
98
|
+
end
|
99
|
+
}
|
100
|
+
|
101
|
+
RedisMock.start(s1) do |s1_port|
|
102
|
+
RedisMock.start(s2) do |s2_port|
|
103
|
+
sentinels[0][:port] = s1_port
|
104
|
+
sentinels[1][:port] = s2_port
|
105
|
+
redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master)
|
106
|
+
|
107
|
+
assert redis.ping
|
108
|
+
|
109
|
+
redis.quit
|
110
|
+
|
111
|
+
assert redis.ping
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
assert_equal commands[:s1], [%w[get-master-addr-by-name master1]]
|
116
|
+
assert_equal commands[:s2], [%w[get-master-addr-by-name master1], %w[get-master-addr-by-name master1]]
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_sentinel_with_non_sentinel_options
|
120
|
+
sentinels = [{:host => "127.0.0.1", :port => 26381}]
|
121
|
+
|
122
|
+
commands = {
|
123
|
+
:s1 => [],
|
124
|
+
:m1 => []
|
125
|
+
}
|
126
|
+
|
127
|
+
sentinel = lambda do |port|
|
128
|
+
{
|
129
|
+
:auth => lambda do |pass|
|
130
|
+
commands[:s1] << ["auth", pass]
|
131
|
+
"-ERR unknown command 'auth'"
|
132
|
+
end,
|
133
|
+
:select => lambda do |db|
|
134
|
+
commands[:s1] << ["select", db]
|
135
|
+
"-ERR unknown command 'select'"
|
136
|
+
end,
|
137
|
+
:sentinel => lambda do |command, *args|
|
138
|
+
commands[:s1] << [command, *args]
|
139
|
+
["127.0.0.1", port.to_s]
|
140
|
+
end
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
master = {
|
145
|
+
:auth => lambda do |pass|
|
146
|
+
commands[:m1] << ["auth", pass]
|
147
|
+
"+OK"
|
148
|
+
end,
|
149
|
+
:role => lambda do
|
150
|
+
commands[:m1] << ["role"]
|
151
|
+
["master"]
|
152
|
+
end
|
153
|
+
}
|
154
|
+
|
155
|
+
RedisMock.start(master) do |master_port|
|
156
|
+
RedisMock.start(sentinel.call(master_port)) do |sen_port|
|
157
|
+
sentinels[0][:port] = sen_port
|
158
|
+
redis = Redis.new(:url => "redis://:foo@master1/15", :sentinels => sentinels, :role => :master)
|
159
|
+
|
160
|
+
assert redis.ping
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
assert_equal [%w[get-master-addr-by-name master1]], commands[:s1]
|
165
|
+
assert_equal [%w[auth foo], %w[role]], commands[:m1]
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_sentinel_role_mismatch
|
169
|
+
sentinels = [{:host => "127.0.0.1", :port => 26381}]
|
170
|
+
|
171
|
+
sentinel = lambda do |port|
|
172
|
+
{
|
173
|
+
:sentinel => lambda do |command, *args|
|
174
|
+
["127.0.0.1", port.to_s]
|
175
|
+
end
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
master = {
|
180
|
+
:role => lambda do
|
181
|
+
["slave"]
|
182
|
+
end
|
183
|
+
}
|
184
|
+
|
185
|
+
ex = assert_raise(Redis::ConnectionError) do
|
186
|
+
RedisMock.start(master) do |master_port|
|
187
|
+
RedisMock.start(sentinel.call(master_port)) do |sen_port|
|
188
|
+
sentinels[0][:port] = sen_port
|
189
|
+
redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master)
|
190
|
+
|
191
|
+
assert redis.ping
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
assert_match(/Instance role mismatch/, ex.message)
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_sentinel_retries
|
200
|
+
sentinels = [{:host => "127.0.0.1", :port => 26381},
|
201
|
+
{:host => "127.0.0.1", :port => 26382}]
|
202
|
+
|
203
|
+
connections = []
|
204
|
+
|
205
|
+
handler = lambda do |id, port|
|
206
|
+
{
|
207
|
+
:sentinel => lambda do |command, *args|
|
208
|
+
connections << id
|
209
|
+
|
210
|
+
if connections.count(id) < 2
|
211
|
+
:close
|
212
|
+
else
|
213
|
+
["127.0.0.1", port.to_s]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
}
|
217
|
+
end
|
218
|
+
|
219
|
+
master = {
|
220
|
+
:role => lambda do
|
221
|
+
["master"]
|
222
|
+
end
|
223
|
+
}
|
224
|
+
|
225
|
+
RedisMock.start(master) do |master_port|
|
226
|
+
RedisMock.start(handler.call(:s1, master_port)) do |s1_port|
|
227
|
+
RedisMock.start(handler.call(:s2, master_port)) do |s2_port|
|
228
|
+
sentinels[0][:port] = s1_port
|
229
|
+
sentinels[1][:port] = s2_port
|
230
|
+
redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master, :reconnect_attempts => 1)
|
231
|
+
|
232
|
+
assert redis.ping
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
assert_equal [:s1, :s2, :s1], connections
|
238
|
+
|
239
|
+
connections.clear
|
240
|
+
|
241
|
+
ex = assert_raise(Redis::CannotConnectError) do
|
242
|
+
RedisMock.start(master) do |master_port|
|
243
|
+
RedisMock.start(handler.call(:s1, master_port)) do |s1_port|
|
244
|
+
RedisMock.start(handler.call(:s2, master_port)) do |s2_port|
|
245
|
+
redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master, :reconnect_attempts => 0)
|
246
|
+
|
247
|
+
assert redis.ping
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
assert_match(/No sentinels available/, ex.message)
|
254
|
+
end
|
255
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
+
|
5
|
+
class TestSorting < Test::Unit::TestCase
|
6
|
+
|
7
|
+
include Helper::Client
|
8
|
+
|
9
|
+
def test_sort
|
10
|
+
r.set("foo:1", "s1")
|
11
|
+
r.set("foo:2", "s2")
|
12
|
+
|
13
|
+
r.rpush("bar", "1")
|
14
|
+
r.rpush("bar", "2")
|
15
|
+
|
16
|
+
assert_equal ["s1"], r.sort("bar", :get => "foo:*", :limit => [0, 1])
|
17
|
+
assert_equal ["s2"], r.sort("bar", :get => "foo:*", :limit => [0, 1], :order => "desc alpha")
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_sort_with_an_array_of_gets
|
21
|
+
r.set("foo:1:a", "s1a")
|
22
|
+
r.set("foo:1:b", "s1b")
|
23
|
+
|
24
|
+
r.set("foo:2:a", "s2a")
|
25
|
+
r.set("foo:2:b", "s2b")
|
26
|
+
|
27
|
+
r.rpush("bar", "1")
|
28
|
+
r.rpush("bar", "2")
|
29
|
+
|
30
|
+
assert_equal [["s1a", "s1b"]], r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :limit => [0, 1])
|
31
|
+
assert_equal [["s2a", "s2b"]], r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :limit => [0, 1], :order => "desc alpha")
|
32
|
+
assert_equal [["s1a", "s1b"], ["s2a", "s2b"]], r.sort("bar", :get => ["foo:*:a", "foo:*:b"])
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_sort_with_store
|
36
|
+
r.set("foo:1", "s1")
|
37
|
+
r.set("foo:2", "s2")
|
38
|
+
|
39
|
+
r.rpush("bar", "1")
|
40
|
+
r.rpush("bar", "2")
|
41
|
+
|
42
|
+
r.sort("bar", :get => "foo:*", :store => "baz")
|
43
|
+
assert_equal ["s1", "s2"], r.lrange("baz", 0, -1)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_sort_with_an_array_of_gets_and_with_store
|
47
|
+
r.set("foo:1:a", "s1a")
|
48
|
+
r.set("foo:1:b", "s1b")
|
49
|
+
|
50
|
+
r.set("foo:2:a", "s2a")
|
51
|
+
r.set("foo:2:b", "s2b")
|
52
|
+
|
53
|
+
r.rpush("bar", "1")
|
54
|
+
r.rpush("bar", "2")
|
55
|
+
|
56
|
+
r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :store => 'baz')
|
57
|
+
assert_equal ["s1a", "s1b", "s2a", "s2b"], r.lrange("baz", 0, -1)
|
58
|
+
end
|
59
|
+
end
|
data/test/ssl_test.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
if RUBY_VERSION >= "1.9.3"
|
4
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
5
|
+
|
6
|
+
class SslTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
include Helper::Client
|
9
|
+
|
10
|
+
driver(:ruby) do
|
11
|
+
|
12
|
+
def test_verified_ssl_connection
|
13
|
+
RedisMock.start({ :ping => proc { "+PONG" } }, ssl_server_opts("trusted")) do |port|
|
14
|
+
redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
|
15
|
+
assert_equal redis.ping, "PONG"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_unverified_ssl_connection
|
20
|
+
assert_raise(OpenSSL::SSL::SSLError) do
|
21
|
+
RedisMock.start({ :ping => proc { "+PONG" } }, ssl_server_opts("untrusted")) do |port|
|
22
|
+
redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
|
23
|
+
redis.ping
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_ssl_blocking
|
29
|
+
RedisMock.start({}, ssl_server_opts("trusted")) do |port|
|
30
|
+
redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
|
31
|
+
assert_equal redis.set("boom", "a" * 10_000_000), "OK"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
driver(:hiredis, :synchrony) do
|
38
|
+
|
39
|
+
def test_ssl_not_implemented_exception
|
40
|
+
assert_raise(NotImplementedError) do
|
41
|
+
RedisMock.start({ :ping => proc { "+PONG" } }, ssl_server_opts("trusted")) do |port|
|
42
|
+
redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
|
43
|
+
redis.ping
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def ssl_server_opts(prefix)
|
53
|
+
ssl_cert = File.join(cert_path, "#{prefix}-cert.crt")
|
54
|
+
ssl_key = File.join(cert_path, "#{prefix}-cert.key")
|
55
|
+
|
56
|
+
{
|
57
|
+
:ssl => true,
|
58
|
+
:ssl_params => {
|
59
|
+
:cert => OpenSSL::X509::Certificate.new(File.read(ssl_cert)),
|
60
|
+
:key => OpenSSL::PKey::RSA.new(File.read(ssl_key))
|
61
|
+
}
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def ssl_ca_file
|
66
|
+
File.join(cert_path, "trusted-ca.crt")
|
67
|
+
end
|
68
|
+
|
69
|
+
def cert_path
|
70
|
+
File.expand_path("../support/ssl/", __FILE__)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "support/wire/thread"
|
@@ -0,0 +1 @@
|
|
1
|
+
require "support/wire/thread"
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require "socket"
|
2
|
+
|
3
|
+
module RedisMock
|
4
|
+
class Server
|
5
|
+
def initialize(options = {}, &block)
|
6
|
+
tcp_server = TCPServer.new(options[:host] || "127.0.0.1", 0)
|
7
|
+
tcp_server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
|
8
|
+
|
9
|
+
if options[:ssl]
|
10
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
11
|
+
|
12
|
+
ssl_params = options.fetch(:ssl_params, {})
|
13
|
+
ctx.set_params(ssl_params) unless ssl_params.empty?
|
14
|
+
|
15
|
+
@server = OpenSSL::SSL::SSLServer.new(tcp_server, ctx)
|
16
|
+
else
|
17
|
+
@server = tcp_server
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def port
|
22
|
+
@server.addr[1]
|
23
|
+
end
|
24
|
+
|
25
|
+
def start(&block)
|
26
|
+
@thread = Thread.new { run(&block) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def shutdown
|
30
|
+
@thread.kill
|
31
|
+
end
|
32
|
+
|
33
|
+
def run
|
34
|
+
begin
|
35
|
+
loop do
|
36
|
+
session = @server.accept
|
37
|
+
|
38
|
+
begin
|
39
|
+
return if yield(session) == :exit
|
40
|
+
ensure
|
41
|
+
session.close
|
42
|
+
end
|
43
|
+
end
|
44
|
+
rescue => ex
|
45
|
+
$stderr.puts "Error running mock server: #{ex.message}"
|
46
|
+
$stderr.puts ex.backtrace
|
47
|
+
retry
|
48
|
+
ensure
|
49
|
+
@server.close
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Starts a mock Redis server in a thread.
|
55
|
+
#
|
56
|
+
# The server will use the lambda handler passed as argument to handle
|
57
|
+
# connections. For example:
|
58
|
+
#
|
59
|
+
# handler = lambda { |session| session.close }
|
60
|
+
# RedisMock.start_with_handler(handler) do
|
61
|
+
# # Every connection will be closed immediately
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
def self.start_with_handler(blk, options = {})
|
65
|
+
server = Server.new(options)
|
66
|
+
port = server.port
|
67
|
+
|
68
|
+
begin
|
69
|
+
server.start(&blk)
|
70
|
+
yield(port)
|
71
|
+
ensure
|
72
|
+
server.shutdown
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Starts a mock Redis server in a thread.
|
77
|
+
#
|
78
|
+
# The server will reply with a `+OK` to all commands, but you can
|
79
|
+
# customize it by providing a hash. For example:
|
80
|
+
#
|
81
|
+
# RedisMock.start(:ping => lambda { "+PONG" }) do |port|
|
82
|
+
# assert_equal "PONG", Redis.new(:port => port).ping
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
def self.start(commands, options = {}, &blk)
|
86
|
+
handler = lambda do |session|
|
87
|
+
while line = session.gets
|
88
|
+
argv = Array.new(line[1..-3].to_i) do
|
89
|
+
bytes = session.gets[1..-3].to_i
|
90
|
+
arg = session.read(bytes)
|
91
|
+
session.read(2) # Discard \r\n
|
92
|
+
arg
|
93
|
+
end
|
94
|
+
|
95
|
+
command = argv.shift
|
96
|
+
blk = commands[command.to_sym]
|
97
|
+
blk ||= lambda { |*_| "+OK" }
|
98
|
+
|
99
|
+
response = blk.call(*argv)
|
100
|
+
|
101
|
+
# Convert a nil response to :close
|
102
|
+
response ||= :close
|
103
|
+
|
104
|
+
if response == :exit
|
105
|
+
break :exit
|
106
|
+
elsif response == :close
|
107
|
+
break :close
|
108
|
+
elsif response.is_a?(Array)
|
109
|
+
session.write("*%d\r\n" % response.size)
|
110
|
+
|
111
|
+
response.each do |resp|
|
112
|
+
if resp.is_a?(Array)
|
113
|
+
session.write("*%d\r\n" % resp.size)
|
114
|
+
resp.each do |r|
|
115
|
+
session.write("$%d\r\n%s\r\n" % [r.length, r])
|
116
|
+
end
|
117
|
+
else
|
118
|
+
session.write("$%d\r\n%s\r\n" % [resp.length, resp])
|
119
|
+
end
|
120
|
+
end
|
121
|
+
else
|
122
|
+
session.write(response)
|
123
|
+
session.write("\r\n") unless response.end_with?("\r\n")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
start_with_handler(handler, options, &blk)
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
get_subject() {
|
4
|
+
if [ "$1" = "trusted" ]
|
5
|
+
then
|
6
|
+
echo "/C=IT/ST=Sicily/L=Catania/O=Redis/OU=Security/CN=127.0.0.1"
|
7
|
+
else
|
8
|
+
echo "/C=XX/ST=Untrusted/L=Evilville/O=Evil Hacker/OU=Attack Department/CN=127.0.0.1"
|
9
|
+
fi
|
10
|
+
}
|
11
|
+
|
12
|
+
# Generate two CAs: one to be considered trusted, and one that's untrusted
|
13
|
+
for type in trusted untrusted; do
|
14
|
+
rm -rf ./demoCA
|
15
|
+
mkdir -p ./demoCA
|
16
|
+
mkdir -p ./demoCA/certs
|
17
|
+
mkdir -p ./demoCA/crl
|
18
|
+
mkdir -p ./demoCA/newcerts
|
19
|
+
mkdir -p ./demoCA/private
|
20
|
+
touch ./demoCA/index.txt
|
21
|
+
|
22
|
+
openssl genrsa -out ${type}-ca.key 2048
|
23
|
+
openssl req -new -x509 -days 12500 -key ${type}-ca.key -out ${type}-ca.crt -subj "$(get_subject $type)"
|
24
|
+
openssl x509 -in ${type}-ca.crt -noout -next_serial -out ./demoCA/serial
|
25
|
+
|
26
|
+
openssl req -newkey rsa:2048 -keyout ${type}-cert.key -nodes -out ${type}-cert.req -subj "$(get_subject $type)"
|
27
|
+
openssl ca -days 12500 -cert ${type}-ca.crt -keyfile ${type}-ca.key -out ${type}-cert.crt -infiles ${type}-cert.req
|
28
|
+
rm ${type}-cert.req
|
29
|
+
done
|
30
|
+
|
31
|
+
rm -rf ./demoCA
|
@@ -0,0 +1,25 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIEIDCCAwigAwIBAgIJAM7kyjC89Qj/MA0GCSqGSIb3DQEBCwUAMGcxCzAJBgNV
|
3
|
+
BAYTAklUMQ8wDQYDVQQIEwZTaWNpbHkxEDAOBgNVBAcTB0NhdGFuaWExDjAMBgNV
|
4
|
+
BAoTBVJlZGlzMREwDwYDVQQLEwhTZWN1cml0eTESMBAGA1UEAxMJMTI3LjAuMC4x
|
5
|
+
MCAXDTE2MDQwMjAzMzQ0MVoYDzIwNTAwNjIzMDMzNDQxWjBnMQswCQYDVQQGEwJJ
|
6
|
+
VDEPMA0GA1UECBMGU2ljaWx5MRAwDgYDVQQHEwdDYXRhbmlhMQ4wDAYDVQQKEwVS
|
7
|
+
ZWRpczERMA8GA1UECxMIU2VjdXJpdHkxEjAQBgNVBAMTCTEyNy4wLjAuMTCCASIw
|
8
|
+
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMeibFqEG38mtN9DSXy6NZdd7AjH
|
9
|
+
4/D+VdDzlbJlI5IBACCV9p6P2j5PFlFvkHFE6vr6biMaLXNAmUHYfDzeT95LODHH
|
10
|
+
t+8HlR51cNYrnt9B3eiVwEnJ7+axuDHg6nUgLXeKeog+vEqreZwLnFibxt2qpFze
|
11
|
+
xzyKJ37Pm+iAey5glCc/v7ECYQ4sWVVV+ciC+sAwmZDfZXCBQtRRokJ6ikqQDwWV
|
12
|
+
DugGcV46feTpu79OmkLLM8PI3E7ow2F/3iv67gmdlO5m9wX1ahWzJKUapBTxgf4X
|
13
|
+
QG0s60WbC9iJIvgXRGW7wWSsqSVJkfLYllDTPgfpLyl1+FR3A4awrsPiMVUCAwEA
|
14
|
+
AaOBzDCByTAdBgNVHQ4EFgQU+YG9kJR3Vy31d7QVyxRAYyKTK18wgZkGA1UdIwSB
|
15
|
+
kTCBjoAU+YG9kJR3Vy31d7QVyxRAYyKTK1+ha6RpMGcxCzAJBgNVBAYTAklUMQ8w
|
16
|
+
DQYDVQQIEwZTaWNpbHkxEDAOBgNVBAcTB0NhdGFuaWExDjAMBgNVBAoTBVJlZGlz
|
17
|
+
MREwDwYDVQQLEwhTZWN1cml0eTESMBAGA1UEAxMJMTI3LjAuMC4xggkAzuTKMLz1
|
18
|
+
CP8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAeFKB7DUixmxbdvNw
|
19
|
+
n/mNoHK+OOZXmfxZDCo0v2gcQ4WXUiCqL6MagrImCvkEz5RL6Fk2ZflEV2iGQ5Ds
|
20
|
+
CmF2n47ISpqG29bfI5R1rcbfqK/5tazUIhQu12ThNmkEh7hCuW/0LqJrnmxpuRLy
|
21
|
+
le9e3svCC96lwjFczzU/utWurKt7S7Di3C4P+AXAJJuszDMLMCBLaB/3j24cNpOx
|
22
|
+
zzeZo02x4rpsD2+MMfRDWMWezVEyk63KnI0kt3JGnepsKCFc48ZOk09LwFk3Rfaq
|
23
|
+
zuKSgEJJw1mfsdBfysM0HQw20yyjSdoTEfQq3bXctTNi+pEOgW6x7TMsnngYYLXV
|
24
|
+
9XTrpg==
|
25
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEpQIBAAKCAQEAx6JsWoQbfya030NJfLo1l13sCMfj8P5V0POVsmUjkgEAIJX2
|
3
|
+
no/aPk8WUW+QcUTq+vpuIxotc0CZQdh8PN5P3ks4Mce37weVHnVw1iue30Hd6JXA
|
4
|
+
Scnv5rG4MeDqdSAtd4p6iD68Sqt5nAucWJvG3aqkXN7HPIonfs+b6IB7LmCUJz+/
|
5
|
+
sQJhDixZVVX5yIL6wDCZkN9lcIFC1FGiQnqKSpAPBZUO6AZxXjp95Om7v06aQssz
|
6
|
+
w8jcTujDYX/eK/ruCZ2U7mb3BfVqFbMkpRqkFPGB/hdAbSzrRZsL2Iki+BdEZbvB
|
7
|
+
ZKypJUmR8tiWUNM+B+kvKXX4VHcDhrCuw+IxVQIDAQABAoIBAQCzbGHiQJXOA+XQ
|
8
|
+
O9OSjHGaJ8n6Yl2VvaE3eZXzjj8X/Fo271GGVVgbZE10x8aUZxKim+3dEqwCx+52
|
9
|
+
ZbHTqyMxcX2CEDRaWwBFLdxKQU467iIZ5m26ZAp/1v7rpXBT8KWsqQNT7L6ihdd4
|
10
|
+
zl6orOlhVPsAlSGQYcL5kHJZ1w/fL0phEbwdISd3PYhGHXMNmqfXorzJYHDQA4R+
|
11
|
+
yR7WpP1dmnUeEKrHc9FFcBZ75BGlWjdCPZMFKc7IndZumarhBpWH9yZMUxrUIo4V
|
12
|
+
SCweRUFdD5H1lMZ0YiIAE25wKNEQ2iGd3Jfr8Vj1KFSHC9I2FJA3aFRRUgTwxx/W
|
13
|
+
h0mJy1ZJAoGBAPYsSSlwQdxZjnyZiVkNSD4MoLdof//nRxeKGejq6AiXDvcsLyJy
|
14
|
+
0MKk4YBFw2249TWm/KBbMAFiBE7d8uPtP5pPfjNVPX6VltH3AhSZ7Ugbpo6C3NFA
|
15
|
+
GpzFVtNaWgCVDloDVdmsY7ssDFuAIih0paklPAqnLY+Ua9m1BiEPrB+bAoGBAM+a
|
16
|
+
i+0NMR4AyKpuo1exdd+7BIHw5HNPwGmR1ggdGWduH0zsOhEawQKKFv1X9xKAcXxW
|
17
|
+
PyeD56/Tmn7fkWvuE8dOu9E6em0vgmxhYyn4nyLAFYF5uKXYo78MpIEThdpl1ldT
|
18
|
+
iHwG/25vunaBUHhwbHPUD+F989tmRuCjoFkuA5nPAoGAaqPIlcDhZvkMtoE0dHVC
|
19
|
+
hE6oGIuWV17y9wmGK9YG6iG2A/EKAhxGvur6HL0b6Z4j6zgJW9Xkt9SkFR4kqAQQ
|
20
|
+
d2JUQxx75SgcC5y7M/1yQrhnsHiT+7mPTbZW5HvRXUs0yl2DhSYeleiA+epJ4ciW
|
21
|
+
Mu3EUsEVBYvAJLE8lHnbkF0CgYEAhyxpz3+3a4G3JsHDOWYjCfoLhVAEb9CNyC9c
|
22
|
+
3QuVbvMVDlEBvgFdivm+3lZYWYOoYP0HQgNw59svzUxks5Hg7vUk9abN8CnvEgKX
|
23
|
+
PszTUR0g450NzW6xr8PbmO/NR9bnKRUK2Tb1OkMldePdMY6CDykU7g3EqiZ+H+Zq
|
24
|
+
kaaUUaECgYEAmk5W+S94q5jLemnfAChC5lva/0/aHdhtaoH4Lo+j0haMsdiy8/ZE
|
25
|
+
sh+3gQ8pqwaCAwnKxAcppt/FNZ7tHRsH3oyY6biypn3WppQj+BA41nuzbspOKJhR
|
26
|
+
ZDXKFCItbzUjyi23Dx4P4DgMivkpV+e88RMIuBnv4yjl5iOLq+vf4Rg=
|
27
|
+
-----END RSA PRIVATE KEY-----
|