Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad950e4c32 | ||
|
|
0e9710d4dd | ||
|
|
9317bf6465 | ||
|
|
1f7c148be2 | ||
|
|
91863dd854 | ||
|
|
2f27ebb414 | ||
|
|
89391e42c8 | ||
|
|
8f30d2a604 | ||
|
|
24c0bf5c5b | ||
|
|
b313fa4166 | ||
|
|
f613873de0 | ||
|
|
405d14fd44 | ||
|
|
2d4b9401f1 | ||
|
|
521e05cb8b | ||
|
|
7afd1724dd | ||
|
|
eee173a64e | ||
|
|
126348536a | ||
|
|
e030e351fd | ||
|
|
720ea82eab | ||
|
|
0a8a45f94d | ||
|
|
f44b6a0e9a | ||
|
|
1797c5e374 | ||
|
|
683a4ce4b2 | ||
|
|
2322f3d87b | ||
|
|
f084778cea | ||
|
|
658f9bf8c4 | ||
|
|
9c76b1ab55 | ||
|
|
e12aacf3a2 | ||
|
|
137696d808 | ||
|
|
37b3b2a7e0 | ||
|
|
5453899878 | ||
|
|
3148f3e8a5 | ||
|
|
3ebace932d | ||
|
|
423c78f4fa | ||
|
|
de4d78d7df | ||
|
|
e5e642aa9b | ||
|
|
52cccfbe94 | ||
|
|
052e01e75d | ||
|
|
5d66aa3d22 | ||
|
|
2f94d40e02 | ||
|
|
d345d1da9b | ||
|
|
0a3ae0f0a4 | ||
|
|
6029bd560e | ||
|
|
39e5a6fa2f | ||
|
|
4ac3d79bfc | ||
|
|
51fa40ff42 | ||
|
|
042512aa36 | ||
|
|
3f39c18ac1 | ||
|
|
d8102f5ff8 | ||
|
|
65ad4ff87e | ||
|
|
ec5564a4cf | ||
|
|
2cd5f6f3ff | ||
|
|
fbcd591b28 | ||
|
|
e1b17f1232 | ||
|
|
8c76e0f288 | ||
|
|
b4de9e832d | ||
|
|
70aaf50082 | ||
|
|
66472a5ee3 | ||
|
|
fd7bde5726 | ||
|
|
9d40f1cbb5 | ||
|
|
389c5e14ad | ||
|
|
64c657a8af | ||
|
|
e6f67092f8 | ||
|
|
3053337043 | ||
|
|
215c5eacb0 | ||
|
|
afb48c6cc9 | ||
|
|
7fddebc272 | ||
|
|
28d20b8f14 | ||
|
|
ba2feb3004 | ||
|
|
aa0606db08 | ||
|
|
49c1c96fc1 | ||
|
|
d92f2f5ad6 | ||
|
|
e1c09d5982 | ||
|
|
8d9e75c769 | ||
|
|
2643c691be | ||
|
|
ef78757e69 | ||
|
|
35e697de83 | ||
|
|
159981e73c | ||
|
|
ec06e93319 | ||
|
|
d6a8e64e69 | ||
|
|
a06f10b009 | ||
|
|
0a26869a13 | ||
|
|
2c529844fc | ||
|
|
33f7e12b88 | ||
|
|
90891a7dde | ||
|
|
4a59230d82 | ||
|
|
e24b947c9b | ||
|
|
c5a753c890 | ||
|
|
64327e4169 | ||
|
|
aab9b12786 | ||
|
|
066b68328e | ||
|
|
db3e3ebcb2 | ||
|
|
86db5091c7 | ||
|
|
93c1d31d97 | ||
|
|
8fca090ede | ||
|
|
e38d0b5a5f | ||
|
|
94013b8ecb | ||
|
|
968cd2b967 | ||
|
|
19c2b179c7 | ||
|
|
3ba7c6acbe | ||
|
|
936ee01759 | ||
|
|
8a0515d9a3 | ||
|
|
21ab5d4f78 | ||
|
|
0fb96d55bd | ||
|
|
f5d8a36983 | ||
|
|
11b602fbf8 | ||
|
|
b2ce3719af | ||
|
|
21414ad480 | ||
|
|
13c1e1f298 | ||
|
|
34505d26f7 | ||
|
|
4930d19e70 | ||
|
|
aba9517542 | ||
|
|
7540708a61 | ||
|
|
664c7b36a5 | ||
|
|
c390972c76 | ||
|
|
73d286d523 | ||
|
|
4cbccab2bd | ||
|
|
23ed37dd87 | ||
|
|
9b25484a13 | ||
|
|
49f8f43890 | ||
|
|
c936f801c0 | ||
|
|
694a869e78 | ||
|
|
a2e8a3a241 | ||
|
|
2f8ec0e024 | ||
|
|
09c63c45dd | ||
|
|
1ed0f049fe | ||
|
|
4b04ca0b18 | ||
|
|
2f54107289 | ||
|
|
e34f06ae5d | ||
|
|
77386ae011 | ||
|
|
0c959294a8 | ||
|
|
8892b5cf9e | ||
|
|
9da232c05a | ||
|
|
1ce06604de | ||
|
|
530c70b0a9 | ||
|
|
dadc67a92e | ||
|
|
2775a3526e | ||
|
|
8f241ab3b8 | ||
|
|
3e09be56a8 | ||
|
|
757f8f771e | ||
|
|
04ba485042 | ||
|
|
b1149a49b2 | ||
|
|
db09f6eb2e | ||
|
|
835d15b536 | ||
|
|
bae0512c8a | ||
|
|
8d6134952a | ||
|
|
1d7c0e5949 | ||
|
|
37b0f3617d | ||
|
|
6866117194 | ||
|
|
b622537199 | ||
|
|
355b1b6a57 | ||
|
|
d6f4273241 | ||
|
|
de1b19ea88 | ||
|
|
79fa5618f1 | ||
|
|
91bf2ab86d | ||
|
|
88655019cc | ||
|
|
fad44611dc | ||
|
|
8884971223 | ||
|
|
07e1248686 | ||
|
|
6cd84b64f0 | ||
|
|
c6b3966d02 | ||
|
|
432e056659 | ||
|
|
bbb6c2fb53 | ||
|
|
095a6e5937 | ||
|
|
56020fd821 | ||
|
|
11235c0e40 | ||
|
|
406caa5f56 | ||
|
|
8bd5e39102 | ||
|
|
c547dff650 | ||
|
|
e6df2f6210 | ||
|
|
312ccae003 | ||
|
|
ff27217639 | ||
|
|
3f4f9b6331 | ||
|
|
5b8e395174 | ||
|
|
9914b676b3 | ||
|
|
abd44c8393 | ||
|
|
f70916f8b0 | ||
|
|
ef3ad68020 | ||
|
|
f9470c9a17 | ||
|
|
e9a1438ac4 | ||
|
|
5102c0da92 | ||
|
|
d68b775df5 | ||
|
|
ed995ffa03 | ||
|
|
875a1f07d8 | ||
|
|
d070546568 | ||
|
|
511be70eba | ||
|
|
840f7f61c5 | ||
|
|
9d69d6e8fb | ||
|
|
15c078df61 | ||
|
|
e90e5640e7 | ||
|
|
2df6695f2b | ||
|
|
92e3b1802f | ||
|
|
0463520693 | ||
|
|
922e3bf59f | ||
|
|
42f2ad0516 | ||
|
|
1eab6202ac | ||
|
|
d30ee6c44a | ||
|
|
4f2da00e94 | ||
|
|
34b9a3fa2e | ||
|
|
5c7b869e61 | ||
|
|
6cbea7d29b | ||
|
|
8cfa37fc21 | ||
|
|
16c53085f7 | ||
|
|
2ccb926314 | ||
|
|
6384fe3414 | ||
|
|
ef64333e63 | ||
|
|
0851705304 | ||
|
|
b97a4ad7f2 | ||
|
|
d01afe885c | ||
|
|
959d6035e5 | ||
|
|
aa730ef1ea | ||
|
|
f5ca1f9ee9 | ||
|
|
61d3fdb474 | ||
|
|
53a4d6c3b1 | ||
|
|
f40ca9cb58 | ||
|
|
7a3d1487e4 | ||
|
|
3a955d9ad4 | ||
|
|
0413fbc7d0 | ||
|
|
c0f5c678c2 | ||
|
|
a9897b0084 | ||
|
|
f4b5a4d869 | ||
|
|
a60016e061 | ||
|
|
374401d786 | ||
|
|
7d749d810b | ||
|
|
d63d02601f | ||
|
|
0a2621c673 | ||
|
|
07601b7e07 | ||
|
|
645c664fbb | ||
|
|
d7920ff9b1 | ||
|
|
c6cd1e59b1 | ||
|
|
4938052f6b | ||
|
|
b278e44376 | ||
|
|
c07e16fadd | ||
|
|
38da8d07d0 | ||
|
|
733daef127 | ||
|
|
175a9e3199 | ||
|
|
5e3a15ae1b | ||
|
|
a0e19e3cf1 | ||
|
|
cd03e293c3 | ||
|
|
d96f47cf06 | ||
|
|
4c14e8668c | ||
|
|
4de1031d8a | ||
|
|
073598ed8f | ||
|
|
808f3004f0 | ||
|
|
4724dd439e | ||
|
|
3b74b55084 | ||
|
|
a3da3e592d | ||
|
|
1cab962098 | ||
|
|
1eb85249e7 | ||
|
|
44d8b039e8 | ||
|
|
370ab4c4db | ||
|
|
843f769b96 | ||
|
|
d5935bb0a4 | ||
|
|
e5d50b236c | ||
|
|
07f39ae47b | ||
|
|
569a3f4548 | ||
|
|
636aa8de76 | ||
|
|
68ba4d43ac | ||
|
|
2c120af61e | ||
|
|
91f4f41665 | ||
|
|
6a052af890 | ||
|
|
5629dbe715 | ||
|
|
8cbd858d45 | ||
|
|
28375ff63e | ||
|
|
cd81dcf18b | ||
|
|
65311a3360 | ||
|
|
315df9ada0 | ||
|
|
036963a7da | ||
|
|
54fd3d4024 | ||
|
|
a79bb86856 | ||
|
|
e138698e54 | ||
|
|
db6655deb4 | ||
|
|
cf0a909e2d | ||
|
|
1976acfe3e | ||
|
|
497351ad07 | ||
|
|
4a2c4477c9 | ||
|
|
98d2e0017d | ||
|
|
7de6451818 | ||
|
|
3060de88ce | ||
|
|
f6e1a94e03 | ||
|
|
dc39b0c190 | ||
|
|
ccc39f64f8 | ||
|
|
b45b0d81bb | ||
|
|
d026647f4f | ||
|
|
a2af2a306b | ||
|
|
725762c48d | ||
|
|
6bf81eb7b1 | ||
|
|
eef45934fa | ||
|
|
c4ef1efdb7 | ||
|
|
c3df27d1ea | ||
|
|
2f717c156a | ||
|
|
a7c02b19bf | ||
|
|
0418253a9b | ||
|
|
9ae4f5c73d | ||
|
|
169be0426c | ||
|
|
6097d14d4d | ||
|
|
f657315e60 | ||
|
|
a19c4058be | ||
|
|
ec34806c1e | ||
|
|
f141329fb7 | ||
|
|
6da2bc96e2 | ||
|
|
95360c2e0c | ||
|
|
18b59f35ef | ||
|
|
40d555dbb7 | ||
|
|
df5f543b65 | ||
|
|
e1d98bca5a | ||
|
|
dba33a943d | ||
|
|
84d056d0f7 | ||
|
|
3a5905fa85 | ||
|
|
5b48d90049 | ||
|
|
3c09ce26fb | ||
|
|
a4f03bd7eb | ||
|
|
4ae1bea323 | ||
|
|
b70d81f60b | ||
|
|
c945e1126a | ||
|
|
3d0b427c30 | ||
|
|
ceb3a7a8f6 | ||
|
|
61a73de64d | ||
|
|
7778f1b4b0 | ||
|
|
62a197516b | ||
|
|
95d6297db8 | ||
|
|
53774e69fa | ||
|
|
1ccfd6a1f7 | ||
|
|
25f8d4fb41 | ||
|
|
af2175326c | ||
|
|
9b4edfdf08 | ||
|
|
817894c012 | ||
|
|
7d81f39222 | ||
|
|
e58118cda6 | ||
|
|
20377a6f3d | ||
|
|
f491eee2a1 | ||
|
|
e8e6ca6309 | ||
|
|
367ba4125f | ||
|
|
3d9c6a7ce6 | ||
|
|
55c74617d7 | ||
|
|
3c7d6a1853 | ||
|
|
f07b7393a0 | ||
|
|
ac91822952 | ||
|
|
ea1b48bd12 | ||
|
|
fba391ae66 | ||
|
|
257009afff | ||
|
|
5d180d2834 | ||
|
|
0bad00d049 | ||
|
|
92bde124ca | ||
|
|
7018ad69a2 | ||
|
|
46df4db87b | ||
|
|
c19530bc71 | ||
|
|
349ef3f6a0 | ||
|
|
a031d268b1 | ||
|
|
81a55d026f | ||
|
|
1bf02e1ca9 | ||
|
|
a66814fd7a | ||
|
|
18ff8cd1fb | ||
|
|
17c226b070 | ||
|
|
6122f1c450 | ||
|
|
4a474843fb | ||
|
|
7d43159a7d | ||
|
|
3ea4c43add | ||
|
|
c6f0ea2c81 | ||
|
|
60d5ef4d82 | ||
|
|
21316d5709 | ||
|
|
aba61d9ebd | ||
|
|
965356ae63 | ||
|
|
d1b5767a82 | ||
|
|
ae7d5bf617 | ||
|
|
e3667cb908 | ||
|
|
f745c0181a | ||
|
|
dd885780d6 | ||
|
|
95ea74549c | ||
|
|
445aa844b9 | ||
|
|
f098fe319b | ||
|
|
2ab6fef099 | ||
|
|
8e83bcd2ac | ||
|
|
d32f2e9999 | ||
|
|
4739131ca6 | ||
|
|
b164c4dec3 | ||
|
|
f5235b2d76 | ||
|
|
362f2a84bd | ||
|
|
d828f90c26 | ||
|
|
f687ac0c32 | ||
|
|
0772098b1b | ||
|
|
06966d2a0e | ||
|
|
46346e9e3a | ||
|
|
303465af35 | ||
|
|
71ab81ec69 | ||
|
|
aab479f8cf | ||
|
|
fd052d2a86 | ||
|
|
acb32d472d | ||
|
|
683e530cf3 | ||
|
|
f521498b43 | ||
|
|
fb3457d157 | ||
|
|
b1929bb2b6 | ||
|
|
141ac8df59 | ||
|
|
30775bc3e3 | ||
|
|
0bc8c9c8f9 | ||
|
|
efccd6353b | ||
|
|
8c42d1257f | ||
|
|
94bc26e652 | ||
|
|
29ac9aea5d | ||
|
|
b5ca1e9e53 | ||
|
|
899c85ae67 | ||
|
|
4554a49d32 | ||
|
|
203f357c32 | ||
|
|
8f9958dc24 | ||
|
|
dbcc0a85d0 | ||
|
|
b2351ea0dc | ||
|
|
1f12be3072 | ||
|
|
88272cf7ac | ||
|
|
62b1f32062 | ||
|
|
02ab14cc2e | ||
|
|
018f7b7378 | ||
|
|
5b8350aaaa | ||
|
|
be83bb13a8 | ||
|
|
aea6e71ef8 | ||
|
|
b3bdcd2278 | ||
|
|
18ac41973b | ||
|
|
b7b23a0ff5 | ||
|
|
ded1655d49 | ||
|
|
52fb306535 | ||
|
|
de6f3ad017 | ||
|
|
a3718cde06 | ||
|
|
9760475a39 | ||
|
|
f2a5fe3678 | ||
|
|
2dba1e391d | ||
|
|
95338f9cc4 | ||
|
|
d0854927fc | ||
|
|
b57d0eb418 | ||
|
|
8f27578de2 | ||
|
|
5a7eb9c881 | ||
|
|
eacccd2acb | ||
|
|
f0c5052aa8 | ||
|
|
b9a0500f16 | ||
|
|
49b3663332 | ||
|
|
0d18a1e85f | ||
|
|
26301897d0 | ||
|
|
4bb5ccbefb | ||
|
|
01cbf17ba2 | ||
|
|
a16739a3ac | ||
|
|
4355145a62 | ||
|
|
bb7cd97439 | ||
|
|
17b34c7309 | ||
|
|
3a5049042a | ||
|
|
0367a80819 | ||
|
|
f395119ede | ||
|
|
ad7d4c6b70 | ||
|
|
9e56d3969a | ||
|
|
1aad55b66f | ||
|
|
70789cf4bb | ||
|
|
951d25ff66 | ||
|
|
437e258384 | ||
|
|
f225891526 | ||
|
|
9c1483100a | ||
|
|
8449a5df87 | ||
|
|
0232778f35 | ||
|
|
f28fccd2db | ||
|
|
3f3cb9d09b | ||
|
|
53cdf8a3d1 | ||
|
|
4f9de66f0e | ||
|
|
b548ffabbe | ||
|
|
ac5f21d613 | ||
|
|
5a77d015be | ||
|
|
baf92f3f1c | ||
|
|
16258f21d1 | ||
|
|
6401920d70 | ||
|
|
1be29606c5 | ||
|
|
fb66e2e249 | ||
|
|
aaf71b380e | ||
|
|
a29aec9abb | ||
|
|
d42ea9e88a | ||
|
|
4985c11bd6 | ||
|
|
5198b513d3 | ||
|
|
366a16ff05 | ||
|
|
522d93607a | ||
|
|
f5cf1e46a4 | ||
|
|
294f93af97 | ||
|
|
fcb3dfe56d | ||
|
|
ebf20b83b2 | ||
|
|
108afbc0bb | ||
|
|
9cb9f98d2f | ||
|
|
f5577fdbd8 | ||
|
|
8a81ed1b5a | ||
|
|
593cde16bc | ||
|
|
ec2d180739 | ||
|
|
049f2f0805 | ||
|
|
b34fc03e5e | ||
|
|
4f8458d8d6 | ||
|
|
b24b490393 | ||
|
|
264953871b | ||
|
|
542455ce2a | ||
|
|
fe2fdef7b0 | ||
|
|
430dadaf83 | ||
|
|
36f1dea5e1 | ||
|
|
8dd16caec8 | ||
|
|
ea5350c5ec | ||
|
|
814aad65f1 | ||
|
|
5843a45d01 | ||
|
|
999494cef8 | ||
|
|
47579bdf5c | ||
|
|
b02780c41d | ||
|
|
ea930a352c | ||
|
|
f9dacf8aac | ||
|
|
b5029dfdad | ||
|
|
cfcd0fa6f7 | ||
|
|
714e103ac3 | ||
|
|
595ecd5f4b | ||
|
|
324070c8f6 | ||
|
|
2017407b4d | ||
|
|
df9c213bb0 | ||
|
|
9dcdc7e79a | ||
|
|
10f94b0ab1 | ||
|
|
ecd5351870 | ||
|
|
08ad6abd04 | ||
|
|
33fb617053 | ||
|
|
91690a2920 | ||
|
|
7896debe6c | ||
|
|
41b2ed2bbc | ||
|
|
71fbe6e800 | ||
|
|
152b5d46c4 | ||
|
|
90b9f08e5d | ||
|
|
1f5a73a530 | ||
|
|
049cf8cdf4 | ||
|
|
19d4705ffd | ||
|
|
299f9ebffa | ||
|
|
f44186e575 | ||
|
|
e18068d9d8 | ||
|
|
e6c1aeaf08 | ||
|
|
fae5ceef2a | ||
|
|
4617960863 | ||
|
|
78eaf503fd | ||
|
|
f6711b7da5 | ||
|
|
955e00fbec | ||
|
|
4e36925c66 | ||
|
|
c4b52fc7c9 | ||
|
|
e87c31de66 | ||
|
|
9bd212cf24 | ||
|
|
ef14c18c8e | ||
|
|
98f39a37fb | ||
|
|
723b4a15a3 | ||
|
|
c07d3bd8dd | ||
|
|
456c347d45 | ||
|
|
60f13e7a86 | ||
|
|
35fc7fda7a | ||
|
|
50d750733e | ||
|
|
7a7c60459e | ||
|
|
2e8f8c9b0c | ||
|
|
b3dc23c5a8 | ||
|
|
59ff42c421 | ||
|
|
efaf09ee4b | ||
|
|
55abd1c6d0 | ||
|
|
58e9c26115 | ||
|
|
ee59dc1b5c | ||
|
|
266949c7fc | ||
|
|
2426aaa099 | ||
|
|
b51f5da314 | ||
|
|
781d7b0d9b | ||
|
|
92a483bca2 | ||
|
|
e7047ec2fc | ||
|
|
411c18bbce | ||
|
|
4bc14da2b3 | ||
|
|
4c13945c37 | ||
|
|
51eb0da824 | ||
|
|
11b3325e99 | ||
|
|
f48afb4710 | ||
|
|
0f3e0cb4ad | ||
|
|
a56cbd3036 | ||
|
|
407da77ea4 | ||
|
|
6413e5f81a | ||
|
|
4f67d0b647 | ||
|
|
1f42bd7057 | ||
|
|
7993780dda | ||
|
|
b8c67ce41b | ||
|
|
9acd40d97b | ||
|
|
cfb449cc80 | ||
|
|
7d9b09adaa | ||
|
|
1e301ff83e | ||
|
|
19d46f8f2f | ||
|
|
86e3395c11 | ||
|
|
f74c32cad2 | ||
|
|
ab60dcf564 | ||
|
|
8a23ca0b28 | ||
|
|
5abdf9a556 | ||
|
|
bde33501c4 | ||
|
|
e3ff414513 | ||
|
|
63c1303cfb | ||
|
|
ddd43b6bc3 | ||
|
|
feba7cbf4d | ||
|
|
4d06d99bf8 | ||
|
|
8c291b97b9 | ||
|
|
4e064fbab4 | ||
|
|
cf3d79d4c1 | ||
|
|
6a3c4ac50a | ||
|
|
48efc25f74 | ||
|
|
a102b21d17 | ||
|
|
1acc315cea | ||
|
|
00db1b5579 | ||
|
|
37f45d9e56 | ||
|
|
ec02c761aa | ||
|
|
09b7f6c2a1 | ||
|
|
6bb5503524 | ||
|
|
b5e99bd064 | ||
|
|
bccbc5509a | ||
|
|
ec951cdc15 | ||
|
|
e288430c05 | ||
|
|
7ca00d694d | ||
|
|
3716950cfc | ||
|
|
5b44631397 | ||
|
|
c31055db61 | ||
|
|
01c13bddea | ||
|
|
ca1c182567 | ||
|
|
c4fdf09c05 | ||
|
|
1df5bb5687 | ||
|
|
75f9dec644 | ||
|
|
2f41a38568 | ||
|
|
441c490024 | ||
|
|
4cd1fb1f40 | ||
|
|
7459652e3e | ||
|
|
b55a827ea2 | ||
|
|
84e0489562 | ||
|
|
7885faf18b | ||
|
|
69b7113bb5 | ||
|
|
3ba2281f96 | ||
|
|
c1b1e8c329 | ||
|
|
c85bf2352d | ||
|
|
04056b767f | ||
|
|
d322e7baba | ||
|
|
ada2ac9ae2 | ||
|
|
cb5eadb33b | ||
|
|
bbc2c44541 | ||
|
|
cf88779527 | ||
|
|
79a7c17176 | ||
|
|
0719388cfb | ||
|
|
25f457c7f6 | ||
|
|
7e5a6313f0 | ||
|
|
f16b52cb7d | ||
|
|
0963edbc65 | ||
|
|
0b1d89d7ae | ||
|
|
08d3e929e5 | ||
|
|
f207e1682f | ||
|
|
e6fa47380a | ||
|
|
61954951ed | ||
|
|
a221a313ae | ||
|
|
f8ae991717 | ||
|
|
d87a0d0286 | ||
|
|
39f716a121 | ||
|
|
d638b05834 | ||
|
|
ea7cf737a1 | ||
|
|
9812e88959 | ||
|
|
c1aaad06d8 | ||
|
|
d8fd48c436 | ||
|
|
204a14b8cf | ||
|
|
92ec592520 | ||
|
|
c170365dcf | ||
|
|
a0576bdeea | ||
|
|
48a9d63af3 | ||
|
|
d5059ba5e1 | ||
|
|
dd1f20edc5 | ||
|
|
3feff7d78a | ||
|
|
5d0c6b0868 | ||
|
|
c09b5941ce | ||
|
|
19c29b6007 | ||
|
|
97d647a139 | ||
|
|
bcc46a2f25 | ||
|
|
d428de590f | ||
|
|
cd1c600548 | ||
|
|
254367788a | ||
|
|
7e4325cbc9 | ||
|
|
7ace7231c6 | ||
|
|
1fd456f91a | ||
|
|
f6546eff45 | ||
|
|
2faa0f19eb | ||
|
|
d8fbd3a8ba | ||
|
|
10b5006934 | ||
|
|
f4ca3d8757 | ||
|
|
762be79f0b | ||
|
|
639b73cd2a | ||
|
|
a698a6391a | ||
|
|
89c78a9808 | ||
|
|
f210e197f3 | ||
|
|
9122379abc | ||
|
|
1a91a2700b | ||
|
|
b5f3996250 | ||
|
|
84b3c18f71 | ||
|
|
441bfa2dfb | ||
|
|
d310beb417 | ||
|
|
411bcf1a41 | ||
|
|
efd17316ab | ||
|
|
4342703743 | ||
|
|
6987176059 | ||
|
|
39436b2152 | ||
|
|
66037309c6 | ||
|
|
380f6048e0 | ||
|
|
a9c0602149 | ||
|
|
825fe7bd23 | ||
|
|
dc899c4c88 | ||
|
|
7fa56dd773 | ||
|
|
dac26729a9 | ||
|
|
4d1120f5fd | ||
|
|
9824fe3e39 | ||
|
|
4e2e5be201 | ||
|
|
01acfa71ca | ||
|
|
d2af0f25be | ||
|
|
27f4c212f3 | ||
|
|
ebfa769925 | ||
|
|
e05a7df7f9 | ||
|
|
0f370f9b66 | ||
|
|
556acefe75 | ||
|
|
c96ece9f5e | ||
|
|
843a13e88f | ||
|
|
6cf23d6610 | ||
|
|
a425e1d26d | ||
|
|
04a0af9085 | ||
|
|
0047702aab | ||
|
|
457b7073b5 | ||
|
|
ef92f507dd | ||
|
|
f328194d12 | ||
|
|
f6010e106b | ||
|
|
786d6d55cf | ||
|
|
19418b6b28 | ||
|
|
37fd3d40ae | ||
|
|
f559a57206 | ||
|
|
24092eea06 | ||
|
|
aaacb8c955 | ||
|
|
addf47dcac | ||
|
|
8b497881f2 | ||
|
|
056a43e1a6 | ||
|
|
adc3183cd2 | ||
|
|
0aec98dce2 | ||
|
|
907da0580b | ||
|
|
7d117d7591 | ||
|
|
9b7f8ba84b | ||
|
|
dab5ec9b8d | ||
|
|
c3f9e01794 | ||
|
|
2127f7c8eb | ||
|
|
f659d23619 | ||
|
|
216c110609 | ||
|
|
6f3b91fe26 | ||
|
|
57b8e00695 | ||
|
|
bea40e6a41 | ||
|
|
f87a47dc97 | ||
|
|
6418d767e8 | ||
|
|
66ee45b65c | ||
|
|
3945a32177 | ||
|
|
eb6241a3dd | ||
|
|
dc803d25a6 | ||
|
|
b8187d39fb | ||
|
|
8fb89a5728 | ||
|
|
67b43f75e2 | ||
|
|
cfccfbd6f4 | ||
|
|
1591e3479d | ||
|
|
448c435b1b | ||
|
|
ef33252c43 | ||
|
|
f971a5d8ed | ||
|
|
49383a4af7 | ||
|
|
ce8bfc56ad | ||
|
|
c2e5546071 | ||
|
|
8aa083bd28 | ||
|
|
a295770e32 | ||
|
|
c9f00bcce2 | ||
|
|
f1863a1fe7 | ||
|
|
c30bd02c9d | ||
|
|
0d62caab21 | ||
|
|
323029baa6 | ||
|
|
57709c4bc6 | ||
|
|
b464afb9e2 | ||
|
|
795c454db1 | ||
|
|
b7ce583a5e | ||
|
|
e08bf16637 | ||
|
|
66a13ccbdf | ||
|
|
a735bf5c2a | ||
|
|
c3edaa7941 | ||
|
|
63cd4d4e20 | ||
|
|
b96c3595af | ||
|
|
26ca04a8a2 | ||
|
|
1980f639b1 | ||
|
|
63a05dde46 | ||
|
|
1bb5794a1f | ||
|
|
23b50bcccc | ||
|
|
9216b96b41 | ||
|
|
d3faed875d | ||
|
|
c37acb82aa | ||
|
|
647cac5bb4 | ||
|
|
f11a0c8f30 | ||
|
|
2458e54814 | ||
|
|
dfe9714c86 | ||
|
|
1b3b75208c | ||
|
|
eabe3eaec0 | ||
|
|
b002d2b4f1 | ||
|
|
445a4b669a | ||
|
|
092cfca522 | ||
|
|
f866023399 | ||
|
|
8b70cb0ef8 | ||
|
|
810e28a397 | ||
|
|
6ff741b5cf | ||
|
|
98c8ac0d70 | ||
|
|
cb2c7c4d34 | ||
|
|
78a6e5eb2b | ||
|
|
ed9bfe2262 | ||
|
|
86511bbbef | ||
|
|
1c71038540 | ||
|
|
8ea131fc80 | ||
|
|
de8d320230 | ||
|
|
b2a73c404b | ||
|
|
0666267d27 | ||
|
|
042189fd87 | ||
|
|
a8b7268911 | ||
|
|
1461f02deb | ||
|
|
63730d9dd0 | ||
|
|
818a746e32 | ||
|
|
ce15620dc1 | ||
|
|
b2419c31c1 | ||
|
|
918abd7276 | ||
|
|
b120366d48 | ||
|
|
340963b2d7 | ||
|
|
750acf3a45 | ||
|
|
0a1e734193 | ||
|
|
5496b4a7cd | ||
|
|
f22fa9594d | ||
|
|
b491d477c3 | ||
|
|
2df4cb93ac | ||
|
|
42ba7a1b75 | ||
|
|
573246f73c | ||
|
|
2b998de460 | ||
|
|
fe5da2e60d | ||
|
|
1b7ba44e79 | ||
|
|
b65e5aca86 | ||
|
|
677d14c213 | ||
|
|
e3e69c25fd | ||
|
|
9d527d076b | ||
|
|
58e5feb3f4 | ||
|
|
12f798dc18 | ||
|
|
9ef8d2f671 | ||
|
|
d52ce4ea1a | ||
|
|
8b0747d657 | ||
|
|
f22f64f0db | ||
|
|
747b4004ea | ||
|
|
b35d6e5cff | ||
|
|
d75ad774a9 | ||
|
|
571571ca19 | ||
|
|
b61b663895 | ||
|
|
ee4a15aae0 | ||
|
|
c01e94a431 | ||
|
|
9fcd9e191e | ||
|
|
8bdcbbb085 | ||
|
|
b7289e912c | ||
|
|
daef1f00c2 | ||
|
|
21784def70 | ||
|
|
5b0a06af48 | ||
|
|
43af28f5b4 | ||
|
|
6b4ae919e8 | ||
|
|
5449a2a8b5 | ||
|
|
8b428cf0f7 | ||
|
|
9204a9b2c2 | ||
|
|
f80f3f492a | ||
|
|
e61adc0d89 | ||
|
|
89f2bfbb58 | ||
|
|
a3d4d7bf68 | ||
|
|
65c24bd3d4 | ||
|
|
cbaf3c5bba | ||
|
|
34c3be365a | ||
|
|
cbd9af8583 | ||
|
|
89d544d6f2 | ||
|
|
b87c288016 | ||
|
|
0f741a9e2d | ||
|
|
88662c243d | ||
|
|
93d87d6d4c | ||
|
|
cdd925b289 | ||
|
|
64c360c515 | ||
|
|
fb2a94af3f | ||
|
|
1407a055ee | ||
|
|
275068c86f | ||
|
|
6866158f0d | ||
|
|
bffbbeaa9a | ||
|
|
11cd983d58 | ||
|
|
d10b2f3173 | ||
|
|
47637bea6d | ||
|
|
2530dc0ebd | ||
|
|
f3df3ec134 | ||
|
|
79c506ebf0 | ||
|
|
f2db379fa3 | ||
|
|
ddcbb628a1 | ||
|
|
fbed632f3a | ||
|
|
e2a71338eb | ||
|
|
82451a4717 | ||
|
|
ff1e4a7063 | ||
|
|
28a8465102 | ||
|
|
5dd499c6d4 | ||
|
|
589e610ebc | ||
|
|
bd4b33d7a2 | ||
|
|
2e69bfe44d | ||
|
|
09af8447a1 | ||
|
|
8e937ce4cc | ||
|
|
6f11acbd67 | ||
|
|
944cca98c8 | ||
|
|
59d437c727 | ||
|
|
f39c9404a3 | ||
|
|
229327ad8b | ||
|
|
efe92ee546 | ||
|
|
d6220f12a9 | ||
|
|
3941a275f7 | ||
|
|
3f073b1d9c | ||
|
|
3f494cc49d | ||
|
|
e2d64485b8 | ||
|
|
ca95b71f67 | ||
|
|
6dcc6898be | ||
|
|
ea7eeb2fd2 | ||
|
|
81879bc171 | ||
|
|
6a06a5a597 | ||
|
|
3f67b03378 | ||
|
|
1ef014ee6b | ||
|
|
b118502a05 | ||
|
|
e966264188 | ||
|
|
7ee5a41aac | ||
|
|
5f536b5d23 | ||
|
|
3ddb6ffa34 | ||
|
|
c17e597d05 | ||
|
|
81f8524a12 | ||
|
|
90b717e723 | ||
|
|
e5a50ed3c4 | ||
|
|
24c539251f | ||
|
|
1aa31e4da9 | ||
|
|
51077c8212 | ||
|
|
3244fae7d4 | ||
|
|
40c7628fc8 | ||
|
|
f11f26cc53 | ||
|
|
824bd2ac11 | ||
|
|
2f454e5ae8 | ||
|
|
f2da3efc78 | ||
|
|
f7e7775990 | ||
|
|
50f5181488 | ||
|
|
4ac1f9ac55 | ||
|
|
f3352daf4f | ||
|
|
4faad81f50 | ||
|
|
b55c1602b5 | ||
|
|
7af05f07ff | ||
|
|
f6cad30bb6 | ||
|
|
63dae52324 | ||
|
|
f33acb3f02 | ||
|
|
c69a9b2f61 | ||
|
|
bedf1b2126 | ||
|
|
784ceeb90d | ||
|
|
109b5ccdcd | ||
|
|
f31260b044 | ||
|
|
5f4a2801cb | ||
|
|
6d92eee69b | ||
|
|
c75512d89d | ||
|
|
4126ca466f | ||
|
|
da840e9851 | ||
|
|
8a57969fd7 | ||
|
|
8d82639319 | ||
|
|
40d7fca368 | ||
|
|
99e6e73235 | ||
|
|
3e35ac9d7f | ||
|
|
9fc8a5efe5 | ||
|
|
818dc3a089 | ||
|
|
36b9494385 | ||
|
|
f57e844b2e | ||
|
|
343dd9bcce | ||
|
|
f7f77a746a | ||
|
|
3f2fbc4c61 | ||
|
|
4e8f2d6881 | ||
|
|
2fbd0271f6 | ||
|
|
13e50935a8 | ||
|
|
e4d7de608c | ||
|
|
19f192c3a5 | ||
|
|
86fed3fe09 | ||
|
|
2f4e9c3f9f | ||
|
|
416f3f20b4 | ||
|
|
5df0a64d30 | ||
|
|
a25df9dee0 | ||
|
|
d484b8a04e | ||
|
|
8596d483bc | ||
|
|
5f716ea467 | ||
|
|
9242ccf238 | ||
|
|
c2b5f1c15b | ||
|
|
d85af4d6f5 | ||
|
|
93bdbf5aa4 | ||
|
|
dc690161d5 | ||
|
|
a176cb56a3 | ||
|
|
254c962554 | ||
|
|
8128d39737 | ||
|
|
938c35302f | ||
|
|
6a81450144 | ||
|
|
e5227aab89 | ||
|
|
02ef355f98 | ||
|
|
7f19a04f0f | ||
|
|
67660881ed | ||
|
|
e9aba28932 | ||
|
|
279b4a1464 | ||
|
|
d5648d617e | ||
|
|
af39d750cd | ||
|
|
d792db7948 | ||
|
|
3e6f2b1a45 | ||
|
|
5266293a0f | ||
|
|
6a014af79a | ||
|
|
d9f970d8d3 | ||
|
|
b23e251036 | ||
|
|
6f8a8647de | ||
|
|
5977a94842 | ||
|
|
d6180c8c86 | ||
|
|
909bc97c52 | ||
|
|
8e76e13472 | ||
|
|
69ade87325 | ||
|
|
9bbf768d3c | ||
|
|
1978f996d8 | ||
|
|
ad0a9df77a | ||
|
|
760021e677 | ||
|
|
b2c86acd0a | ||
|
|
959cdb358b | ||
|
|
a5a3a7bbc6 | ||
|
|
2d6d9f7552 | ||
|
|
6bbbdd26f4 | ||
|
|
65a3307bc9 | ||
|
|
21f62c3346 | ||
|
|
746297314f | ||
|
|
59fd178014 | ||
|
|
42fd522c63 | ||
|
|
1bfa2d27a6 | ||
|
|
5e46e0800b | ||
|
|
f800b52172 | ||
|
|
ab4cc1e11b | ||
|
|
4ee011adb5 | ||
|
|
71fafd761a | ||
|
|
4b8d8826af | ||
|
|
62fc7f4f24 | ||
|
|
784479939d | ||
|
|
6ce2ed870d | ||
|
|
c92464db69 | ||
|
|
a89bf734a9 | ||
|
|
1055398849 | ||
|
|
a66298e6f1 | ||
|
|
ca58198a76 | ||
|
|
7008a0ba66 | ||
|
|
0091125cae | ||
|
|
89ca2afd6b | ||
|
|
e63a5ba122 | ||
|
|
a7936ef96d | ||
|
|
2ebcd63d6a | ||
|
|
cdad0e6485 | ||
|
|
2a05fa0d48 | ||
|
|
48b2915c18 | ||
|
|
254f06c131 | ||
|
|
8183a4ca7f | ||
|
|
265a7e7ec7 | ||
|
|
cfffda83fb | ||
|
|
74a203a050 | ||
|
|
12504105c4 | ||
|
|
2bb297b102 | ||
|
|
2fa077b0e9 | ||
|
|
7eba5c308a | ||
|
|
44b76a75d2 | ||
|
|
61074b43a6 | ||
|
|
d1e23e04aa | ||
|
|
5d0774d62f | ||
|
|
ad6b71352d | ||
|
|
52e56bf70d | ||
|
|
c480af9007 | ||
|
|
f644b112ad | ||
|
|
bdd5411747 | ||
|
|
1f8ea99b4b | ||
|
|
5ef157bae1 | ||
|
|
23f2b4d0a8 | ||
|
|
9d38a270db | ||
|
|
6f1ddda9b2 | ||
|
|
6a16a636bf | ||
|
|
bb06a8c870 | ||
|
|
28e574bc4b | ||
|
|
a4efbd590d | ||
|
|
2a8af8e675 | ||
|
|
90f3856fd5 | ||
|
|
15bcb813d4 | ||
|
|
484af8ed53 | ||
|
|
1cf33a46d5 | ||
|
|
151b12a80f | ||
|
|
484cfc3d76 | ||
|
|
32d0df0c1f | ||
|
|
22472fe5a1 | ||
|
|
325409a011 | ||
|
|
3984dc6539 | ||
|
|
94c026cd19 | ||
|
|
7d35939206 | ||
|
|
caf7c50408 | ||
|
|
1965c0099e | ||
|
|
b9abecfc4c | ||
|
|
e258a1c087 | ||
|
|
091fb64681 | ||
|
|
92a3ff6168 | ||
|
|
9e055a0f1f | ||
|
|
eec769be59 | ||
|
|
b89e6d74fa | ||
|
|
c1da9420c7 | ||
|
|
adc5df1bc3 | ||
|
|
07c6bee78f | ||
|
|
b407590cee | ||
|
|
40161553e2 | ||
|
|
285817b28a | ||
|
|
ee93a70e7b | ||
|
|
42f5da5d2d | ||
|
|
5109f16b77 | ||
|
|
90fda5c97c | ||
|
|
06a59fd44d | ||
|
|
5edb1beb63 | ||
|
|
fe640e5858 | ||
|
|
e387f5ba62 | ||
|
|
9f932bcc82 | ||
|
|
86f0e873c7 | ||
|
|
9018ddc32a | ||
|
|
1afb002d69 | ||
|
|
4f3ff46a81 | ||
|
|
af34245692 | ||
|
|
88d71f4793 | ||
|
|
39d1e39212 | ||
|
|
5d83e9e1de | ||
|
|
23a85ba191 | ||
|
|
d949e8fe18 | ||
|
|
a9200ed0b3 | ||
|
|
9fba05f758 | ||
|
|
07e1417ef0 | ||
|
|
edc1f7b176 | ||
|
|
abff264000 | ||
|
|
bf3a67be43 | ||
|
|
dd78f7463f | ||
|
|
c9ef9f1c48 | ||
|
|
05db78e9c5 | ||
|
|
75c11d7fec | ||
|
|
0ca2f4f824 | ||
|
|
96bb0c9471 | ||
|
|
08d00dad52 | ||
|
|
357aace895 | ||
|
|
624742d9b4 | ||
|
|
3cd92e87d1 | ||
|
|
1a7cd2c0e2 | ||
|
|
f4d2abd3df | ||
|
|
f0f30fc43f | ||
|
|
8f5c2bc8aa | ||
|
|
112e19a59c | ||
|
|
67a4fb912f | ||
|
|
0dea1142da | ||
|
|
41cabca2ab | ||
|
|
c38fd1f661 | ||
|
|
dec6fd3adc | ||
|
|
3e6acc24d6 | ||
|
|
a4d0720fa1 | ||
|
|
bc4667acbc | ||
|
|
db39b04d76 | ||
|
|
827e6cc644 | ||
|
|
4715ce5903 | ||
|
|
27e25e9d1e | ||
|
|
b726d64229 | ||
|
|
b4416280cf | ||
|
|
905e28ee87 | ||
|
|
70c4851f2c | ||
|
|
bc9efb577b | ||
|
|
450f0d7248 | ||
|
|
4d1178cc24 | ||
|
|
1750513ac7 | ||
|
|
4387ba6a17 | ||
|
|
79de9d6c78 | ||
|
|
00a3bc4359 | ||
|
|
5fa6f9ebe1 | ||
|
|
4afa243ff1 | ||
|
|
e17f9311c8 | ||
|
|
2e2f57e23b | ||
|
|
028eb45723 | ||
|
|
18c4c7bfe7 | ||
|
|
9cfb1c88d6 | ||
|
|
24173d11bf | ||
|
|
54cb8214d9 | ||
|
|
150ba0e1ff | ||
|
|
5508c228e7 | ||
|
|
373a3384ac | ||
|
|
51ab31dbab | ||
|
|
415c4f613c | ||
|
|
e49d97298a | ||
|
|
64347aaaf9 | ||
|
|
1f246bfcc8 | ||
|
|
140c20fb5e | ||
|
|
5fdc5e5c25 | ||
|
|
154e735c46 | ||
|
|
8257a3c1f6 | ||
|
|
1063f1d833 | ||
|
|
538c2b2d8d | ||
|
|
4477ee33ac | ||
|
|
70a80ef3ad | ||
|
|
6fe2b5236d | ||
|
|
1aee086a09 | ||
|
|
68b5eed41d | ||
|
|
fe9acb3469 | ||
|
|
acf566b291 | ||
|
|
68ad748e77 | ||
|
|
bce3d08c66 | ||
|
|
b3f3d5a42f | ||
|
|
57697373ef | ||
|
|
7dcf3eb3fc | ||
|
|
deee2c1ef2 | ||
|
|
e598d9b59e | ||
|
|
e697ef6dec | ||
|
|
1a0deab2a5 | ||
|
|
6726b3c2cb | ||
|
|
93021da221 | ||
|
|
a07a4ada19 | ||
|
|
365316aa59 | ||
|
|
fe980e2334 | ||
|
|
69ce235c7b | ||
|
|
684c5907dd | ||
|
|
6fcdf75325 | ||
|
|
15b040907c | ||
|
|
058c727282 | ||
|
|
cec388f208 | ||
|
|
528ea98bd3 | ||
|
|
fd8f39a283 | ||
|
|
551fed3169 | ||
|
|
7c29c9eec1 | ||
|
|
86a1386d6f | ||
|
|
1a5c3a9a60 | ||
|
|
d2a6c5ffa2 | ||
|
|
4024bc7eee | ||
|
|
d4a2970092 | ||
|
|
057865a6a5 | ||
|
|
f95a88d988 | ||
|
|
e0de7c0852 | ||
|
|
1e2aee3919 | ||
|
|
ce25ceda58 | ||
|
|
615eae2c82 | ||
|
|
4d4c8c8a40 | ||
|
|
9a3dab0a2e | ||
|
|
e8bc306850 | ||
|
|
d31c0c5264 | ||
|
|
31781e97b6 | ||
|
|
73316758be | ||
|
|
09a5c07886 | ||
|
|
911a2d6cb4 | ||
|
|
828736e7d0 | ||
|
|
4447ddc8bb | ||
|
|
3497fd007f | ||
|
|
a48dfe98cf | ||
|
|
2192a91d62 | ||
|
|
486e45ffaf | ||
|
|
022f09447b | ||
|
|
8a7f255cd0 | ||
|
|
fb732f7a94 | ||
|
|
57a0c9c98d | ||
|
|
e83d5b64dc | ||
|
|
42d309fffc | ||
|
|
25725957e9 | ||
|
|
72f0751905 | ||
|
|
12cd67b7ca | ||
|
|
2a3d933cc6 | ||
|
|
96ebfc3b36 | ||
|
|
9ae8254e20 | ||
|
|
a95a086be7 | ||
|
|
dc1bc0f423 | ||
|
|
889eaba2b7 | ||
|
|
6fd2d7cfee | ||
|
|
c2db3de498 | ||
|
|
8d67211450 | ||
|
|
58d61dd639 | ||
|
|
3e738c8a6c | ||
|
|
2d1968f841 | ||
|
|
04f1a280e0 | ||
|
|
c6ccc2f690 | ||
|
|
6b547c3956 | ||
|
|
92974f9a98 | ||
|
|
94f2e7f9f9 | ||
|
|
80b6f9b0cb | ||
|
|
071197fb64 | ||
|
|
c49fb47fbe | ||
|
|
c8a720d17d | ||
|
|
c7db333abb | ||
|
|
85d1d1f870 | ||
|
|
2321939218 | ||
|
|
f9d624c504 | ||
|
|
1bc557c9c5 | ||
|
|
5010da6ab0 | ||
|
|
9d27e00ddb | ||
|
|
c479eace45 | ||
|
|
002052f8de | ||
|
|
eae2615348 | ||
|
|
a71a6b5acf | ||
|
|
ac441c7413 | ||
|
|
b9fa42a197 | ||
|
|
37b135a400 | ||
|
|
c9388ecc73 | ||
|
|
12b9fd4f03 | ||
|
|
503a5a24fb | ||
|
|
3519a5a026 | ||
|
|
e8dd99fa53 | ||
|
|
7f794a122f | ||
|
|
dfd3ead90d | ||
|
|
9cf500a3f6 | ||
|
|
7428f51217 | ||
|
|
552b36e657 | ||
|
|
086c1db2d9 | ||
|
|
33386bed66 | ||
|
|
5fece85491 | ||
|
|
49ccd2a8e1 | ||
|
|
1323afed8b | ||
|
|
a063c8c188 | ||
|
|
4f3b15e6ea | ||
|
|
17bf8dc99c | ||
|
|
29760b2eb7 | ||
|
|
767977c533 | ||
|
|
839bbcb039 | ||
|
|
3072498665 | ||
|
|
6ad44b76b9 | ||
|
|
d88f52ee7d | ||
|
|
399a6b2b47 | ||
|
|
451872527c | ||
|
|
3fbfa1885c | ||
|
|
3d5b2d41b5 | ||
|
|
30adc62232 | ||
|
|
96a54866ab | ||
|
|
51b9de037d | ||
|
|
96688aa646 | ||
|
|
a6350f717d | ||
|
|
f69876280c | ||
|
|
e93dcba46e | ||
|
|
9a9109431b | ||
|
|
bb30b9f913 | ||
|
|
094b47391d | ||
|
|
2c42f6a8b8 | ||
|
|
121c51f4f3 | ||
|
|
af3c722fec | ||
|
|
ca8d6f1072 | ||
|
|
8dc28b6c75 | ||
|
|
7bd5ccaad0 | ||
|
|
7261a5550f | ||
|
|
25063f75d7 | ||
|
|
af5c11874c | ||
|
|
4bc4d2c767 | ||
|
|
cf3789f045 | ||
|
|
b0bbfed238 | ||
|
|
c76d0e7be9 | ||
|
|
1b0d30aeb7 | ||
|
|
a76a961cdd | ||
|
|
f80b946bea | ||
|
|
a8857320a5 | ||
|
|
cbf212f981 | ||
|
|
4158664d2b | ||
|
|
4e8e7fb067 | ||
|
|
ef610802c7 | ||
|
|
5b73ea3cf6 | ||
|
|
eb9ca33414 | ||
|
|
78df016584 | ||
|
|
4665b3ebfb | ||
|
|
85a0d29d04 | ||
|
|
416d394cd9 | ||
|
|
0062194d74 | ||
|
|
10b626b3d5 | ||
|
|
2f53ad30ee | ||
|
|
5a3d85745a | ||
|
|
3f96e1623d | ||
|
|
88351275fe | ||
|
|
fa4f50739a | ||
|
|
dfef407499 | ||
|
|
8d6dc136e1 | ||
|
|
b73d87f5e5 | ||
|
|
babada8b5f | ||
|
|
80ec0431e8 | ||
|
|
545a5046f5 | ||
|
|
10afb9639a | ||
|
|
88e66ecf94 | ||
|
|
8cdc15c309 | ||
|
|
4cbf3f5ddd | ||
|
|
c9c03c3ee6 | ||
|
|
b3400559be | ||
|
|
1010c1b43e | ||
|
|
c4dc5b80b2 | ||
|
|
38076fd6ba | ||
|
|
1d13ff0b39 | ||
|
|
0c52ce6c8e | ||
|
|
a20c206de7 | ||
|
|
6c8221580c | ||
|
|
d6eb3afd13 | ||
|
|
1b4bc60999 | ||
|
|
4379b8b411 | ||
|
|
95f154985c | ||
|
|
9dcf878f1b | ||
|
|
7698316fda | ||
|
|
f6d5996ead | ||
|
|
3ef59b50c4 | ||
|
|
12d7479144 | ||
|
|
86c76cad74 | ||
|
|
1ef44f8243 | ||
|
|
3f0afab301 | ||
|
|
aca7f36b57 | ||
|
|
6d2f1188ac | ||
|
|
e8afadd52c | ||
|
|
36ee294e83 | ||
|
|
dd7e61d77f | ||
|
|
0e22cb2680 | ||
|
|
13e4c2a9b1 | ||
|
|
aa9d92d94a | ||
|
|
077f965426 | ||
|
|
8d11e0df7a | ||
|
|
324a8c91d0 | ||
|
|
8d116cc8a2 | ||
|
|
97f1c808ce | ||
|
|
9a1843ef2d | ||
|
|
1547d72cf3 | ||
|
|
2ea7f0ecad | ||
|
|
3b29556a0c | ||
|
|
c4d7f30e25 | ||
|
|
57fa355e56 | ||
|
|
f15042dbf0 | ||
|
|
643bc48a00 | ||
|
|
ec007559ff | ||
|
|
c80d81c80a | ||
|
|
61de1c1146 | ||
|
|
918086e253 | ||
|
|
38514e3c8d | ||
|
|
493a7f9823 | ||
|
|
19f5be231d | ||
|
|
7c5dc07016 | ||
|
|
b9e5be5f56 | ||
|
|
89f46f0fa1 | ||
|
|
d1788a5ddb | ||
|
|
34d5982bd5 | ||
|
|
a2732291cd | ||
|
|
2dab5015b7 | ||
|
|
4c08ae3ff6 | ||
|
|
fa9aa90813 | ||
|
|
5634ee973c | ||
|
|
93bb42a0b5 | ||
|
|
1e16b9384d | ||
|
|
5497a44037 | ||
|
|
f9c56dbb09 | ||
|
|
262262fbb2 | ||
|
|
86691ccff5 | ||
|
|
f16eaadd4f | ||
|
|
c1295bb9f1 | ||
|
|
c52415b219 | ||
|
|
82e1807769 | ||
|
|
f6029fb925 | ||
|
|
af5167b7f3 | ||
|
|
d9de9d5478 | ||
|
|
b3a97004f4 | ||
|
|
29b9d0a245 | ||
|
|
771df8a436 | ||
|
|
9321c7871f | ||
|
|
bce9a68b39 | ||
|
|
8609e68161 | ||
|
|
15338ab694 | ||
|
|
f1e4af2c29 | ||
|
|
c46c76a399 | ||
|
|
a6a0e05a1a | ||
|
|
573c4673ee | ||
|
|
681c6f21b7 | ||
|
|
606a01df70 | ||
|
|
453e01a091 | ||
|
|
ebf1acd33c | ||
|
|
0628030bf4 | ||
|
|
cf6cbbb881 | ||
|
|
3c95c92b0f | ||
|
|
22ae0ddf15 | ||
|
|
b1db7acf72 | ||
|
|
5babacad9b | ||
|
|
a5f5091041 | ||
|
|
2091d73ef5 | ||
|
|
513931dfea | ||
|
|
63c4697b46 | ||
|
|
7d703de7e9 | ||
|
|
336458d4b5 | ||
|
|
bd28dbee0e | ||
|
|
2bd360feca | ||
|
|
374b1192a3 | ||
|
|
106a57c5ae | ||
|
|
6548157293 | ||
|
|
13707f988b | ||
|
|
af347a8c86 | ||
|
|
27641ee490 | ||
|
|
1b72f4b749 | ||
|
|
2b7b74ad68 | ||
|
|
45ee620e9c | ||
|
|
4983eb6a23 | ||
|
|
3bab69e921 | ||
|
|
94376f46ad | ||
|
|
d16bb64fa4 | ||
|
|
ff024e3064 | ||
|
|
f0acdee4c5 | ||
|
|
2f3bfd1c13 | ||
|
|
5e2319c326 | ||
|
|
f88f8661ac | ||
|
|
d3e8f3fb62 | ||
|
|
bd07f121b9 | ||
|
|
6dd8de1774 | ||
|
|
7ca81170c7 | ||
|
|
98b23cce29 | ||
|
|
c8ae90fef6 | ||
|
|
8a3e9a6d44 | ||
|
|
86cbddc475 | ||
|
|
d25709cdea | ||
|
|
0285c22a2d | ||
|
|
4ccb56d0e6 | ||
|
|
d7f18f5019 | ||
|
|
f0d9a4e1a4 | ||
|
|
d4e375fc1e | ||
|
|
45f318d2ff | ||
|
|
cf31602bce | ||
|
|
7e64aa80d8 | ||
|
|
9abdc089f8 | ||
|
|
43f39f2e37 | ||
|
|
ec73881a4d | ||
|
|
d8ab7e8012 | ||
|
|
e77db59660 | ||
|
|
652c532292 | ||
|
|
a477b68109 | ||
|
|
ca825701dc | ||
|
|
f7e05dc755 | ||
|
|
4f29753726 | ||
|
|
37612118c8 | ||
|
|
049969971e | ||
|
|
d2c5f80e2e | ||
|
|
79de8f3c25 | ||
|
|
685dcaa1e6 | ||
|
|
2f1a1c3835 | ||
|
|
c913be0464 | ||
|
|
537893420b | ||
|
|
1ab5508324 | ||
|
|
319a6831a9 | ||
|
|
07acdd4aa8 | ||
|
|
b6378edcd6 | ||
|
|
3fbb41ecc9 | ||
|
|
73305861f5 | ||
|
|
0a643efa0c | ||
|
|
376a806bfb | ||
|
|
a6c7e61e61 | ||
|
|
62adabd0e0 | ||
|
|
28bde89172 | ||
|
|
743cfc0ad6 | ||
|
|
9947956d7b | ||
|
|
8341a7d472 | ||
|
|
e78c4e813c | ||
|
|
c6954de3ea | ||
|
|
8a14fff545 | ||
|
|
a8d70ac568 | ||
|
|
ab8c383082 | ||
|
|
c552fad6d4 | ||
|
|
155526a44e | ||
|
|
e741b0c257 | ||
|
|
9fe02ce1cd | ||
|
|
42c4d6aec7 | ||
|
|
c8c78bd4b9 | ||
|
|
770cb0ba97 | ||
|
|
485425cec7 | ||
|
|
7d4ebe114f | ||
|
|
6fff2cf9b6 | ||
|
|
c1b5220ee7 | ||
|
|
d63a435362 | ||
|
|
df45fed050 | ||
|
|
20eeddfb8a | ||
|
|
ddb80bb3d4 | ||
|
|
8a44b2cc7e | ||
|
|
090bc0c1a3 | ||
|
|
8ea7a3ee68 | ||
|
|
47177c9edc | ||
|
|
f6e32a832f | ||
|
|
c21c23bbba | ||
|
|
6922ccc0b9 | ||
|
|
40194a2a68 | ||
|
|
8aa0b19d83 | ||
|
|
f54bb2a330 | ||
|
|
dc8f947d7c | ||
|
|
71f3f3f1af | ||
|
|
3f7ba86255 | ||
|
|
dfe126f3e9 | ||
|
|
f53cc00c09 | ||
|
|
67dbc5f365 | ||
|
|
256ec6c52f | ||
|
|
ef764dde1c | ||
|
|
85e4777d5c | ||
|
|
1ea6672430 | ||
|
|
d933d6f2a4 | ||
|
|
92357b2d61 | ||
|
|
46216b0e83 | ||
|
|
5c73a6e206 | ||
|
|
c8c3281f06 | ||
|
|
9c00bdd86e | ||
|
|
92dc5e1fa4 | ||
|
|
50d4326e3b | ||
|
|
d5c6a833c8 | ||
|
|
1012514e0f | ||
|
|
28ef18a894 | ||
|
|
5bfba06a3b | ||
|
|
2e1dd00c80 | ||
|
|
730cacf672 | ||
|
|
4abba65ec7 | ||
|
|
33f613bf87 | ||
|
|
4aafdb185a | ||
|
|
be520829b8 | ||
|
|
13741fb99d | ||
|
|
08b218bfa5 | ||
|
|
5558c0e4cf | ||
|
|
7cf53252ee | ||
|
|
44266d6d92 | ||
|
|
58a3a3fa68 | ||
|
|
711b13801e | ||
|
|
f25962337d | ||
|
|
15c7f1317e | ||
|
|
91c41b6dde | ||
|
|
31ffbf1133 | ||
|
|
e33fffbde1 | ||
|
|
6d29c34da7 | ||
|
|
69e8ea7143 | ||
|
|
aac6a4cf13 | ||
|
|
86e302f5f3 | ||
|
|
485d5d4a18 | ||
|
|
85cc696f50 | ||
|
|
d454d5a4f5 | ||
|
|
a17bddf2a1 | ||
|
|
7e1d5954e5 | ||
|
|
ac2c96f5b1 | ||
|
|
bb3d45a386 | ||
|
|
fe7e8dc955 | ||
|
|
edfe1b2f8b | ||
|
|
1e02d599dc | ||
|
|
3795aaf42a | ||
|
|
774d8cd721 | ||
|
|
44ac202fbf | ||
|
|
4abf0e1e10 | ||
|
|
ffdd0620d0 | ||
|
|
9ac6cb9ce4 | ||
|
|
1c7a2269d6 | ||
|
|
90fae58b49 | ||
|
|
64a73e9293 | ||
|
|
0c1a4b5576 | ||
|
|
540b917a26 | ||
|
|
cfe39b7859 | ||
|
|
6ca1ad1e27 | ||
|
|
eb2196f5ce | ||
|
|
01eaf53bb3 | ||
|
|
2400f5782e | ||
|
|
726abe4deb | ||
|
|
138ed120dd | ||
|
|
2fda5f5c98 | ||
|
|
560e1e6c4b | ||
|
|
53ac8c7df2 | ||
|
|
d72b7ed4fb | ||
|
|
2deb55512f | ||
|
|
b189a21974 | ||
|
|
396161765b | ||
|
|
93edb3ff3a | ||
|
|
30a466ba38 | ||
|
|
6671032faf | ||
|
|
17ff3173d6 | ||
|
|
bf53f9280a | ||
|
|
61dffd8669 | ||
|
|
e8d0057710 | ||
|
|
3e9e27e98f | ||
|
|
6f2240aefd | ||
|
|
bd60c11bd8 | ||
|
|
38f6207f88 | ||
|
|
dd05b7f67f | ||
|
|
67ee87522a | ||
|
|
3f6d00d387 | ||
|
|
7ef2270ee7 | ||
|
|
456bbc02c8 | ||
|
|
1d4ea00d12 | ||
|
|
abf8150b2e | ||
|
|
2d7dc71b6f | ||
|
|
7d5fb5df3f | ||
|
|
28d146bed6 | ||
|
|
9506c860f6 | ||
|
|
baa88a1c14 | ||
|
|
d28948b143 | ||
|
|
a8c912ead6 | ||
|
|
24896427fc | ||
|
|
b5f52bf01c | ||
|
|
8105f91a02 | ||
|
|
b8b8dd5c15 | ||
|
|
9466dae4e5 | ||
|
|
5234bff579 | ||
|
|
51a01ca8a6 | ||
|
|
b8d315ff76 | ||
|
|
bb93686754 | ||
|
|
64ef0ad71a | ||
|
|
0f3c353dd3 | ||
|
|
f616ecf361 | ||
|
|
2ee4f9f34e | ||
|
|
c89ec5af1e | ||
|
|
0e38c5f39c | ||
|
|
7080a28efc | ||
|
|
fda2bf9c92 | ||
|
|
63e46e5f8d | ||
|
|
d6a13f4440 | ||
|
|
18c2676084 | ||
|
|
9387f7333e | ||
|
|
2f8134a7ff | ||
|
|
408e8e9f44 | ||
|
|
0af467d18f | ||
|
|
e5565a793e | ||
|
|
2bc8db9ca5 | ||
|
|
3848849013 | ||
|
|
0992ada2fe | ||
|
|
a3b0d8631f | ||
|
|
d7d13721d3 | ||
|
|
a351e74fe9 | ||
|
|
6e4f70b817 | ||
|
|
faea46710c | ||
|
|
5521910de7 | ||
|
|
3ff95d9074 | ||
|
|
1bae5dfff6 | ||
|
|
0c3fe52ef7 | ||
|
|
1f75ce30df | ||
|
|
0c898bdb07 | ||
|
|
660645c742 | ||
|
|
af02478ba0 | ||
|
|
337dcde9fa | ||
|
|
324e22accf | ||
|
|
dfb598cf33 | ||
|
|
3c0ed0309a | ||
|
|
5e0fe4c77c | ||
|
|
173cca5413 | ||
|
|
8beec4f0e7 | ||
|
|
45af2b1742 | ||
|
|
1398fac3f1 | ||
|
|
8b33975944 | ||
|
|
dfb12f0628 |
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Help us improve Redis by reporting a bug
|
||||
title: '[BUG]'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
A short description of the bug.
|
||||
|
||||
**To reproduce**
|
||||
|
||||
Steps to reproduce the behavior and/or a minimal code sample.
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
A description of what you expected to happen.
|
||||
|
||||
**Additional information**
|
||||
|
||||
Any additional information that is relevant to the problem.
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: Crash report
|
||||
about: Submit a crash report
|
||||
title: '[CRASH] <short description>'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Notice!
|
||||
- If a Redis module was involved, please open an issue in the module's repo instead!
|
||||
- If you're using docker on Apple M1, please make sure the image you're using was compiled for ARM!
|
||||
|
||||
|
||||
**Crash report**
|
||||
|
||||
Paste the complete crash log between the quotes below. Please include a few lines from the log preceding the crash report to provide some context.
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
**Aditional information**
|
||||
|
||||
1. OS distribution and version
|
||||
2. Steps to reproduce (if any)
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest a feature for Redis
|
||||
title: '[NEW]'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**The problem/use-case that the feature addresses**
|
||||
|
||||
A description of the problem that the feature will solve, or the use-case with which the feature will be used.
|
||||
|
||||
**Description of the feature**
|
||||
|
||||
A description of what you want to happen.
|
||||
|
||||
**Alternatives you've considered**
|
||||
|
||||
Any alternative solutions or features you've considered, including references to existing open and closed feature requests in this repository.
|
||||
|
||||
**Additional information**
|
||||
|
||||
Any additional information that is relevant to the feature request.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
name: Other
|
||||
about: Can't find the right issue type? Use this one!
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask the Redis developers
|
||||
title: '[QUESTION]'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please keep in mind that this issue tracker should be used for reporting bugs or proposing improvements to the Redis server.
|
||||
|
||||
Generally, questions about using Redis should be directed to the [community](https://redis.io/community):
|
||||
|
||||
* [the mailing list](https://groups.google.com/forum/#!forum/redis-db)
|
||||
* [the `redis` tag at StackOverflow](http://stackoverflow.com/questions/tagged/redis)
|
||||
* [/r/redis subreddit](http://www.reddit.com/r/redis)
|
||||
* [the irc channel #redis](http://webchat.freenode.net/?channels=redis) on freenode
|
||||
|
||||
It is also possible that your question was already asked here, so please do a quick issues search before submitting. Lastly, if your question is about one of Redis' [clients](https://redis.io/clients), you may to contact your client's developers for help.
|
||||
|
||||
That said, please feel free to replace all this with your question :)
|
||||
+50
-15
@@ -3,26 +3,61 @@ name: CI
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build-ubuntu:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [ubuntu-latest, ubuntu-16.04]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
test-ubuntu-latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make
|
||||
# Fail build if there are warnings
|
||||
# build with TLS just for compilation coverage
|
||||
run: make REDIS_CFLAGS='-Werror' BUILD_TLS=yes
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.5
|
||||
make test
|
||||
sudo apt-get install tcl8.6
|
||||
./runtest --verbose
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
|
||||
build-debian-old:
|
||||
runs-on: ubuntu-latest
|
||||
container: debian:oldoldstable
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update && apt-get install -y build-essential
|
||||
make REDIS_CFLAGS='-Werror'
|
||||
|
||||
build-macos-latest:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [macos-latest, macOS-10.14]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make
|
||||
run: make REDIS_CFLAGS='-Werror'
|
||||
|
||||
build-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386
|
||||
make REDIS_CFLAGS='-Werror' 32bit
|
||||
|
||||
build-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror' MALLOC=libc
|
||||
|
||||
build-centos7-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
container: centos:7
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install gcc make
|
||||
make REDIS_CFLAGS='-Werror'
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
name: Daily
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
# any PR to a release branch.
|
||||
- '[0-9].[0-9]'
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
|
||||
test-ubuntu-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror -DREDIS_TEST'
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
- name: unittest
|
||||
run: ./src/redis-server test all
|
||||
|
||||
test-ubuntu-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make MALLOC=libc
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-ubuntu-no-malloc-usable-size:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make MALLOC=libc CFLAGS=-DNO_MALLOC_USABLE_SIZE
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-ubuntu-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386
|
||||
make 32bit REDIS_CFLAGS='-Werror -DREDIS_TEST'
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: |
|
||||
make -C tests/modules 32bit # the script below doesn't have an argument, we must build manually ahead of time
|
||||
./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
- name: unittest
|
||||
run: ./src/redis-server test all
|
||||
|
||||
test-ubuntu-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
make BUILD_TLS=yes
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6 tcl-tls
|
||||
./utils/gen-test-certs.sh
|
||||
./runtest --accurate --verbose --tls --dump-logs
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --tls
|
||||
./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: |
|
||||
./runtest-sentinel --tls
|
||||
./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: |
|
||||
./runtest-cluster --tls
|
||||
./runtest-cluster
|
||||
|
||||
test-ubuntu-io-threads:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
make
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6 tcl-tls
|
||||
./runtest --config io-threads 4 --config io-threads-do-reads yes --accurate --verbose --tags network --dump-logs
|
||||
- name: cluster tests
|
||||
run: |
|
||||
./runtest-cluster --config io-threads 4 --config io-threads-do-reads yes
|
||||
|
||||
test-valgrind:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make valgrind REDIS_CFLAGS='-Werror -DREDIS_TEST'
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install tcl8.6 valgrind -y
|
||||
./runtest --valgrind --verbose --clients 1 --tags -large-memory --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1
|
||||
- name: unittest
|
||||
run: |
|
||||
valgrind --track-origins=yes --suppressions=./src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full --log-file=err.txt ./src/redis-server test all
|
||||
if grep -q 0x err.txt; then cat err.txt; exit 1; fi
|
||||
|
||||
test-valgrind-no-malloc-usable-size:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make valgrind CFLAGS="-DNO_MALLOC_USABLE_SIZE"
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install tcl8.6 valgrind -y
|
||||
./runtest --valgrind --verbose --clients 1 --tags -large-memory --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1
|
||||
|
||||
test-centos7-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install gcc make
|
||||
make
|
||||
- name: test
|
||||
run: |
|
||||
yum -y install which tcl
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-centos7-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install centos-release-scl epel-release
|
||||
yum -y install devtoolset-7 openssl-devel openssl
|
||||
scl enable devtoolset-7 "make BUILD_TLS=yes"
|
||||
- name: test
|
||||
run: |
|
||||
yum -y install tcl tcltls
|
||||
./utils/gen-test-certs.sh
|
||||
./runtest --accurate --verbose --tls --dump-logs
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --tls
|
||||
./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: |
|
||||
./runtest-sentinel --tls
|
||||
./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: |
|
||||
./runtest-cluster --tls
|
||||
./runtest-cluster
|
||||
|
||||
test-macos-latest:
|
||||
runs-on: macos-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make
|
||||
- name: test
|
||||
run: |
|
||||
./runtest --accurate --verbose --no-latency --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-freebsd:
|
||||
runs-on: macos-12
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: test
|
||||
uses: vmactions/freebsd-vm@v0.3.0
|
||||
with:
|
||||
usesh: true
|
||||
sync: rsync
|
||||
copyback: false
|
||||
prepare: pkg install -y bash gmake lang/tcl86
|
||||
run: >
|
||||
gmake &&
|
||||
./runtest --accurate --verbose --no-latency --tags -large-memory --dump-logs &&
|
||||
MAKE=gmake ./runtest-moduleapi --verbose &&
|
||||
./runtest-sentinel &&
|
||||
./runtest-cluster
|
||||
|
||||
test-alpine-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
container: alpine:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
apk add build-base
|
||||
make REDIS_CFLAGS='-Werror'
|
||||
- name: test
|
||||
run: |
|
||||
apk add tcl procps
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-alpine-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
container: alpine:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
apk add build-base
|
||||
make REDIS_CFLAGS='-Werror' USE_JEMALLOC=no CFLAGS=-DUSE_MALLOC_USABLE_SIZE
|
||||
- name: test
|
||||
run: |
|
||||
apk add tcl procps
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
@@ -1,5 +1,8 @@
|
||||
.*.swp
|
||||
*.o
|
||||
*.xo
|
||||
*.so
|
||||
*.d
|
||||
*.log
|
||||
dump.rdb
|
||||
redis-benchmark
|
||||
@@ -24,8 +27,14 @@ src/nodes.conf
|
||||
deps/lua/src/lua
|
||||
deps/lua/src/luac
|
||||
deps/lua/src/liblua.a
|
||||
tests/tls/*
|
||||
.make-*
|
||||
.prerequisites
|
||||
*.dSYM
|
||||
Makefile.dep
|
||||
.vscode/*
|
||||
.idea/*
|
||||
.ccls
|
||||
.ccls-cache/*
|
||||
compile_commands.json
|
||||
redis.code-workspace
|
||||
|
||||
+905
-11
@@ -1,16 +1,910 @@
|
||||
Hello! This file is just a placeholder, since this is the "unstable" branch
|
||||
of Redis, the place where all the development happens.
|
||||
Redis 6.2 release notes
|
||||
=======================
|
||||
|
||||
There is no release notes for this branch, it gets forked into another branch
|
||||
every time there is a partial feature freeze in order to eventually create
|
||||
a new stable release.
|
||||
--------------------------------------------------------------------------------
|
||||
Upgrade urgency levels:
|
||||
|
||||
Usually "unstable" is stable enough for you to use it in development environments
|
||||
however you should never use it in production environments. It is possible
|
||||
to download the latest stable release here:
|
||||
LOW: No need to upgrade unless there are new features you want to use.
|
||||
MODERATE: Program an upgrade of the server, but it's not urgent.
|
||||
HIGH: There is a critical bug that may affect a subset of users. Upgrade!
|
||||
CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
|
||||
SECURITY: There are security fixes in the release.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
http://download.redis.io/releases/redis-stable.tar.gz
|
||||
|
||||
More information is available at http://redis.io
|
||||
Redis Community Edition 6.2.16 Released Wed 02 Oct 2024 20:17:04 IDT
|
||||
================================================================================
|
||||
|
||||
Happy hacking!
|
||||
Upgrade urgency SECURITY: See security fixes below.
|
||||
|
||||
Security fixes
|
||||
==============
|
||||
|
||||
* (CVE-2024-31449) Lua library commands may lead to stack overflow and potential RCE.
|
||||
* (CVE-2024-31228) Potential Denial-of-service due to unbounded pattern matching.
|
||||
|
||||
6.2.15 was an erroneous release,
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.14 Released Wed 18 Oct 2023 10:33:40 IDT
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency SECURITY: See security fixes below.
|
||||
|
||||
Security fixes
|
||||
==============
|
||||
|
||||
* (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a
|
||||
race condition that can be used by another process to bypass desired Unix
|
||||
socket permissions on startup.
|
||||
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.13 Released Mon July 10 12:00:00 IDT 2023
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency SECURITY: See security fixes below.
|
||||
|
||||
Security Fixes:
|
||||
* (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger
|
||||
a heap overflow in the cjson and cmsgpack libraries, and result in heap
|
||||
corruption and potentially remote code execution. The problem exists in all
|
||||
versions of Redis with Lua scripting support, starting from 2.6, and affects
|
||||
only authenticated and authorized users.
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Re-enable downscale rehashing while there is a fork child (#12276)
|
||||
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.12 Released Mon Apr 17 16:00:00 IST 2023
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: SECURITY, contains fixes to security issues.
|
||||
|
||||
Security Fixes:
|
||||
* (CVE-2023-28856) Authenticated users can use the HINCRBYFLOAT command to create
|
||||
an invalid hash field that will crash Redis on access
|
||||
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Fix CLIENT REPLY OFF|SKIP to not silence push notifications (#11875)
|
||||
* Disconnect pub-sub subscribers when revoking allchannels permission (#11992)
|
||||
* Trim excessive memory usage in stream nodes when exceeding `stream-node-max-bytes` (#11885)
|
||||
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.11 Released Tue Feb 28 12:00:00 IST 2023
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: SECURITY, contains fixes to security issues.
|
||||
|
||||
Security Fixes:
|
||||
* (CVE-2023-25155) Specially crafted SRANDMEMBER, ZRANDMEMBER, and HRANDFIELD
|
||||
commands can trigger an integer overflow, resulting in a runtime assertion
|
||||
and termination of the Redis server process.
|
||||
* (CVE-2022-36021) String matching commands (like SCAN or KEYS) with a specially
|
||||
crafted pattern to trigger a denial-of-service attack on Redis, causing it to
|
||||
hang and consume 100% CPU time.
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Fix a crash when reaching the maximum invalidations limit of client-side tracking (#11814)
|
||||
* Fix cluster inbound link keepalive time (#11785)
|
||||
* Make sure that fork child doesn't do incremental rehashing (#11692)
|
||||
|
||||
Performance and resource utilization improvements
|
||||
=================================================
|
||||
|
||||
* Avoid realloc to reduce size of strings when it is unneeded (#11766)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.10 Released Mon Jan 17 12:00:00 IST 2023
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: MODERATE, a quick followup fix for a recently released 6.2.9.
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Revert the change to KEYS in the recent client output buffer limit fix (#11676)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.9 Released Mon Jan 16 12:00:00 IDT 2023
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: SECURITY, contains fixes to security issues.
|
||||
|
||||
Security Fixes:
|
||||
* (CVE-2022-35977) Integer overflow in the Redis SETRANGE and SORT/SORT_RO
|
||||
commands can drive Redis to OOM panic
|
||||
* (CVE-2023-22458) Integer overflow in the Redis HRANDFIELD and ZRANDMEMBER
|
||||
commands can lead to denial-of-service
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Avoid possible hang when client issues long KEYS, SRANDMEMBER, HRANDFIELD,
|
||||
and ZRANDMEMBER commands and gets disconnected by client output buffer limit (#11676)
|
||||
* Fix sentinel issue if replica changes IP (#11590)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.8 Released Mon 12 Dec 2022 12:00:00 IST
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: MODERATE, Contains fixes for a few non-critical or unlikely bugs
|
||||
|
||||
Performance and resource utilization improvements
|
||||
=================================================
|
||||
|
||||
* Optimize zset conversion on large ZRANGESTORE (#10789)
|
||||
|
||||
Module API changes
|
||||
==================
|
||||
|
||||
* Fix crash in CLIENT_CHANGE event, when the selected database is not 0 (#11500)
|
||||
* Fix RM_SetAbsExpire and RM_GetAbsExpire API registration (#11025, #8564)
|
||||
|
||||
Security improvements
|
||||
=====================
|
||||
|
||||
* Sentinel: avoid logging auth-pass value (#9652)
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Fix a crash when a Lua script returns a meta-table (#11032)
|
||||
* Fix ZRANGESTORE crash when zset_max_listpack_entries is 0 (#10767)
|
||||
* Unpause clients after manual failover ends instead of waiting for timed (#9676)
|
||||
* TLS: Notify clients on connection shutdown (#10931)
|
||||
* Avoid hang of diskless replication fork child when parent crashes (#11463)
|
||||
* Fix sentinel function that compares hostnames if failed resolve (#11419)
|
||||
* Fix a hang when eviction is combined with lazy-free and maxmemory-eviction-tenacity
|
||||
is set to 100 (#11237)
|
||||
* Fix bug with scripts ignoring client tracking NOLOOP (#11052)
|
||||
* Fix client-side tracking breaking protocol when FLUSHDB / FLUSHALL / SWAPDB is
|
||||
used inside MULTI-EXEC (#11038)
|
||||
* Fix BITFIELD overflow detection on some compilers due to undefined behaviour (#9601)
|
||||
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.7 Released Wed Apr 27 12:00:00 IDT 2022
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: SECURITY, contains fixes to security issues.
|
||||
|
||||
Security Fixes:
|
||||
* (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script
|
||||
can cause NULL pointer dereference which will result with a crash of the
|
||||
redis-server process. This issue affects all versions of Redis.
|
||||
[reported by Aviv Yahav].
|
||||
* (CVE-2022-24735) By exploiting weaknesses in the Lua script execution
|
||||
environment, an attacker with access to Redis can inject Lua code that will
|
||||
execute with the (potentially higher) privileges of another Redis user.
|
||||
[reported by Aviv Yahav].
|
||||
|
||||
|
||||
Potentially Breaking Fixes
|
||||
==========================
|
||||
|
||||
* LPOP/RPOP with count against non-existing list return null array (#10095)
|
||||
* LPOP/RPOP used to produce wrong replies when count is 0 (#9692)
|
||||
|
||||
|
||||
Performance and resource utilization improvements
|
||||
=================================================
|
||||
|
||||
* Speed optimization in command execution pipeline (#10502)
|
||||
* Fix regression in Z[REV]RANGE commands (by-rank) introduced in Redis 6.2 (#10337)
|
||||
|
||||
|
||||
Platform / toolchain support related improvements
|
||||
=================================================
|
||||
|
||||
* Fix RSS metrics on NetBSD and OpenBSD (#10116, #10149)
|
||||
* Fix OpenSSL 3.0.x related issues (#10291)
|
||||
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Lua: Add checks for min-slave-* configs when evaluating Lua scripts (#10160)
|
||||
* Lua: fix crash on a script call with many arguments, a regression in v6.2.6 (#9809)
|
||||
* Tracking: Make invalidation messages always after command's reply (#9422)
|
||||
* Fix excessive stream trimming due to an overflow (#10068)
|
||||
* Add missed error counting for INFO errorstats (#9646)
|
||||
* Fix geo search bounding box check causing missing results (#10018)
|
||||
* Improve EXPIRE TTL overflow detection (#9839)
|
||||
* Modules: Fix thread safety violation when a module thread adds an error reply, broken in 6.2 (#10278)
|
||||
* Modules: Fix missing and duplicate error stats (#10278)
|
||||
* Module APIs: release clients blocked on module commands in cluster resharding
|
||||
and down state (#9483)
|
||||
* Sentinel: Fix memory leak with TLS (#9753)
|
||||
* Sentinel: Fix issues with hostname support (#10146)
|
||||
* Sentinel: Fix election failures on certain container environments (#10197)
|
||||
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.6 Released Mon Oct 4 12:00:00 IDT 2021
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: SECURITY, contains fixes to security issues.
|
||||
|
||||
Security Fixes:
|
||||
* (CVE-2021-41099) Integer to heap buffer overflow handling certain string
|
||||
commands and network payloads, when proto-max-bulk-len is manually configured
|
||||
to a non-default, very large value [reported by yiyuaner].
|
||||
* (CVE-2021-32762) Integer to heap buffer overflow issue in redis-cli and
|
||||
redis-sentinel parsing large multi-bulk replies on some older and less common
|
||||
platforms [reported by Microsoft Vulnerability Research].
|
||||
* (CVE-2021-32687) Integer to heap buffer overflow with intsets, when
|
||||
set-max-intset-entries is manually configured to a non-default, very large
|
||||
value [reported by Pawel Wieczorkiewicz, AWS].
|
||||
* (CVE-2021-32675) Denial Of Service when processing RESP request payloads with
|
||||
a large number of elements on many connections.
|
||||
* (CVE-2021-32672) Random heap reading issue with Lua Debugger [reported by
|
||||
Meir Shpilraien].
|
||||
* (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded
|
||||
data types, when configuring a large, non-default value for
|
||||
hash-max-ziplist-entries, hash-max-ziplist-value, zset-max-ziplist-entries
|
||||
or zset-max-ziplist-value [reported by sundb].
|
||||
* (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when
|
||||
configuring a non-default, large value for proto-max-bulk-len and
|
||||
client-query-buffer-limit [reported by sundb].
|
||||
* (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer
|
||||
overflow [reported by Meir Shpilraien].
|
||||
|
||||
Bug fixes that involve behavior changes:
|
||||
* GEO* STORE with empty source key deletes the destination key and return 0 (#9271)
|
||||
Previously it would have returned an empty array like the non-STORE variant.
|
||||
* PUBSUB NUMPAT replies with number of patterns rather than number of subscriptions (#9209)
|
||||
This actually changed in 6.2.0 but was overlooked and omitted from the release notes.
|
||||
|
||||
Bug fixes that are only applicable to previous releases of Redis 6.2:
|
||||
* Fix CLIENT PAUSE, used an old timeout from previous PAUSE (#9477)
|
||||
* Fix CLIENT PAUSE in a replica would mess the replication offset (#9448)
|
||||
* Add some missing error statistics in INFO errorstats (#9328)
|
||||
|
||||
Other bug fixes:
|
||||
* Fix incorrect reply of COMMAND command key positions for MIGRATE command (#9455)
|
||||
* Fix appendfsync to always guarantee fsync before reply, on MacOS and FreeBSD (kqueue) (#9416)
|
||||
* Fix the wrong mis-detection of sync_file_range system call, affecting performance (#9371)
|
||||
|
||||
CLI tools:
|
||||
* When redis-cli received ASK response, it didn't handle it (#8930)
|
||||
|
||||
Improvements:
|
||||
* Add latency monitor sample when key is deleted via lazy expire (#9317)
|
||||
* Sanitize corrupt payload improvements (#9321, #9399)
|
||||
* Delete empty keys when loading RDB file or handling a RESTORE command (#9297, #9349)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.5 Released Wed Jul 21 16:32:19 IDT 2021
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: SECURITY, contains fixes to security issues that affect
|
||||
authenticated client connections on 32-bit versions. MODERATE otherwise.
|
||||
|
||||
Fix integer overflow in BITFIELD on 32-bit versions (CVE-2021-32761).
|
||||
An integer overflow bug in Redis version 2.2 or newer can be exploited using the
|
||||
BITFIELD command to corrupt the heap and potentially result with remote code
|
||||
execution.
|
||||
|
||||
Bug fixes that involve behavior changes:
|
||||
* Change reply type for ZPOPMAX/MIN with count in RESP3 to nested array (#8981).
|
||||
Was using a flat array like in RESP2 instead of a nested array like ZRANGE does.
|
||||
* Fix reply type for HRANDFIELD and ZRANDMEMBER when key is missing (#9178).
|
||||
Was using a null array instead of an empty array.
|
||||
* Fix reply type for ZRANGESTORE when source key is missing (#9089).
|
||||
Was using an empty array like ZRANGE instead of 0 (used in the STORE variant).
|
||||
|
||||
Bug fixes that are only applicable to previous releases of Redis 6.2:
|
||||
* ZRANDMEMBER WITHSCORES with negative COUNT may return bad score (#9162)
|
||||
* Fix crash after CLIENT UNPAUSE when threaded I/O config is enabled (#9041)
|
||||
* Fix XTRIM or XADD with LIMIT may delete more entries than the limit (#9048)
|
||||
* Fix build issue with OpenSSL 1.1.0 (#9233)
|
||||
|
||||
Other bug fixes:
|
||||
* Fail EXEC command in case a watched key is expired (#9194)
|
||||
* Fix SMOVE not to invalidate dest key (WATCH and tracking) when member already exists (#9244)
|
||||
* Fix SINTERSTORE not to delete dest key when getting a wrong type error (#9032)
|
||||
* Fix overflows on 32-bit versions in GETBIT, SETBIT, BITCOUNT, BITPOS, and BITFIELD (#9191)
|
||||
* Improve MEMORY USAGE on stream keys (#9164)
|
||||
* Set TCP keepalive on inbound cluster bus connections (#9230)
|
||||
* Fix diskless replica loading to recover from RDB short read on module AUX data (#9199)
|
||||
* Fix race in client side tracking (#9116)
|
||||
* Fix ziplist length updates on big-endian platforms (#2080)
|
||||
|
||||
CLI tools:
|
||||
* redis-cli cluster import command may issue wrong MIGRATE command, sending COPY instead of REPLACE (#8945)
|
||||
* redis-cli --rdb fixes when using "-" to write to stdout (#9136, #9135)
|
||||
* redis-cli support for RESP3 set type in CSV and RAW output (#7338)
|
||||
|
||||
Modules:
|
||||
* Module API for getting current command name (#8792)
|
||||
* Fix RM_StringTruncate when newlen is 0 (#3718)
|
||||
* Fix CLIENT UNBLOCK crashing modules without timeout callback (#9167)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.4 Released Tue June 1 12:00:00 IST 2021
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: SECURITY, Contains fixes to security issues that affect
|
||||
authenticated client connections. MODERATE otherwise.
|
||||
|
||||
Fix integer overflow in STRALGO LCS (CVE-2021-32625)
|
||||
An integer overflow bug in Redis version 6.0 or newer can be exploited using the
|
||||
STRALGO LCS command to corrupt the heap and potentially result with remote code
|
||||
execution. This is a result of an incomplete fix by CVE-2021-29477.
|
||||
|
||||
Bug fixes that are only applicable to previous releases of Redis 6.2:
|
||||
* Fix crash after a diskless replication fork child is terminated (#8991)
|
||||
* Fix redis-benchmark crash on unsupported configs (#8916)
|
||||
|
||||
Other bug fixes:
|
||||
* Fix crash in UNLINK on a stream key with deleted consumer groups (#8932)
|
||||
* SINTERSTORE: Add missing keyspace del event when none of the sources exist (#8949)
|
||||
* Sentinel: Fix CONFIG SET of empty string sentinel-user/sentinel-pass configs (#8958)
|
||||
* Enforce client output buffer soft limit when no traffic (#8833)
|
||||
|
||||
Improvements:
|
||||
* Hide AUTH passwords in MIGRATE command from slowlog (#8859)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.3 Released Mon May 3 19:00:00 IST 2021
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: SECURITY, Contains fixes to security issues that affect
|
||||
authenticated client connections. LOW otherwise.
|
||||
|
||||
Integer overflow in STRALGO LCS command (CVE-2021-29477):
|
||||
An integer overflow bug in Redis version 6.0 or newer could be exploited using
|
||||
the STRALGO LCS command to corrupt the heap and potentially result in remote
|
||||
code execution. The integer overflow bug exists in all versions of Redis
|
||||
starting with 6.0.
|
||||
|
||||
Integer overflow in COPY command for large intsets (CVE-2021-29478):
|
||||
An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and
|
||||
potentially result with remote code execution. The vulnerability involves
|
||||
changing the default set-max-intset-entries configuration value, creating a
|
||||
large set key that consists of integer values and using the COPY command to
|
||||
duplicate it. The integer overflow bug exists in all versions of Redis starting
|
||||
with 2.6, where it could result with a corrupted RDB or DUMP payload, but not
|
||||
exploited through COPY (which did not exist before 6.2).
|
||||
|
||||
Bug fixes that are only applicable to previous releases of Redis 6.2:
|
||||
* Fix memory leak in moduleDefragGlobals (#8853)
|
||||
* Fix memory leak when doing lazy freeing client tracking table (#8822)
|
||||
* Block abusive replicas from sending command that could assert and crash redis (#8868)
|
||||
|
||||
Other bug fixes:
|
||||
* Use a monotonic clock to check for Lua script timeout (#8812)
|
||||
* redis-cli: Do not use unix socket when we got redirected in cluster mode (#8870)
|
||||
|
||||
Modules:
|
||||
* Fix RM_GetClusterNodeInfo() to correctly populate master id (#8846)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.2 Released Mon April 19 19:00:00 IST 2021
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: HIGH, if you're using ACL and pub/sub, CONFIG REWRITE, or
|
||||
suffering from performance regression. see below.
|
||||
|
||||
Bug fixes for regressions in previous releases of Redis 6.2:
|
||||
* Fix BGSAVE, AOFRW, and replication slowdown due to child reporting CoW (#8645)
|
||||
* Fix short busy loop when timer event is about to fire (#8764)
|
||||
* Fix default user, overwritten and reset users losing pubsub channel permissions (#8723)
|
||||
* Fix config rewrite with an empty `save` config resulsing in default `save` values (#8719)
|
||||
* Fix not starting on alpine/libmusl without IPv6 (#8655)
|
||||
* Fix issues with propagation and MULTI/EXEC in modules (#8617)
|
||||
Several issues around nested calls and thread safe contexts
|
||||
|
||||
Bug fixes that are only applicable to previous releases of Redis 6.2:
|
||||
* ACL Pub/Sub channels permission handling for save/load scenario (#8794)
|
||||
* Fix early rejection of PUBLISH inside MULTI-EXEC transaction (#8534)
|
||||
* Fix missing SLOWLOG records for blocked commands (#8632)
|
||||
* Allow RESET command during busy scripts (#8629)
|
||||
* Fix some error replies were not counted on stats (#8659)
|
||||
|
||||
Bug fixes:
|
||||
* Add a timeout mechanism for replicas stuck in fullsync (#8762)
|
||||
* Process HELLO command even if the default user has no permissions (#8633)
|
||||
* Client issuing a long running script and using a pipeline, got disconnected (#8715)
|
||||
* Fix script kill to work also on scripts that use `pcall` (#8661)
|
||||
* Fix list-compress-depth may compress more node than required (#8311)
|
||||
* Fix redis-cli handling of rediss:// URL scheme (#8705)
|
||||
* Cluster: Skip unnecessary check which may prevent failure detection (#8585)
|
||||
* Cluster: Fix hang manual failover when replica just started (#8651)
|
||||
* Sentinel: Fix info-refresh time field before sentinel get first response (#8567)
|
||||
* Sentinel: Fix possible crash on failed connection attempt (#8627)
|
||||
* Systemd: Send the readiness notification when a replica is ready to accept connections (#8409)
|
||||
|
||||
Command behavior changes:
|
||||
* ZADD: fix wrong reply when INCR used with GT/LT which blocked the update (#8717)
|
||||
It was responding with the incremented value rather than nil
|
||||
* XAUTOCLAIM: fix response to return the next available id as the cursor (#8725)
|
||||
Previous behavior was retuning the last one which was already scanned
|
||||
* XAUTOCLAIM: fix JUSTID to prevent incrementing delivery_count (#8724)
|
||||
|
||||
New config options:
|
||||
* Add cluster-allow-replica-migration config option (#5285)
|
||||
* Add replica-announced config option (#8653)
|
||||
* Add support for plaintext clients in TLS cluster (#8587)
|
||||
* Add support for reading encrypted keyfiles (#8644)
|
||||
|
||||
Improvements:
|
||||
* Fix performance regression in BRPOP on Redis 6.0 (#8689)
|
||||
* Avoid adding slowlog entries for config with sensitive data (#8584)
|
||||
* Improve redis-cli non-binary safe string handling (#8566)
|
||||
* Optimize CLUSTER SLOTS reply (#8541)
|
||||
* Handle remaining fsync errors (#8419)
|
||||
|
||||
Info fields and introspection changes:
|
||||
* Strip % sign from current_fork_perc info field (#8628)
|
||||
* Fix RSS memory info on FreeBSD (#8620)
|
||||
* Fix client_recent_max_input/output_buffer in 'INFO CLIENTS' when all clients drop (#8588)
|
||||
* Fix invalid master_link_down_since_seconds in info replication (#8785)
|
||||
|
||||
Platform and deployment-related changes:
|
||||
* Fix FreeBSD <12.x builds (#8603)
|
||||
|
||||
Modules:
|
||||
* Add macros for RedisModule_log logging levels (#4246)
|
||||
* Add RedisModule_GetAbsExpire / RedisModule_SetAbsExpire (#8564)
|
||||
* Add a module type for key space notification (#8759)
|
||||
* Set module eviction context flag only in masters (#8631)
|
||||
* Fix unusable RedisModule_IsAOFClient API (#8596)
|
||||
* Fix missing EXEC on modules propagation after failed EVAL execution (#8654)
|
||||
* Fix edge-case when a module client is unblocked (#8618)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.1 Released Mon Mar 1 17:51:36 IST 2021
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: LOW.
|
||||
|
||||
Here is a comprehensive list of changes in this release compared to 6.2.0,
|
||||
each one includes the PR number that added it, so you can get more details
|
||||
at https://github.com/redis/redis/pull/<number>
|
||||
|
||||
Bug fixes:
|
||||
* Fix sanitize-dump-payload for stream with deleted records (#8568)
|
||||
* Prevent client-query-buffer-limit config from being set to lower than 1mb (#8557)
|
||||
|
||||
Improvements:
|
||||
* Make port, tls-port and bind config options modifiable at runtime (#8510)
|
||||
|
||||
Platform and deployment-related changes:
|
||||
* Fix compilation error on non-glibc systems if jemalloc is not used (#8533)
|
||||
* Improved memory consumption and memory usage tracking on FreeBSD (#8545)
|
||||
* Fix compilation on ARM64 MacOS with jemalloc (#8458)
|
||||
|
||||
Modules:
|
||||
* New Module API for getting user name of a client (#8508)
|
||||
* Optimize RM_Call by utilizing a shared reusable client (#8516)
|
||||
* Fix crash running CLIENT INFO via RM_Call (#8560)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2.0 GA Released Tue Feb 22 14:00:00 IST 2021
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: SECURITY if you use 32bit build of redis (see bellow), MODERATE
|
||||
if you used earlier versions of Redis 6.2, LOW otherwise.
|
||||
|
||||
Integer overflow on 32-bit systems (CVE-2021-21309):
|
||||
Redis 4.0 or newer uses a configurable limit for the maximum supported bulk
|
||||
input size. By default, it is 512MB which is a safe value for all platforms.
|
||||
If the limit is significantly increased, receiving a large request from a client
|
||||
may trigger several integer overflow scenarios, which would result with buffer
|
||||
overflow and heap corruption.
|
||||
|
||||
Here is a comprehensive list of changes in this release compared to 6.2 RC3,
|
||||
each one includes the PR number that added it, so you can get more details
|
||||
at https://github.com/redis/redis/pull/<number>
|
||||
|
||||
Bug fixes:
|
||||
* Avoid 32-bit overflows when proto-max-bulk-len is set high (#8522)
|
||||
* Fix broken protocol in client tracking tracking-redir-broken message (#8456)
|
||||
* Avoid unsafe field name characters in INFO commandstats, errorstats, modules (#8492)
|
||||
* XINFO able to access expired keys during CLIENT PAUSE WRITE (#8436)
|
||||
* Fix allowed length for REPLCONF ip-address, needed due to Sentinel's support for hostnames (#8517)
|
||||
* Fix broken protocol in redis-benchmark when used with -a or --dbnum (#8486)
|
||||
* XADD counts deleted records too when considering switching to a new listpack (#8390)
|
||||
|
||||
Bug fixes that are only applicable to previous releases of Redis 6.2:
|
||||
* Fixes in GEOSEARCH bybox (accuracy and mismatch between width and height) (#8445)
|
||||
* Fix risk of OOM panic in HRANDFIELD, ZRANDMEMBER commands with huge negative count (#8429)
|
||||
* Fix duplicate replicas issue in Sentinel, needed due to hostname support (#8481)
|
||||
* Fix Sentinel configuration rewrite, an improvement of #8271 (#8480)
|
||||
|
||||
Command behavior changes:
|
||||
* SRANDMEMBER uses RESP3 array type instead of set type (#8504)
|
||||
* EXPIRE, EXPIREAT, SETEX, GETEX: Return error when provided expire time overflows (#8287)
|
||||
|
||||
Other behavior changes:
|
||||
* Remove ACL subcommand validation if fully added command exists. (#8483)
|
||||
|
||||
Improvements:
|
||||
* Optimize sorting in GEORADIUS / GEOSEARCH with COUNT (#8326)
|
||||
* Optimize HRANDFIELD and ZRANDMEMBER case 4 when ziplist encoded (#8444)
|
||||
* Optimize in-place replacement of elements in HSET, HINCRBY, LSET (#8493)
|
||||
* Remove redundant list to store pubsub patterns (#8472)
|
||||
* Add --insecure option to command line tools (#8416)
|
||||
|
||||
Info fields and introspection changes:
|
||||
* Add INFO fields to track progress of BGSAVE, AOFRW, replication (#8414)
|
||||
|
||||
Modules:
|
||||
* RM_ZsetRem: Delete key if empty, the bug could leave empty zset keys (#8453)
|
||||
* RM_HashSet: Add COUNT_ALL flag and set errno (#8446)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2 RC3 Released Tue Feb 1 14:00:00 IST 2021
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the third Release Candidate of Redis 6.2.
|
||||
|
||||
Here is a comprehensive list of changes in this release compared to 6.2 RC2,
|
||||
each one includes the PR number that added it, so you can get more details
|
||||
at https://github.com/redis/redis/pull/<number>
|
||||
|
||||
New commands / args:
|
||||
* Add HRANDFIELD and ZRANDMEMBER commands (#8297)
|
||||
* Add FAILOVER command (#8315)
|
||||
* Add GETEX, GETDEL commands (#8327)
|
||||
* Add PXAT/EXAT arguments to SET command (#8327)
|
||||
* Add SYNC arg to FLUSHALL and FLUSHDB, and ASYNC/SYNC arg to SCRIPT FLUSH (#8258)
|
||||
|
||||
Sentinel:
|
||||
* Add hostname support to Sentinel (#8282)
|
||||
* Prevent file descriptors from leaking into Sentinel scripts (#8242)
|
||||
* Fix config file line order dependency and config rewrite sequence (#8271)
|
||||
|
||||
New configuration options:
|
||||
* Add set-proc-title config option to disable changes to the process title (#3623)
|
||||
* Add proc-title-template option to control what's shown in the process title (#8397)
|
||||
* Add lazyfree-lazy-user-flush config option to control FLUSHALL, FLUSHDB and SCRIPT FLUSH (#8258)
|
||||
|
||||
Bug fixes:
|
||||
* AOF: recover from last write error by turning on/off appendonly config (#8030)
|
||||
* Exit on fsync error when the AOF fsync policy is 'always' (#8347)
|
||||
* Avoid assertions (on older kernels) when testing arm64 CoW bug (#8405)
|
||||
* CONFIG REWRITE should honor umask settings (#8371)
|
||||
* Fix firstkey,lastkey,step in COMMAND command for some commands (#8367)
|
||||
|
||||
Special considerations:
|
||||
* Fix misleading description of the save configuration directive (#8337)
|
||||
|
||||
Improvements:
|
||||
* A way to get RDB file via replication without excessive replication buffers (#8303)
|
||||
* Optimize performance of clusterGenNodesDescription for large clusters (#8182)
|
||||
|
||||
Info fields and introspection changes:
|
||||
* SLOWLOG and LATENCY monitor include unblocking time of blocked commands (#7491)
|
||||
|
||||
Modules:
|
||||
* Add modules API for streams (#8288)
|
||||
* Add event for fork child birth and termination (#8289)
|
||||
* Add RM_BlockedClientMeasureTime* etc, to track background processing in commandstats (#7491)
|
||||
* Fix bug in v6.2, wrong value passed to the new unlink callback (#8381)
|
||||
* Fix bug in v6.2, modules blocked on keys unblock on commands like LPUSH (#8356)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2 RC2 Released Tue Jan 12 16:17:20 IST 2021
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the second Release Candidate of Redis 6.2.
|
||||
|
||||
IMPORTANT: If you're running Redis on ARM64 or a big-endian system, upgrade may
|
||||
have significant implications. Please be sure to read the notes below.
|
||||
|
||||
Here is a comprehensive list of changes in this release compared to 6.2 RC1,
|
||||
each one includes the PR number that added it, so you can get more details
|
||||
at https://github.com/redis/redis/pull/<number>
|
||||
|
||||
New commands / args:
|
||||
* Add the REV, BYLEX and BYSCORE arguments to ZRANGE, and the ZRANGESTORE command (#7844)
|
||||
* Add the XAUTOCLAIM command (#7973)
|
||||
* Add the MINID trimming strategy and the LIMIT argument to XADD and XTRIM (#8169)
|
||||
* Add the ANY argument to GEOSEARCH and GEORADIUS (#8259)
|
||||
* Add the CH, NX, XX arguments to GEOADD (#8227)
|
||||
* Add the COUNT argument to LPOP and RPOP (#8179)
|
||||
* Add the WRITE argument to CLIENT PAUSE for pausing write commands exclusively (#8170)
|
||||
* Change the proto-ver argument of HELLO to optional (#7377)
|
||||
* Add the CLIENT TRACKINGINFO subcommand (#7309)
|
||||
|
||||
Command behavior changes:
|
||||
* CLIENT TRACKING yields an error when given overlapping BCAST prefixes (#8176)
|
||||
* SWAPDB invalidates WATCHed keys (#8239)
|
||||
* SORT command behaves differently when used on a writable replica (#8283)
|
||||
|
||||
Other behavior changes:
|
||||
* Avoid propagating MULTI/EXEC for read-only transactions (#8216)
|
||||
* Remove the read-only flag from TIME, ECHO, ROLE, LASTSAVE (#8216)
|
||||
* Fix the command flags of PFDEBUG (#8222)
|
||||
* Tracking clients will no longer receive unnecessary key invalidation messages after FLUSHDB (#8039)
|
||||
* Sentinel: Fix missing updates to the config file after SENTINEL SET command (#8229)
|
||||
|
||||
Bug fixes with compatibility implications (bugs introduced in Redis 6.0):
|
||||
* Fix RDB CRC64 checksum on big-endian systems (#8270)
|
||||
If you're using big-endian please consider the compatibility implications with
|
||||
RESTORE, replication and persistence.
|
||||
* Fix wrong order of key/value in Lua's map response (#8266)
|
||||
If your scripts use redis.setresp() or return a map (new in Redis 6.0), please
|
||||
consider the implications.
|
||||
|
||||
Bug fixes that are only applicable to previous releases of Redis 6.2:
|
||||
* Resolve rare assertions in active defragmentation while loading (#8284, #8281)
|
||||
|
||||
Bug fixes:
|
||||
* Fix the selection of a random element from large hash tables (#8133)
|
||||
* Fix an issue where a forked process deletes the parent's pidfile (#8231)
|
||||
* Fix crashes when enabling io-threads-do-reads (#8230)
|
||||
* Fix a crash in redis-cli after executing cluster backup (#8267)
|
||||
* Fix redis-benchmark to use an IP address for the first cluster node (#8154)
|
||||
* Fix saving of strings larger than 2GB into RDB files (#8306)
|
||||
|
||||
Additional improvements:
|
||||
* Improve replication handshake time (#8214)
|
||||
* Release client tracking table memory asynchronously in cases where the DB is also freed asynchronously (#8039)
|
||||
* Avoid wasteful transient memory allocation in certain cases (#8286, #5954)
|
||||
* Handle binary string values by the 'requirepass' and 'masterauth' configs (#8200)
|
||||
|
||||
Platform and deployment-related changes:
|
||||
* Install redis-check-rdb and redis-check-aof as symlinks to redis-server (#5745)
|
||||
* Add a check for an ARM64 Linux kernel bug (#8224)
|
||||
Due to the potential severity of this issue, Redis will refuse to run on
|
||||
affected platforms by default.
|
||||
|
||||
Info fields and introspection changes:
|
||||
* Add the errorstats section to the INFO command (#8217)
|
||||
* Add the failed_calls and rejected_calls fields INFO's commandstats section (#8217)
|
||||
* Report child copy-on-write metrics continuously (#8264)
|
||||
|
||||
Module API changes:
|
||||
* Add the RedisModule_SendChildCOWInfo API (#8264)
|
||||
* Add the may-replicate command flag (#8170)
|
||||
|
||||
================================================================================
|
||||
Redis 6.2 RC1 Released Mon Dec 14 11:50:00 IST 2020
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the first Release Candidate of Redis 6.2.
|
||||
|
||||
Introduction to the Redis 6.2 release
|
||||
=====================================
|
||||
|
||||
This release is the first significant Redis release managed by the core team
|
||||
under the new project governance model.
|
||||
|
||||
Redis 6.2 includes many new commands and improvements, but no big features. It
|
||||
mainly makes Redis more complete and addresses issues that have been requested
|
||||
by many users frequently or for a long time.
|
||||
|
||||
Many of these changes were not eligible for 6.0.x for several reasons:
|
||||
|
||||
1. They are not backward compatible, which is always the case with new or
|
||||
extended commands (that cannot be replicated to an older replica).
|
||||
2. They require a longer release-candidate test cycle.
|
||||
|
||||
|
||||
Here is a comprehensive list of changes in this release compared to 6.0.9,
|
||||
each one includes the PR number that added it, so you can get more details
|
||||
at https://github.com/redis/redis/pull/<number>
|
||||
|
||||
New commands / args:
|
||||
* Add SMISMEMBER command that checks multiple members (#7615)
|
||||
* Add ZMSCORE command that returns an array of scores (#7593)
|
||||
* Add LMOVE and BLMOVE commands that pop and push arbitrarily (#6929)
|
||||
* Add RESET command that resets client connection state (#7982)
|
||||
* Add COPY command that copies keys (#7953)
|
||||
* Add ZDIFF and ZDIFFSTORE commands (#7961)
|
||||
* Add ZINTER and ZUNION commands (#7794)
|
||||
* Add GEOSEARCH/GEOSEARCHSTORE commands for bounding box spatial queries (#8094)
|
||||
* Add GET parameter to SET command, for more powerful GETSET (#7852)
|
||||
* Add exclusive range query to XPENDING (#8130)
|
||||
* Add exclusive range query to X[REV]RANGE (#8072)
|
||||
* Add GT and LT options to ZADD for conditional score updates (#7818)
|
||||
* Add CLIENT INFO and CLIENT LIST for specific ids (#8113)
|
||||
* Add IDLE argument to XPENDING command (#7972)
|
||||
* Add local address to CLIENT LIST, and a CLIENT KILL filter. (#7913)
|
||||
* Add NOMKSTREAM option to XADD command (#7910)
|
||||
* Add command introspection to Sentinel (#7940)
|
||||
* Add SENTINEL MYID subcommand (#7858)
|
||||
|
||||
New features:
|
||||
* Dump payload sanitization: prevent corrupt payload causing crashes (#7807)
|
||||
Has flags to enable full O(N) validation (disabled by default).
|
||||
* ACL patterns for Pub/Sub channels (#7993)
|
||||
* Support ACL for Sentinel mode (#7888)
|
||||
* Support getting configuration from both stdin and file at the same time (#7893)
|
||||
Lets you avoid storing secrets on the disk.
|
||||
|
||||
New features in CLI tools:
|
||||
* redis-cli RESP3 push support (#7609)
|
||||
* redis-cli cluster import support source and target that require auth (#7994)
|
||||
* redis-cli URIs able to provide user name in addition to password (#8048)
|
||||
* redis-cli/redis-benchmark allow specifying the prefered ciphers/ciphersuites (#8005)
|
||||
* redis-cli add -e option to exit with code when command execution fails (#8136)
|
||||
|
||||
Command behavior changes:
|
||||
* EXISTS should not alter LRU (#8016)
|
||||
In Redis 5.0 and 6.0 it would have touched the LRU/LFU of the key.
|
||||
* OBJECT should not reveal logically expired keys (#8016)
|
||||
Will now behave the same TYPE or any other non-DEBUG command.
|
||||
* Improve db id range check for SELECT and MOVE (#8085)
|
||||
Changes the error message text on a wrong db index.
|
||||
* Modify AUTH / HELLO error message (#7648)
|
||||
Changes the error message text when the user isn't found or is disabled.
|
||||
* BITOPS length limited to proto_max_bulk_len rather than 512MB (#8096)
|
||||
The limit is now configurable like in SETRANGE, and APPEND.
|
||||
* GEORADIUS[BYMEMBER] can fail with -OOM if Redis is over the memory limit (#8107)
|
||||
|
||||
Other behavior changes:
|
||||
* Optionally (default) fail to start if requested bind address is not available (#7936)
|
||||
If you rely on Redis starting successfully even if one of the bind addresses
|
||||
is not available, you'll need to tune the new config.
|
||||
* Limit the main db dictionaries expansion to prevent key eviction (#7954)
|
||||
In the past big dictionary rehashing could result in massive data eviction.
|
||||
Now this rehashing is delayed (up to a limit), which can result in performance
|
||||
loss due to hash collisions.
|
||||
* CONFIG REWRITE is atomic and safer, but requires write access to the config file's folder (#7824, #8051)
|
||||
This change was already present in 6.0.9, but was missing from the release
|
||||
notes.
|
||||
* A new incremental eviction mechanism that reduces latency on eviction spikes (#7653)
|
||||
In pathological cases this can cause memory to grow uncontrolled and may require
|
||||
specific tuning.
|
||||
* Not resetting "save" config when Redis is started with command line arguments. (#7092)
|
||||
In case you provide command line arguments without "save" and count on it
|
||||
being disabled, Now the defaults "save" config will kick in.
|
||||
* Update memory metrics for INFO during loading (#7690)
|
||||
* When "supervised" config is enabled, it takes precedence over "daemonize". (#8036)
|
||||
* Assertion and panic, print crash log without generating SIGSEGV (#7585)
|
||||
* Added crash log report on SIGABRT, instead of silently exiting (#8004)
|
||||
* Disable THP (Transparent Huge Pages) if enabled (#7381)
|
||||
If you deliberately enabled it, you'll need to config Redis to keep it.
|
||||
|
||||
Bug fixes:
|
||||
* Handle output buffer limits for module blocked clients (#8141)
|
||||
Could result in a module sending reply to a blocked client to go beyond the
|
||||
limit.
|
||||
* Fix setproctitle related crashes. (#8150, #8088)
|
||||
Caused various crashes on startup, mainly on Apple M1 chips or under
|
||||
instrumentation.
|
||||
* A module doing RM_Call could cause replicas to get nested MULTI (#8097).
|
||||
* Backup/restore cluster mode keys to slots map for repl-diskless-load=swapdb (#8108)
|
||||
In cluster mode with repl-diskless-load, when loading failed, slot map
|
||||
wouldn't have been restored.
|
||||
* Fix oom-score-adj-values range, and bug when used in config file (#8046)
|
||||
Enabling setting this in the config file in a line after enabling it, would
|
||||
have been buggy.
|
||||
* Reset average ttl when empty databases (#8106)
|
||||
Just causing misleading metric in INFO
|
||||
* Disable rehash when Redis has child process (#8007)
|
||||
This could have caused excessive CoW during BGSAVE, replication or AOFRW.
|
||||
* Further improved ACL algorithm for picking categories (#7966)
|
||||
Output of ACL GETUSER is now more similar to the one provided by ACL SETUSER.
|
||||
* Fix bug with module GIL being released prematurely (#8061)
|
||||
Could in theory (and rarely) cause multi-threaded modules to corrupt memory.
|
||||
* Fix cluster redirect for module command with no firstkey. (#7539)
|
||||
* Reduce effect of client tracking causing feedback loop in key eviction (#8100)
|
||||
* Kill disk-based fork child when all replicas drop and 'save' is not enabled (#7819)
|
||||
* Rewritten commands (modified for propagation) are logged as their original command (#8006)
|
||||
* Fix cluster access to unaligned memory (SIGBUS on old ARM) #7958
|
||||
* If diskless repl child is killed, make sure to reap the child pid (#7742)
|
||||
* Broadcast a PONG message when slot's migration is over, may reduce MOVED responses (#7571)
|
||||
|
||||
Other improvements:
|
||||
* TLS Support in redis-benchmark (#7959)
|
||||
* Accelerate diskless master connections, and general re-connections (#6271)
|
||||
* Run active defrag while blocked / loading (#7726)
|
||||
* Performance and memory reporting improvement - sds take control of its internal fragmentation (#7875)
|
||||
* Speedup cluster failover. (#7948)
|
||||
|
||||
Platform / toolchain support related improvements:
|
||||
* Optionally (not by default) use H/W Monotonic clock for faster time sampling (#7644)
|
||||
* Remove the requirements for C11 and _Atomic supporting compiler (#7707)
|
||||
This would allow to more easily build and use Redis on older systems and
|
||||
compilers again.
|
||||
* Fix crash log registers output on ARM. (#8020)
|
||||
* Raspberry build fix. (#8095)
|
||||
* Setting process title support for Haiku. (#8060)
|
||||
* DragonFlyBSD RSS memory sampling support. (#8023)
|
||||
|
||||
New configuration options:
|
||||
* Enable configuring OpenSSL using the standard openssl.cnf (#8143)
|
||||
* oom-score-adj-values config can now take absolute values (besides relative ones) (#8046)
|
||||
* TLS: Add different client cert support. (#8076)
|
||||
* Note that a few other changes listed above added their config options.
|
||||
|
||||
Info fields and introspection changes:
|
||||
* Add INFO fields to track diskless and disk-based replication progress (#7981)
|
||||
* Add INFO field for main thread cpu time, and scrape system time. (#8132)
|
||||
* Add total_forks to INFO STATS (#8155)
|
||||
* Add maxclients and cluster_connections to INFO CLIENTS (#7979)
|
||||
* Add tracking bcast flag and client redirection in client list (#7995)
|
||||
* Fixed INFO client_recent_max_input_buffer includes argv array (#8065, see #7874)
|
||||
* Note that a few other changes listed above added their info fields.
|
||||
|
||||
Module API changes:
|
||||
* Add CTX_FLAGS_DENY_BLOCKING as a unified the way to know if blocking is allowed (#8025)
|
||||
* Add data type callbacks for lazy free effort, and unlink (#7912)
|
||||
* Add data type callback for COPY command (#8112)
|
||||
* Add callbacks for defrag support. (#8149)
|
||||
* Add module event for repl-diskless-load swapdb (#8153)
|
||||
|
||||
Module related fixes:
|
||||
* Moved RMAPI_FUNC_SUPPORTED so that it's usable (#8037)
|
||||
* Improve timer accuracy (#7987)
|
||||
* Allow '\0' inside of result of RM_CreateStringPrintf (#6260)
|
||||
|
||||
|
||||
Thanks to all the users and developers who made this release possible.
|
||||
We'll follow up with more RC releases, until the code looks production ready
|
||||
and we don't get reports of serious issues for a while.
|
||||
|
||||
A special thank you for the amount of work put into this release by:
|
||||
- Oran Agra
|
||||
- Yossi Gottlieb
|
||||
- Viktor Söderqvist
|
||||
- Yang Bodong
|
||||
- Filipe Oliveira
|
||||
- Guy Benoish
|
||||
- Itamar Haber
|
||||
- Madelyn Olson
|
||||
- Wang Yuan
|
||||
- Felipe Machado
|
||||
- Wen Hui
|
||||
- Tatsuya Arisawa
|
||||
- Jonah H. Harris
|
||||
- Raghav Muddur
|
||||
- Jim Brunner
|
||||
- Yaacov Hazan
|
||||
- Allen Farris
|
||||
- Chen Yang
|
||||
- Nitai Caro
|
||||
- sundb
|
||||
- Meir Shpilraien
|
||||
- maohuazhu
|
||||
- Valentino Geron
|
||||
- Zhao Zhao
|
||||
- Qu Chen
|
||||
- George Prekas
|
||||
- Tyson Andre
|
||||
- Uri Yagelnik
|
||||
- Michael Grunder
|
||||
- Huang Zw
|
||||
- alexronke-channeladvisor
|
||||
- Andy Pan
|
||||
- Wu Yunlong
|
||||
- Wei Kukey
|
||||
- Yoav Steinberg
|
||||
- Greg Femec
|
||||
- Uri Shachar
|
||||
- Nykolas Laurentino de Lima
|
||||
- xhe
|
||||
- zhenwei pi
|
||||
- David CARLIER
|
||||
|
||||
Migrating from 6.0 to 6.2
|
||||
=========================
|
||||
|
||||
Redis 6.2 is mostly a strict superset of 6.0, you should not have any problem
|
||||
upgrading your application from 6.0 to 6.2. However there are some small changes
|
||||
of behavior listed above, please make sure you are not badly affected by any of
|
||||
them.
|
||||
|
||||
Specifically these sections:
|
||||
* Command behavior changes
|
||||
* Other behavior changes
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Cheers,
|
||||
The Redis team
|
||||
|
||||
@@ -1 +1 @@
|
||||
Please check https://github.com/antirez/redis/issues
|
||||
Please check https://github.com/redis/redis/issues
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
Contributor Covenant Code of Conduct
|
||||
Our Pledge
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
Our Standards
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others’ private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
Enforcement Responsibilities
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
Scope
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
Enforcement
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
this email address: redis@redis.io.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
Enforcement Guidelines
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
1. Correction
|
||||
Community Impact: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
Consequence: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
2. Warning
|
||||
Community Impact: A violation through a single incident or series
|
||||
of actions.
|
||||
Consequence: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
3. Temporary Ban
|
||||
Community Impact: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
Consequence: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
4. Permanent Ban
|
||||
Community Impact: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
Consequence: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
Attribution
|
||||
This Code of Conduct is adapted from the Contributor Covenant,
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
Community Impact Guidelines were inspired by Mozilla’s code of conduct
|
||||
enforcement ladder.
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
+32
-8
@@ -7,18 +7,42 @@ each source file that you contribute.
|
||||
|
||||
# IMPORTANT: HOW TO USE REDIS GITHUB ISSUES
|
||||
|
||||
* Github issues SHOULD ONLY BE USED to report bugs, and for DETAILED feature
|
||||
requests. Everything else belongs to the Redis Google Group:
|
||||
Github issues SHOULD ONLY BE USED to report bugs, and for DETAILED feature
|
||||
requests. Everything else belongs to the Redis Google Group:
|
||||
|
||||
https://groups.google.com/forum/m/#!forum/Redis-db
|
||||
https://groups.google.com/forum/m/#!forum/Redis-db
|
||||
|
||||
PLEASE DO NOT POST GENERAL QUESTIONS that are not about bugs or suspected
|
||||
bugs in the Github issues system. We'll be very happy to help you and provide
|
||||
all the support in the mailing list.
|
||||
PLEASE DO NOT POST GENERAL QUESTIONS that are not about bugs or suspected
|
||||
bugs in the Github issues system. We'll be very happy to help you and provide
|
||||
all the support in the mailing list.
|
||||
|
||||
There is also an active community of Redis users at Stack Overflow:
|
||||
There is also an active community of Redis users at Stack Overflow:
|
||||
|
||||
http://stackoverflow.com/questions/tagged/redis
|
||||
http://stackoverflow.com/questions/tagged/redis
|
||||
|
||||
# Reporting Security Bugs
|
||||
|
||||
*If you are reporting a security bug*, please contact the core team privately
|
||||
by emailing redis@redis.io. Your report will be acknowledged by a core team
|
||||
member and once the report has been reviewed you will receive a more detailed
|
||||
response including next steps.
|
||||
|
||||
If you do not receive a reply you can escalate to the Redis Google Group,
|
||||
linked above. Because this group is a public space please do not disclose the
|
||||
issue in detail, only say that you are trying to reach the core team for a
|
||||
security issue.
|
||||
|
||||
Redis follows a responsible disclosure process:
|
||||
|
||||
1. Reports are reviewed and analyzed privately
|
||||
2. Patches are prepared for supported versions of Redis
|
||||
3. Vendor lists are notified with an embargo date to reduce the public impact
|
||||
4. We push a fix release and your bug can be posted publicly with credit in
|
||||
release notes and the version history (and our thanks!)
|
||||
|
||||
Issues and pull requests for documentation belong on the redis-doc repo:
|
||||
|
||||
https://github.com/redis/redis-doc
|
||||
|
||||
# How to provide a patch for a new feature
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2006-2015, Salvatore Sanfilippo
|
||||
Copyright (c) 2006-2020, Salvatore Sanfilippo
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -3,22 +3,22 @@ This README is just a fast *quick start* document. You can find more detailed do
|
||||
What is Redis?
|
||||
--------------
|
||||
|
||||
Redis is often referred as a *data structures* server. What this means is that Redis provides access to mutable data structures via a set of commands, which are sent using a *server-client* model with TCP sockets and a simple protocol. So different processes can query and modify the same data structures in a shared way.
|
||||
Redis is often referred to as a *data structures* server. What this means is that Redis provides access to mutable data structures via a set of commands, which are sent using a *server-client* model with TCP sockets and a simple protocol. So different processes can query and modify the same data structures in a shared way.
|
||||
|
||||
Data structures implemented into Redis have a few special properties:
|
||||
|
||||
* Redis cares to store them on disk, even if they are always served and modified into the server memory. This means that Redis is fast, but that is also non-volatile.
|
||||
* Implementation of data structures stress on memory efficiency, so data structures inside Redis will likely use less memory compared to the same data structure modeled using an high level programming language.
|
||||
* Redis offers a number of features that are natural to find in a database, like replication, tunable levels of durability, cluster, high availability.
|
||||
* Redis cares to store them on disk, even if they are always served and modified into the server memory. This means that Redis is fast, but that it is also non-volatile.
|
||||
* The implementation of data structures emphasizes memory efficiency, so data structures inside Redis will likely use less memory compared to the same data structure modelled using a high-level programming language.
|
||||
* Redis offers a number of features that are natural to find in a database, like replication, tunable levels of durability, clustering, and high availability.
|
||||
|
||||
Another good example is to think of Redis as a more complex version of memcached, where the operations are not just SETs and GETs, but operations to work with complex data types like Lists, Sets, ordered data structures, and so forth.
|
||||
Another good example is to think of Redis as a more complex version of memcached, where the operations are not just SETs and GETs, but operations that work with complex data types like Lists, Sets, ordered data structures, and so forth.
|
||||
|
||||
If you want to know more, this is a list of selected starting points:
|
||||
|
||||
* Introduction to Redis data types. http://redis.io/topics/data-types-intro
|
||||
* Introduction to Redis data types. https://redis.io/topics/data-types-intro
|
||||
* Try Redis directly inside your browser. http://try.redis.io
|
||||
* The full list of Redis commands. http://redis.io/commands
|
||||
* There is much more inside the Redis official documentation. http://redis.io/documentation
|
||||
* The full list of Redis commands. https://redis.io/commands
|
||||
* There is much more inside the official Redis documentation. https://redis.io/documentation
|
||||
|
||||
Building Redis
|
||||
--------------
|
||||
@@ -29,13 +29,27 @@ and 64 bit systems.
|
||||
|
||||
It may compile on Solaris derived systems (for instance SmartOS) but our
|
||||
support for this platform is *best effort* and Redis is not guaranteed to
|
||||
work as well as in Linux, OSX, and \*BSD there.
|
||||
work as well as in Linux, OSX, and \*BSD.
|
||||
|
||||
It is as simple as:
|
||||
|
||||
% make
|
||||
|
||||
You can run a 32 bit Redis binary using:
|
||||
To build with TLS support, you'll need OpenSSL development libraries (e.g.
|
||||
libssl-dev on Debian/Ubuntu) and run:
|
||||
|
||||
% make BUILD_TLS=yes
|
||||
|
||||
To build with systemd support, you'll need systemd development libraries (such
|
||||
as libsystemd-dev on Debian/Ubuntu or systemd-devel on CentOS) and run:
|
||||
|
||||
% make USE_SYSTEMD=yes
|
||||
|
||||
To append a suffix to Redis program names, use:
|
||||
|
||||
% make PROG_SUFFIX="-alt"
|
||||
|
||||
You can build a 32 bit Redis binary using:
|
||||
|
||||
% make 32bit
|
||||
|
||||
@@ -43,10 +57,17 @@ After building Redis, it is a good idea to test it using:
|
||||
|
||||
% make test
|
||||
|
||||
If TLS is built, running the tests with TLS enabled (you will need `tcl-tls`
|
||||
installed):
|
||||
|
||||
% ./utils/gen-test-certs.sh
|
||||
% ./runtest --tls
|
||||
|
||||
|
||||
Fixing build problems with dependencies or cached build options
|
||||
---------
|
||||
|
||||
Redis has some dependencies which are included into the `deps` directory.
|
||||
Redis has some dependencies which are included in the `deps` directory.
|
||||
`make` does not automatically rebuild dependencies even if something in
|
||||
the source code of dependencies changes.
|
||||
|
||||
@@ -73,7 +94,7 @@ with a 64 bit target, or the other way around, you need to perform a
|
||||
In case of build errors when trying to build a 32 bit binary of Redis, try
|
||||
the following steps:
|
||||
|
||||
* Install the packages libc6-dev-i386 (also try g++-multilib).
|
||||
* Install the package libc6-dev-i386 (also try g++-multilib).
|
||||
* Try using the following command line instead of `make 32bit`:
|
||||
`make CFLAGS="-m32 -march=native" LDFLAGS="-m32"`
|
||||
|
||||
@@ -94,18 +115,30 @@ To compile against jemalloc on Mac OS X systems, use:
|
||||
|
||||
% make MALLOC=jemalloc
|
||||
|
||||
Monotonic clock
|
||||
---------------
|
||||
|
||||
By default, Redis will build using the POSIX clock_gettime function as the
|
||||
monotonic clock source. On most modern systems, the internal processor clock
|
||||
can be used to improve performance. Cautions can be found here:
|
||||
http://oliveryang.net/2015/09/pitfalls-of-TSC-usage/
|
||||
|
||||
To build with support for the processor's internal instruction clock, use:
|
||||
|
||||
% make CFLAGS="-DUSE_PROCESSOR_CLOCK"
|
||||
|
||||
Verbose build
|
||||
-------------
|
||||
|
||||
Redis will build with a user friendly colorized output by default.
|
||||
If you want to see a more verbose output use the following:
|
||||
Redis will build with a user-friendly colorized output by default.
|
||||
If you want to see a more verbose output, use the following:
|
||||
|
||||
% make V=1
|
||||
|
||||
Running Redis
|
||||
-------------
|
||||
|
||||
To run Redis with the default configuration just type:
|
||||
To run Redis with the default configuration, just type:
|
||||
|
||||
% cd src
|
||||
% ./redis-server
|
||||
@@ -125,6 +158,12 @@ as options using the command line. Examples:
|
||||
All the options in redis.conf are also supported as options using the command
|
||||
line, with exactly the same name.
|
||||
|
||||
Running Redis with TLS:
|
||||
------------------
|
||||
|
||||
Please consult the [TLS.md](TLS.md) file for more information on
|
||||
how to use Redis with TLS.
|
||||
|
||||
Playing with Redis
|
||||
------------------
|
||||
|
||||
@@ -145,12 +184,12 @@ then in another terminal try the following:
|
||||
(integer) 2
|
||||
redis>
|
||||
|
||||
You can find the list of all the available commands at http://redis.io/commands.
|
||||
You can find the list of all the available commands at https://redis.io/commands.
|
||||
|
||||
Installing Redis
|
||||
-----------------
|
||||
|
||||
In order to install Redis binaries into /usr/local/bin just use:
|
||||
In order to install Redis binaries into /usr/local/bin, just use:
|
||||
|
||||
% make install
|
||||
|
||||
@@ -159,8 +198,8 @@ different destination.
|
||||
|
||||
Make install will just install binaries in your system, but will not configure
|
||||
init scripts and configuration files in the appropriate place. This is not
|
||||
needed if you want just to play a bit with Redis, but if you are installing
|
||||
it the proper way for a production system, we have a script doing this
|
||||
needed if you just want to play a bit with Redis, but if you are installing
|
||||
it the proper way for a production system, we have a script that does this
|
||||
for Ubuntu and Debian systems:
|
||||
|
||||
% cd utils
|
||||
@@ -178,17 +217,17 @@ You'll be able to stop and start Redis using the script named
|
||||
Code contributions
|
||||
-----------------
|
||||
|
||||
Note: by contributing code to the Redis project in any form, including sending
|
||||
Note: By contributing code to the Redis project in any form, including sending
|
||||
a pull request via Github, a code fragment or patch via private email or
|
||||
public discussion groups, you agree to release your code under the terms
|
||||
of the BSD license that you can find in the [COPYING][1] file included in the Redis
|
||||
source distribution.
|
||||
|
||||
Please see the [CONTRIBUTING][2] file in this source distribution for more
|
||||
information.
|
||||
information, including details on our process for security bugs/vulnerabilities.
|
||||
|
||||
[1]: https://github.com/antirez/redis/blob/unstable/COPYING
|
||||
[2]: https://github.com/antirez/redis/blob/unstable/CONTRIBUTING
|
||||
[1]: https://github.com/redis/redis/blob/unstable/COPYING
|
||||
[2]: https://github.com/redis/redis/blob/unstable/CONTRIBUTING
|
||||
|
||||
Redis internals
|
||||
===
|
||||
@@ -218,7 +257,7 @@ Inside the root are the following important directories:
|
||||
|
||||
* `src`: contains the Redis implementation, written in C.
|
||||
* `tests`: contains the unit tests, implemented in Tcl.
|
||||
* `deps`: contains libraries Redis uses. Everything needed to compile Redis is inside this directory; your system just needs to provide `libc`, a POSIX compatible interface and a C compiler. Notably `deps` contains a copy of `jemalloc`, which is the default allocator of Redis under Linux. Note that under `deps` there are also things which started with the Redis project, but for which the main repository is not `antirez/redis`.
|
||||
* `deps`: contains libraries Redis uses. Everything needed to compile Redis is inside this directory; your system just needs to provide `libc`, a POSIX compatible interface and a C compiler. Notably `deps` contains a copy of `jemalloc`, which is the default allocator of Redis under Linux. Note that under `deps` there are also things which started with the Redis project, but for which the main repository is not `redis/redis`.
|
||||
|
||||
There are a few more directories but they are not very important for our goals
|
||||
here. We'll focus mostly on `src`, where the Redis implementation is contained,
|
||||
@@ -228,7 +267,7 @@ of complexity incrementally.
|
||||
|
||||
Note: lately Redis was refactored quite a bit. Function names and file
|
||||
names have been changed, so you may find that this documentation reflects the
|
||||
`unstable` branch more closely. For instance in Redis 3.0 the `server.c`
|
||||
`unstable` branch more closely. For instance, in Redis 3.0 the `server.c`
|
||||
and `server.h` files were named `redis.c` and `redis.h`. However the overall
|
||||
structure is the same. Keep in mind that all the new developments and pull
|
||||
requests should be performed against the `unstable` branch.
|
||||
@@ -255,25 +294,25 @@ the structure definition.
|
||||
Another important Redis data structure is the one defining a client.
|
||||
In the past it was called `redisClient`, now just `client`. The structure
|
||||
has many fields, here we'll just show the main ones:
|
||||
|
||||
struct client {
|
||||
int fd;
|
||||
sds querybuf;
|
||||
int argc;
|
||||
robj **argv;
|
||||
redisDb *db;
|
||||
int flags;
|
||||
list *reply;
|
||||
char buf[PROTO_REPLY_CHUNK_BYTES];
|
||||
... many other fields ...
|
||||
}
|
||||
|
||||
```c
|
||||
struct client {
|
||||
int fd;
|
||||
sds querybuf;
|
||||
int argc;
|
||||
robj **argv;
|
||||
redisDb *db;
|
||||
int flags;
|
||||
list *reply;
|
||||
char buf[PROTO_REPLY_CHUNK_BYTES];
|
||||
// ... many other fields ...
|
||||
}
|
||||
```
|
||||
The client structure defines a *connected client*:
|
||||
|
||||
* The `fd` field is the client socket file descriptor.
|
||||
* `argc` and `argv` are populated with the command the client is executing, so that functions implementing a given Redis command can read the arguments.
|
||||
* `querybuf` accumulates the requests from the client, which are parsed by the Redis server according to the Redis protocol and executed by calling the implementations of the commands the client is executing.
|
||||
* `reply` and `buf` are dynamic and static buffers that accumulate the replies the server sends to the client. These buffers are incrementally written to the socket as soon as the file descriptor is writable.
|
||||
* `reply` and `buf` are dynamic and static buffers that accumulate the replies the server sends to the client. These buffers are incrementally written to the socket as soon as the file descriptor is writeable.
|
||||
|
||||
As you can see in the client structure above, arguments in a command
|
||||
are described as `robj` structures. The following is the full `robj`
|
||||
@@ -306,20 +345,20 @@ This is the entry point of the Redis server, where the `main()` function
|
||||
is defined. The following are the most important steps in order to startup
|
||||
the Redis server.
|
||||
|
||||
* `initServerConfig()` setups the default values of the `server` structure.
|
||||
* `initServerConfig()` sets up the default values of the `server` structure.
|
||||
* `initServer()` allocates the data structures needed to operate, setup the listening socket, and so forth.
|
||||
* `aeMain()` starts the event loop which listens for new connections.
|
||||
|
||||
There are two special functions called periodically by the event loop:
|
||||
|
||||
1. `serverCron()` is called periodically (according to `server.hz` frequency), and performs tasks that must be performed from time to time, like checking for timedout clients.
|
||||
1. `serverCron()` is called periodically (according to `server.hz` frequency), and performs tasks that must be performed from time to time, like checking for timed out clients.
|
||||
2. `beforeSleep()` is called every time the event loop fired, Redis served a few requests, and is returning back into the event loop.
|
||||
|
||||
Inside server.c you can find code that handles other vital things of the Redis server:
|
||||
|
||||
* `call()` is used in order to call a given command in the context of a given client.
|
||||
* `activeExpireCycle()` handles eviciton of keys with a time to live set via the `EXPIRE` command.
|
||||
* `freeMemoryIfNeeded()` is called when a new write command should be performed but Redis is out of memory according to the `maxmemory` directive.
|
||||
* `activeExpireCycle()` handles eviction of keys with a time to live set via the `EXPIRE` command.
|
||||
* `performEvictions()` is called when a new write command should be performed but Redis is out of memory according to the `maxmemory` directive.
|
||||
* The global variable `redisCommandTable` defines all the Redis commands, specifying the name of the command, the function implementing the command, the number of arguments required, and other properties of each command.
|
||||
|
||||
networking.c
|
||||
@@ -329,16 +368,16 @@ This file defines all the I/O functions with clients, masters and replicas
|
||||
(which in Redis are just special clients):
|
||||
|
||||
* `createClient()` allocates and initializes a new client.
|
||||
* the `addReply*()` family of functions are used by commands implementations in order to append data to the client structure, that will be transmitted to the client as a reply for a given command executed.
|
||||
* the `addReply*()` family of functions are used by command implementations in order to append data to the client structure, that will be transmitted to the client as a reply for a given command executed.
|
||||
* `writeToClient()` transmits the data pending in the output buffers to the client and is called by the *writable event handler* `sendReplyToClient()`.
|
||||
* `readQueryFromClient()` is the *readable event handler* and accumulates data from read from the client into the query buffer.
|
||||
* `readQueryFromClient()` is the *readable event handler* and accumulates data read from the client into the query buffer.
|
||||
* `processInputBuffer()` is the entry point in order to parse the client query buffer according to the Redis protocol. Once commands are ready to be processed, it calls `processCommand()` which is defined inside `server.c` in order to actually execute the command.
|
||||
* `freeClient()` deallocates, disconnects and removes a client.
|
||||
|
||||
aof.c and rdb.c
|
||||
---
|
||||
|
||||
As you can guess from the names these files implement the RDB and AOF
|
||||
As you can guess from the names, these files implement the RDB and AOF
|
||||
persistence for Redis. Redis uses a persistence model based on the `fork()`
|
||||
system call in order to create a thread with the same (shared) memory
|
||||
content of the main Redis thread. This secondary thread dumps the content
|
||||
@@ -350,13 +389,13 @@ The implementation inside `aof.c` has additional functions in order to
|
||||
implement an API that allows commands to append new commands into the AOF
|
||||
file as clients execute them.
|
||||
|
||||
The `call()` function defined inside `server.c` is responsible to call
|
||||
The `call()` function defined inside `server.c` is responsible for calling
|
||||
the functions that in turn will write the commands into the AOF.
|
||||
|
||||
db.c
|
||||
---
|
||||
|
||||
Certain Redis commands operate on specific data types, others are general.
|
||||
Certain Redis commands operate on specific data types; others are general.
|
||||
Examples of generic commands are `DEL` and `EXPIRE`. They operate on keys
|
||||
and not on their values specifically. All those generic commands are
|
||||
defined inside `db.c`.
|
||||
@@ -364,7 +403,7 @@ defined inside `db.c`.
|
||||
Moreover `db.c` implements an API in order to perform certain operations
|
||||
on the Redis dataset without directly accessing the internal data structures.
|
||||
|
||||
The most important functions inside `db.c` which are used in many commands
|
||||
The most important functions inside `db.c` which are used in many command
|
||||
implementations are the following:
|
||||
|
||||
* `lookupKeyRead()` and `lookupKeyWrite()` are used in order to get a pointer to the value associated to a given key, or `NULL` if the key does not exist.
|
||||
@@ -382,7 +421,7 @@ The `robj` structure defining Redis objects was already described. Inside
|
||||
a basic level, like functions to allocate new objects, handle the reference
|
||||
counting and so forth. Notable functions inside this file:
|
||||
|
||||
* `incrRefcount()` and `decrRefCount()` are used in order to increment or decrement an object reference count. When it drops to 0 the object is finally freed.
|
||||
* `incrRefCount()` and `decrRefCount()` are used in order to increment or decrement an object reference count. When it drops to 0 the object is finally freed.
|
||||
* `createObject()` allocates a new object. There are also specialized functions to allocate string objects having a specific content, like `createStringObjectFromLongLong()` and similar functions.
|
||||
|
||||
This file also implements the `OBJECT` command.
|
||||
@@ -406,15 +445,15 @@ replicas, or to continue the replication after a disconnection.
|
||||
Other C files
|
||||
---
|
||||
|
||||
* `t_hash.c`, `t_list.c`, `t_set.c`, `t_string.c`, `t_zset.c` and `t_stream.c` contains the implementation of the Redis data types. They implement both an API to access a given data type, and the client commands implementations for these data types.
|
||||
* `t_hash.c`, `t_list.c`, `t_set.c`, `t_string.c`, `t_zset.c` and `t_stream.c` contains the implementation of the Redis data types. They implement both an API to access a given data type, and the client command implementations for these data types.
|
||||
* `ae.c` implements the Redis event loop, it's a self contained library which is simple to read and understand.
|
||||
* `sds.c` is the Redis string library, check http://github.com/antirez/sds for more information.
|
||||
* `anet.c` is a library to use POSIX networking in a simpler way compared to the raw interface exposed by the kernel.
|
||||
* `dict.c` is an implementation of a non-blocking hash table which rehashes incrementally.
|
||||
* `scripting.c` implements Lua scripting. It is completely self contained from the rest of the Redis implementation and is simple enough to understand if you are familar with the Lua API.
|
||||
* `scripting.c` implements Lua scripting. It is completely self-contained and isolated from the rest of the Redis implementation and is simple enough to understand if you are familiar with the Lua API.
|
||||
* `cluster.c` implements the Redis Cluster. Probably a good read only after being very familiar with the rest of the Redis code base. If you want to read `cluster.c` make sure to read the [Redis Cluster specification][3].
|
||||
|
||||
[3]: http://redis.io/topics/cluster-spec
|
||||
[3]: https://redis.io/topics/cluster-spec
|
||||
|
||||
Anatomy of a Redis command
|
||||
---
|
||||
@@ -437,12 +476,12 @@ top comment inside `server.c`.
|
||||
After the command operates in some way, it returns a reply to the client,
|
||||
usually using `addReply()` or a similar function defined inside `networking.c`.
|
||||
|
||||
There are tons of commands implementations inside the Redis source code
|
||||
that can serve as examples of actual commands implementations. To write
|
||||
a few toy commands can be a good exercise to familiarize with the code base.
|
||||
There are tons of command implementations inside the Redis source code
|
||||
that can serve as examples of actual commands implementations. Writing
|
||||
a few toy commands can be a good exercise to get familiar with the code base.
|
||||
|
||||
There are also many other files not described here, but it is useless to
|
||||
cover everything. We want to just help you with the first steps.
|
||||
cover everything. We just want to help you with the first steps.
|
||||
Eventually you'll find your way inside the Redis code base :-)
|
||||
|
||||
Enjoy!
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
TLS Support -- Work In Progress
|
||||
===============================
|
||||
|
||||
This is a brief note to capture current thoughts/ideas and track pending action
|
||||
items.
|
||||
TLS Support
|
||||
===========
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
@@ -69,37 +66,21 @@ probably not be so hard. For cluster keys migration it might be more difficult,
|
||||
but there are probably other good reasons to improve that part anyway.
|
||||
|
||||
To-Do List
|
||||
==========
|
||||
----------
|
||||
|
||||
Additional TLS Features
|
||||
-----------------------
|
||||
- [ ] redis-benchmark support. The current implementation is a mix of using
|
||||
hiredis for parsing and basic networking (establishing connections), but
|
||||
directly manipulating sockets for most actions. This will need to be cleaned
|
||||
up for proper TLS support. The best approach is probably to migrate to hiredis
|
||||
async mode.
|
||||
- [ ] redis-cli `--slave` and `--rdb` support.
|
||||
|
||||
1. Add metrics to INFO?
|
||||
2. Add session caching support. Check if/how it's handled by clients to assess
|
||||
how useful/important it is.
|
||||
|
||||
redis-benchmark
|
||||
---------------
|
||||
|
||||
The current implementation is a mix of using hiredis for parsing and basic
|
||||
networking (establishing connections), but directly manipulating sockets for
|
||||
most actions.
|
||||
|
||||
This will need to be cleaned up for proper TLS support. The best approach is
|
||||
probably to migrate to hiredis async mode.
|
||||
|
||||
redis-cli
|
||||
---------
|
||||
|
||||
1. Add support for TLS in --slave and --rdb modes.
|
||||
|
||||
Others
|
||||
------
|
||||
Multi-port
|
||||
----------
|
||||
|
||||
Consider the implications of allowing TLS to be configured on a separate port,
|
||||
making Redis listening on multiple ports.
|
||||
making Redis listening on multiple ports:
|
||||
|
||||
This impacts many things, like
|
||||
1. Startup banner port notification
|
||||
2. Proctitle
|
||||
3. How slaves announce themselves
|
||||
|
||||
Vendored
+17
-3
@@ -2,6 +2,8 @@
|
||||
|
||||
uname_S:= $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
|
||||
LUA_COVERAGE?=no
|
||||
|
||||
CCCOLOR="\033[34m"
|
||||
LINKCOLOR="\033[34;1m"
|
||||
SRCCOLOR="\033[33m"
|
||||
@@ -37,6 +39,7 @@ distclean:
|
||||
-(cd linenoise && $(MAKE) clean) > /dev/null || true
|
||||
-(cd lua && $(MAKE) clean) > /dev/null || true
|
||||
-(cd jemalloc && [ -f Makefile ] && $(MAKE) distclean) > /dev/null || true
|
||||
-(cd hdr_histogram && $(MAKE) clean) > /dev/null || true
|
||||
-(rm -f .make-*)
|
||||
|
||||
.PHONY: distclean
|
||||
@@ -57,18 +60,29 @@ linenoise: .make-prerequisites
|
||||
|
||||
.PHONY: linenoise
|
||||
|
||||
hdr_histogram: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd hdr_histogram && $(MAKE)
|
||||
|
||||
.PHONY: hdr_histogram
|
||||
|
||||
ifeq ($(uname_S),SunOS)
|
||||
# Make isinf() available
|
||||
LUA_CFLAGS= -D__C99FEATURES__=1
|
||||
endif
|
||||
|
||||
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI -DENABLE_CJSON_GLOBAL -DREDIS_STATIC='' $(CFLAGS)
|
||||
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI -DENABLE_CJSON_GLOBAL -DREDIS_STATIC='' -DLUA_USE_MKSTEMP $(CFLAGS)
|
||||
LUA_LDFLAGS+= $(LDFLAGS)
|
||||
ifeq ($(LUA_COVERAGE),yes)
|
||||
LUA_CFLAGS += -fprofile-arcs -ftest-coverage
|
||||
LUA_LDFLAGS += -fprofile-arcs -ftest-coverage
|
||||
endif
|
||||
|
||||
# lua's Makefile defines AR="ar rcu", which is unusual, and makes it more
|
||||
# challenging to cross-compile lua (and redis). These defines make it easier
|
||||
# to fit redis into cross-compilation environments, which typically set AR.
|
||||
AR=ar
|
||||
ARFLAGS=rcu
|
||||
ARFLAGS=rc
|
||||
|
||||
lua: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
@@ -81,7 +95,7 @@ JEMALLOC_LDFLAGS= $(LDFLAGS)
|
||||
|
||||
jemalloc: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd jemalloc && ./configure --with-version=5.1.0-0-g0 --with-lg-quantum=3 --with-jemalloc-prefix=je_ --enable-cc-silence CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)"
|
||||
cd jemalloc && ./configure --with-version=5.1.0-0-g0 --with-lg-quantum=3 --with-jemalloc-prefix=je_ CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)"
|
||||
cd jemalloc && $(MAKE) CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)" lib/libjemalloc.a
|
||||
|
||||
.PHONY: jemalloc
|
||||
|
||||
Vendored
+6
-6
@@ -17,11 +17,11 @@ active defragmentation logic. However this feature of Redis is not mandatory
|
||||
and Redis is able to understand if the Jemalloc version it is compiled
|
||||
against supports such Redis-specific modifications. So in theory, if you
|
||||
are not interested in the active defragmentation, you can replace Jemalloc
|
||||
just following tose steps:
|
||||
just following these steps:
|
||||
|
||||
1. Remove the jemalloc directory.
|
||||
2. Substitute it with the new jemalloc source tree.
|
||||
3. Edit the Makefile localted in the same directory as the README you are
|
||||
3. Edit the Makefile located in the same directory as the README you are
|
||||
reading, and change the --with-version in the Jemalloc configure script
|
||||
options with the version you are using. This is required because otherwise
|
||||
Jemalloc configuration script is broken and will not work nested in another
|
||||
@@ -33,7 +33,7 @@ If you want to upgrade Jemalloc while also providing support for
|
||||
active defragmentation, in addition to the above steps you need to perform
|
||||
the following additional steps:
|
||||
|
||||
5. In Jemalloc three, file `include/jemalloc/jemalloc_macros.h.in`, make sure
|
||||
5. In Jemalloc tree, file `include/jemalloc/jemalloc_macros.h.in`, make sure
|
||||
to add `#define JEMALLOC_FRAG_HINT`.
|
||||
6. Implement the function `je_get_defrag_hint()` inside `src/jemalloc.c`. You
|
||||
can see how it is implemented in the current Jemalloc source tree shipped
|
||||
@@ -47,9 +47,9 @@ Hiredis
|
||||
Hiredis uses the SDS string library, that must be the same version used inside Redis itself. Hiredis is also very critical for Sentinel. Historically Redis often used forked versions of hiredis in a way or the other. In order to upgrade it is advised to take a lot of care:
|
||||
|
||||
1. Check with diff if hiredis API changed and what impact it could have in Redis.
|
||||
2. Make sure thet the SDS library inside Hiredis and inside Redis are compatible.
|
||||
2. Make sure that the SDS library inside Hiredis and inside Redis are compatible.
|
||||
3. After the upgrade, run the Redis Sentinel test.
|
||||
4. Check manually that redis-cli and redis-benchmark behave as expecteed, since we have no tests for CLI utilities currently.
|
||||
4. Check manually that redis-cli and redis-benchmark behave as expected, since we have no tests for CLI utilities currently.
|
||||
|
||||
Linenoise
|
||||
---
|
||||
@@ -77,6 +77,6 @@ and our version:
|
||||
|
||||
1. Makefile is modified to allow a different compiler than GCC.
|
||||
2. We have the implementation source code, and directly link to the following external libraries: `lua_cjson.o`, `lua_struct.o`, `lua_cmsgpack.o` and `lua_bit.o`.
|
||||
3. There is a security fix in `ldo.c`, line 498: The check for `LUA_SIGNATURE[0]` is removed in order toa void direct bytecode execution.
|
||||
3. There is a security fix in `ldo.c`, line 498: The check for `LUA_SIGNATURE[0]` is removed in order to avoid direct bytecode execution.
|
||||
|
||||
|
||||
|
||||
Vendored
+121
@@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
The code in this repository code was Written by Gil Tene, Michael Barker,
|
||||
and Matt Warren, and released to the public domain, as explained at
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
For users of this code who wish to consume it under the "BSD" license
|
||||
rather than under the public domain or CC0 contribution text mentioned
|
||||
above, the code found under this directory is *also* provided under the
|
||||
following license (commonly referred to as the BSD 2-Clause License). This
|
||||
license does not detract from the above stated release of the code into
|
||||
the public domain, and simply represents an additional license granted by
|
||||
the Author.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
** Beginning of "BSD 2-Clause License" text. **
|
||||
|
||||
Copyright (c) 2012, 2013, 2014 Gil Tene
|
||||
Copyright (c) 2014 Michael Barker
|
||||
Copyright (c) 2014 Matt Warren
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
STD=
|
||||
WARN= -Wall
|
||||
OPT= -Os
|
||||
|
||||
R_CFLAGS= $(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS)
|
||||
R_LDFLAGS= $(LDFLAGS)
|
||||
DEBUG= -g
|
||||
|
||||
R_CC=$(CC) $(R_CFLAGS)
|
||||
R_LD=$(CC) $(R_LDFLAGS)
|
||||
|
||||
hdr_histogram.o: hdr_histogram.h hdr_histogram.c
|
||||
|
||||
.c.o:
|
||||
$(R_CC) -c $<
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
|
||||
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
HdrHistogram_c v0.11.0
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
This port contains a subset of the 'C' version of High Dynamic Range (HDR) Histogram available at [github.com/HdrHistogram/HdrHistogram_c](https://github.com/HdrHistogram/HdrHistogram_c).
|
||||
|
||||
|
||||
The code present on `hdr_histogram.c`, `hdr_histogram.h`, and `hdr_atomic.c` was Written by Gil Tene, Michael Barker,
|
||||
and Matt Warren, and released to the public domain, as explained at
|
||||
http://creativecommons.org/publicdomain/zero/1.0/.
|
||||
Vendored
+146
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* hdr_atomic.h
|
||||
* Written by Philip Orwig and released to the public domain,
|
||||
* as explained at http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#ifndef HDR_ATOMIC_H__
|
||||
#define HDR_ATOMIC_H__
|
||||
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <intrin.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static void __inline * hdr_atomic_load_pointer(void** pointer)
|
||||
{
|
||||
_ReadBarrier();
|
||||
return *pointer;
|
||||
}
|
||||
|
||||
static void hdr_atomic_store_pointer(void** pointer, void* value)
|
||||
{
|
||||
_WriteBarrier();
|
||||
*pointer = value;
|
||||
}
|
||||
|
||||
static int64_t __inline hdr_atomic_load_64(int64_t* field)
|
||||
{
|
||||
_ReadBarrier();
|
||||
return *field;
|
||||
}
|
||||
|
||||
static void __inline hdr_atomic_store_64(int64_t* field, int64_t value)
|
||||
{
|
||||
_WriteBarrier();
|
||||
*field = value;
|
||||
}
|
||||
|
||||
static int64_t __inline hdr_atomic_exchange_64(volatile int64_t* field, int64_t value)
|
||||
{
|
||||
#if defined(_WIN64)
|
||||
return _InterlockedExchange64(field, value);
|
||||
#else
|
||||
int64_t comparand;
|
||||
int64_t initial_value = *field;
|
||||
do
|
||||
{
|
||||
comparand = initial_value;
|
||||
initial_value = _InterlockedCompareExchange64(field, value, comparand);
|
||||
}
|
||||
while (comparand != initial_value);
|
||||
|
||||
return initial_value;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int64_t __inline hdr_atomic_add_fetch_64(volatile int64_t* field, int64_t value)
|
||||
{
|
||||
#if defined(_WIN64)
|
||||
return _InterlockedExchangeAdd64(field, value) + value;
|
||||
#else
|
||||
int64_t comparand;
|
||||
int64_t initial_value = *field;
|
||||
do
|
||||
{
|
||||
comparand = initial_value;
|
||||
initial_value = _InterlockedCompareExchange64(field, comparand + value, comparand);
|
||||
}
|
||||
while (comparand != initial_value);
|
||||
|
||||
return initial_value + value;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool __inline hdr_atomic_compare_exchange_64(volatile int64_t* field, int64_t* expected, int64_t desired)
|
||||
{
|
||||
return *expected == _InterlockedCompareExchange64(field, desired, *expected);
|
||||
}
|
||||
|
||||
#elif defined(__ATOMIC_SEQ_CST)
|
||||
|
||||
#define hdr_atomic_load_pointer(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
|
||||
#define hdr_atomic_store_pointer(f,v) __atomic_store_n(f,v, __ATOMIC_SEQ_CST)
|
||||
#define hdr_atomic_load_64(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
|
||||
#define hdr_atomic_store_64(f,v) __atomic_store_n(f,v, __ATOMIC_SEQ_CST)
|
||||
#define hdr_atomic_exchange_64(f,i) __atomic_exchange_n(f,i, __ATOMIC_SEQ_CST)
|
||||
#define hdr_atomic_add_fetch_64(field, value) __atomic_add_fetch(field, value, __ATOMIC_SEQ_CST)
|
||||
#define hdr_atomic_compare_exchange_64(field, expected, desired) __atomic_compare_exchange_n(field, expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static inline void* hdr_atomic_load_pointer(void** pointer)
|
||||
{
|
||||
void* p = *pointer;
|
||||
asm volatile ("" ::: "memory");
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline void hdr_atomic_store_pointer(void** pointer, void* value)
|
||||
{
|
||||
asm volatile ("lock; xchgq %0, %1" : "+q" (value), "+m" (*pointer));
|
||||
}
|
||||
|
||||
static inline int64_t hdr_atomic_load_64(int64_t* field)
|
||||
{
|
||||
int64_t i = *field;
|
||||
asm volatile ("" ::: "memory");
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline void hdr_atomic_store_64(int64_t* field, int64_t value)
|
||||
{
|
||||
asm volatile ("lock; xchgq %0, %1" : "+q" (value), "+m" (*field));
|
||||
}
|
||||
|
||||
static inline int64_t hdr_atomic_exchange_64(volatile int64_t* field, int64_t value)
|
||||
{
|
||||
int64_t result = 0;
|
||||
asm volatile ("lock; xchgq %1, %2" : "=r" (result), "+q" (value), "+m" (*field));
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int64_t hdr_atomic_add_fetch_64(volatile int64_t* field, int64_t value)
|
||||
{
|
||||
return __sync_add_and_fetch(field, value);
|
||||
}
|
||||
|
||||
static inline bool hdr_atomic_compare_exchange_64(volatile int64_t* field, int64_t* expected, int64_t desired)
|
||||
{
|
||||
int64_t original;
|
||||
asm volatile( "lock; cmpxchgq %2, %1" : "=a"(original), "+m"(*field) : "q"(desired), "0"(*expected));
|
||||
return original == *expected;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error "Unable to determine atomic operations for your platform"
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HDR_ATOMIC_H__ */
|
||||
Vendored
+1155
File diff suppressed because it is too large
Load Diff
Vendored
+509
@@ -0,0 +1,509 @@
|
||||
/**
|
||||
* hdr_histogram.h
|
||||
* Written by Michael Barker and released to the public domain,
|
||||
* as explained at http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*
|
||||
* The source for the hdr_histogram utilises a few C99 constructs, specifically
|
||||
* the use of stdint/stdbool and inline variable declaration.
|
||||
*/
|
||||
|
||||
#ifndef HDR_HISTOGRAM_H
|
||||
#define HDR_HISTOGRAM_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct hdr_histogram
|
||||
{
|
||||
int64_t lowest_trackable_value;
|
||||
int64_t highest_trackable_value;
|
||||
int32_t unit_magnitude;
|
||||
int32_t significant_figures;
|
||||
int32_t sub_bucket_half_count_magnitude;
|
||||
int32_t sub_bucket_half_count;
|
||||
int64_t sub_bucket_mask;
|
||||
int32_t sub_bucket_count;
|
||||
int32_t bucket_count;
|
||||
int64_t min_value;
|
||||
int64_t max_value;
|
||||
int32_t normalizing_index_offset;
|
||||
double conversion_ratio;
|
||||
int32_t counts_len;
|
||||
int64_t total_count;
|
||||
int64_t* counts;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Allocate the memory and initialise the hdr_histogram.
|
||||
*
|
||||
* Due to the size of the histogram being the result of some reasonably
|
||||
* involved math on the input parameters this function it is tricky to stack allocate.
|
||||
* The histogram should be released with hdr_close
|
||||
*
|
||||
* @param lowest_trackable_value The smallest possible value to be put into the
|
||||
* histogram.
|
||||
* @param highest_trackable_value The largest possible value to be put into the
|
||||
* histogram.
|
||||
* @param significant_figures The level of precision for this histogram, i.e. the number
|
||||
* of figures in a decimal number that will be maintained. E.g. a value of 3 will mean
|
||||
* the results from the histogram will be accurate up to the first three digits. Must
|
||||
* be a value between 1 and 5 (inclusive).
|
||||
* @param result Output parameter to capture allocated histogram.
|
||||
* @return 0 on success, EINVAL if lowest_trackable_value is < 1 or the
|
||||
* significant_figure value is outside of the allowed range, ENOMEM if malloc
|
||||
* failed.
|
||||
*/
|
||||
int hdr_init(
|
||||
int64_t lowest_trackable_value,
|
||||
int64_t highest_trackable_value,
|
||||
int significant_figures,
|
||||
struct hdr_histogram** result);
|
||||
|
||||
/**
|
||||
* Free the memory and close the hdr_histogram.
|
||||
*
|
||||
* @param h The histogram you want to close.
|
||||
*/
|
||||
void hdr_close(struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Allocate the memory and initialise the hdr_histogram. This is the equivalent of calling
|
||||
* hdr_init(1, highest_trackable_value, significant_figures, result);
|
||||
*
|
||||
* @deprecated use hdr_init.
|
||||
*/
|
||||
int hdr_alloc(int64_t highest_trackable_value, int significant_figures, struct hdr_histogram** result);
|
||||
|
||||
|
||||
/**
|
||||
* Reset a histogram to zero - empty out a histogram and re-initialise it
|
||||
*
|
||||
* If you want to re-use an existing histogram, but reset everything back to zero, this
|
||||
* is the routine to use.
|
||||
*
|
||||
* @param h The histogram you want to reset to empty.
|
||||
*
|
||||
*/
|
||||
void hdr_reset(struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Get the memory size of the hdr_histogram.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @return The amount of memory used by the hdr_histogram in bytes
|
||||
*/
|
||||
size_t hdr_get_memory_size(struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Records a value in the histogram, will round this value of to a precision at or better
|
||||
* than the significant_figure specified at construction time.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_value(struct hdr_histogram* h, int64_t value);
|
||||
|
||||
/**
|
||||
* Records a value in the histogram, will round this value of to a precision at or better
|
||||
* than the significant_figure specified at construction time.
|
||||
*
|
||||
* Will record this value atomically, however the whole structure may appear inconsistent
|
||||
* when read concurrently with this update. Do NOT mix calls to this method with calls
|
||||
* to non-atomic updates.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_value_atomic(struct hdr_histogram* h, int64_t value);
|
||||
|
||||
/**
|
||||
* Records count values in the histogram, will round this value of to a
|
||||
* precision at or better than the significant_figure specified at construction
|
||||
* time.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param count Number of 'value's to add to the histogram
|
||||
* @return false if any value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_values(struct hdr_histogram* h, int64_t value, int64_t count);
|
||||
|
||||
/**
|
||||
* Records count values in the histogram, will round this value of to a
|
||||
* precision at or better than the significant_figure specified at construction
|
||||
* time.
|
||||
*
|
||||
* Will record this value atomically, however the whole structure may appear inconsistent
|
||||
* when read concurrently with this update. Do NOT mix calls to this method with calls
|
||||
* to non-atomic updates.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param count Number of 'value's to add to the histogram
|
||||
* @return false if any value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_values_atomic(struct hdr_histogram* h, int64_t value, int64_t count);
|
||||
|
||||
/**
|
||||
* Record a value in the histogram and backfill based on an expected interval.
|
||||
*
|
||||
* Records a value in the histogram, will round this value of to a precision at or better
|
||||
* than the significant_figure specified at contruction time. This is specifically used
|
||||
* for recording latency. If the value is larger than the expected_interval then the
|
||||
* latency recording system has experienced co-ordinated omission. This method fills in the
|
||||
* values that would have occured had the client providing the load not been blocked.
|
||||
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param expected_interval The delay between recording values.
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_corrected_value(struct hdr_histogram* h, int64_t value, int64_t expexcted_interval);
|
||||
|
||||
/**
|
||||
* Record a value in the histogram and backfill based on an expected interval.
|
||||
*
|
||||
* Records a value in the histogram, will round this value of to a precision at or better
|
||||
* than the significant_figure specified at contruction time. This is specifically used
|
||||
* for recording latency. If the value is larger than the expected_interval then the
|
||||
* latency recording system has experienced co-ordinated omission. This method fills in the
|
||||
* values that would have occured had the client providing the load not been blocked.
|
||||
*
|
||||
* Will record this value atomically, however the whole structure may appear inconsistent
|
||||
* when read concurrently with this update. Do NOT mix calls to this method with calls
|
||||
* to non-atomic updates.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param expected_interval The delay between recording values.
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_corrected_value_atomic(struct hdr_histogram* h, int64_t value, int64_t expexcted_interval);
|
||||
|
||||
/**
|
||||
* Record a value in the histogram 'count' times. Applies the same correcting logic
|
||||
* as 'hdr_record_corrected_value'.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param count Number of 'value's to add to the histogram
|
||||
* @param expected_interval The delay between recording values.
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_corrected_values(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval);
|
||||
|
||||
/**
|
||||
* Record a value in the histogram 'count' times. Applies the same correcting logic
|
||||
* as 'hdr_record_corrected_value'.
|
||||
*
|
||||
* Will record this value atomically, however the whole structure may appear inconsistent
|
||||
* when read concurrently with this update. Do NOT mix calls to this method with calls
|
||||
* to non-atomic updates.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param count Number of 'value's to add to the histogram
|
||||
* @param expected_interval The delay between recording values.
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_corrected_values_atomic(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval);
|
||||
|
||||
/**
|
||||
* Adds all of the values from 'from' to 'this' histogram. Will return the
|
||||
* number of values that are dropped when copying. Values will be dropped
|
||||
* if they around outside of h.lowest_trackable_value and
|
||||
* h.highest_trackable_value.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param from Histogram to copy values from.
|
||||
* @return The number of values dropped when copying.
|
||||
*/
|
||||
int64_t hdr_add(struct hdr_histogram* h, const struct hdr_histogram* from);
|
||||
|
||||
/**
|
||||
* Adds all of the values from 'from' to 'this' histogram. Will return the
|
||||
* number of values that are dropped when copying. Values will be dropped
|
||||
* if they around outside of h.lowest_trackable_value and
|
||||
* h.highest_trackable_value.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param from Histogram to copy values from.
|
||||
* @return The number of values dropped when copying.
|
||||
*/
|
||||
int64_t hdr_add_while_correcting_for_coordinated_omission(
|
||||
struct hdr_histogram* h, struct hdr_histogram* from, int64_t expected_interval);
|
||||
|
||||
/**
|
||||
* Get minimum value from the histogram. Will return 2^63-1 if the histogram
|
||||
* is empty.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
*/
|
||||
int64_t hdr_min(const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Get maximum value from the histogram. Will return 0 if the histogram
|
||||
* is empty.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
*/
|
||||
int64_t hdr_max(const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Get the value at a specific percentile.
|
||||
*
|
||||
* @param h "This" pointer.
|
||||
* @param percentile The percentile to get the value for
|
||||
*/
|
||||
int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile);
|
||||
|
||||
/**
|
||||
* Gets the standard deviation for the values in the histogram.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @return The standard deviation
|
||||
*/
|
||||
double hdr_stddev(const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Gets the mean for the values in the histogram.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @return The mean
|
||||
*/
|
||||
double hdr_mean(const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Determine if two values are equivalent with the histogram's resolution.
|
||||
* Where "equivalent" means that value samples recorded for any two
|
||||
* equivalent values are counted in a common total count.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param a first value to compare
|
||||
* @param b second value to compare
|
||||
* @return 'true' if values are equivalent with the histogram's resolution.
|
||||
*/
|
||||
bool hdr_values_are_equivalent(const struct hdr_histogram* h, int64_t a, int64_t b);
|
||||
|
||||
/**
|
||||
* Get the lowest value that is equivalent to the given value within the histogram's resolution.
|
||||
* Where "equivalent" means that value samples recorded for any two
|
||||
* equivalent values are counted in a common total count.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value The given value
|
||||
* @return The lowest value that is equivalent to the given value within the histogram's resolution.
|
||||
*/
|
||||
int64_t hdr_lowest_equivalent_value(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
/**
|
||||
* Get the count of recorded values at a specific value
|
||||
* (to within the histogram resolution at the value level).
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value The value for which to provide the recorded count
|
||||
* @return The total count of values recorded in the histogram within the value range that is
|
||||
* {@literal >=} lowestEquivalentValue(<i>value</i>) and {@literal <=} highestEquivalentValue(<i>value</i>)
|
||||
*/
|
||||
int64_t hdr_count_at_value(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
int64_t hdr_count_at_index(const struct hdr_histogram* h, int32_t index);
|
||||
|
||||
int64_t hdr_value_at_index(const struct hdr_histogram* h, int32_t index);
|
||||
|
||||
struct hdr_iter_percentiles
|
||||
{
|
||||
bool seen_last_value;
|
||||
int32_t ticks_per_half_distance;
|
||||
double percentile_to_iterate_to;
|
||||
double percentile;
|
||||
};
|
||||
|
||||
struct hdr_iter_recorded
|
||||
{
|
||||
int64_t count_added_in_this_iteration_step;
|
||||
};
|
||||
|
||||
struct hdr_iter_linear
|
||||
{
|
||||
int64_t value_units_per_bucket;
|
||||
int64_t count_added_in_this_iteration_step;
|
||||
int64_t next_value_reporting_level;
|
||||
int64_t next_value_reporting_level_lowest_equivalent;
|
||||
};
|
||||
|
||||
struct hdr_iter_log
|
||||
{
|
||||
double log_base;
|
||||
int64_t count_added_in_this_iteration_step;
|
||||
int64_t next_value_reporting_level;
|
||||
int64_t next_value_reporting_level_lowest_equivalent;
|
||||
};
|
||||
|
||||
/**
|
||||
* The basic iterator. This is a generic structure
|
||||
* that supports all of the types of iteration. Use
|
||||
* the appropriate initialiser to get the desired
|
||||
* iteration.
|
||||
*
|
||||
* @
|
||||
*/
|
||||
struct hdr_iter
|
||||
{
|
||||
const struct hdr_histogram* h;
|
||||
/** raw index into the counts array */
|
||||
int32_t counts_index;
|
||||
/** snapshot of the length at the time the iterator is created */
|
||||
int64_t total_count;
|
||||
/** value directly from array for the current counts_index */
|
||||
int64_t count;
|
||||
/** sum of all of the counts up to and including the count at this index */
|
||||
int64_t cumulative_count;
|
||||
/** The current value based on counts_index */
|
||||
int64_t value;
|
||||
int64_t highest_equivalent_value;
|
||||
int64_t lowest_equivalent_value;
|
||||
int64_t median_equivalent_value;
|
||||
int64_t value_iterated_from;
|
||||
int64_t value_iterated_to;
|
||||
|
||||
union
|
||||
{
|
||||
struct hdr_iter_percentiles percentiles;
|
||||
struct hdr_iter_recorded recorded;
|
||||
struct hdr_iter_linear linear;
|
||||
struct hdr_iter_log log;
|
||||
} specifics;
|
||||
|
||||
bool (* _next_fp)(struct hdr_iter* iter);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Initalises the basic iterator.
|
||||
*
|
||||
* @param itr 'This' pointer
|
||||
* @param h The histogram to iterate over
|
||||
*/
|
||||
void hdr_iter_init(struct hdr_iter* iter, const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Initialise the iterator for use with percentiles.
|
||||
*/
|
||||
void hdr_iter_percentile_init(struct hdr_iter* iter, const struct hdr_histogram* h, int32_t ticks_per_half_distance);
|
||||
|
||||
/**
|
||||
* Initialise the iterator for use with recorded values.
|
||||
*/
|
||||
void hdr_iter_recorded_init(struct hdr_iter* iter, const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Initialise the iterator for use with linear values.
|
||||
*/
|
||||
void hdr_iter_linear_init(
|
||||
struct hdr_iter* iter,
|
||||
const struct hdr_histogram* h,
|
||||
int64_t value_units_per_bucket);
|
||||
|
||||
/**
|
||||
* Update the iterator value units per bucket
|
||||
*/
|
||||
void hdr_iter_linear_set_value_units_per_bucket(struct hdr_iter* iter, int64_t value_units_per_bucket);
|
||||
|
||||
/**
|
||||
* Initialise the iterator for use with logarithmic values
|
||||
*/
|
||||
void hdr_iter_log_init(
|
||||
struct hdr_iter* iter,
|
||||
const struct hdr_histogram* h,
|
||||
int64_t value_units_first_bucket,
|
||||
double log_base);
|
||||
|
||||
/**
|
||||
* Iterate to the next value for the iterator. If there are no more values
|
||||
* available return faluse.
|
||||
*
|
||||
* @param itr 'This' pointer
|
||||
* @return 'false' if there are no values remaining for this iterator.
|
||||
*/
|
||||
bool hdr_iter_next(struct hdr_iter* iter);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CLASSIC,
|
||||
CSV
|
||||
} format_type;
|
||||
|
||||
/**
|
||||
* Print out a percentile based histogram to the supplied stream. Note that
|
||||
* this call will not flush the FILE, this is left up to the user.
|
||||
*
|
||||
* @param h 'This' pointer
|
||||
* @param stream The FILE to write the output to
|
||||
* @param ticks_per_half_distance The number of iteration steps per half-distance to 100%
|
||||
* @param value_scale Scale the output values by this amount
|
||||
* @param format_type Format to use, e.g. CSV.
|
||||
* @return 0 on success, error code on failure. EIO if an error occurs writing
|
||||
* the output.
|
||||
*/
|
||||
int hdr_percentiles_print(
|
||||
struct hdr_histogram* h, FILE* stream, int32_t ticks_per_half_distance,
|
||||
double value_scale, format_type format);
|
||||
|
||||
/**
|
||||
* Internal allocation methods, used by hdr_dbl_histogram.
|
||||
*/
|
||||
struct hdr_histogram_bucket_config
|
||||
{
|
||||
int64_t lowest_trackable_value;
|
||||
int64_t highest_trackable_value;
|
||||
int64_t unit_magnitude;
|
||||
int64_t significant_figures;
|
||||
int32_t sub_bucket_half_count_magnitude;
|
||||
int32_t sub_bucket_half_count;
|
||||
int64_t sub_bucket_mask;
|
||||
int32_t sub_bucket_count;
|
||||
int32_t bucket_count;
|
||||
int32_t counts_len;
|
||||
};
|
||||
|
||||
int hdr_calculate_bucket_config(
|
||||
int64_t lowest_trackable_value,
|
||||
int64_t highest_trackable_value,
|
||||
int significant_figures,
|
||||
struct hdr_histogram_bucket_config* cfg);
|
||||
|
||||
void hdr_init_preallocated(struct hdr_histogram* h, struct hdr_histogram_bucket_config* cfg);
|
||||
|
||||
int64_t hdr_size_of_equivalent_value_range(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
int64_t hdr_next_non_equivalent_value(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
int64_t hdr_median_equivalent_value(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
/**
|
||||
* Used to reset counters after importing data manuallying into the histogram, used by the logging code
|
||||
* and other custom serialisation tools.
|
||||
*/
|
||||
void hdr_reset_internal_counters(struct hdr_histogram* h);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -6,3 +6,4 @@
|
||||
/*.a
|
||||
/*.pc
|
||||
*.dSYM
|
||||
tags
|
||||
|
||||
Vendored
+43
-9
@@ -1,5 +1,4 @@
|
||||
language: c
|
||||
sudo: false
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
@@ -8,17 +7,34 @@ os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
dist: bionic
|
||||
|
||||
branches:
|
||||
only:
|
||||
- staging
|
||||
- trying
|
||||
- master
|
||||
- /^release\/.*$/
|
||||
|
||||
install:
|
||||
- if [ "$BITS" == "64" ]; then
|
||||
wget https://github.com/redis/redis/archive/6.0.6.tar.gz;
|
||||
tar -xzvf 6.0.6.tar.gz;
|
||||
pushd redis-6.0.6 && BUILD_TLS=yes make && export PATH=$PWD/src:$PATH && popd;
|
||||
fi
|
||||
|
||||
before_script:
|
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi
|
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
curl -O https://distfiles.macports.org/MacPorts/MacPorts-2.6.2-10.13-HighSierra.pkg;
|
||||
sudo installer -pkg MacPorts-2.6.2-10.13-HighSierra.pkg -target /;
|
||||
export PATH=$PATH:/opt/local/bin && sudo port -v selfupdate;
|
||||
sudo port -N install openssl redis;
|
||||
fi;
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:chris-lea/redis-server'
|
||||
packages:
|
||||
- libc6-dbg
|
||||
- libc6-dev
|
||||
@@ -27,14 +43,20 @@ addons:
|
||||
- libc6-dbg:i386
|
||||
- gcc-multilib
|
||||
- g++-multilib
|
||||
- libssl-dev
|
||||
- libssl-dev:i386
|
||||
- valgrind
|
||||
- redis
|
||||
|
||||
env:
|
||||
- BITS="32"
|
||||
- BITS="64"
|
||||
|
||||
script:
|
||||
- EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DHIREDIS_SSL:BOOL=ON";
|
||||
- EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON";
|
||||
if [ "$BITS" == "64" ]; then
|
||||
EXTRA_CMAKE_OPTS="$EXTRA_CMAKE_OPTS -DENABLE_SSL_TESTS:BOOL=ON";
|
||||
fi;
|
||||
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
if [ "$BITS" == "32" ]; then
|
||||
CFLAGS="-m32 -Werror";
|
||||
@@ -58,12 +80,24 @@ script:
|
||||
fi;
|
||||
fi;
|
||||
export CFLAGS CXXFLAGS LDFLAGS TEST_PREFIX EXTRA_CMAKE_OPTS
|
||||
- make && make clean;
|
||||
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
if [ "$BITS" == "64" ]; then
|
||||
OPENSSL_PREFIX="$(ls -d /usr/local/Cellar/openssl@1.1/*)" USE_SSL=1 make;
|
||||
fi;
|
||||
else
|
||||
USE_SSL=1 make;
|
||||
fi;
|
||||
- mkdir build/ && cd build/
|
||||
- cmake .. ${EXTRA_CMAKE_OPTS}
|
||||
- make VERBOSE=1
|
||||
- ctest -V
|
||||
- if [ "$BITS" == "64" ]; then
|
||||
TEST_SSL=1 SKIPS_AS_FAILS=1 ctest -V;
|
||||
else
|
||||
SKIPS_AS_FAILS=1 ctest -V;
|
||||
fi;
|
||||
|
||||
matrix:
|
||||
jobs:
|
||||
include:
|
||||
# Windows MinGW cross compile on Linux
|
||||
- os: linux
|
||||
@@ -89,9 +123,9 @@ matrix:
|
||||
- eval "${MATRIX_EVAL}"
|
||||
install:
|
||||
- choco install ninja
|
||||
- choco install -y memurai-developer
|
||||
script:
|
||||
- mkdir build && cd build
|
||||
- cmd.exe /C '"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" amd64 &&
|
||||
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release &&
|
||||
ninja -v'
|
||||
- ctest -V
|
||||
- cmd.exe //C 'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat' amd64 '&&'
|
||||
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DENABLE_EXAMPLES=ON '&&' ninja -v
|
||||
- ./hiredis-test.exe
|
||||
|
||||
Vendored
+164
-18
@@ -1,28 +1,175 @@
|
||||
### 1.0.0 (unreleased)
|
||||
## [1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) - (2020-08-03)
|
||||
|
||||
Announcing Hiredis v1.0.0, which adds support for RESP3, SSL connections, allocator injection, and better Windows support! :tada:
|
||||
|
||||
_A big thanks to everyone who helped with this release. The following list includes everyone who contributed at least five lines, sorted by lines contributed._ :sparkling_heart:
|
||||
|
||||
[Michael Grunder](https://github.com/michael-grunder), [Yossi Gottlieb](https://github.com/yossigo),
|
||||
[Mark Nunberg](https://github.com/mnunberg), [Marcus Geelnard](https://github.com/mbitsnbites),
|
||||
[Justin Brewer](https://github.com/justinbrewer), [Valentino Geron](https://github.com/valentinogeron),
|
||||
[Minun Dragonation](https://github.com/dragonation), [Omri Steiner](https://github.com/OmriSteiner),
|
||||
[Sangmoon Yi](https://github.com/jman-krafton), [Jinjiazh](https://github.com/jinjiazhang),
|
||||
[Odin Hultgren Van Der Horst](https://github.com/Miniwoffer), [Muhammad Zahalqa](https://github.com/tryfinally),
|
||||
[Nick Rivera](https://github.com/heronr), [Qi Yang](https://github.com/movebean),
|
||||
[kevin1018](https://github.com/kevin1018)
|
||||
|
||||
[Full Changelog](https://github.com/redis/hiredis/compare/v0.14.1...v1.0.0)
|
||||
|
||||
**BREAKING CHANGES**:
|
||||
|
||||
* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
|
||||
protocol errors. This is consistent with the RESP specification. On 32-bit
|
||||
platforms, the upper bound is lowered to `SIZE_MAX`.
|
||||
* `redisOptions` now has two timeout fields. One for connecting, and one for commands. If you're presently using `options->timeout` you will need to change it to use `options->connect_timeout`. (See [example](https://github.com/redis/hiredis/commit/38b5ae543f5c99eb4ccabbe277770fc6bc81226f#diff-86ba39d37aa829c8c82624cce4f049fbL36))
|
||||
|
||||
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
|
||||
|
||||
User code should compare this to `size_t` values as well. If it was used to
|
||||
compare to other values, casting might be necessary or can be removed, if
|
||||
casting was applied before.
|
||||
|
||||
### 0.x.x (unreleased)
|
||||
**BREAKING CHANGES**:
|
||||
|
||||
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
|
||||
|
||||
User code should compare this to `size_t` values as well.
|
||||
If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.
|
||||
* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now protocol errors. This is consistent
|
||||
with the RESP specification. On 32-bit platforms, the upper bound is lowered to `SIZE_MAX`.
|
||||
|
||||
* `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter.
|
||||
|
||||
**New features:**
|
||||
- Support for RESP3
|
||||
[\#697](https://github.com/redis/hiredis/pull/697),
|
||||
[\#805](https://github.com/redis/hiredis/pull/805),
|
||||
[\#819](https://github.com/redis/hiredis/pull/819),
|
||||
[\#841](https://github.com/redis/hiredis/pull/841)
|
||||
([Yossi Gottlieb](https://github.com/yossigo), [Michael Grunder](https://github.com/michael-grunder))
|
||||
- Support for SSL connections
|
||||
[\#645](https://github.com/redis/hiredis/pull/645),
|
||||
[\#699](https://github.com/redis/hiredis/pull/699),
|
||||
[\#702](https://github.com/redis/hiredis/pull/702),
|
||||
[\#708](https://github.com/redis/hiredis/pull/708),
|
||||
[\#711](https://github.com/redis/hiredis/pull/711),
|
||||
[\#821](https://github.com/redis/hiredis/pull/821),
|
||||
[more](https://github.com/redis/hiredis/pulls?q=is%3Apr+is%3Amerged+SSL)
|
||||
([Mark Nunberg](https://github.com/mnunberg), [Yossi Gottlieb](https://github.com/yossigo))
|
||||
- Run-time allocator injection
|
||||
[\#800](https://github.com/redis/hiredis/pull/800)
|
||||
([Michael Grunder](https://github.com/michael-grunder))
|
||||
- Improved Windows support (including MinGW and Windows CI)
|
||||
[\#652](https://github.com/redis/hiredis/pull/652),
|
||||
[\#663](https://github.com/redis/hiredis/pull/663)
|
||||
([Marcus Geelnard](https://www.bitsnbites.eu/author/m/))
|
||||
- Adds support for distinct connect and command timeouts
|
||||
[\#839](https://github.com/redis/hiredis/pull/839),
|
||||
[\#829](https://github.com/redis/hiredis/pull/829)
|
||||
([Valentino Geron](https://github.com/valentinogeron))
|
||||
- Add generic pointer and destructor to `redisContext` that users can use for context.
|
||||
[\#855](https://github.com/redis/hiredis/pull/855)
|
||||
([Michael Grunder](https://github.com/michael-grunder))
|
||||
|
||||
**Closed issues (that involved code changes):**
|
||||
|
||||
- Makefile does not install TLS libraries [\#809](https://github.com/redis/hiredis/issues/809)
|
||||
- redisConnectWithOptions should not set command timeout [\#722](https://github.com/redis/hiredis/issues/722), [\#829](https://github.com/redis/hiredis/pull/829) ([valentinogeron](https://github.com/valentinogeron))
|
||||
- Fix integer overflow in `sdsrange` [\#827](https://github.com/redis/hiredis/issues/827)
|
||||
- INFO & CLUSTER commands failed when using RESP3 [\#802](https://github.com/redis/hiredis/issues/802)
|
||||
- Windows compatibility patches [\#687](https://github.com/redis/hiredis/issues/687), [\#838](https://github.com/redis/hiredis/issues/838), [\#842](https://github.com/redis/hiredis/issues/842)
|
||||
- RESP3 PUSH messages incorrectly use pending callback [\#825](https://github.com/redis/hiredis/issues/825)
|
||||
- Asynchronous PSUBSCRIBE command fails when using RESP3 [\#815](https://github.com/redis/hiredis/issues/815)
|
||||
- New SSL API [\#804](https://github.com/redis/hiredis/issues/804), [\#813](https://github.com/redis/hiredis/issues/813)
|
||||
- Hard-coded limit of nested reply depth [\#794](https://github.com/redis/hiredis/issues/794)
|
||||
- Fix TCP_NODELAY in Windows/OSX [\#679](https://github.com/redis/hiredis/issues/679), [\#690](https://github.com/redis/hiredis/issues/690), [\#779](https://github.com/redis/hiredis/issues/779), [\#785](https://github.com/redis/hiredis/issues/785),
|
||||
- Added timers to libev adapter. [\#778](https://github.com/redis/hiredis/issues/778), [\#795](https://github.com/redis/hiredis/pull/795)
|
||||
- Initialization discards const qualifier [\#777](https://github.com/redis/hiredis/issues/777)
|
||||
- \[BUG\]\[MinGW64\] Error setting socket timeout [\#775](https://github.com/redis/hiredis/issues/775)
|
||||
- undefined reference to hi_malloc [\#769](https://github.com/redis/hiredis/issues/769)
|
||||
- hiredis pkg-config file incorrectly ignores multiarch libdir spec'n [\#767](https://github.com/redis/hiredis/issues/767)
|
||||
- Don't use -G to build shared object on Solaris [\#757](https://github.com/redis/hiredis/issues/757)
|
||||
- error when make USE\_SSL=1 [\#748](https://github.com/redis/hiredis/issues/748)
|
||||
- Allow to change SSL Mode [\#646](https://github.com/redis/hiredis/issues/646)
|
||||
- hiredis/adapters/libevent.h memleak [\#618](https://github.com/redis/hiredis/issues/618)
|
||||
- redisLibuvPoll crash when server closes the connetion [\#545](https://github.com/redis/hiredis/issues/545)
|
||||
- about redisAsyncDisconnect question [\#518](https://github.com/redis/hiredis/issues/518)
|
||||
- hiredis adapters libuv error for help [\#508](https://github.com/redis/hiredis/issues/508)
|
||||
- API/ABI changes analysis [\#506](https://github.com/redis/hiredis/issues/506)
|
||||
- Memory leak patch in Redis [\#502](https://github.com/redis/hiredis/issues/502)
|
||||
- Remove the depth limitation [\#421](https://github.com/redis/hiredis/issues/421)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Move SSL management to a distinct private pointer [\#855](https://github.com/redis/hiredis/pull/855) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Move include to sockcompat.h to maintain style [\#850](https://github.com/redis/hiredis/pull/850) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Remove erroneous tag and add license to push example [\#849](https://github.com/redis/hiredis/pull/849) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- fix windows compiling with mingw [\#848](https://github.com/redis/hiredis/pull/848) ([rmalizia44](https://github.com/rmalizia44))
|
||||
- Some Windows quality of life improvements. [\#846](https://github.com/redis/hiredis/pull/846) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Use \_WIN32 define instead of WIN32 [\#845](https://github.com/redis/hiredis/pull/845) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Non Linux CI fixes [\#844](https://github.com/redis/hiredis/pull/844) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Resp3 oob push support [\#841](https://github.com/redis/hiredis/pull/841) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- fix \#785: defer TCP\_NODELAY in async tcp connections [\#836](https://github.com/redis/hiredis/pull/836) ([OmriSteiner](https://github.com/OmriSteiner))
|
||||
- sdsrange overflow fix [\#830](https://github.com/redis/hiredis/pull/830) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Use explicit pointer casting for c++ compatibility [\#826](https://github.com/redis/hiredis/pull/826) ([aureus1](https://github.com/aureus1))
|
||||
- Document allocator injection and completeness fix in test.c [\#824](https://github.com/redis/hiredis/pull/824) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Use unique names for allocator struct members [\#823](https://github.com/redis/hiredis/pull/823) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- New SSL API to replace redisSecureConnection\(\). [\#821](https://github.com/redis/hiredis/pull/821) ([yossigo](https://github.com/yossigo))
|
||||
- Add logic to handle RESP3 push messages [\#819](https://github.com/redis/hiredis/pull/819) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Use standrad isxdigit instead of custom helper function. [\#814](https://github.com/redis/hiredis/pull/814) ([tryfinally](https://github.com/tryfinally))
|
||||
- Fix missing SSL build/install options. [\#812](https://github.com/redis/hiredis/pull/812) ([yossigo](https://github.com/yossigo))
|
||||
- Add link to ABI tracker [\#808](https://github.com/redis/hiredis/pull/808) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Resp3 verbatim string support [\#805](https://github.com/redis/hiredis/pull/805) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Allow users to replace allocator and handle OOM everywhere. [\#800](https://github.com/redis/hiredis/pull/800) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Remove nested depth limitation. [\#797](https://github.com/redis/hiredis/pull/797) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Attempt to fix compilation on Solaris [\#796](https://github.com/redis/hiredis/pull/796) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Support timeouts in libev adapater [\#795](https://github.com/redis/hiredis/pull/795) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Fix pkgconfig when installing to a custom lib dir [\#793](https://github.com/redis/hiredis/pull/793) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Fix USE\_SSL=1 make/cmake on OSX and CMake tests [\#789](https://github.com/redis/hiredis/pull/789) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Use correct libuv call on Windows [\#784](https://github.com/redis/hiredis/pull/784) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Added CMake package config and fixed hiredis\_ssl on Windows [\#783](https://github.com/redis/hiredis/pull/783) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- CMake: Set hiredis\_ssl shared object version. [\#780](https://github.com/redis/hiredis/pull/780) ([yossigo](https://github.com/yossigo))
|
||||
- Win32 tests and timeout fix [\#776](https://github.com/redis/hiredis/pull/776) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Provides an optional cleanup callback for async data. [\#768](https://github.com/redis/hiredis/pull/768) ([heronr](https://github.com/heronr))
|
||||
- Housekeeping fixes [\#764](https://github.com/redis/hiredis/pull/764) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- install alloc.h [\#756](https://github.com/redis/hiredis/pull/756) ([ch1aki](https://github.com/ch1aki))
|
||||
- fix spelling mistakes [\#746](https://github.com/redis/hiredis/pull/746) ([ShooterIT](https://github.com/ShooterIT))
|
||||
- Free the reply in redisGetReply when passed NULL [\#741](https://github.com/redis/hiredis/pull/741) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Fix dead code in sslLogCallback relating to should\_log variable. [\#737](https://github.com/redis/hiredis/pull/737) ([natoscott](https://github.com/natoscott))
|
||||
- Fix typo in dict.c. [\#731](https://github.com/redis/hiredis/pull/731) ([Kevin-Xi](https://github.com/Kevin-Xi))
|
||||
- Adding an option to DISABLE\_TESTS [\#727](https://github.com/redis/hiredis/pull/727) ([pbotros](https://github.com/pbotros))
|
||||
- Update README with SSL support. [\#720](https://github.com/redis/hiredis/pull/720) ([yossigo](https://github.com/yossigo))
|
||||
- Fixes leaks in unit tests [\#715](https://github.com/redis/hiredis/pull/715) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- SSL Tests [\#711](https://github.com/redis/hiredis/pull/711) ([yossigo](https://github.com/yossigo))
|
||||
- SSL Reorganization [\#708](https://github.com/redis/hiredis/pull/708) ([yossigo](https://github.com/yossigo))
|
||||
- Fix MSVC build. [\#706](https://github.com/redis/hiredis/pull/706) ([yossigo](https://github.com/yossigo))
|
||||
- SSL: Properly report SSL\_connect\(\) errors. [\#702](https://github.com/redis/hiredis/pull/702) ([yossigo](https://github.com/yossigo))
|
||||
- Silent SSL trace to stdout by default. [\#699](https://github.com/redis/hiredis/pull/699) ([yossigo](https://github.com/yossigo))
|
||||
- Port RESP3 support from Redis. [\#697](https://github.com/redis/hiredis/pull/697) ([yossigo](https://github.com/yossigo))
|
||||
- Removed whitespace before newline [\#691](https://github.com/redis/hiredis/pull/691) ([Miniwoffer](https://github.com/Miniwoffer))
|
||||
- Add install adapters header files [\#688](https://github.com/redis/hiredis/pull/688) ([kevin1018](https://github.com/kevin1018))
|
||||
- Remove unnecessary null check before free [\#684](https://github.com/redis/hiredis/pull/684) ([qlyoung](https://github.com/qlyoung))
|
||||
- redisReaderGetReply leak memory [\#671](https://github.com/redis/hiredis/pull/671) ([movebean](https://github.com/movebean))
|
||||
- fix timeout code in windows [\#670](https://github.com/redis/hiredis/pull/670) ([jman-krafton](https://github.com/jman-krafton))
|
||||
- test: fix errstr matching for musl libc [\#665](https://github.com/redis/hiredis/pull/665) ([ghost](https://github.com/ghost))
|
||||
- Windows: MinGW fixes and Windows Travis builders [\#663](https://github.com/redis/hiredis/pull/663) ([mbitsnbites](https://github.com/mbitsnbites))
|
||||
- The setsockopt and getsockopt API diffs from BSD socket and WSA one [\#662](https://github.com/redis/hiredis/pull/662) ([dragonation](https://github.com/dragonation))
|
||||
- Fix Compile Error On Windows \(Visual Studio\) [\#658](https://github.com/redis/hiredis/pull/658) ([jinjiazhang](https://github.com/jinjiazhang))
|
||||
- Fix NXDOMAIN test case [\#653](https://github.com/redis/hiredis/pull/653) ([michael-grunder](https://github.com/michael-grunder))
|
||||
- Add MinGW support [\#652](https://github.com/redis/hiredis/pull/652) ([mbitsnbites](https://github.com/mbitsnbites))
|
||||
- SSL Support [\#645](https://github.com/redis/hiredis/pull/645) ([mnunberg](https://github.com/mnunberg))
|
||||
- Fix Invalid argument after redisAsyncConnectUnix [\#644](https://github.com/redis/hiredis/pull/644) ([codehz](https://github.com/codehz))
|
||||
- Makefile: use predefined AR [\#632](https://github.com/redis/hiredis/pull/632) ([Mic92](https://github.com/Mic92))
|
||||
- FreeBSD build fix [\#628](https://github.com/redis/hiredis/pull/628) ([devnexen](https://github.com/devnexen))
|
||||
- Fix errors not propagating properly with libuv.h. [\#624](https://github.com/redis/hiredis/pull/624) ([yossigo](https://github.com/yossigo))
|
||||
- Update README.md [\#621](https://github.com/redis/hiredis/pull/621) ([Crunsher](https://github.com/Crunsher))
|
||||
- Fix redisBufferRead documentation [\#620](https://github.com/redis/hiredis/pull/620) ([hacst](https://github.com/hacst))
|
||||
- Add CPPFLAGS to REAL\_CFLAGS [\#614](https://github.com/redis/hiredis/pull/614) ([thomaslee](https://github.com/thomaslee))
|
||||
- Update createArray to take size\_t [\#597](https://github.com/redis/hiredis/pull/597) ([justinbrewer](https://github.com/justinbrewer))
|
||||
- fix common realloc mistake and add null check more [\#580](https://github.com/redis/hiredis/pull/580) ([charsyam](https://github.com/charsyam))
|
||||
- Proper error reporting for connect failures [\#578](https://github.com/redis/hiredis/pull/578) ([mnunberg](https://github.com/mnunberg))
|
||||
|
||||
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
||||
|
||||
## [1.0.0-rc1](https://github.com/redis/hiredis/tree/v1.0.0-rc1) - (2020-07-29)
|
||||
|
||||
_Note: There were no changes to code between v1.0.0-rc1 and v1.0.0 so see v1.0.0 for changelog_
|
||||
|
||||
### 0.14.1 (2020-03-13)
|
||||
|
||||
* Adds safe allocation wrappers (CVE-2020-7105, #747, #752) (Michael Grunder)
|
||||
|
||||
### 0.14.0 (2018-09-25)
|
||||
**BREAKING CHANGES**:
|
||||
|
||||
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
|
||||
|
||||
User code should compare this to `size_t` values as well.
|
||||
If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.
|
||||
|
||||
* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])
|
||||
* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537])
|
||||
@@ -196,4 +343,3 @@ The parser, standalone since v0.12.0, can now be compiled on Windows
|
||||
### 0.10.0
|
||||
|
||||
* See commit log.
|
||||
|
||||
|
||||
Vendored
+84
-9
@@ -3,6 +3,8 @@ INCLUDE(GNUInstallDirs)
|
||||
PROJECT(hiredis)
|
||||
|
||||
OPTION(ENABLE_SSL "Build hiredis_ssl for SSL support" OFF)
|
||||
OPTION(DISABLE_TESTS "If tests should be compiled or not" OFF)
|
||||
OPTION(ENABLE_SSL_TESTS, "Should we test SSL connections" OFF)
|
||||
|
||||
MACRO(getVersionBit name)
|
||||
SET(VERSION_REGEX "^#define ${name} (.+)$")
|
||||
@@ -22,7 +24,8 @@ PROJECT(hiredis VERSION "${VERSION}")
|
||||
|
||||
SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples")
|
||||
|
||||
ADD_LIBRARY(hiredis SHARED
|
||||
SET(hiredis_sources
|
||||
alloc.c
|
||||
async.c
|
||||
dict.c
|
||||
hiredis.c
|
||||
@@ -31,20 +34,32 @@ ADD_LIBRARY(hiredis SHARED
|
||||
sds.c
|
||||
sockcompat.c)
|
||||
|
||||
SET(hiredis_sources ${hiredis_sources})
|
||||
|
||||
IF(WIN32)
|
||||
ADD_COMPILE_DEFINITIONS(_CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN)
|
||||
ENDIF()
|
||||
|
||||
ADD_LIBRARY(hiredis SHARED ${hiredis_sources})
|
||||
|
||||
SET_TARGET_PROPERTIES(hiredis
|
||||
PROPERTIES
|
||||
PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE
|
||||
VERSION "${HIREDIS_SONAME}")
|
||||
IF(WIN32 OR MINGW)
|
||||
TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32)
|
||||
ENDIF()
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC .)
|
||||
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC $<INSTALL_INTERFACE:.> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
|
||||
CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY)
|
||||
|
||||
INSTALL(TARGETS hiredis
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
||||
EXPORT hiredis-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
INSTALL(FILES hiredis.h read.h sds.h async.h
|
||||
INSTALL(FILES hiredis.h read.h sds.h async.h alloc.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
||||
|
||||
INSTALL(DIRECTORY adapters
|
||||
@@ -53,6 +68,26 @@ INSTALL(DIRECTORY adapters
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
||||
export(EXPORT hiredis-targets
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis-targets.cmake"
|
||||
NAMESPACE hiredis::)
|
||||
|
||||
SET(CMAKE_CONF_INSTALL_DIR share/hiredis)
|
||||
SET(INCLUDE_INSTALL_DIR include)
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_package_config_file(hiredis-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake
|
||||
INSTALL_DESTINATION ${CMAKE_CONF_INSTALL_DIR}
|
||||
PATH_VARS INCLUDE_INSTALL_DIR)
|
||||
|
||||
INSTALL(EXPORT hiredis-targets
|
||||
FILE hiredis-targets.cmake
|
||||
NAMESPACE hiredis::
|
||||
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
|
||||
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake
|
||||
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
|
||||
|
||||
|
||||
IF(ENABLE_SSL)
|
||||
IF (NOT OPENSSL_ROOT_DIR)
|
||||
IF (APPLE)
|
||||
@@ -60,26 +95,66 @@ IF(ENABLE_SSL)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
FIND_PACKAGE(OpenSSL REQUIRED)
|
||||
ADD_LIBRARY(hiredis_ssl SHARED
|
||||
SET(hiredis_ssl_sources
|
||||
ssl.c)
|
||||
ADD_LIBRARY(hiredis_ssl SHARED
|
||||
${hiredis_ssl_sources})
|
||||
|
||||
IF (APPLE)
|
||||
SET_PROPERTY(TARGET hiredis_ssl PROPERTY LINK_FLAGS "-Wl,-undefined -Wl,dynamic_lookup")
|
||||
ENDIF()
|
||||
|
||||
SET_TARGET_PROPERTIES(hiredis_ssl
|
||||
PROPERTIES
|
||||
WINDOWS_EXPORT_ALL_SYMBOLS TRUE
|
||||
VERSION "${HIREDIS_SONAME}")
|
||||
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE "${OPENSSL_INCLUDE_DIR}")
|
||||
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES})
|
||||
IF (WIN32 OR MINGW)
|
||||
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE hiredis)
|
||||
ENDIF()
|
||||
CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY)
|
||||
|
||||
INSTALL(TARGETS hiredis_ssl
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
||||
EXPORT hiredis_ssl-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
INSTALL(FILES hiredis_ssl.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
||||
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
||||
export(EXPORT hiredis_ssl-targets
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-targets.cmake"
|
||||
NAMESPACE hiredis::)
|
||||
|
||||
SET(CMAKE_CONF_INSTALL_DIR share/hiredis_ssl)
|
||||
configure_package_config_file(hiredis_ssl-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-config.cmake
|
||||
INSTALL_DESTINATION ${CMAKE_CONF_INSTALL_DIR}
|
||||
PATH_VARS INCLUDE_INSTALL_DIR)
|
||||
|
||||
INSTALL(EXPORT hiredis_ssl-targets
|
||||
FILE hiredis_ssl-targets.cmake
|
||||
NAMESPACE hiredis::
|
||||
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
|
||||
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-config.cmake
|
||||
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
|
||||
ENDIF()
|
||||
|
||||
IF(NOT (WIN32 OR MINGW))
|
||||
IF(NOT DISABLE_TESTS)
|
||||
ENABLE_TESTING()
|
||||
ADD_EXECUTABLE(hiredis-test test.c)
|
||||
TARGET_LINK_LIBRARIES(hiredis-test hiredis)
|
||||
IF(ENABLE_SSL_TESTS)
|
||||
ADD_DEFINITIONS(-DHIREDIS_TEST_SSL=1)
|
||||
TARGET_LINK_LIBRARIES(hiredis-test hiredis hiredis_ssl)
|
||||
ELSE()
|
||||
TARGET_LINK_LIBRARIES(hiredis-test hiredis)
|
||||
ENDIF()
|
||||
ADD_TEST(NAME hiredis-test
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh)
|
||||
ENDIF()
|
||||
|
||||
Vendored
+59
-19
@@ -3,16 +3,16 @@
|
||||
# Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
# This file is released under the BSD license, see the COPYING file
|
||||
|
||||
OBJ=net.o hiredis.o sds.o async.o read.o sockcompat.o
|
||||
OBJ=alloc.o net.o hiredis.o sds.o async.o read.o sockcompat.o
|
||||
SSL_OBJ=ssl.o
|
||||
EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib
|
||||
EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib hiredis-example-push
|
||||
ifeq ($(USE_SSL),1)
|
||||
EXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl
|
||||
endif
|
||||
TESTS=hiredis-test
|
||||
LIBNAME=libhiredis
|
||||
SSL_LIBNAME=libhiredis_ssl
|
||||
PKGCONFNAME=hiredis.pc
|
||||
SSL_LIBNAME=libhiredis_ssl
|
||||
SSL_PKGCONFNAME=hiredis_ssl.pc
|
||||
|
||||
HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}')
|
||||
@@ -55,12 +55,17 @@ STLIBSUFFIX=a
|
||||
DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)
|
||||
DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
|
||||
DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
|
||||
SSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX)
|
||||
|
||||
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME)
|
||||
STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
|
||||
SSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX)
|
||||
STLIB_MAKE_CMD=$(AR) rcs
|
||||
|
||||
SSL_DYLIB_MINOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)
|
||||
SSL_DYLIB_MAJOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
|
||||
SSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX)
|
||||
SSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX)
|
||||
SSL_DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(SSL_DYLIB_MINOR_NAME)
|
||||
|
||||
# Platform-specific overrides
|
||||
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
|
||||
@@ -72,7 +77,12 @@ ifeq ($(USE_SSL),1)
|
||||
endif
|
||||
|
||||
ifeq ($(uname_S),Linux)
|
||||
SSL_LDFLAGS=-lssl -lcrypto
|
||||
ifdef OPENSSL_PREFIX
|
||||
CFLAGS+=-I$(OPENSSL_PREFIX)/include
|
||||
SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto
|
||||
else
|
||||
SSL_LDFLAGS=-lssl -lcrypto
|
||||
endif
|
||||
else
|
||||
OPENSSL_PREFIX?=/usr/local/opt/openssl
|
||||
CFLAGS+=-I$(OPENSSL_PREFIX)/include
|
||||
@@ -80,13 +90,22 @@ else
|
||||
endif
|
||||
|
||||
ifeq ($(uname_S),SunOS)
|
||||
IS_SUN_CC=$(shell sh -c '$(CC) -V 2>&1 |egrep -i -c "sun|studio"')
|
||||
ifeq ($(IS_SUN_CC),1)
|
||||
SUN_SHARED_FLAG=-G
|
||||
else
|
||||
SUN_SHARED_FLAG=-shared
|
||||
endif
|
||||
REAL_LDFLAGS+= -ldl -lnsl -lsocket
|
||||
DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
|
||||
DYLIB_MAKE_CMD=$(CC) $(SUN_SHARED_FLAG) -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
|
||||
SSL_DYLIB_MAKE_CMD=$(CC) $(SUN_SHARED_FLAG) -o $(SSL_DYLIBNAME) -h $(SSL_DYLIB_MINOR_NAME) $(LDFLAGS) $(SSL_LDFLAGS)
|
||||
endif
|
||||
ifeq ($(uname_S),Darwin)
|
||||
DYLIBSUFFIX=dylib
|
||||
DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX)
|
||||
DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
|
||||
SSL_DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME) -o $(SSL_DYLIBNAME) $(LDFLAGS) $(SSL_LDFLAGS)
|
||||
DYLIB_PLUGIN=-Wl,-undefined -Wl,dynamic_lookup
|
||||
endif
|
||||
|
||||
all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME)
|
||||
@@ -95,15 +114,16 @@ all: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)
|
||||
endif
|
||||
|
||||
# Deps (use make dep to generate this)
|
||||
async.o: async.c fmacros.h async.h hiredis.h read.h sds.h net.h dict.c dict.h
|
||||
dict.o: dict.c fmacros.h dict.h
|
||||
hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h net.h win32.h
|
||||
net.o: net.c fmacros.h net.h hiredis.h read.h sds.h sockcompat.h win32.h
|
||||
read.o: read.c fmacros.h read.h sds.h
|
||||
sds.o: sds.c sds.h
|
||||
alloc.o: alloc.c fmacros.h alloc.h
|
||||
async.o: async.c fmacros.h alloc.h async.h hiredis.h read.h sds.h net.h dict.c dict.h win32.h async_private.h
|
||||
dict.o: dict.c fmacros.h alloc.h dict.h
|
||||
hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h alloc.h net.h async.h win32.h
|
||||
net.o: net.c fmacros.h net.h hiredis.h read.h sds.h alloc.h sockcompat.h win32.h
|
||||
read.o: read.c fmacros.h alloc.h read.h sds.h win32.h
|
||||
sds.o: sds.c sds.h sdsalloc.h alloc.h
|
||||
sockcompat.o: sockcompat.c sockcompat.h
|
||||
ssl.o: ssl.c hiredis.h
|
||||
test.o: test.c fmacros.h hiredis.h read.h sds.h
|
||||
ssl.o: ssl.c hiredis.h read.h sds.h alloc.h async.h win32.h async_private.h
|
||||
test.o: test.c fmacros.h hiredis.h read.h sds.h alloc.h net.h sockcompat.h win32.h
|
||||
|
||||
$(DYLIBNAME): $(OBJ)
|
||||
$(DYLIB_MAKE_CMD) -o $(DYLIBNAME) $(OBJ) $(REAL_LDFLAGS)
|
||||
@@ -112,7 +132,7 @@ $(STLIBNAME): $(OBJ)
|
||||
$(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ)
|
||||
|
||||
$(SSL_DYLIBNAME): $(SSL_OBJ)
|
||||
$(DYLIB_MAKE_CMD) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
|
||||
$(SSL_DYLIB_MAKE_CMD) $(DYLIB_PLUGIN) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(LDFLAGS) $(SSL_LDFLAGS)
|
||||
|
||||
$(SSL_STLIBNAME): $(SSL_OBJ)
|
||||
$(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ)
|
||||
@@ -146,6 +166,7 @@ hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME)
|
||||
hiredis-example-ssl: examples/example-ssl.c $(STLIBNAME) $(SSL_STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
|
||||
|
||||
|
||||
ifndef AE_DIR
|
||||
hiredis-example-ae:
|
||||
@echo "Please specify AE_DIR (e.g. <redis repository>/src)"
|
||||
@@ -180,13 +201,19 @@ endif
|
||||
hiredis-example: examples/example.c $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
hiredis-example-push: examples/example-push.c $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
examples: $(EXAMPLES)
|
||||
|
||||
TEST_LIBS = $(STLIBNAME)
|
||||
ifeq ($(USE_SSL),1)
|
||||
TEST_LIBS += $(SSL_STLIBNAME) -lssl -lcrypto -lpthread
|
||||
TEST_LIBS += $(SSL_STLIBNAME)
|
||||
TEST_LDFLAGS = $(SSL_LDFLAGS) -lssl -lcrypto -lpthread
|
||||
endif
|
||||
|
||||
hiredis-test: test.o $(TEST_LIBS)
|
||||
$(CC) -o $@ $(REAL_CFLAGS) -I. $^ $(REAL_LDFLAGS) $(TEST_LDFLAGS)
|
||||
|
||||
hiredis-%: %.o $(STLIBNAME)
|
||||
$(CC) $(REAL_CFLAGS) -o $@ $< $(TEST_LIBS) $(REAL_LDFLAGS)
|
||||
@@ -221,7 +248,7 @@ $(PKGCONFNAME): hiredis.h
|
||||
@echo Libs: -L\$${libdir} -lhiredis >> $@
|
||||
@echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@
|
||||
|
||||
$(SSL_PKGCONFNAME): hiredis.h
|
||||
$(SSL_PKGCONFNAME): hiredis_ssl.h
|
||||
@echo "Generating $@ for pkgconfig..."
|
||||
@echo prefix=$(PREFIX) > $@
|
||||
@echo exec_prefix=\$${prefix} >> $@
|
||||
@@ -237,7 +264,7 @@ $(SSL_PKGCONFNAME): hiredis.h
|
||||
|
||||
install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)
|
||||
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH)
|
||||
$(INSTALL) hiredis.h async.h read.h sds.h $(INSTALL_INCLUDE_PATH)
|
||||
$(INSTALL) hiredis.h async.h read.h sds.h alloc.h $(INSTALL_INCLUDE_PATH)
|
||||
$(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters
|
||||
$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
|
||||
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME)
|
||||
@@ -245,6 +272,19 @@ install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)
|
||||
mkdir -p $(INSTALL_PKGCONF_PATH)
|
||||
$(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
|
||||
|
||||
ifeq ($(USE_SSL),1)
|
||||
install: install-ssl
|
||||
|
||||
install-ssl: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)
|
||||
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)
|
||||
$(INSTALL) hiredis_ssl.h $(INSTALL_INCLUDE_PATH)
|
||||
$(INSTALL) $(SSL_DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME)
|
||||
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(SSL_DYLIB_MINOR_NAME) $(SSL_DYLIBNAME)
|
||||
$(INSTALL) $(SSL_STLIBNAME) $(INSTALL_LIBRARY_PATH)
|
||||
mkdir -p $(INSTALL_PKGCONF_PATH)
|
||||
$(INSTALL) $(SSL_PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
|
||||
endif
|
||||
|
||||
32bit:
|
||||
@echo ""
|
||||
@echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386"
|
||||
|
||||
Vendored
+259
-13
@@ -1,6 +1,6 @@
|
||||
[](https://travis-ci.org/redis/hiredis)
|
||||
|
||||
**This Readme reflects the latest changed in the master branch. See [v0.13.3](https://github.com/redis/hiredis/tree/v0.13.3) for the Readme and documentation for the latest release.**
|
||||
**This Readme reflects the latest changed in the master branch. See [v1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) for the Readme and documentation for the latest release ([API/ABI history](https://abi-laboratory.pro/?view=timeline&l=hiredis)).**
|
||||
|
||||
# HIREDIS
|
||||
|
||||
@@ -24,12 +24,31 @@ The library comes with multiple APIs. There is the
|
||||
|
||||
## Upgrading to `1.0.0`
|
||||
|
||||
Version 1.0.0 marks a stable release of hiredis.
|
||||
Version 1.0.0 marks the first stable release of Hiredis.
|
||||
It includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory.
|
||||
It also bundles the updated `sds` library, to sync up with upstream and Redis.
|
||||
For most applications a recompile against the new hiredis should be enough.
|
||||
For code changes see the [Changelog](CHANGELOG.md).
|
||||
|
||||
_Note: As described below, a few member names have been changed but most applications should be able to upgrade with minor code changes and recompiling._
|
||||
|
||||
## IMPORTANT: Breaking changes from `0.14.1` -> `1.0.0`
|
||||
|
||||
* `redisContext` has two additional members (`free_privdata`, and `privctx`).
|
||||
* `redisOptions.timeout` has been renamed to `redisOptions.connect_timeout`, and we've added `redisOptions.command_timeout`.
|
||||
* `redisReplyObjectFunctions.createArray` now takes `size_t` instead of `int` for its length parameter.
|
||||
|
||||
## IMPORTANT: Breaking changes when upgrading from 0.13.x -> 0.14.x
|
||||
|
||||
Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
|
||||
protocol errors. This is consistent with the RESP specification. On 32-bit
|
||||
platforms, the upper bound is lowered to `SIZE_MAX`.
|
||||
|
||||
Change `redisReply.len` to `size_t`, as it denotes the the size of a string
|
||||
|
||||
User code should compare this to `size_t` values as well. If it was used to
|
||||
compare to other values, casting might be necessary or can be removed, if
|
||||
casting was applied before.
|
||||
|
||||
## Upgrading from `<0.9.0`
|
||||
|
||||
Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing
|
||||
@@ -110,6 +129,8 @@ The standard replies that `redisCommand` are of the type `redisReply`. The
|
||||
`type` field in the `redisReply` should be used to test what kind of reply
|
||||
was received:
|
||||
|
||||
### RESP2
|
||||
|
||||
* **`REDIS_REPLY_STATUS`**:
|
||||
* The command replied with a status reply. The status string can be accessed using `reply->str`.
|
||||
The length of this string can be accessed using `reply->len`.
|
||||
@@ -134,16 +155,51 @@ was received:
|
||||
and can be accessed via `reply->element[..index..]`.
|
||||
Redis may reply with nested arrays but this is fully supported.
|
||||
|
||||
### RESP3
|
||||
|
||||
Hiredis also supports every new `RESP3` data type which are as follows. For more information about the protocol see the `RESP3` [specification.](https://github.com/antirez/RESP3/blob/master/spec.md)
|
||||
|
||||
* **`REDIS_REPLY_DOUBLE`**:
|
||||
* The command replied with a double-precision floating point number.
|
||||
The value is stored as a string in the `str` member, and can be converted with `strtod` or similar.
|
||||
|
||||
* **`REDIS_REPLY_BOOL`**:
|
||||
* A boolean true/false reply.
|
||||
The value is stored in the `integer` member and will be either `0` or `1`.
|
||||
|
||||
* **`REDIS_REPLY_MAP`**:
|
||||
* An array with the added invariant that there will always be an even number of elements.
|
||||
The MAP is functionally equivelant to `REDIS_REPLY_ARRAY` except for the previously mentioned invariant.
|
||||
|
||||
* **`REDIS_REPLY_SET`**:
|
||||
* An array response where each entry is unique.
|
||||
Like the MAP type, the data is identical to an array response except there are no duplicate values.
|
||||
|
||||
* **`REDIS_REPLY_PUSH`**:
|
||||
* An array that can be generated spontaneously by Redis.
|
||||
This array response will always contain at least two subelements. The first contains the type of `PUSH` message (e.g. `message`, or `invalidate`), and the second being a sub-array with the `PUSH` payload itself.
|
||||
|
||||
* **`REDIS_REPLY_ATTR`**:
|
||||
* An array structurally identical to a `MAP` but intended as meta-data about a reply.
|
||||
_As of Redis 6.0.6 this reply type is not used in Redis_
|
||||
|
||||
* **`REDIS_REPLY_BIGNUM`**:
|
||||
* A string representing an arbitrarily large signed or unsigned integer value.
|
||||
The number will be encoded as a string in the `str` member of `redisReply`.
|
||||
|
||||
* **`REDIS_REPLY_VERB`**:
|
||||
* A verbatim string, intended to be presented to the user without modification.
|
||||
The string payload is stored in the `str` memeber, and type data is stored in the `vtype` member (e.g. `txt` for raw text or `md` for markdown).
|
||||
|
||||
Replies should be freed using the `freeReplyObject()` function.
|
||||
Note that this function will take care of freeing sub-reply objects
|
||||
contained in arrays and nested arrays, so there is no need for the user to
|
||||
free the sub replies (it is actually harmful and will corrupt the memory).
|
||||
|
||||
**Important:** the current version of hiredis (0.10.0) frees replies when the
|
||||
**Important:** the current version of hiredis (1.0.0) frees replies when the
|
||||
asynchronous API is used. This means you should not call `freeReplyObject` when
|
||||
you use this API. The reply is cleaned up by hiredis _after_ the callback
|
||||
returns. This behavior will probably change in future releases, so make sure to
|
||||
keep an eye on the changelog when upgrading (see issue #39).
|
||||
returns. We may introduce a flag to make this configurable in future versions of the library.
|
||||
|
||||
### Cleaning up
|
||||
|
||||
@@ -205,16 +261,16 @@ a single call to `read(2)`):
|
||||
redisReply *reply;
|
||||
redisAppendCommand(context,"SET foo bar");
|
||||
redisAppendCommand(context,"GET foo");
|
||||
redisGetReply(context,&reply); // reply for SET
|
||||
redisGetReply(context,(void *)&reply); // reply for SET
|
||||
freeReplyObject(reply);
|
||||
redisGetReply(context,&reply); // reply for GET
|
||||
redisGetReply(context,(void *)&reply); // reply for GET
|
||||
freeReplyObject(reply);
|
||||
```
|
||||
This API can also be used to implement a blocking subscriber:
|
||||
```c
|
||||
reply = redisCommand(context,"SUBSCRIBE foo");
|
||||
freeReplyObject(reply);
|
||||
while(redisGetReply(context,&reply) == REDIS_OK) {
|
||||
while(redisGetReply(context,(void *)&reply) == REDIS_OK) {
|
||||
// consume message
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
@@ -404,9 +460,199 @@ This should be done only in order to maximize performances when working with
|
||||
large payloads. The context should be set back to `REDIS_READER_MAX_BUF` again
|
||||
as soon as possible in order to prevent allocation of useless memory.
|
||||
|
||||
### Reader max array elements
|
||||
|
||||
By default the hiredis reply parser sets the maximum number of multi-bulk elements
|
||||
to 2^32 - 1 or 4,294,967,295 entries. If you need to process multi-bulk replies
|
||||
with more than this many elements you can set the value higher or to zero, meaning
|
||||
unlimited with:
|
||||
```c
|
||||
context->reader->maxelements = 0;
|
||||
```
|
||||
|
||||
## SSL/TLS Support
|
||||
|
||||
### Building
|
||||
|
||||
SSL/TLS support is not built by default and requires an explicit flag:
|
||||
|
||||
make USE_SSL=1
|
||||
|
||||
This requires OpenSSL development package (e.g. including header files to be
|
||||
available.
|
||||
|
||||
When enabled, SSL/TLS support is built into extra `libhiredis_ssl.a` and
|
||||
`libhiredis_ssl.so` static/dynamic libraries. This leaves the original libraries
|
||||
unaffected so no additional dependencies are introduced.
|
||||
|
||||
### Using it
|
||||
|
||||
First, you'll need to make sure you include the SSL header file:
|
||||
|
||||
```c
|
||||
#include "hiredis.h"
|
||||
#include "hiredis_ssl.h"
|
||||
```
|
||||
|
||||
You will also need to link against `libhiredis_ssl`, **in addition** to
|
||||
`libhiredis` and add `-lssl -lcrypto` to satisfy its dependencies.
|
||||
|
||||
Hiredis implements SSL/TLS on top of its normal `redisContext` or
|
||||
`redisAsyncContext`, so you will need to establish a connection first and then
|
||||
initiate an SSL/TLS handshake.
|
||||
|
||||
#### Hiredis OpenSSL Wrappers
|
||||
|
||||
Before Hiredis can negotiate an SSL/TLS connection, it is necessary to
|
||||
initialize OpenSSL and create a context. You can do that in two ways:
|
||||
|
||||
1. Work directly with the OpenSSL API to initialize the library's global context
|
||||
and create `SSL_CTX *` and `SSL *` contexts. With an `SSL *` object you can
|
||||
call `redisInitiateSSL()`.
|
||||
2. Work with a set of Hiredis-provided wrappers around OpenSSL, create a
|
||||
`redisSSLContext` object to hold configuration and use
|
||||
`redisInitiateSSLWithContext()` to initiate the SSL/TLS handshake.
|
||||
|
||||
```c
|
||||
/* An Hiredis SSL context. It holds SSL configuration and can be reused across
|
||||
* many contexts.
|
||||
*/
|
||||
redisSSLContext *ssl;
|
||||
|
||||
/* An error variable to indicate what went wrong, if the context fails to
|
||||
* initialize.
|
||||
*/
|
||||
redisSSLContextError ssl_error;
|
||||
|
||||
/* Initialize global OpenSSL state.
|
||||
*
|
||||
* You should call this only once when your app initializes, and only if
|
||||
* you don't explicitly or implicitly initialize OpenSSL it elsewhere.
|
||||
*/
|
||||
redisInitOpenSSL();
|
||||
|
||||
/* Create SSL context */
|
||||
ssl = redisCreateSSLContext(
|
||||
"cacertbundle.crt", /* File name of trusted CA/ca bundle file, optional */
|
||||
"/path/to/certs", /* Path of trusted certificates, optional */
|
||||
"client_cert.pem", /* File name of client certificate file, optional */
|
||||
"client_key.pem", /* File name of client private key, optional */
|
||||
"redis.mydomain.com", /* Server name to request (SNI), optional */
|
||||
&ssl_error
|
||||
) != REDIS_OK) {
|
||||
printf("SSL error: %s\n", redisSSLContextGetError(ssl_error);
|
||||
/* Abort... */
|
||||
}
|
||||
|
||||
/* Create Redis context and establish connection */
|
||||
c = redisConnect("localhost", 6443);
|
||||
if (c == NULL || c->err) {
|
||||
/* Handle error and abort... */
|
||||
}
|
||||
|
||||
/* Negotiate SSL/TLS */
|
||||
if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) {
|
||||
/* Handle error, in c->err / c->errstr */
|
||||
}
|
||||
```
|
||||
|
||||
## RESP3 PUSH replies
|
||||
Redis 6.0 introduced PUSH replies with the reply-type `>`. These messages are generated spontaneously and can arrive at any time, so must be handled using callbacks.
|
||||
|
||||
### Default behavior
|
||||
Hiredis installs handlers on `redisContext` and `redisAsyncContext` by default, which will intercept and free any PUSH replies detected. This means existing code will work as-is after upgrading to Redis 6 and switching to `RESP3`.
|
||||
|
||||
### Custom PUSH handler prototypes
|
||||
The callback prototypes differ between `redisContext` and `redisAsyncContext`.
|
||||
|
||||
#### redisContext
|
||||
```c
|
||||
void my_push_handler(void *privdata, void *reply) {
|
||||
/* Handle the reply */
|
||||
|
||||
/* Note: We need to free the reply in our custom handler for
|
||||
blocking contexts. This lets us keep the reply if
|
||||
we want. */
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
```
|
||||
|
||||
#### redisAsyncContext
|
||||
```c
|
||||
void my_async_push_handler(redisAsyncContext *ac, void *reply) {
|
||||
/* Handle the reply */
|
||||
|
||||
/* Note: Because async hiredis always frees replies, you should
|
||||
not call freeReplyObject in an async push callback. */
|
||||
}
|
||||
```
|
||||
|
||||
### Installing a custom handler
|
||||
There are two ways to set your own PUSH handlers.
|
||||
|
||||
1. Set `push_cb` or `async_push_cb` in the `redisOptions` struct and connect with `redisConnectWithOptions` or `redisAsyncConnectWithOptions`.
|
||||
```c
|
||||
redisOptions = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
|
||||
options->push_cb = my_push_handler;
|
||||
redisContext *context = redisConnectWithOptions(&options);
|
||||
```
|
||||
2. Call `redisSetPushCallback` or `redisAsyncSetPushCallback` on a connected context.
|
||||
```c
|
||||
redisContext *context = redisConnect("127.0.0.1", 6379);
|
||||
redisSetPushCallback(context, my_push_handler);
|
||||
```
|
||||
|
||||
_Note `redisSetPushCallback` and `redisAsyncSetPushCallback` both return any currently configured handler, making it easy to override and then return to the old value._
|
||||
|
||||
### Specifying no handler
|
||||
If you have a unique use-case where you don't want hiredis to automatically intercept and free PUSH replies, you will want to configure no handler at all. This can be done in two ways.
|
||||
1. Set the `REDIS_OPT_NO_PUSH_AUTOFREE` flag in `redisOptions` and leave the callback function pointer `NULL`.
|
||||
```c
|
||||
redisOptions = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
|
||||
options->options |= REDIS_OPT_NO_PUSH_AUTOFREE;
|
||||
redisContext *context = redisConnectWithOptions(&options);
|
||||
```
|
||||
3. Call `redisSetPushCallback` with `NULL` once connected.
|
||||
```c
|
||||
redisContext *context = redisConnect("127.0.0.1", 6379);
|
||||
redisSetPushCallback(context, NULL);
|
||||
```
|
||||
|
||||
_Note: With no handler configured, calls to `redisCommand` may generate more than one reply, so this strategy is only applicable when there's some kind of blocking`redisGetReply()` loop (e.g. `MONITOR` or `SUBSCRIBE` workloads)._
|
||||
|
||||
## Allocator injection
|
||||
|
||||
Hiredis uses a pass-thru structure of function pointers defined in [alloc.h](https://github.com/redis/hiredis/blob/f5d25850/alloc.h#L41) that contain the currently configured allocation and deallocation functions. By default they just point to libc (`malloc`, `calloc`, `realloc`, etc).
|
||||
|
||||
### Overriding
|
||||
|
||||
One can override the allocators like so:
|
||||
|
||||
```c
|
||||
hiredisAllocFuncs myfuncs = {
|
||||
.mallocFn = my_malloc,
|
||||
.callocFn = my_calloc,
|
||||
.reallocFn = my_realloc,
|
||||
.strdupFn = my_strdup,
|
||||
.freeFn = my_free,
|
||||
};
|
||||
|
||||
// Override allocators (function returns current allocators if needed)
|
||||
hiredisAllocFuncs orig = hiredisSetAllocators(&myfuncs);
|
||||
```
|
||||
|
||||
To reset the allocators to their default libc function simply call:
|
||||
|
||||
```c
|
||||
hiredisResetAllocators();
|
||||
```
|
||||
|
||||
## AUTHORS
|
||||
|
||||
Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and
|
||||
Pieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license.
|
||||
Hiredis is currently maintained by Matt Stancliff (matt at genges dot com) and
|
||||
Jan-Erik Rediger (janerik at fnordig dot com)
|
||||
Salvatore Sanfilippo (antirez at gmail),\
|
||||
Pieter Noordhuis (pcnoordhuis at gmail)\
|
||||
Michael Grunder (michael dot grunder at gmail)
|
||||
|
||||
_Hiredis is released under the BSD license._
|
||||
|
||||
Vendored
+5
-2
@@ -96,7 +96,7 @@ static void redisAeCleanup(void *privdata) {
|
||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
||||
redisAeDelRead(privdata);
|
||||
redisAeDelWrite(privdata);
|
||||
free(e);
|
||||
hi_free(e);
|
||||
}
|
||||
|
||||
static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
|
||||
@@ -108,7 +108,10 @@ static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisAeEvents*)malloc(sizeof(*e));
|
||||
e = (redisAeEvents*)hi_malloc(sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e->context = ac;
|
||||
e->loop = loop;
|
||||
e->fd = c->fd;
|
||||
|
||||
Vendored
+3
@@ -134,6 +134,9 @@ redis_source_new (redisAsyncContext *ac)
|
||||
g_return_val_if_fail(ac != NULL, NULL);
|
||||
|
||||
source = (RedisSource *)g_source_new(&source_funcs, sizeof *source);
|
||||
if (source == NULL)
|
||||
return NULL;
|
||||
|
||||
source->ac = ac;
|
||||
source->poll_fd.fd = c->fd;
|
||||
source->poll_fd.events = 0;
|
||||
|
||||
Vendored
+5
-2
@@ -43,7 +43,7 @@ static void redisIvykisCleanup(void *privdata) {
|
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||
|
||||
iv_fd_unregister(&e->fd);
|
||||
free(e);
|
||||
hi_free(e);
|
||||
}
|
||||
|
||||
static int redisIvykisAttach(redisAsyncContext *ac) {
|
||||
@@ -55,7 +55,10 @@ static int redisIvykisAttach(redisAsyncContext *ac) {
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisIvykisEvents*)malloc(sizeof(*e));
|
||||
e = (redisIvykisEvents*)hi_malloc(sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e->context = ac;
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
|
||||
Vendored
+35
-3
@@ -41,6 +41,7 @@ typedef struct redisLibevEvents {
|
||||
struct ev_loop *loop;
|
||||
int reading, writing;
|
||||
ev_io rev, wev;
|
||||
ev_timer timer;
|
||||
} redisLibevEvents;
|
||||
|
||||
static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
|
||||
@@ -103,11 +104,39 @@ static void redisLibevDelWrite(void *privdata) {
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibevStopTimer(void *privdata) {
|
||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
||||
struct ev_loop *loop = e->loop;
|
||||
((void)loop);
|
||||
ev_timer_stop(EV_A_ &e->timer);
|
||||
}
|
||||
|
||||
static void redisLibevCleanup(void *privdata) {
|
||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
||||
redisLibevDelRead(privdata);
|
||||
redisLibevDelWrite(privdata);
|
||||
free(e);
|
||||
redisLibevStopTimer(privdata);
|
||||
hi_free(e);
|
||||
}
|
||||
|
||||
static void redisLibevTimeout(EV_P_ ev_timer *timer, int revents) {
|
||||
((void)revents);
|
||||
redisLibevEvents *e = (redisLibevEvents*)timer->data;
|
||||
redisAsyncHandleTimeout(e->context);
|
||||
}
|
||||
|
||||
static void redisLibevSetTimeout(void *privdata, struct timeval tv) {
|
||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
||||
struct ev_loop *loop = e->loop;
|
||||
((void)loop);
|
||||
|
||||
if (!ev_is_active(&e->timer)) {
|
||||
ev_init(&e->timer, redisLibevTimeout);
|
||||
e->timer.data = e;
|
||||
}
|
||||
|
||||
e->timer.repeat = tv.tv_sec + tv.tv_usec / 1000000.00;
|
||||
ev_timer_again(EV_A_ &e->timer);
|
||||
}
|
||||
|
||||
static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
|
||||
@@ -119,14 +148,16 @@ static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisLibevEvents*)malloc(sizeof(*e));
|
||||
e = (redisLibevEvents*)hi_calloc(1, sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e->context = ac;
|
||||
#if EV_MULTIPLICITY
|
||||
e->loop = loop;
|
||||
#else
|
||||
e->loop = NULL;
|
||||
#endif
|
||||
e->reading = e->writing = 0;
|
||||
e->rev.data = e;
|
||||
e->wev.data = e;
|
||||
|
||||
@@ -136,6 +167,7 @@ static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
|
||||
ac->ev.addWrite = redisLibevAddWrite;
|
||||
ac->ev.delWrite = redisLibevDelWrite;
|
||||
ac->ev.cleanup = redisLibevCleanup;
|
||||
ac->ev.scheduleTimer = redisLibevSetTimeout;
|
||||
ac->ev.data = e;
|
||||
|
||||
/* Initialize read/write events */
|
||||
|
||||
Vendored
+5
-2
@@ -47,7 +47,7 @@ typedef struct redisLibeventEvents {
|
||||
} redisLibeventEvents;
|
||||
|
||||
static void redisLibeventDestroy(redisLibeventEvents *e) {
|
||||
free(e);
|
||||
hi_free(e);
|
||||
}
|
||||
|
||||
static void redisLibeventHandler(int fd, short event, void *arg) {
|
||||
@@ -152,7 +152,10 @@ static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisLibeventEvents*)calloc(1, sizeof(*e));
|
||||
e = (redisLibeventEvents*)hi_calloc(1, sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e->context = ac;
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
|
||||
Vendored
+5
-7
@@ -73,7 +73,7 @@ static void redisLibuvDelWrite(void *privdata) {
|
||||
static void on_close(uv_handle_t* handle) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
|
||||
|
||||
free(p);
|
||||
hi_free(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,15 +98,13 @@ static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {
|
||||
ac->ev.delWrite = redisLibuvDelWrite;
|
||||
ac->ev.cleanup = redisLibuvCleanup;
|
||||
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)malloc(sizeof(*p));
|
||||
|
||||
if (!p) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)hi_malloc(sizeof(*p));
|
||||
if (p == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
if (uv_poll_init(loop, &p->handle, c->fd) != 0) {
|
||||
if (uv_poll_init_socket(loop, &p->handle, c->fd) != 0) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+4
-3
@@ -27,7 +27,7 @@ static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {
|
||||
CFSocketInvalidate(redisRunLoop->socketRef);
|
||||
CFRelease(redisRunLoop->socketRef);
|
||||
}
|
||||
free(redisRunLoop);
|
||||
hi_free(redisRunLoop);
|
||||
}
|
||||
return REDIS_ERR;
|
||||
}
|
||||
@@ -80,8 +80,9 @@ static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLo
|
||||
/* Nothing should be attached when something is already attached */
|
||||
if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR;
|
||||
|
||||
RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop));
|
||||
if( !redisRunLoop ) return REDIS_ERR;
|
||||
RedisRunLoop* redisRunLoop = (RedisRunLoop*) hi_calloc(1, sizeof(RedisRunLoop));
|
||||
if (redisRunLoop == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Setup redis stuff */
|
||||
redisRunLoop->context = redisAsyncCtx;
|
||||
|
||||
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include "alloc.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
hiredisAllocFuncs hiredisAllocFns = {
|
||||
.mallocFn = malloc,
|
||||
.callocFn = calloc,
|
||||
.reallocFn = realloc,
|
||||
.strdupFn = strdup,
|
||||
.freeFn = free,
|
||||
};
|
||||
|
||||
/* Override hiredis' allocators with ones supplied by the user */
|
||||
hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *override) {
|
||||
hiredisAllocFuncs orig = hiredisAllocFns;
|
||||
|
||||
hiredisAllocFns = *override;
|
||||
|
||||
return orig;
|
||||
}
|
||||
|
||||
/* Reset allocators to use libc defaults */
|
||||
void hiredisResetAllocators(void) {
|
||||
hiredisAllocFns = (hiredisAllocFuncs) {
|
||||
.mallocFn = malloc,
|
||||
.callocFn = calloc,
|
||||
.reallocFn = realloc,
|
||||
.strdupFn = strdup,
|
||||
.freeFn = free,
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
void *hi_malloc(size_t size) {
|
||||
return hiredisAllocFns.mallocFn(size);
|
||||
}
|
||||
|
||||
void *hi_calloc(size_t nmemb, size_t size) {
|
||||
/* Overflow check as the user can specify any arbitrary allocator */
|
||||
if (SIZE_MAX / size < nmemb)
|
||||
return NULL;
|
||||
|
||||
return hiredisAllocFns.callocFn(nmemb, size);
|
||||
}
|
||||
|
||||
void *hi_realloc(void *ptr, size_t size) {
|
||||
return hiredisAllocFns.reallocFn(ptr, size);
|
||||
}
|
||||
|
||||
char *hi_strdup(const char *str) {
|
||||
return hiredisAllocFns.strdupFn(str);
|
||||
}
|
||||
|
||||
void hi_free(void *ptr) {
|
||||
hiredisAllocFns.freeFn(ptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef HIREDIS_ALLOC_H
|
||||
#define HIREDIS_ALLOC_H
|
||||
|
||||
#include <stddef.h> /* for size_t */
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Structure pointing to our actually configured allocators */
|
||||
typedef struct hiredisAllocFuncs {
|
||||
void *(*mallocFn)(size_t);
|
||||
void *(*callocFn)(size_t,size_t);
|
||||
void *(*reallocFn)(void*,size_t);
|
||||
char *(*strdupFn)(const char*);
|
||||
void (*freeFn)(void*);
|
||||
} hiredisAllocFuncs;
|
||||
|
||||
hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha);
|
||||
void hiredisResetAllocators(void);
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
/* Hiredis' configured allocator function pointer struct */
|
||||
extern hiredisAllocFuncs hiredisAllocFns;
|
||||
|
||||
static inline void *hi_malloc(size_t size) {
|
||||
return hiredisAllocFns.mallocFn(size);
|
||||
}
|
||||
|
||||
static inline void *hi_calloc(size_t nmemb, size_t size) {
|
||||
/* Overflow check as the user can specify any arbitrary allocator */
|
||||
if (SIZE_MAX / size < nmemb)
|
||||
return NULL;
|
||||
|
||||
return hiredisAllocFns.callocFn(nmemb, size);
|
||||
}
|
||||
|
||||
static inline void *hi_realloc(void *ptr, size_t size) {
|
||||
return hiredisAllocFns.reallocFn(ptr, size);
|
||||
}
|
||||
|
||||
static inline char *hi_strdup(const char *str) {
|
||||
return hiredisAllocFns.strdupFn(str);
|
||||
}
|
||||
|
||||
static inline void hi_free(void *ptr) {
|
||||
hiredisAllocFns.freeFn(ptr);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void *hi_malloc(size_t size);
|
||||
void *hi_calloc(size_t nmemb, size_t size);
|
||||
void *hi_realloc(void *ptr, size_t size);
|
||||
char *hi_strdup(const char *str);
|
||||
void hi_free(void *ptr);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HIREDIS_ALLOC_H */
|
||||
Vendored
+169
-48
@@ -30,6 +30,7 @@
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include "alloc.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef _MSC_VER
|
||||
@@ -46,18 +47,24 @@
|
||||
|
||||
#include "async_private.h"
|
||||
|
||||
/* Forward declaration of function in hiredis.c */
|
||||
/* Forward declarations of hiredis.c functions */
|
||||
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
/* Functions managing dictionary of callbacks for pub/sub. */
|
||||
static unsigned int callbackHash(const void *key) {
|
||||
return dictGenHashFunction((const unsigned char *)key,
|
||||
sdslen((const sds)key));
|
||||
hi_sdslen((const hisds)key));
|
||||
}
|
||||
|
||||
static void *callbackValDup(void *privdata, const void *src) {
|
||||
((void) privdata);
|
||||
redisCallback *dup = malloc(sizeof(*dup));
|
||||
redisCallback *dup;
|
||||
|
||||
dup = hi_malloc(sizeof(*dup));
|
||||
if (dup == NULL)
|
||||
return NULL;
|
||||
|
||||
memcpy(dup,src,sizeof(*dup));
|
||||
return dup;
|
||||
}
|
||||
@@ -66,20 +73,20 @@ static int callbackKeyCompare(void *privdata, const void *key1, const void *key2
|
||||
int l1, l2;
|
||||
((void) privdata);
|
||||
|
||||
l1 = sdslen((const sds)key1);
|
||||
l2 = sdslen((const sds)key2);
|
||||
l1 = hi_sdslen((const hisds)key1);
|
||||
l2 = hi_sdslen((const hisds)key2);
|
||||
if (l1 != l2) return 0;
|
||||
return memcmp(key1,key2,l1) == 0;
|
||||
}
|
||||
|
||||
static void callbackKeyDestructor(void *privdata, void *key) {
|
||||
((void) privdata);
|
||||
sdsfree((sds)key);
|
||||
hi_sdsfree((hisds)key);
|
||||
}
|
||||
|
||||
static void callbackValDestructor(void *privdata, void *val) {
|
||||
((void) privdata);
|
||||
free(val);
|
||||
hi_free(val);
|
||||
}
|
||||
|
||||
static dictType callbackDict = {
|
||||
@@ -93,10 +100,19 @@ static dictType callbackDict = {
|
||||
|
||||
static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
||||
redisAsyncContext *ac;
|
||||
dict *channels = NULL, *patterns = NULL;
|
||||
|
||||
ac = realloc(c,sizeof(redisAsyncContext));
|
||||
channels = dictCreate(&callbackDict,NULL);
|
||||
if (channels == NULL)
|
||||
goto oom;
|
||||
|
||||
patterns = dictCreate(&callbackDict,NULL);
|
||||
if (patterns == NULL)
|
||||
goto oom;
|
||||
|
||||
ac = hi_realloc(c,sizeof(redisAsyncContext));
|
||||
if (ac == NULL)
|
||||
return NULL;
|
||||
goto oom;
|
||||
|
||||
c = &(ac->c);
|
||||
|
||||
@@ -108,6 +124,7 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
||||
ac->err = 0;
|
||||
ac->errstr = NULL;
|
||||
ac->data = NULL;
|
||||
ac->dataCleanup = NULL;
|
||||
|
||||
ac->ev.data = NULL;
|
||||
ac->ev.addRead = NULL;
|
||||
@@ -124,9 +141,14 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
||||
ac->replies.tail = NULL;
|
||||
ac->sub.invalid.head = NULL;
|
||||
ac->sub.invalid.tail = NULL;
|
||||
ac->sub.channels = dictCreate(&callbackDict,NULL);
|
||||
ac->sub.patterns = dictCreate(&callbackDict,NULL);
|
||||
ac->sub.channels = channels;
|
||||
ac->sub.patterns = patterns;
|
||||
|
||||
return ac;
|
||||
oom:
|
||||
if (channels) dictRelease(channels);
|
||||
if (patterns) dictRelease(patterns);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We want the error field to be accessible directly instead of requiring
|
||||
@@ -145,16 +167,26 @@ redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options) {
|
||||
redisContext *c;
|
||||
redisAsyncContext *ac;
|
||||
|
||||
/* Clear any erroneously set sync callback and flag that we don't want to
|
||||
* use freeReplyObject by default. */
|
||||
myOptions.push_cb = NULL;
|
||||
myOptions.options |= REDIS_OPT_NO_PUSH_AUTOFREE;
|
||||
|
||||
myOptions.options |= REDIS_OPT_NONBLOCK;
|
||||
c = redisConnectWithOptions(&myOptions);
|
||||
if (c == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ac = redisAsyncInitialize(c);
|
||||
if (ac == NULL) {
|
||||
redisFree(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set any configured async push handler */
|
||||
redisAsyncSetPushCallback(ac, myOptions.async_push_cb);
|
||||
|
||||
__redisAsyncCopyError(ac);
|
||||
return ac;
|
||||
}
|
||||
@@ -214,7 +246,7 @@ static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
|
||||
redisCallback *cb;
|
||||
|
||||
/* Copy callback from stack to heap */
|
||||
cb = malloc(sizeof(*cb));
|
||||
cb = hi_malloc(sizeof(*cb));
|
||||
if (cb == NULL)
|
||||
return REDIS_ERR_OOM;
|
||||
|
||||
@@ -242,7 +274,7 @@ static int __redisShiftCallback(redisCallbackList *list, redisCallback *target)
|
||||
/* Copy callback from heap to stack */
|
||||
if (target != NULL)
|
||||
memcpy(target,cb,sizeof(*cb));
|
||||
free(cb);
|
||||
hi_free(cb);
|
||||
return REDIS_OK;
|
||||
}
|
||||
return REDIS_ERR;
|
||||
@@ -257,6 +289,14 @@ static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisRe
|
||||
}
|
||||
}
|
||||
|
||||
static void __redisRunPushCallback(redisAsyncContext *ac, redisReply *reply) {
|
||||
if (ac->push_cb != NULL) {
|
||||
ac->c.flags |= REDIS_IN_CALLBACK;
|
||||
ac->push_cb(ac, reply);
|
||||
ac->c.flags &= ~REDIS_IN_CALLBACK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to free the context. */
|
||||
static void __redisAsyncFree(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
@@ -272,18 +312,28 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
|
||||
while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)
|
||||
__redisRunCallback(ac,&cb,NULL);
|
||||
|
||||
/* Run subscription callbacks callbacks with NULL reply */
|
||||
it = dictGetIterator(ac->sub.channels);
|
||||
while ((de = dictNext(it)) != NULL)
|
||||
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
|
||||
dictReleaseIterator(it);
|
||||
dictRelease(ac->sub.channels);
|
||||
/* Run subscription callbacks with NULL reply */
|
||||
if (ac->sub.channels) {
|
||||
it = dictGetIterator(ac->sub.channels);
|
||||
if (it != NULL) {
|
||||
while ((de = dictNext(it)) != NULL)
|
||||
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
|
||||
dictReleaseIterator(it);
|
||||
}
|
||||
|
||||
it = dictGetIterator(ac->sub.patterns);
|
||||
while ((de = dictNext(it)) != NULL)
|
||||
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
|
||||
dictReleaseIterator(it);
|
||||
dictRelease(ac->sub.patterns);
|
||||
dictRelease(ac->sub.channels);
|
||||
}
|
||||
|
||||
if (ac->sub.patterns) {
|
||||
it = dictGetIterator(ac->sub.patterns);
|
||||
if (it != NULL) {
|
||||
while ((de = dictNext(it)) != NULL)
|
||||
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
|
||||
dictReleaseIterator(it);
|
||||
}
|
||||
|
||||
dictRelease(ac->sub.patterns);
|
||||
}
|
||||
|
||||
/* Signal event lib to clean up */
|
||||
_EL_CLEANUP(ac);
|
||||
@@ -298,6 +348,10 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ac->dataCleanup) {
|
||||
ac->dataCleanup(ac->data);
|
||||
}
|
||||
|
||||
/* Cleanup self */
|
||||
redisFree(c);
|
||||
}
|
||||
@@ -364,11 +418,11 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
|
||||
dictEntry *de;
|
||||
int pvariant;
|
||||
char *stype;
|
||||
sds sname;
|
||||
hisds sname;
|
||||
|
||||
/* Custom reply functions are not supported for pub/sub. This will fail
|
||||
* very hard when they are used... */
|
||||
if (reply->type == REDIS_REPLY_ARRAY) {
|
||||
if (reply->type == REDIS_REPLY_ARRAY || reply->type == REDIS_REPLY_PUSH) {
|
||||
assert(reply->elements >= 2);
|
||||
assert(reply->element[0]->type == REDIS_REPLY_STRING);
|
||||
stype = reply->element[0]->str;
|
||||
@@ -381,7 +435,10 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
|
||||
|
||||
/* Locate the right callback */
|
||||
assert(reply->element[1]->type == REDIS_REPLY_STRING);
|
||||
sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
|
||||
sname = hi_sdsnewlen(reply->element[1]->str,reply->element[1]->len);
|
||||
if (sname == NULL)
|
||||
goto oom;
|
||||
|
||||
de = dictFind(callbacks,sname);
|
||||
if (de != NULL) {
|
||||
cb = dictGetEntryVal(de);
|
||||
@@ -409,12 +466,39 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
|
||||
c->flags &= ~REDIS_SUBSCRIBED;
|
||||
}
|
||||
}
|
||||
sdsfree(sname);
|
||||
hi_sdsfree(sname);
|
||||
} else {
|
||||
/* Shift callback for invalid commands. */
|
||||
__redisShiftCallback(&ac->sub.invalid,dstcb);
|
||||
}
|
||||
return REDIS_OK;
|
||||
oom:
|
||||
__redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
#define redisIsSpontaneousPushReply(r) \
|
||||
(redisIsPushReply(r) && !redisIsSubscribeReply(r))
|
||||
|
||||
static int redisIsSubscribeReply(redisReply *reply) {
|
||||
char *str;
|
||||
size_t len, off;
|
||||
|
||||
/* We will always have at least one string with the subscribe/message type */
|
||||
if (reply->elements < 1 || reply->element[0]->type != REDIS_REPLY_STRING ||
|
||||
reply->element[0]->len < sizeof("message") - 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the string/len moving past 'p' if needed */
|
||||
off = tolower(reply->element[0]->str[0]) == 'p';
|
||||
str = reply->element[0]->str + off;
|
||||
len = reply->element[0]->len - off;
|
||||
|
||||
return !strncasecmp(str, "subscribe", len) ||
|
||||
!strncasecmp(str, "message", len);
|
||||
|
||||
}
|
||||
|
||||
void redisProcessCallbacks(redisAsyncContext *ac) {
|
||||
@@ -427,7 +511,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
|
||||
if (reply == NULL) {
|
||||
/* When the connection is being disconnected and there are
|
||||
* no more replies, this is the cue to really disconnect. */
|
||||
if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0
|
||||
if (c->flags & REDIS_DISCONNECTING && hi_sdslen(c->obuf) == 0
|
||||
&& ac->replies.head == NULL) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
return;
|
||||
@@ -443,8 +527,18 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Even if the context is subscribed, pending regular callbacks will
|
||||
* get a reply before pub/sub messages arrive. */
|
||||
/* Send any non-subscribe related PUSH messages to our PUSH handler
|
||||
* while allowing subscribe related PUSH messages to pass through.
|
||||
* This allows existing code to be backward compatible and work in
|
||||
* either RESP2 or RESP3 mode. */
|
||||
if (redisIsSpontaneousPushReply(reply)) {
|
||||
__redisRunPushCallback(ac, reply);
|
||||
c->reader->fn->freeObject(reply);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Even if the context is subscribed, pending regular
|
||||
* callbacks will get a reply before pub/sub messages arrive. */
|
||||
if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
|
||||
/*
|
||||
* A spontaneous reply in a not-subscribed context can be the error
|
||||
@@ -497,20 +591,31 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
}
|
||||
|
||||
static void __redisAsyncHandleConnectFailure(redisAsyncContext *ac) {
|
||||
if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);
|
||||
__redisAsyncDisconnect(ac);
|
||||
}
|
||||
|
||||
/* Internal helper function to detect socket status the first time a read or
|
||||
* write event fires. When connecting was not successful, the connect callback
|
||||
* is called with a REDIS_ERR status and the context is free'd. */
|
||||
static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
|
||||
int completed = 0;
|
||||
redisContext *c = &(ac->c);
|
||||
|
||||
if (redisCheckConnectDone(c, &completed) == REDIS_ERR) {
|
||||
/* Error! */
|
||||
redisCheckSocketError(c);
|
||||
if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);
|
||||
__redisAsyncDisconnect(ac);
|
||||
__redisAsyncHandleConnectFailure(ac);
|
||||
return REDIS_ERR;
|
||||
} else if (completed == 1) {
|
||||
/* connected! */
|
||||
if (c->connection_type == REDIS_CONN_TCP &&
|
||||
redisSetTcpNoDelay(c) == REDIS_ERR) {
|
||||
__redisAsyncHandleConnectFailure(ac);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (ac->onConnect) ac->onConnect(ac, REDIS_OK);
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
return REDIS_OK;
|
||||
@@ -582,8 +687,6 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
|
||||
c->funcs->async_write(ac);
|
||||
}
|
||||
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
void redisAsyncHandleTimeout(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisCallback cb;
|
||||
@@ -641,7 +744,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
||||
const char *cstr, *astr;
|
||||
size_t clen, alen;
|
||||
const char *p;
|
||||
sds sname;
|
||||
hisds sname;
|
||||
int ret;
|
||||
|
||||
/* Don't accept new commands when the connection is about to be closed. */
|
||||
@@ -665,7 +768,10 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
||||
|
||||
/* Add every channel/pattern to the list of subscription callbacks. */
|
||||
while ((p = nextArgument(p,&astr,&alen)) != NULL) {
|
||||
sname = sdsnewlen(astr,alen);
|
||||
sname = hi_sdsnewlen(astr,alen);
|
||||
if (sname == NULL)
|
||||
goto oom;
|
||||
|
||||
if (pvariant)
|
||||
cbdict = ac->sub.patterns;
|
||||
else
|
||||
@@ -680,7 +786,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
||||
|
||||
ret = dictReplace(cbdict,sname,&cb);
|
||||
|
||||
if (ret == 0) sdsfree(sname);
|
||||
if (ret == 0) hi_sdsfree(sname);
|
||||
}
|
||||
} else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
|
||||
/* It is only useful to call (P)UNSUBSCRIBE when the context is
|
||||
@@ -709,6 +815,9 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
||||
_EL_ADD_WRITE(ac);
|
||||
|
||||
return REDIS_OK;
|
||||
oom:
|
||||
__redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) {
|
||||
@@ -722,7 +831,7 @@ int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdat
|
||||
return REDIS_ERR;
|
||||
|
||||
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -736,14 +845,14 @@ int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata
|
||||
}
|
||||
|
||||
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
|
||||
sds cmd;
|
||||
hisds cmd;
|
||||
int len;
|
||||
int status;
|
||||
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
|
||||
if (len < 0)
|
||||
return REDIS_ERR;
|
||||
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
||||
sdsfree(cmd);
|
||||
hi_sdsfree(cmd);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -752,15 +861,27 @@ int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
||||
return status;
|
||||
}
|
||||
|
||||
void redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {
|
||||
if (!ac->c.timeout) {
|
||||
ac->c.timeout = calloc(1, sizeof(tv));
|
||||
redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn) {
|
||||
redisAsyncPushFn *old = ac->push_cb;
|
||||
ac->push_cb = fn;
|
||||
return old;
|
||||
}
|
||||
|
||||
int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {
|
||||
if (!ac->c.command_timeout) {
|
||||
ac->c.command_timeout = hi_calloc(1, sizeof(tv));
|
||||
if (ac->c.command_timeout == NULL) {
|
||||
__redisSetError(&ac->c, REDIS_ERR_OOM, "Out of memory");
|
||||
__redisAsyncCopyError(ac);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
if (tv.tv_sec == ac->c.timeout->tv_sec &&
|
||||
tv.tv_usec == ac->c.timeout->tv_usec) {
|
||||
return;
|
||||
if (tv.tv_sec != ac->c.command_timeout->tv_sec ||
|
||||
tv.tv_usec != ac->c.command_timeout->tv_usec)
|
||||
{
|
||||
*ac->c.command_timeout = tv;
|
||||
}
|
||||
|
||||
*ac->c.timeout = tv;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
Vendored
+6
-1
@@ -70,6 +70,7 @@ typedef struct redisAsyncContext {
|
||||
|
||||
/* Not used by hiredis */
|
||||
void *data;
|
||||
void (*dataCleanup)(void *privdata);
|
||||
|
||||
/* Event library data and hooks */
|
||||
struct {
|
||||
@@ -105,6 +106,9 @@ typedef struct redisAsyncContext {
|
||||
struct dict *channels;
|
||||
struct dict *patterns;
|
||||
} sub;
|
||||
|
||||
/* Any configured RESP3 PUSH handler */
|
||||
redisAsyncPushFn *push_cb;
|
||||
} redisAsyncContext;
|
||||
|
||||
/* Functions that proxy to hiredis */
|
||||
@@ -117,7 +121,8 @@ redisAsyncContext *redisAsyncConnectUnix(const char *path);
|
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
|
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
||||
|
||||
void redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);
|
||||
redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn);
|
||||
int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);
|
||||
void redisAsyncDisconnect(redisAsyncContext *ac);
|
||||
void redisAsyncFree(redisAsyncContext *ac);
|
||||
|
||||
|
||||
Vendored
+13
-10
@@ -51,18 +51,21 @@
|
||||
#define _EL_CLEANUP(ctx) do { \
|
||||
if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \
|
||||
ctx->ev.cleanup = NULL; \
|
||||
} while(0);
|
||||
} while(0)
|
||||
|
||||
static inline void refreshTimeout(redisAsyncContext *ctx) {
|
||||
if (ctx->c.timeout && ctx->ev.scheduleTimer &&
|
||||
(ctx->c.timeout->tv_sec || ctx->c.timeout->tv_usec)) {
|
||||
ctx->ev.scheduleTimer(ctx->ev.data, *ctx->c.timeout);
|
||||
// } else {
|
||||
// printf("Not scheduling timer.. (tmo=%p)\n", ctx->c.timeout);
|
||||
// if (ctx->c.timeout){
|
||||
// printf("tv_sec: %u. tv_usec: %u\n", ctx->c.timeout->tv_sec,
|
||||
// ctx->c.timeout->tv_usec);
|
||||
// }
|
||||
#define REDIS_TIMER_ISSET(tvp) \
|
||||
(tvp && ((tvp)->tv_sec || (tvp)->tv_usec))
|
||||
|
||||
#define REDIS_EL_TIMER(ac, tvp) \
|
||||
if ((ac)->ev.scheduleTimer && REDIS_TIMER_ISSET(tvp)) { \
|
||||
(ac)->ev.scheduleTimer((ac)->ev.data, *(tvp)); \
|
||||
}
|
||||
|
||||
if (ctx->c.flags & REDIS_CONNECTED) {
|
||||
REDIS_EL_TIMER(ctx, ctx->c.command_timeout);
|
||||
} else {
|
||||
REDIS_EL_TIMER(ctx, ctx->c.connect_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+25
-11
@@ -34,6 +34,7 @@
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include "alloc.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
@@ -71,7 +72,10 @@ static void _dictReset(dict *ht) {
|
||||
|
||||
/* Create a new hash table */
|
||||
static dict *dictCreate(dictType *type, void *privDataPtr) {
|
||||
dict *ht = malloc(sizeof(*ht));
|
||||
dict *ht = hi_malloc(sizeof(*ht));
|
||||
if (ht == NULL)
|
||||
return NULL;
|
||||
|
||||
_dictInit(ht,type,privDataPtr);
|
||||
return ht;
|
||||
}
|
||||
@@ -97,7 +101,9 @@ static int dictExpand(dict *ht, unsigned long size) {
|
||||
_dictInit(&n, ht->type, ht->privdata);
|
||||
n.size = realsize;
|
||||
n.sizemask = realsize-1;
|
||||
n.table = calloc(realsize,sizeof(dictEntry*));
|
||||
n.table = hi_calloc(realsize,sizeof(dictEntry*));
|
||||
if (n.table == NULL)
|
||||
return DICT_ERR;
|
||||
|
||||
/* Copy all the elements from the old to the new table:
|
||||
* note that if the old hash table is empty ht->size is zero,
|
||||
@@ -124,7 +130,7 @@ static int dictExpand(dict *ht, unsigned long size) {
|
||||
}
|
||||
}
|
||||
assert(ht->used == 0);
|
||||
free(ht->table);
|
||||
hi_free(ht->table);
|
||||
|
||||
/* Remap the new hashtable in the old */
|
||||
*ht = n;
|
||||
@@ -142,7 +148,10 @@ static int dictAdd(dict *ht, void *key, void *val) {
|
||||
return DICT_ERR;
|
||||
|
||||
/* Allocates the memory and stores key */
|
||||
entry = malloc(sizeof(*entry));
|
||||
entry = hi_malloc(sizeof(*entry));
|
||||
if (entry == NULL)
|
||||
return DICT_ERR;
|
||||
|
||||
entry->next = ht->table[index];
|
||||
ht->table[index] = entry;
|
||||
|
||||
@@ -166,6 +175,9 @@ static int dictReplace(dict *ht, void *key, void *val) {
|
||||
return 1;
|
||||
/* It already exists, get the entry */
|
||||
entry = dictFind(ht, key);
|
||||
if (entry == NULL)
|
||||
return 0;
|
||||
|
||||
/* Free the old value and set the new one */
|
||||
/* Set the new value and free the old one. Note that it is important
|
||||
* to do that in this order, as the value may just be exactly the same
|
||||
@@ -199,7 +211,7 @@ static int dictDelete(dict *ht, const void *key) {
|
||||
|
||||
dictFreeEntryKey(ht,de);
|
||||
dictFreeEntryVal(ht,de);
|
||||
free(de);
|
||||
hi_free(de);
|
||||
ht->used--;
|
||||
return DICT_OK;
|
||||
}
|
||||
@@ -222,13 +234,13 @@ static int _dictClear(dict *ht) {
|
||||
nextHe = he->next;
|
||||
dictFreeEntryKey(ht, he);
|
||||
dictFreeEntryVal(ht, he);
|
||||
free(he);
|
||||
hi_free(he);
|
||||
ht->used--;
|
||||
he = nextHe;
|
||||
}
|
||||
}
|
||||
/* Free the table and the allocated cache structure */
|
||||
free(ht->table);
|
||||
hi_free(ht->table);
|
||||
/* Re-initialize the table */
|
||||
_dictReset(ht);
|
||||
return DICT_OK; /* never fails */
|
||||
@@ -237,7 +249,7 @@ static int _dictClear(dict *ht) {
|
||||
/* Clear & Release the hash table */
|
||||
static void dictRelease(dict *ht) {
|
||||
_dictClear(ht);
|
||||
free(ht);
|
||||
hi_free(ht);
|
||||
}
|
||||
|
||||
static dictEntry *dictFind(dict *ht, const void *key) {
|
||||
@@ -256,7 +268,9 @@ static dictEntry *dictFind(dict *ht, const void *key) {
|
||||
}
|
||||
|
||||
static dictIterator *dictGetIterator(dict *ht) {
|
||||
dictIterator *iter = malloc(sizeof(*iter));
|
||||
dictIterator *iter = hi_malloc(sizeof(*iter));
|
||||
if (iter == NULL)
|
||||
return NULL;
|
||||
|
||||
iter->ht = ht;
|
||||
iter->index = -1;
|
||||
@@ -286,7 +300,7 @@ static dictEntry *dictNext(dictIterator *iter) {
|
||||
}
|
||||
|
||||
static void dictReleaseIterator(dictIterator *iter) {
|
||||
free(iter);
|
||||
hi_free(iter);
|
||||
}
|
||||
|
||||
/* ------------------------- private functions ------------------------------ */
|
||||
@@ -294,7 +308,7 @@ static void dictReleaseIterator(dictIterator *iter) {
|
||||
/* Expand the hash table if needed */
|
||||
static int _dictExpandIfNeeded(dict *ht) {
|
||||
/* If the hash table is empty expand it to the initial size,
|
||||
* if the table is "full" dobule its size. */
|
||||
* if the table is "full" double its size. */
|
||||
if (ht->size == 0)
|
||||
return dictExpand(ht, DICT_HT_INITIAL_SIZE);
|
||||
if (ht->used == ht->size)
|
||||
|
||||
Vendored
+3
@@ -44,3 +44,6 @@ ENDIF()
|
||||
|
||||
ADD_EXECUTABLE(example example.c)
|
||||
TARGET_LINK_LIBRARIES(example hiredis)
|
||||
|
||||
ADD_EXECUTABLE(example-push example-push.c)
|
||||
TARGET_LINK_LIBRARIES(example-push hiredis)
|
||||
|
||||
+2
@@ -33,7 +33,9 @@ void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
iv_init();
|
||||
|
||||
|
||||
+2
@@ -33,7 +33,9 @@ void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
|
||||
+18
-1
@@ -34,7 +34,10 @@ void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
struct event_base *base = event_base_new();
|
||||
if (argc < 5) {
|
||||
fprintf(stderr,
|
||||
@@ -52,13 +55,25 @@ int main (int argc, char **argv) {
|
||||
const char *certKey = argv[5];
|
||||
const char *caCert = argc > 5 ? argv[6] : NULL;
|
||||
|
||||
redisSSLContext *ssl;
|
||||
redisSSLContextError ssl_error;
|
||||
|
||||
redisInitOpenSSL();
|
||||
|
||||
ssl = redisCreateSSLContext(caCert, NULL,
|
||||
cert, certKey, NULL, &ssl_error);
|
||||
if (!ssl) {
|
||||
printf("Error: %s\n", redisSSLContextGetError(ssl_error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect(hostname, port);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
if (redisSecureConnection(&c->c, caCert, cert, certKey, "sni") != REDIS_OK) {
|
||||
if (redisInitiateSSLWithContext(&c->c, ssl) != REDIS_OK) {
|
||||
printf("SSL Error!\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -69,5 +84,7 @@ int main (int argc, char **argv) {
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", value, nvalue);
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
event_base_dispatch(base);
|
||||
|
||||
redisFreeSSLContext(ssl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+4
-1
@@ -38,13 +38,16 @@ void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
struct event_base *base = event_base_new();
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
|
||||
struct timeval tv = {0};
|
||||
tv.tv_sec = 1;
|
||||
options.timeout = &tv;
|
||||
options.connect_timeout = &tv;
|
||||
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnectWithOptions(&options);
|
||||
|
||||
+3
@@ -33,7 +33,10 @@ void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
uv_loop_t* loop = uv_default_loop();
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
|
||||
Vendored
+160
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <hiredis.h>
|
||||
#include <win32.h>
|
||||
|
||||
#define KEY_COUNT 5
|
||||
|
||||
#define panicAbort(fmt, ...) \
|
||||
do { \
|
||||
fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, __VA_ARGS__); \
|
||||
exit(-1); \
|
||||
} while (0)
|
||||
|
||||
static void assertReplyAndFree(redisContext *context, redisReply *reply, int type) {
|
||||
if (reply == NULL)
|
||||
panicAbort("NULL reply from server (error: %s)", context->errstr);
|
||||
|
||||
if (reply->type != type) {
|
||||
if (reply->type == REDIS_REPLY_ERROR)
|
||||
fprintf(stderr, "Redis Error: %s\n", reply->str);
|
||||
|
||||
panicAbort("Expected reply type %d but got type %d", type, reply->type);
|
||||
}
|
||||
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
/* Switch to the RESP3 protocol and enable client tracking */
|
||||
static void enableClientTracking(redisContext *c) {
|
||||
redisReply *reply = redisCommand(c, "HELLO 3");
|
||||
if (reply == NULL || c->err) {
|
||||
panicAbort("NULL reply or server error (error: %s)", c->errstr);
|
||||
}
|
||||
|
||||
if (reply->type != REDIS_REPLY_MAP) {
|
||||
fprintf(stderr, "Error: Can't send HELLO 3 command. Are you sure you're ");
|
||||
fprintf(stderr, "connected to redis-server >= 6.0.0?\nRedis error: %s\n",
|
||||
reply->type == REDIS_REPLY_ERROR ? reply->str : "(unknown)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Enable client tracking */
|
||||
reply = redisCommand(c, "CLIENT TRACKING ON");
|
||||
assertReplyAndFree(c, reply, REDIS_REPLY_STATUS);
|
||||
}
|
||||
|
||||
void pushReplyHandler(void *privdata, void *r) {
|
||||
redisReply *reply = r;
|
||||
int *invalidations = privdata;
|
||||
|
||||
/* Sanity check on the invalidation reply */
|
||||
if (reply->type != REDIS_REPLY_PUSH || reply->elements != 2 ||
|
||||
reply->element[1]->type != REDIS_REPLY_ARRAY ||
|
||||
reply->element[1]->element[0]->type != REDIS_REPLY_STRING)
|
||||
{
|
||||
panicAbort("%s", "Can't parse PUSH message!");
|
||||
}
|
||||
|
||||
/* Increment our invalidation count */
|
||||
*invalidations += 1;
|
||||
|
||||
printf("pushReplyHandler(): INVALIDATE '%s' (invalidation count: %d)\n",
|
||||
reply->element[1]->element[0]->str, *invalidations);
|
||||
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
/* We aren't actually freeing anything here, but it is included to show that we can
|
||||
* have hiredis call our data destructor when freeing the context */
|
||||
void privdata_dtor(void *privdata) {
|
||||
unsigned int *icount = privdata;
|
||||
printf("privdata_dtor(): In context privdata dtor (invalidations: %u)\n", *icount);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned int j, invalidations = 0;
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
|
||||
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
|
||||
int port = (argc > 2) ? atoi(argv[2]) : 6379;
|
||||
|
||||
redisOptions o = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&o, hostname, port);
|
||||
|
||||
/* Set our context privdata to the address of our invalidation counter. Each
|
||||
* time our PUSH handler is called, hiredis will pass the privdata for context.
|
||||
*
|
||||
* This could also be done after we create the context like so:
|
||||
*
|
||||
* c->privdata = &invalidations;
|
||||
* c->free_privdata = privdata_dtor;
|
||||
*/
|
||||
REDIS_OPTIONS_SET_PRIVDATA(&o, &invalidations, privdata_dtor);
|
||||
|
||||
/* Set our custom PUSH message handler */
|
||||
o.push_cb = pushReplyHandler;
|
||||
|
||||
c = redisConnectWithOptions(&o);
|
||||
if (c == NULL || c->err)
|
||||
panicAbort("Connection error: %s", c ? c->errstr : "OOM");
|
||||
|
||||
/* Enable RESP3 and turn on client tracking */
|
||||
enableClientTracking(c);
|
||||
|
||||
/* Set some keys and then read them back. Once we do that, Redis will deliver
|
||||
* invalidation push messages whenever the key is modified */
|
||||
for (j = 0; j < KEY_COUNT; j++) {
|
||||
reply = redisCommand(c, "SET key:%d initial:%d", j, j);
|
||||
assertReplyAndFree(c, reply, REDIS_REPLY_STATUS);
|
||||
|
||||
reply = redisCommand(c, "GET key:%d", j);
|
||||
assertReplyAndFree(c, reply, REDIS_REPLY_STRING);
|
||||
}
|
||||
|
||||
/* Trigger invalidation messages by updating keys we just read */
|
||||
for (j = 0; j < KEY_COUNT; j++) {
|
||||
printf(" main(): SET key:%d update:%d\n", j, j);
|
||||
reply = redisCommand(c, "SET key:%d update:%d", j, j);
|
||||
assertReplyAndFree(c, reply, REDIS_REPLY_STATUS);
|
||||
printf(" main(): SET REPLY OK\n");
|
||||
}
|
||||
|
||||
printf("\nTotal detected invalidations: %d, expected: %d\n", invalidations, KEY_COUNT);
|
||||
|
||||
/* PING server */
|
||||
redisFree(c);
|
||||
}
|
||||
Vendored
+15
-2
@@ -4,9 +4,12 @@
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <hiredis_ssl.h>
|
||||
#include <win32.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned int j;
|
||||
redisSSLContext *ssl;
|
||||
redisSSLContextError ssl_error;
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
if (argc < 4) {
|
||||
@@ -19,10 +22,18 @@ int main(int argc, char **argv) {
|
||||
const char *key = argv[4];
|
||||
const char *ca = argc > 4 ? argv[5] : NULL;
|
||||
|
||||
redisInitOpenSSL();
|
||||
ssl = redisCreateSSLContext(ca, NULL, cert, key, NULL, &ssl_error);
|
||||
if (!ssl) {
|
||||
printf("SSL Context error: %s\n",
|
||||
redisSSLContextGetError(ssl_error));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct timeval tv = { 1, 500000 }; // 1.5 seconds
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, hostname, port);
|
||||
options.timeout = &tv;
|
||||
options.connect_timeout = &tv;
|
||||
c = redisConnectWithOptions(&options);
|
||||
|
||||
if (c == NULL || c->err) {
|
||||
@@ -35,7 +46,7 @@ int main(int argc, char **argv) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (redisSecureConnection(c, ca, cert, key, "sni") != REDIS_OK) {
|
||||
if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) {
|
||||
printf("Couldn't initialize SSL!\n");
|
||||
printf("Error: %s\n", c->errstr);
|
||||
redisFree(c);
|
||||
@@ -93,5 +104,7 @@ int main(int argc, char **argv) {
|
||||
/* Disconnects and frees the context */
|
||||
redisFree(c);
|
||||
|
||||
redisFreeSSLContext(ssl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -1,8 +1,8 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <win32.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned int j, isunix = 0;
|
||||
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set_and_check(hiredis_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
|
||||
|
||||
IF (NOT TARGET hiredis::hiredis)
|
||||
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis-targets.cmake)
|
||||
ENDIF()
|
||||
|
||||
SET(hiredis_LIBRARIES hiredis::hiredis)
|
||||
SET(hiredis_INCLUDE_DIRS ${hiredis_INCLUDEDIR})
|
||||
|
||||
check_required_components(hiredis)
|
||||
|
||||
Vendored
+202
-114
@@ -44,8 +44,11 @@
|
||||
#include "async.h"
|
||||
#include "win32.h"
|
||||
|
||||
extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout);
|
||||
extern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout);
|
||||
|
||||
static redisContextFuncs redisContextDefaultFuncs = {
|
||||
.free_privdata = NULL,
|
||||
.free_privctx = NULL,
|
||||
.async_read = redisAsyncRead,
|
||||
.async_write = redisAsyncWrite,
|
||||
.read = redisNetRead,
|
||||
@@ -74,7 +77,7 @@ static redisReplyObjectFunctions defaultFunctions = {
|
||||
|
||||
/* Create a reply object */
|
||||
static redisReply *createReplyObject(int type) {
|
||||
redisReply *r = calloc(1,sizeof(*r));
|
||||
redisReply *r = hi_calloc(1,sizeof(*r));
|
||||
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
@@ -97,20 +100,22 @@ void freeReplyObject(void *reply) {
|
||||
case REDIS_REPLY_ARRAY:
|
||||
case REDIS_REPLY_MAP:
|
||||
case REDIS_REPLY_SET:
|
||||
case REDIS_REPLY_PUSH:
|
||||
if (r->element != NULL) {
|
||||
for (j = 0; j < r->elements; j++)
|
||||
freeReplyObject(r->element[j]);
|
||||
free(r->element);
|
||||
hi_free(r->element);
|
||||
}
|
||||
break;
|
||||
case REDIS_REPLY_ERROR:
|
||||
case REDIS_REPLY_STATUS:
|
||||
case REDIS_REPLY_STRING:
|
||||
case REDIS_REPLY_DOUBLE:
|
||||
free(r->str);
|
||||
case REDIS_REPLY_VERB:
|
||||
hi_free(r->str);
|
||||
break;
|
||||
}
|
||||
free(r);
|
||||
hi_free(r);
|
||||
}
|
||||
|
||||
static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
|
||||
@@ -128,22 +133,18 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
|
||||
|
||||
/* Copy string value */
|
||||
if (task->type == REDIS_REPLY_VERB) {
|
||||
buf = malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */
|
||||
if (buf == NULL) {
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
}
|
||||
buf = hi_malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */
|
||||
if (buf == NULL) goto oom;
|
||||
|
||||
memcpy(r->vtype,str,3);
|
||||
r->vtype[3] = '\0';
|
||||
memcpy(buf,str+4,len-4);
|
||||
buf[len-4] = '\0';
|
||||
r->len = len-4;
|
||||
r->len = len - 4;
|
||||
} else {
|
||||
buf = malloc(len+1);
|
||||
if (buf == NULL) {
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
}
|
||||
buf = hi_malloc(len+1);
|
||||
if (buf == NULL) goto oom;
|
||||
|
||||
memcpy(buf,str,len);
|
||||
buf[len] = '\0';
|
||||
r->len = len;
|
||||
@@ -154,10 +155,15 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
|
||||
parent = task->parent->obj;
|
||||
assert(parent->type == REDIS_REPLY_ARRAY ||
|
||||
parent->type == REDIS_REPLY_MAP ||
|
||||
parent->type == REDIS_REPLY_SET);
|
||||
parent->type == REDIS_REPLY_SET ||
|
||||
parent->type == REDIS_REPLY_PUSH);
|
||||
parent->element[task->idx] = r;
|
||||
}
|
||||
return r;
|
||||
|
||||
oom:
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *createArrayObject(const redisReadTask *task, size_t elements) {
|
||||
@@ -168,7 +174,7 @@ static void *createArrayObject(const redisReadTask *task, size_t elements) {
|
||||
return NULL;
|
||||
|
||||
if (elements > 0) {
|
||||
r->element = calloc(elements,sizeof(redisReply*));
|
||||
r->element = hi_calloc(elements,sizeof(redisReply*));
|
||||
if (r->element == NULL) {
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
@@ -181,7 +187,8 @@ static void *createArrayObject(const redisReadTask *task, size_t elements) {
|
||||
parent = task->parent->obj;
|
||||
assert(parent->type == REDIS_REPLY_ARRAY ||
|
||||
parent->type == REDIS_REPLY_MAP ||
|
||||
parent->type == REDIS_REPLY_SET);
|
||||
parent->type == REDIS_REPLY_SET ||
|
||||
parent->type == REDIS_REPLY_PUSH);
|
||||
parent->element[task->idx] = r;
|
||||
}
|
||||
return r;
|
||||
@@ -200,7 +207,8 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
|
||||
parent = task->parent->obj;
|
||||
assert(parent->type == REDIS_REPLY_ARRAY ||
|
||||
parent->type == REDIS_REPLY_MAP ||
|
||||
parent->type == REDIS_REPLY_SET);
|
||||
parent->type == REDIS_REPLY_SET ||
|
||||
parent->type == REDIS_REPLY_PUSH);
|
||||
parent->element[task->idx] = r;
|
||||
}
|
||||
return r;
|
||||
@@ -214,7 +222,7 @@ static void *createDoubleObject(const redisReadTask *task, double value, char *s
|
||||
return NULL;
|
||||
|
||||
r->dval = value;
|
||||
r->str = malloc(len+1);
|
||||
r->str = hi_malloc(len+1);
|
||||
if (r->str == NULL) {
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
@@ -249,7 +257,8 @@ static void *createNilObject(const redisReadTask *task) {
|
||||
parent = task->parent->obj;
|
||||
assert(parent->type == REDIS_REPLY_ARRAY ||
|
||||
parent->type == REDIS_REPLY_MAP ||
|
||||
parent->type == REDIS_REPLY_SET);
|
||||
parent->type == REDIS_REPLY_SET ||
|
||||
parent->type == REDIS_REPLY_PUSH);
|
||||
parent->element[task->idx] = r;
|
||||
}
|
||||
return r;
|
||||
@@ -297,7 +306,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
const char *c = format;
|
||||
char *cmd = NULL; /* final command */
|
||||
int pos; /* position in final command */
|
||||
sds curarg, newarg; /* current argument */
|
||||
hisds curarg, newarg; /* current argument */
|
||||
int touched = 0; /* was the current argument touched? */
|
||||
char **curargv = NULL, **newargv = NULL;
|
||||
int argc = 0;
|
||||
@@ -310,7 +319,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
return -1;
|
||||
|
||||
/* Build the command string accordingly to protocol */
|
||||
curarg = sdsempty();
|
||||
curarg = hi_sdsempty();
|
||||
if (curarg == NULL)
|
||||
return -1;
|
||||
|
||||
@@ -318,19 +327,19 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
if (*c != '%' || c[1] == '\0') {
|
||||
if (*c == ' ') {
|
||||
if (touched) {
|
||||
newargv = realloc(curargv,sizeof(char*)*(argc+1));
|
||||
newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));
|
||||
if (newargv == NULL) goto memory_err;
|
||||
curargv = newargv;
|
||||
curargv[argc++] = curarg;
|
||||
totlen += bulklen(sdslen(curarg));
|
||||
totlen += bulklen(hi_sdslen(curarg));
|
||||
|
||||
/* curarg is put in argv so it can be overwritten. */
|
||||
curarg = sdsempty();
|
||||
curarg = hi_sdsempty();
|
||||
if (curarg == NULL) goto memory_err;
|
||||
touched = 0;
|
||||
}
|
||||
} else {
|
||||
newarg = sdscatlen(curarg,c,1);
|
||||
newarg = hi_sdscatlen(curarg,c,1);
|
||||
if (newarg == NULL) goto memory_err;
|
||||
curarg = newarg;
|
||||
touched = 1;
|
||||
@@ -347,16 +356,16 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
arg = va_arg(ap,char*);
|
||||
size = strlen(arg);
|
||||
if (size > 0)
|
||||
newarg = sdscatlen(curarg,arg,size);
|
||||
newarg = hi_sdscatlen(curarg,arg,size);
|
||||
break;
|
||||
case 'b':
|
||||
arg = va_arg(ap,char*);
|
||||
size = va_arg(ap,size_t);
|
||||
if (size > 0)
|
||||
newarg = sdscatlen(curarg,arg,size);
|
||||
newarg = hi_sdscatlen(curarg,arg,size);
|
||||
break;
|
||||
case '%':
|
||||
newarg = sdscat(curarg,"%");
|
||||
newarg = hi_sdscat(curarg,"%");
|
||||
break;
|
||||
default:
|
||||
/* Try to detect printf format */
|
||||
@@ -444,7 +453,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
if (_l < sizeof(_format)-2) {
|
||||
memcpy(_format,c,_l);
|
||||
_format[_l] = '\0';
|
||||
newarg = sdscatvprintf(curarg,_format,_cpy);
|
||||
newarg = hi_sdscatvprintf(curarg,_format,_cpy);
|
||||
|
||||
/* Update current position (note: outer blocks
|
||||
* increment c twice so compensate here) */
|
||||
@@ -467,13 +476,13 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
|
||||
/* Add the last argument if needed */
|
||||
if (touched) {
|
||||
newargv = realloc(curargv,sizeof(char*)*(argc+1));
|
||||
newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));
|
||||
if (newargv == NULL) goto memory_err;
|
||||
curargv = newargv;
|
||||
curargv[argc++] = curarg;
|
||||
totlen += bulklen(sdslen(curarg));
|
||||
totlen += bulklen(hi_sdslen(curarg));
|
||||
} else {
|
||||
sdsfree(curarg);
|
||||
hi_sdsfree(curarg);
|
||||
}
|
||||
|
||||
/* Clear curarg because it was put in curargv or was free'd. */
|
||||
@@ -483,22 +492,22 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
totlen += 1+countDigits(argc)+2;
|
||||
|
||||
/* Build the command at protocol level */
|
||||
cmd = malloc(totlen+1);
|
||||
cmd = hi_malloc(totlen+1);
|
||||
if (cmd == NULL) goto memory_err;
|
||||
|
||||
pos = sprintf(cmd,"*%d\r\n",argc);
|
||||
for (j = 0; j < argc; j++) {
|
||||
pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j]));
|
||||
memcpy(cmd+pos,curargv[j],sdslen(curargv[j]));
|
||||
pos += sdslen(curargv[j]);
|
||||
sdsfree(curargv[j]);
|
||||
pos += sprintf(cmd+pos,"$%zu\r\n",hi_sdslen(curargv[j]));
|
||||
memcpy(cmd+pos,curargv[j],hi_sdslen(curargv[j]));
|
||||
pos += hi_sdslen(curargv[j]);
|
||||
hi_sdsfree(curargv[j]);
|
||||
cmd[pos++] = '\r';
|
||||
cmd[pos++] = '\n';
|
||||
}
|
||||
assert(pos == totlen);
|
||||
cmd[pos] = '\0';
|
||||
|
||||
free(curargv);
|
||||
hi_free(curargv);
|
||||
*target = cmd;
|
||||
return totlen;
|
||||
|
||||
@@ -513,12 +522,12 @@ memory_err:
|
||||
cleanup:
|
||||
if (curargv) {
|
||||
while(argc--)
|
||||
sdsfree(curargv[argc]);
|
||||
free(curargv);
|
||||
hi_sdsfree(curargv[argc]);
|
||||
hi_free(curargv);
|
||||
}
|
||||
|
||||
sdsfree(curarg);
|
||||
free(cmd);
|
||||
hi_sdsfree(curarg);
|
||||
hi_free(cmd);
|
||||
|
||||
return error_type;
|
||||
}
|
||||
@@ -550,16 +559,16 @@ int redisFormatCommand(char **target, const char *format, ...) {
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Format a command according to the Redis protocol using an sds string and
|
||||
* sdscatfmt for the processing of arguments. This function takes the
|
||||
/* Format a command according to the Redis protocol using an hisds string and
|
||||
* hi_sdscatfmt for the processing of arguments. This function takes the
|
||||
* number of arguments, an array with arguments and an array with their
|
||||
* lengths. If the latter is set to NULL, strlen will be used to compute the
|
||||
* argument lengths.
|
||||
*/
|
||||
int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,
|
||||
int redisFormatSdsCommandArgv(hisds *target, int argc, const char **argv,
|
||||
const size_t *argvlen)
|
||||
{
|
||||
sds cmd;
|
||||
hisds cmd, aux;
|
||||
unsigned long long totlen;
|
||||
int j;
|
||||
size_t len;
|
||||
@@ -576,32 +585,36 @@ int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,
|
||||
}
|
||||
|
||||
/* Use an SDS string for command construction */
|
||||
cmd = sdsempty();
|
||||
cmd = hi_sdsempty();
|
||||
if (cmd == NULL)
|
||||
return -1;
|
||||
|
||||
/* We already know how much storage we need */
|
||||
cmd = sdsMakeRoomFor(cmd, totlen);
|
||||
if (cmd == NULL)
|
||||
aux = hi_sdsMakeRoomFor(cmd, totlen);
|
||||
if (aux == NULL) {
|
||||
hi_sdsfree(cmd);
|
||||
return -1;
|
||||
|
||||
/* Construct command */
|
||||
cmd = sdscatfmt(cmd, "*%i\r\n", argc);
|
||||
for (j=0; j < argc; j++) {
|
||||
len = argvlen ? argvlen[j] : strlen(argv[j]);
|
||||
cmd = sdscatfmt(cmd, "$%u\r\n", len);
|
||||
cmd = sdscatlen(cmd, argv[j], len);
|
||||
cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1);
|
||||
}
|
||||
|
||||
assert(sdslen(cmd)==totlen);
|
||||
cmd = aux;
|
||||
|
||||
/* Construct command */
|
||||
cmd = hi_sdscatfmt(cmd, "*%i\r\n", argc);
|
||||
for (j=0; j < argc; j++) {
|
||||
len = argvlen ? argvlen[j] : strlen(argv[j]);
|
||||
cmd = hi_sdscatfmt(cmd, "$%u\r\n", len);
|
||||
cmd = hi_sdscatlen(cmd, argv[j], len);
|
||||
cmd = hi_sdscatlen(cmd, "\r\n", sizeof("\r\n")-1);
|
||||
}
|
||||
|
||||
assert(hi_sdslen(cmd)==totlen);
|
||||
|
||||
*target = cmd;
|
||||
return totlen;
|
||||
}
|
||||
|
||||
void redisFreeSdsCommand(sds cmd) {
|
||||
sdsfree(cmd);
|
||||
void redisFreeSdsCommand(hisds cmd) {
|
||||
hi_sdsfree(cmd);
|
||||
}
|
||||
|
||||
/* Format a command according to the Redis protocol. This function takes the
|
||||
@@ -627,7 +640,7 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
|
||||
}
|
||||
|
||||
/* Build the command at protocol level */
|
||||
cmd = malloc(totlen+1);
|
||||
cmd = hi_malloc(totlen+1);
|
||||
if (cmd == NULL)
|
||||
return -1;
|
||||
|
||||
@@ -648,7 +661,7 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
|
||||
}
|
||||
|
||||
void redisFreeCommand(char *cmd) {
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
}
|
||||
|
||||
void __redisSetError(redisContext *c, int type, const char *str) {
|
||||
@@ -671,15 +684,21 @@ redisReader *redisReaderCreate(void) {
|
||||
return redisReaderCreateWithFunctions(&defaultFunctions);
|
||||
}
|
||||
|
||||
static redisContext *redisContextInit(const redisOptions *options) {
|
||||
static void redisPushAutoFree(void *privdata, void *reply) {
|
||||
(void)privdata;
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
static redisContext *redisContextInit(void) {
|
||||
redisContext *c;
|
||||
|
||||
c = calloc(1, sizeof(*c));
|
||||
c = hi_calloc(1, sizeof(*c));
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
c->funcs = &redisContextDefaultFuncs;
|
||||
c->obuf = sdsempty();
|
||||
|
||||
c->obuf = hi_sdsempty();
|
||||
c->reader = redisReaderCreate();
|
||||
c->fd = REDIS_INVALID_FD;
|
||||
|
||||
@@ -687,7 +706,7 @@ static redisContext *redisContextInit(const redisOptions *options) {
|
||||
redisFree(c);
|
||||
return NULL;
|
||||
}
|
||||
(void)options; /* options are used in other functions */
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -696,18 +715,23 @@ void redisFree(redisContext *c) {
|
||||
return;
|
||||
redisNetClose(c);
|
||||
|
||||
sdsfree(c->obuf);
|
||||
hi_sdsfree(c->obuf);
|
||||
redisReaderFree(c->reader);
|
||||
free(c->tcp.host);
|
||||
free(c->tcp.source_addr);
|
||||
free(c->unix_sock.path);
|
||||
free(c->timeout);
|
||||
free(c->saddr);
|
||||
if (c->funcs->free_privdata) {
|
||||
c->funcs->free_privdata(c->privdata);
|
||||
}
|
||||
hi_free(c->tcp.host);
|
||||
hi_free(c->tcp.source_addr);
|
||||
hi_free(c->unix_sock.path);
|
||||
hi_free(c->connect_timeout);
|
||||
hi_free(c->command_timeout);
|
||||
hi_free(c->saddr);
|
||||
|
||||
if (c->privdata && c->free_privdata)
|
||||
c->free_privdata(c->privdata);
|
||||
|
||||
if (c->funcs->free_privctx)
|
||||
c->funcs->free_privctx(c->privctx);
|
||||
|
||||
memset(c, 0xff, sizeof(*c));
|
||||
free(c);
|
||||
hi_free(c);
|
||||
}
|
||||
|
||||
redisFD redisFreeKeepFd(redisContext *c) {
|
||||
@@ -721,35 +745,46 @@ int redisReconnect(redisContext *c) {
|
||||
c->err = 0;
|
||||
memset(c->errstr, '\0', strlen(c->errstr));
|
||||
|
||||
if (c->privdata && c->funcs->free_privdata) {
|
||||
c->funcs->free_privdata(c->privdata);
|
||||
c->privdata = NULL;
|
||||
if (c->privctx && c->funcs->free_privctx) {
|
||||
c->funcs->free_privctx(c->privctx);
|
||||
c->privctx = NULL;
|
||||
}
|
||||
|
||||
redisNetClose(c);
|
||||
|
||||
sdsfree(c->obuf);
|
||||
hi_sdsfree(c->obuf);
|
||||
redisReaderFree(c->reader);
|
||||
|
||||
c->obuf = sdsempty();
|
||||
c->obuf = hi_sdsempty();
|
||||
c->reader = redisReaderCreate();
|
||||
|
||||
if (c->obuf == NULL || c->reader == NULL) {
|
||||
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int ret = REDIS_ERR;
|
||||
if (c->connection_type == REDIS_CONN_TCP) {
|
||||
return redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port,
|
||||
c->timeout, c->tcp.source_addr);
|
||||
ret = redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port,
|
||||
c->connect_timeout, c->tcp.source_addr);
|
||||
} else if (c->connection_type == REDIS_CONN_UNIX) {
|
||||
return redisContextConnectUnix(c, c->unix_sock.path, c->timeout);
|
||||
ret = redisContextConnectUnix(c, c->unix_sock.path, c->connect_timeout);
|
||||
} else {
|
||||
/* Something bad happened here and shouldn't have. There isn't
|
||||
enough information in the context to reconnect. */
|
||||
__redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect");
|
||||
ret = REDIS_ERR;
|
||||
}
|
||||
|
||||
return REDIS_ERR;
|
||||
if (c->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
|
||||
redisContextSetTimeout(c, *c->command_timeout);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
redisContext *redisConnectWithOptions(const redisOptions *options) {
|
||||
redisContext *c = redisContextInit(options);
|
||||
redisContext *c = redisContextInit();
|
||||
if (c == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -760,16 +795,32 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
|
||||
c->flags |= REDIS_REUSEADDR;
|
||||
}
|
||||
if (options->options & REDIS_OPT_NOAUTOFREE) {
|
||||
c->flags |= REDIS_NO_AUTO_FREE;
|
||||
c->flags |= REDIS_NO_AUTO_FREE;
|
||||
}
|
||||
|
||||
/* Set any user supplied RESP3 PUSH handler or use freeReplyObject
|
||||
* as a default unless specifically flagged that we don't want one. */
|
||||
if (options->push_cb != NULL)
|
||||
redisSetPushCallback(c, options->push_cb);
|
||||
else if (!(options->options & REDIS_OPT_NO_PUSH_AUTOFREE))
|
||||
redisSetPushCallback(c, redisPushAutoFree);
|
||||
|
||||
c->privdata = options->privdata;
|
||||
c->free_privdata = options->free_privdata;
|
||||
|
||||
if (redisContextUpdateConnectTimeout(c, options->connect_timeout) != REDIS_OK ||
|
||||
redisContextUpdateCommandTimeout(c, options->command_timeout) != REDIS_OK) {
|
||||
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||
return c;
|
||||
}
|
||||
|
||||
if (options->type == REDIS_CONN_TCP) {
|
||||
redisContextConnectBindTcp(c, options->endpoint.tcp.ip,
|
||||
options->endpoint.tcp.port, options->timeout,
|
||||
options->endpoint.tcp.port, options->connect_timeout,
|
||||
options->endpoint.tcp.source_addr);
|
||||
} else if (options->type == REDIS_CONN_UNIX) {
|
||||
redisContextConnectUnix(c, options->endpoint.unix_socket,
|
||||
options->timeout);
|
||||
options->connect_timeout);
|
||||
} else if (options->type == REDIS_CONN_USERFD) {
|
||||
c->fd = options->endpoint.fd;
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
@@ -777,9 +828,11 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
|
||||
// Unknown type - FIXME - FREE
|
||||
return NULL;
|
||||
}
|
||||
if (options->timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
|
||||
redisContextSetTimeout(c, *options->timeout);
|
||||
|
||||
if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
|
||||
redisContextSetTimeout(c, *options->command_timeout);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -795,7 +848,7 @@ redisContext *redisConnect(const char *ip, int port) {
|
||||
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
||||
options.timeout = &tv;
|
||||
options.connect_timeout = &tv;
|
||||
return redisConnectWithOptions(&options);
|
||||
}
|
||||
|
||||
@@ -833,7 +886,7 @@ redisContext *redisConnectUnix(const char *path) {
|
||||
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_UNIX(&options, path);
|
||||
options.timeout = &tv;
|
||||
options.connect_timeout = &tv;
|
||||
return redisConnectWithOptions(&options);
|
||||
}
|
||||
|
||||
@@ -865,6 +918,13 @@ int redisEnableKeepAlive(redisContext *c) {
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
/* Set a user provided RESP3 PUSH handler and return any old one set. */
|
||||
redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn) {
|
||||
redisPushFn *old = c->push_cb;
|
||||
c->push_cb = fn;
|
||||
return old;
|
||||
}
|
||||
|
||||
/* Use this function to handle a read event on the descriptor. It will try
|
||||
* and read some bytes from the socket and feed them to the reply parser.
|
||||
*
|
||||
@@ -906,21 +966,27 @@ int redisBufferWrite(redisContext *c, int *done) {
|
||||
if (c->err)
|
||||
return REDIS_ERR;
|
||||
|
||||
if (sdslen(c->obuf) > 0) {
|
||||
int nwritten = c->funcs->write(c);
|
||||
if (hi_sdslen(c->obuf) > 0) {
|
||||
ssize_t nwritten = c->funcs->write(c);
|
||||
if (nwritten < 0) {
|
||||
return REDIS_ERR;
|
||||
} else if (nwritten > 0) {
|
||||
if (nwritten == (signed)sdslen(c->obuf)) {
|
||||
sdsfree(c->obuf);
|
||||
c->obuf = sdsempty();
|
||||
if (nwritten == (ssize_t)hi_sdslen(c->obuf)) {
|
||||
hi_sdsfree(c->obuf);
|
||||
c->obuf = hi_sdsempty();
|
||||
if (c->obuf == NULL)
|
||||
goto oom;
|
||||
} else {
|
||||
sdsrange(c->obuf,nwritten,-1);
|
||||
if (hi_sdsrange(c->obuf,nwritten,-1) < 0) goto oom;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (done != NULL) *done = (sdslen(c->obuf) == 0);
|
||||
if (done != NULL) *done = (hi_sdslen(c->obuf) == 0);
|
||||
return REDIS_OK;
|
||||
|
||||
oom:
|
||||
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Internal helper function to try and get a reply from the reader,
|
||||
@@ -930,9 +996,21 @@ int redisGetReplyFromReader(redisContext *c, void **reply) {
|
||||
__redisSetError(c,c->reader->err,c->reader->errstr);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
/* Internal helper that returns 1 if the reply was a RESP3 PUSH
|
||||
* message and we handled it with a user-provided callback. */
|
||||
static int redisHandledPushReply(redisContext *c, void *reply) {
|
||||
if (reply && c->push_cb && redisIsPushReply(reply)) {
|
||||
c->push_cb(c->privdata, reply);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int redisGetReply(redisContext *c, void **reply) {
|
||||
int wdone = 0;
|
||||
void *aux = NULL;
|
||||
@@ -953,13 +1031,23 @@ int redisGetReply(redisContext *c, void **reply) {
|
||||
do {
|
||||
if (redisBufferRead(c) == REDIS_ERR)
|
||||
return REDIS_ERR;
|
||||
if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* We loop here in case the user has specified a RESP3
|
||||
* PUSH handler (e.g. for client tracking). */
|
||||
do {
|
||||
if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
|
||||
return REDIS_ERR;
|
||||
} while (redisHandledPushReply(c, aux));
|
||||
} while (aux == NULL);
|
||||
}
|
||||
|
||||
/* Set reply object */
|
||||
if (reply != NULL) *reply = aux;
|
||||
/* Set reply or free it if we were passed NULL */
|
||||
if (reply != NULL) {
|
||||
*reply = aux;
|
||||
} else {
|
||||
freeReplyObject(aux);
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
@@ -971,9 +1059,9 @@ int redisGetReply(redisContext *c, void **reply) {
|
||||
* the reply (or replies in pub/sub).
|
||||
*/
|
||||
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) {
|
||||
sds newbuf;
|
||||
hisds newbuf;
|
||||
|
||||
newbuf = sdscatlen(c->obuf,cmd,len);
|
||||
newbuf = hi_sdscatlen(c->obuf,cmd,len);
|
||||
if (newbuf == NULL) {
|
||||
__redisSetError(c,REDIS_ERR_OOM,"Out of memory");
|
||||
return REDIS_ERR;
|
||||
@@ -1006,11 +1094,11 @@ int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
|
||||
}
|
||||
|
||||
if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
@@ -1025,7 +1113,7 @@ int redisAppendCommand(redisContext *c, const char *format, ...) {
|
||||
}
|
||||
|
||||
int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
|
||||
sds cmd;
|
||||
hisds cmd;
|
||||
int len;
|
||||
|
||||
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
|
||||
@@ -1035,11 +1123,11 @@ int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const s
|
||||
}
|
||||
|
||||
if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
|
||||
sdsfree(cmd);
|
||||
hi_sdsfree(cmd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
sdsfree(cmd);
|
||||
hi_sdsfree(cmd);
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+53
-16
@@ -39,14 +39,16 @@
|
||||
#include <sys/time.h> /* for struct timeval */
|
||||
#else
|
||||
struct timeval; /* forward declaration */
|
||||
typedef long long ssize_t;
|
||||
#endif
|
||||
#include <stdint.h> /* uintXX_t, etc */
|
||||
#include "sds.h" /* for sds */
|
||||
#include "sds.h" /* for hisds */
|
||||
#include "alloc.h" /* for allocation wrappers */
|
||||
|
||||
#define HIREDIS_MAJOR 0
|
||||
#define HIREDIS_MINOR 14
|
||||
#define HIREDIS_MAJOR 1
|
||||
#define HIREDIS_MINOR 0
|
||||
#define HIREDIS_PATCH 0
|
||||
#define HIREDIS_SONAME 0.14
|
||||
#define HIREDIS_SONAME 1.0.0
|
||||
|
||||
/* Connection type can be blocking or non-blocking and is set in the
|
||||
* least significant bit of the flags field in redisContext. */
|
||||
@@ -90,6 +92,15 @@ struct timeval; /* forward declaration */
|
||||
* SO_REUSEADDR is being used. */
|
||||
#define REDIS_CONNECT_RETRIES 10
|
||||
|
||||
/* Forward declarations for structs defined elsewhere */
|
||||
struct redisAsyncContext;
|
||||
struct redisContext;
|
||||
|
||||
/* RESP3 push helpers and callback prototypes */
|
||||
#define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH)
|
||||
typedef void (redisPushFn)(void *, void *);
|
||||
typedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -101,7 +112,7 @@ typedef struct redisReply {
|
||||
double dval; /* The double when type is REDIS_REPLY_DOUBLE */
|
||||
size_t len; /* Length of string */
|
||||
char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING
|
||||
and REDIS_REPLY_DOUBLE (in additionl to dval). */
|
||||
REDIS_REPLY_VERB, and REDIS_REPLY_DOUBLE (in additional to dval). */
|
||||
char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null
|
||||
terminated 3 character content type, such as "txt". */
|
||||
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
|
||||
@@ -117,9 +128,9 @@ void freeReplyObject(void *reply);
|
||||
int redisvFormatCommand(char **target, const char *format, va_list ap);
|
||||
int redisFormatCommand(char **target, const char *format, ...);
|
||||
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
|
||||
int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
|
||||
int redisFormatSdsCommandArgv(hisds *target, int argc, const char ** argv, const size_t *argvlen);
|
||||
void redisFreeCommand(char *cmd);
|
||||
void redisFreeSdsCommand(sds cmd);
|
||||
void redisFreeSdsCommand(hisds cmd);
|
||||
|
||||
enum redisConnectionType {
|
||||
REDIS_CONN_TCP,
|
||||
@@ -138,6 +149,9 @@ struct redisSsl;
|
||||
*/
|
||||
#define REDIS_OPT_NOAUTOFREE 0x04
|
||||
|
||||
/* Don't automatically intercept and free RESP3 PUSH replies. */
|
||||
#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08
|
||||
|
||||
/* In Unix systems a file descriptor is a regular signed int, with -1
|
||||
* representing an invalid descriptor. In Windows it is a SOCKET
|
||||
* (32- or 64-bit unsigned integer depending on the architecture), where
|
||||
@@ -162,8 +176,11 @@ typedef struct {
|
||||
int type;
|
||||
/* bit field of REDIS_OPT_xxx */
|
||||
int options;
|
||||
/* timeout value. if NULL, no timeout is used */
|
||||
const struct timeval *timeout;
|
||||
/* timeout value for connect operation. If NULL, no timeout is used */
|
||||
const struct timeval *connect_timeout;
|
||||
/* timeout value for commands. If NULL, no timeout is used. This can be
|
||||
* updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */
|
||||
const struct timeval *command_timeout;
|
||||
union {
|
||||
/** use this field for tcp/ip connections */
|
||||
struct {
|
||||
@@ -178,6 +195,14 @@ typedef struct {
|
||||
* file descriptor */
|
||||
redisFD fd;
|
||||
} endpoint;
|
||||
|
||||
/* Optional user defined data/destructor */
|
||||
void *privdata;
|
||||
void (*free_privdata)(void *);
|
||||
|
||||
/* A user defined PUSH message callback */
|
||||
redisPushFn *push_cb;
|
||||
redisAsyncPushFn *async_push_cb;
|
||||
} redisOptions;
|
||||
|
||||
/**
|
||||
@@ -192,15 +217,16 @@ typedef struct {
|
||||
(opts)->type = REDIS_CONN_UNIX; \
|
||||
(opts)->endpoint.unix_socket = path;
|
||||
|
||||
struct redisAsyncContext;
|
||||
struct redisContext;
|
||||
#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \
|
||||
(opts)->privdata = data; \
|
||||
(opts)->free_privdata = dtor; \
|
||||
|
||||
typedef struct redisContextFuncs {
|
||||
void (*free_privdata)(void *);
|
||||
void (*free_privctx)(void *);
|
||||
void (*async_read)(struct redisAsyncContext *);
|
||||
void (*async_write)(struct redisAsyncContext *);
|
||||
int (*read)(struct redisContext *, char *, size_t);
|
||||
int (*write)(struct redisContext *);
|
||||
ssize_t (*read)(struct redisContext *, char *, size_t);
|
||||
ssize_t (*write)(struct redisContext *);
|
||||
} redisContextFuncs;
|
||||
|
||||
/* Context for a connection to Redis */
|
||||
@@ -215,7 +241,8 @@ typedef struct redisContext {
|
||||
redisReader *reader; /* Protocol reader */
|
||||
|
||||
enum redisConnectionType connection_type;
|
||||
struct timeval *timeout;
|
||||
struct timeval *connect_timeout;
|
||||
struct timeval *command_timeout;
|
||||
|
||||
struct {
|
||||
char *host;
|
||||
@@ -231,8 +258,17 @@ typedef struct redisContext {
|
||||
struct sockadr *saddr;
|
||||
size_t addrlen;
|
||||
|
||||
/* Additional private data for hiredis addons such as SSL */
|
||||
/* Optional data and corresponding destructor users can use to provide
|
||||
* context to a given redisContext. Not used by hiredis. */
|
||||
void *privdata;
|
||||
void (*free_privdata)(void *);
|
||||
|
||||
/* Internal context pointer presently used by hiredis to manage
|
||||
* SSL connections. */
|
||||
void *privctx;
|
||||
|
||||
/* An optional RESP3 PUSH handler */
|
||||
redisPushFn *push_cb;
|
||||
} redisContext;
|
||||
|
||||
redisContext *redisConnectWithOptions(const redisOptions *options);
|
||||
@@ -259,6 +295,7 @@ redisContext *redisConnectFd(redisFD fd);
|
||||
*/
|
||||
int redisReconnect(redisContext *c);
|
||||
|
||||
redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn);
|
||||
int redisSetTimeout(redisContext *c, const struct timeval tv);
|
||||
int redisEnableKeepAlive(redisContext *c);
|
||||
void redisFree(redisContext *c);
|
||||
|
||||
Vendored
+2
-1
@@ -1,6 +1,7 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
install_libdir=@CMAKE_INSTALL_LIBDIR@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/lib
|
||||
libdir=${exec_prefix}/${install_libdir}
|
||||
includedir=${prefix}/include
|
||||
pkgincludedir=${includedir}/hiredis
|
||||
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set_and_check(hiredis_ssl_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
|
||||
|
||||
IF (NOT TARGET hiredis::hiredis_ssl)
|
||||
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis_ssl-targets.cmake)
|
||||
ENDIF()
|
||||
|
||||
SET(hiredis_ssl_LIBRARIES hiredis::hiredis_ssl)
|
||||
SET(hiredis_ssl_INCLUDE_DIRS ${hiredis_ssl_INCLUDEDIR})
|
||||
|
||||
check_required_components(hiredis_ssl)
|
||||
|
||||
Vendored
+80
-6
@@ -32,22 +32,96 @@
|
||||
#ifndef __HIREDIS_SSL_H
|
||||
#define __HIREDIS_SSL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This is the underlying struct for SSL in ssl.h, which is not included to
|
||||
* keep build dependencies short here.
|
||||
*/
|
||||
struct ssl_st;
|
||||
|
||||
/**
|
||||
* Secure the connection using SSL. This should be done before any command is
|
||||
* executed on the connection.
|
||||
/* A wrapper around OpenSSL SSL_CTX to allow easy SSL use without directly
|
||||
* calling OpenSSL.
|
||||
*/
|
||||
int redisSecureConnection(redisContext *c, const char *capath, const char *certpath,
|
||||
const char *keypath, const char *servername);
|
||||
typedef struct redisSSLContext redisSSLContext;
|
||||
|
||||
/**
|
||||
* Initiate SSL/TLS negotiation on a provided context.
|
||||
* Initialization errors that redisCreateSSLContext() may return.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
REDIS_SSL_CTX_NONE = 0, /* No Error */
|
||||
REDIS_SSL_CTX_CREATE_FAILED, /* Failed to create OpenSSL SSL_CTX */
|
||||
REDIS_SSL_CTX_CERT_KEY_REQUIRED, /* Client cert and key must both be specified or skipped */
|
||||
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED, /* Failed to load CA Certificate or CA Path */
|
||||
REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED, /* Failed to load client certificate */
|
||||
REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED /* Failed to load private key */
|
||||
} redisSSLContextError;
|
||||
|
||||
/**
|
||||
* Return the error message corresponding with the specified error code.
|
||||
*/
|
||||
|
||||
const char *redisSSLContextGetError(redisSSLContextError error);
|
||||
|
||||
/**
|
||||
* Helper function to initialize the OpenSSL library.
|
||||
*
|
||||
* OpenSSL requires one-time initialization before it can be used. Callers should
|
||||
* call this function only once, and only if OpenSSL is not directly initialized
|
||||
* elsewhere.
|
||||
*/
|
||||
int redisInitOpenSSL(void);
|
||||
|
||||
/**
|
||||
* Helper function to initialize an OpenSSL context that can be used
|
||||
* to initiate SSL connections.
|
||||
*
|
||||
* cacert_filename is an optional name of a CA certificate/bundle file to load
|
||||
* and use for validation.
|
||||
*
|
||||
* capath is an optional directory path where trusted CA certificate files are
|
||||
* stored in an OpenSSL-compatible structure.
|
||||
*
|
||||
* cert_filename and private_key_filename are optional names of a client side
|
||||
* certificate and private key files to use for authentication. They need to
|
||||
* be both specified or omitted.
|
||||
*
|
||||
* server_name is an optional and will be used as a server name indication
|
||||
* (SNI) TLS extension.
|
||||
*
|
||||
* If error is non-null, it will be populated in case the context creation fails
|
||||
* (returning a NULL).
|
||||
*/
|
||||
|
||||
redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath,
|
||||
const char *cert_filename, const char *private_key_filename,
|
||||
const char *server_name, redisSSLContextError *error);
|
||||
|
||||
/**
|
||||
* Free a previously created OpenSSL context.
|
||||
*/
|
||||
void redisFreeSSLContext(redisSSLContext *redis_ssl_ctx);
|
||||
|
||||
/**
|
||||
* Initiate SSL on an existing redisContext.
|
||||
*
|
||||
* This is similar to redisInitiateSSL() but does not require the caller
|
||||
* to directly interact with OpenSSL, and instead uses a redisSSLContext
|
||||
* previously created using redisCreateSSLContext().
|
||||
*/
|
||||
|
||||
int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx);
|
||||
|
||||
/**
|
||||
* Initiate SSL/TLS negotiation on a provided OpenSSL SSL object.
|
||||
*/
|
||||
|
||||
int redisInitiateSSL(redisContext *c, struct ssl_st *ssl);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __HIREDIS_SSL_H */
|
||||
|
||||
Vendored
+80
-39
@@ -57,8 +57,8 @@ void redisNetClose(redisContext *c) {
|
||||
}
|
||||
}
|
||||
|
||||
int redisNetRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
int nread = recv(c->fd, buf, bufcap, 0);
|
||||
ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
ssize_t nread = recv(c->fd, buf, bufcap, 0);
|
||||
if (nread == -1) {
|
||||
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
||||
/* Try again later */
|
||||
@@ -79,8 +79,8 @@ int redisNetRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
}
|
||||
}
|
||||
|
||||
int redisNetWrite(redisContext *c) {
|
||||
int nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0);
|
||||
ssize_t redisNetWrite(redisContext *c) {
|
||||
ssize_t nwritten = send(c->fd, c->obuf, hi_sdslen(c->obuf), 0);
|
||||
if (nwritten < 0) {
|
||||
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
||||
/* Try again later */
|
||||
@@ -203,7 +203,7 @@ int redisKeepAlive(redisContext *c, int interval) {
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int redisSetTcpNoDelay(redisContext *c) {
|
||||
int redisSetTcpNoDelay(redisContext *c) {
|
||||
int yes = 1;
|
||||
if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
|
||||
@@ -217,7 +217,7 @@ static int redisSetTcpNoDelay(redisContext *c) {
|
||||
|
||||
static int redisContextTimeoutMsec(redisContext *c, long *result)
|
||||
{
|
||||
const struct timeval *timeout = c->timeout;
|
||||
const struct timeval *timeout = c->connect_timeout;
|
||||
long msec = -1;
|
||||
|
||||
/* Only use timeout when not NULL. */
|
||||
@@ -316,11 +316,7 @@ int redisCheckSocketError(redisContext *c) {
|
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
|
||||
const void *to_ptr = &tv;
|
||||
size_t to_sz = sizeof(tv);
|
||||
#ifdef _WIN32
|
||||
DWORD timeout_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
to_ptr = &timeout_msec;
|
||||
to_sz = sizeof(timeout_msec);
|
||||
#endif
|
||||
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
|
||||
return REDIS_ERR;
|
||||
@@ -332,6 +328,38 @@ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout) {
|
||||
/* Same timeval struct, short circuit */
|
||||
if (c->connect_timeout == timeout)
|
||||
return REDIS_OK;
|
||||
|
||||
/* Allocate context timeval if we need to */
|
||||
if (c->connect_timeout == NULL) {
|
||||
c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout));
|
||||
if (c->connect_timeout == NULL)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout));
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout) {
|
||||
/* Same timeval struct, short circuit */
|
||||
if (c->command_timeout == timeout)
|
||||
return REDIS_OK;
|
||||
|
||||
/* Allocate context timeval if we need to */
|
||||
if (c->command_timeout == NULL) {
|
||||
c->command_timeout = hi_malloc(sizeof(*c->command_timeout));
|
||||
if (c->command_timeout == NULL)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout));
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
||||
const struct timeval *timeout,
|
||||
const char *source_addr) {
|
||||
@@ -356,21 +384,19 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
||||
* This is a bit ugly, but atleast it works and doesn't leak memory.
|
||||
**/
|
||||
if (c->tcp.host != addr) {
|
||||
free(c->tcp.host);
|
||||
hi_free(c->tcp.host);
|
||||
|
||||
c->tcp.host = strdup(addr);
|
||||
c->tcp.host = hi_strdup(addr);
|
||||
if (c->tcp.host == NULL)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (c->timeout != timeout) {
|
||||
if (c->timeout == NULL)
|
||||
c->timeout = malloc(sizeof(struct timeval));
|
||||
|
||||
memcpy(c->timeout, timeout, sizeof(struct timeval));
|
||||
}
|
||||
if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
|
||||
goto oom;
|
||||
} else {
|
||||
free(c->timeout);
|
||||
c->timeout = NULL;
|
||||
hi_free(c->connect_timeout);
|
||||
c->connect_timeout = NULL;
|
||||
}
|
||||
|
||||
if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
|
||||
@@ -379,11 +405,11 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
||||
}
|
||||
|
||||
if (source_addr == NULL) {
|
||||
free(c->tcp.source_addr);
|
||||
hi_free(c->tcp.source_addr);
|
||||
c->tcp.source_addr = NULL;
|
||||
} else if (c->tcp.source_addr != source_addr) {
|
||||
free(c->tcp.source_addr);
|
||||
c->tcp.source_addr = strdup(source_addr);
|
||||
hi_free(c->tcp.source_addr);
|
||||
c->tcp.source_addr = hi_strdup(source_addr);
|
||||
}
|
||||
|
||||
snprintf(_port, 6, "%d", port);
|
||||
@@ -446,8 +472,11 @@ addrretry:
|
||||
}
|
||||
|
||||
/* For repeat connection */
|
||||
free(c->saddr);
|
||||
c->saddr = malloc(p->ai_addrlen);
|
||||
hi_free(c->saddr);
|
||||
c->saddr = hi_malloc(p->ai_addrlen);
|
||||
if (c->saddr == NULL)
|
||||
goto oom;
|
||||
|
||||
memcpy(c->saddr, p->ai_addr, p->ai_addrlen);
|
||||
c->addrlen = p->ai_addrlen;
|
||||
|
||||
@@ -474,12 +503,12 @@ addrretry:
|
||||
wait_for_ready:
|
||||
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
|
||||
goto error;
|
||||
if (redisSetTcpNoDelay(c) != REDIS_OK)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (blocking && redisSetBlocking(c,1) != REDIS_OK)
|
||||
goto error;
|
||||
if (redisSetTcpNoDelay(c) != REDIS_OK)
|
||||
goto error;
|
||||
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
rv = REDIS_OK;
|
||||
@@ -492,6 +521,8 @@ addrretry:
|
||||
goto error;
|
||||
}
|
||||
|
||||
oom:
|
||||
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||
error:
|
||||
rv = REDIS_ERR;
|
||||
end:
|
||||
@@ -525,25 +556,32 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
|
||||
return REDIS_ERR;
|
||||
|
||||
c->connection_type = REDIS_CONN_UNIX;
|
||||
if (c->unix_sock.path != path)
|
||||
c->unix_sock.path = strdup(path);
|
||||
if (c->unix_sock.path != path) {
|
||||
hi_free(c->unix_sock.path);
|
||||
|
||||
c->unix_sock.path = hi_strdup(path);
|
||||
if (c->unix_sock.path == NULL)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (c->timeout != timeout) {
|
||||
if (c->timeout == NULL)
|
||||
c->timeout = malloc(sizeof(struct timeval));
|
||||
|
||||
memcpy(c->timeout, timeout, sizeof(struct timeval));
|
||||
}
|
||||
if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
|
||||
goto oom;
|
||||
} else {
|
||||
free(c->timeout);
|
||||
c->timeout = NULL;
|
||||
hi_free(c->connect_timeout);
|
||||
c->connect_timeout = NULL;
|
||||
}
|
||||
|
||||
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
sa = (struct sockaddr_un*)(c->saddr = malloc(sizeof(struct sockaddr_un)));
|
||||
/* Don't leak sockaddr if we're reconnecting */
|
||||
if (c->saddr) hi_free(c->saddr);
|
||||
|
||||
sa = (struct sockaddr_un*)(c->saddr = hi_malloc(sizeof(struct sockaddr_un)));
|
||||
if (sa == NULL)
|
||||
goto oom;
|
||||
|
||||
c->addrlen = sizeof(struct sockaddr_un);
|
||||
sa->sun_family = AF_UNIX;
|
||||
strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1);
|
||||
@@ -568,4 +606,7 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
|
||||
errno = EPROTONOSUPPORT;
|
||||
return REDIS_ERR;
|
||||
#endif /* _WIN32 */
|
||||
oom:
|
||||
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
Vendored
+4
-2
@@ -38,8 +38,8 @@
|
||||
#include "hiredis.h"
|
||||
|
||||
void redisNetClose(redisContext *c);
|
||||
int redisNetRead(redisContext *c, char *buf, size_t bufcap);
|
||||
int redisNetWrite(redisContext *c);
|
||||
ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap);
|
||||
ssize_t redisNetWrite(redisContext *c);
|
||||
|
||||
int redisCheckSocketError(redisContext *c);
|
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv);
|
||||
@@ -51,4 +51,6 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
|
||||
int redisKeepAlive(redisContext *c, int interval);
|
||||
int redisCheckConnectDone(redisContext *c, int *completed);
|
||||
|
||||
int redisSetTcpNoDelay(redisContext *c);
|
||||
|
||||
#endif
|
||||
|
||||
Vendored
+108
-50
@@ -42,10 +42,14 @@
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "alloc.h"
|
||||
#include "read.h"
|
||||
#include "sds.h"
|
||||
#include "win32.h"
|
||||
|
||||
/* Initial size of our nested reply stack and how much we grow it when needd */
|
||||
#define REDIS_READER_STACK_SIZE 9
|
||||
|
||||
static void __redisReaderSetError(redisReader *r, int type, const char *str) {
|
||||
size_t len;
|
||||
|
||||
@@ -55,7 +59,7 @@ static void __redisReaderSetError(redisReader *r, int type, const char *str) {
|
||||
}
|
||||
|
||||
/* Clear input buffer on errors. */
|
||||
sdsfree(r->buf);
|
||||
hi_sdsfree(r->buf);
|
||||
r->buf = NULL;
|
||||
r->pos = r->len = 0;
|
||||
|
||||
@@ -243,11 +247,12 @@ static void moveToNextTask(redisReader *r) {
|
||||
return;
|
||||
}
|
||||
|
||||
cur = &(r->rstack[r->ridx]);
|
||||
prv = &(r->rstack[r->ridx-1]);
|
||||
cur = r->task[r->ridx];
|
||||
prv = r->task[r->ridx-1];
|
||||
assert(prv->type == REDIS_REPLY_ARRAY ||
|
||||
prv->type == REDIS_REPLY_MAP ||
|
||||
prv->type == REDIS_REPLY_SET);
|
||||
prv->type == REDIS_REPLY_SET ||
|
||||
prv->type == REDIS_REPLY_PUSH);
|
||||
if (cur->idx == prv->elements-1) {
|
||||
r->ridx--;
|
||||
} else {
|
||||
@@ -262,7 +267,7 @@ static void moveToNextTask(redisReader *r) {
|
||||
}
|
||||
|
||||
static int processLineItem(redisReader *r) {
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
redisReadTask *cur = r->task[r->ridx];
|
||||
void *obj;
|
||||
char *p;
|
||||
int len;
|
||||
@@ -297,7 +302,7 @@ static int processLineItem(redisReader *r) {
|
||||
if (strcasecmp(buf,",inf") == 0) {
|
||||
d = INFINITY; /* Positive infinite. */
|
||||
} else if (strcasecmp(buf,",-inf") == 0) {
|
||||
d = -INFINITY; /* Nevative infinite. */
|
||||
d = -INFINITY; /* Negative infinite. */
|
||||
} else {
|
||||
d = strtod((char*)buf,&eptr);
|
||||
if (buf[0] == '\0' || eptr[0] != '\0' || isnan(d)) {
|
||||
@@ -344,7 +349,7 @@ static int processLineItem(redisReader *r) {
|
||||
}
|
||||
|
||||
static int processBulkItem(redisReader *r) {
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
redisReadTask *cur = r->task[r->ridx];
|
||||
void *obj = NULL;
|
||||
char *p, *s;
|
||||
long long len;
|
||||
@@ -415,19 +420,43 @@ static int processBulkItem(redisReader *r) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static int redisReaderGrow(redisReader *r) {
|
||||
redisReadTask **aux;
|
||||
int newlen;
|
||||
|
||||
/* Grow our stack size */
|
||||
newlen = r->tasks + REDIS_READER_STACK_SIZE;
|
||||
aux = hi_realloc(r->task, sizeof(*r->task) * newlen);
|
||||
if (aux == NULL)
|
||||
goto oom;
|
||||
|
||||
r->task = aux;
|
||||
|
||||
/* Allocate new tasks */
|
||||
for (; r->tasks < newlen; r->tasks++) {
|
||||
r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));
|
||||
if (r->task[r->tasks] == NULL)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
oom:
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Process the array, map and set types. */
|
||||
static int processAggregateItem(redisReader *r) {
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
redisReadTask *cur = r->task[r->ridx];
|
||||
void *obj;
|
||||
char *p;
|
||||
long long elements;
|
||||
int root = 0, len;
|
||||
|
||||
/* Set error for nested multi bulks with depth > 7 */
|
||||
if (r->ridx == 8) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"No support for nested multi bulk replies with depth > 7");
|
||||
return REDIS_ERR;
|
||||
if (r->ridx == r->tasks - 1) {
|
||||
if (redisReaderGrow(r) == REDIS_ERR)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if ((p = readLine(r,&len)) != NULL) {
|
||||
@@ -439,7 +468,9 @@ static int processAggregateItem(redisReader *r) {
|
||||
|
||||
root = (r->ridx == 0);
|
||||
|
||||
if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX)) {
|
||||
if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) ||
|
||||
(r->maxelements > 0 && elements > r->maxelements))
|
||||
{
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Multi-bulk length out of range");
|
||||
return REDIS_ERR;
|
||||
@@ -475,12 +506,12 @@ static int processAggregateItem(redisReader *r) {
|
||||
cur->elements = elements;
|
||||
cur->obj = obj;
|
||||
r->ridx++;
|
||||
r->rstack[r->ridx].type = -1;
|
||||
r->rstack[r->ridx].elements = -1;
|
||||
r->rstack[r->ridx].idx = 0;
|
||||
r->rstack[r->ridx].obj = NULL;
|
||||
r->rstack[r->ridx].parent = cur;
|
||||
r->rstack[r->ridx].privdata = r->privdata;
|
||||
r->task[r->ridx]->type = -1;
|
||||
r->task[r->ridx]->elements = -1;
|
||||
r->task[r->ridx]->idx = 0;
|
||||
r->task[r->ridx]->obj = NULL;
|
||||
r->task[r->ridx]->parent = cur;
|
||||
r->task[r->ridx]->privdata = r->privdata;
|
||||
} else {
|
||||
moveToNextTask(r);
|
||||
}
|
||||
@@ -495,7 +526,7 @@ static int processAggregateItem(redisReader *r) {
|
||||
}
|
||||
|
||||
static int processItem(redisReader *r) {
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
redisReadTask *cur = r->task[r->ridx];
|
||||
char *p;
|
||||
|
||||
/* check if we need to read type */
|
||||
@@ -535,6 +566,9 @@ static int processItem(redisReader *r) {
|
||||
case '=':
|
||||
cur->type = REDIS_REPLY_VERB;
|
||||
break;
|
||||
case '>':
|
||||
cur->type = REDIS_REPLY_PUSH;
|
||||
break;
|
||||
default:
|
||||
__redisReaderSetErrorProtocolByte(r,*p);
|
||||
return REDIS_ERR;
|
||||
@@ -560,6 +594,7 @@ static int processItem(redisReader *r) {
|
||||
case REDIS_REPLY_ARRAY:
|
||||
case REDIS_REPLY_MAP:
|
||||
case REDIS_REPLY_SET:
|
||||
case REDIS_REPLY_PUSH:
|
||||
return processAggregateItem(r);
|
||||
default:
|
||||
assert(NULL);
|
||||
@@ -570,33 +605,57 @@ static int processItem(redisReader *r) {
|
||||
redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
|
||||
redisReader *r;
|
||||
|
||||
r = calloc(1,sizeof(redisReader));
|
||||
r = hi_calloc(1,sizeof(redisReader));
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
|
||||
r->fn = fn;
|
||||
r->buf = sdsempty();
|
||||
r->maxbuf = REDIS_READER_MAX_BUF;
|
||||
if (r->buf == NULL) {
|
||||
free(r);
|
||||
return NULL;
|
||||
r->buf = hi_sdsempty();
|
||||
if (r->buf == NULL)
|
||||
goto oom;
|
||||
|
||||
r->task = hi_calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task));
|
||||
if (r->task == NULL)
|
||||
goto oom;
|
||||
|
||||
for (; r->tasks < REDIS_READER_STACK_SIZE; r->tasks++) {
|
||||
r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));
|
||||
if (r->task[r->tasks] == NULL)
|
||||
goto oom;
|
||||
}
|
||||
|
||||
r->fn = fn;
|
||||
r->maxbuf = REDIS_READER_MAX_BUF;
|
||||
r->maxelements = REDIS_READER_MAX_ARRAY_ELEMENTS;
|
||||
r->ridx = -1;
|
||||
|
||||
return r;
|
||||
oom:
|
||||
redisReaderFree(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void redisReaderFree(redisReader *r) {
|
||||
if (r == NULL)
|
||||
return;
|
||||
|
||||
if (r->reply != NULL && r->fn && r->fn->freeObject)
|
||||
r->fn->freeObject(r->reply);
|
||||
sdsfree(r->buf);
|
||||
free(r);
|
||||
|
||||
if (r->task) {
|
||||
/* We know r->task[i] is allocated if i < r->tasks */
|
||||
for (int i = 0; i < r->tasks; i++) {
|
||||
hi_free(r->task[i]);
|
||||
}
|
||||
|
||||
hi_free(r->task);
|
||||
}
|
||||
|
||||
hi_sdsfree(r->buf);
|
||||
hi_free(r);
|
||||
}
|
||||
|
||||
int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
|
||||
sds newbuf;
|
||||
hisds newbuf;
|
||||
|
||||
/* Return early when this reader is in an erroneous state. */
|
||||
if (r->err)
|
||||
@@ -605,26 +664,25 @@ int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
|
||||
/* Copy the provided buffer. */
|
||||
if (buf != NULL && len >= 1) {
|
||||
/* Destroy internal buffer when it is empty and is quite large. */
|
||||
if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {
|
||||
sdsfree(r->buf);
|
||||
r->buf = sdsempty();
|
||||
if (r->len == 0 && r->maxbuf != 0 && hi_sdsavail(r->buf) > r->maxbuf) {
|
||||
hi_sdsfree(r->buf);
|
||||
r->buf = hi_sdsempty();
|
||||
if (r->buf == 0) goto oom;
|
||||
|
||||
r->pos = 0;
|
||||
|
||||
/* r->buf should not be NULL since we just free'd a larger one. */
|
||||
assert(r->buf != NULL);
|
||||
}
|
||||
|
||||
newbuf = sdscatlen(r->buf,buf,len);
|
||||
if (newbuf == NULL) {
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
newbuf = hi_sdscatlen(r->buf,buf,len);
|
||||
if (newbuf == NULL) goto oom;
|
||||
|
||||
r->buf = newbuf;
|
||||
r->len = sdslen(r->buf);
|
||||
r->len = hi_sdslen(r->buf);
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
oom:
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int redisReaderGetReply(redisReader *r, void **reply) {
|
||||
@@ -642,12 +700,12 @@ int redisReaderGetReply(redisReader *r, void **reply) {
|
||||
|
||||
/* Set first item to process when the stack is empty. */
|
||||
if (r->ridx == -1) {
|
||||
r->rstack[0].type = -1;
|
||||
r->rstack[0].elements = -1;
|
||||
r->rstack[0].idx = -1;
|
||||
r->rstack[0].obj = NULL;
|
||||
r->rstack[0].parent = NULL;
|
||||
r->rstack[0].privdata = r->privdata;
|
||||
r->task[0]->type = -1;
|
||||
r->task[0]->elements = -1;
|
||||
r->task[0]->idx = -1;
|
||||
r->task[0]->obj = NULL;
|
||||
r->task[0]->parent = NULL;
|
||||
r->task[0]->privdata = r->privdata;
|
||||
r->ridx = 0;
|
||||
}
|
||||
|
||||
@@ -663,9 +721,9 @@ int redisReaderGetReply(redisReader *r, void **reply) {
|
||||
/* Discard part of the buffer when we've consumed at least 1k, to avoid
|
||||
* doing unnecessary calls to memmove() in sds.c. */
|
||||
if (r->pos >= 1024) {
|
||||
sdsrange(r->buf,r->pos,-1);
|
||||
if (hi_sdsrange(r->buf,r->pos,-1) < 0) return REDIS_ERR;
|
||||
r->pos = 0;
|
||||
r->len = sdslen(r->buf);
|
||||
r->len = hi_sdslen(r->buf);
|
||||
}
|
||||
|
||||
/* Emit a reply when there is one. */
|
||||
|
||||
Vendored
+10
-3
@@ -63,7 +63,11 @@
|
||||
#define REDIS_REPLY_BIGNUM 13
|
||||
#define REDIS_REPLY_VERB 14
|
||||
|
||||
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
|
||||
/* Default max unused reader buffer. */
|
||||
#define REDIS_READER_MAX_BUF (1024*16)
|
||||
|
||||
/* Default multi-bulk element limit */
|
||||
#define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -71,7 +75,7 @@ extern "C" {
|
||||
|
||||
typedef struct redisReadTask {
|
||||
int type;
|
||||
int elements; /* number of elements in multibulk container */
|
||||
long long elements; /* number of elements in multibulk container */
|
||||
int idx; /* index in parent (array) object */
|
||||
void *obj; /* holds user-generated value for a read task */
|
||||
struct redisReadTask *parent; /* parent task */
|
||||
@@ -96,8 +100,11 @@ typedef struct redisReader {
|
||||
size_t pos; /* Buffer cursor */
|
||||
size_t len; /* Buffer length */
|
||||
size_t maxbuf; /* Max length of unused buffer */
|
||||
long long maxelements; /* Max multi-bulk elements */
|
||||
|
||||
redisReadTask **task;
|
||||
int tasks;
|
||||
|
||||
redisReadTask rstack[9];
|
||||
int ridx; /* Index of current read task */
|
||||
void *reply; /* Temporary reply pointer */
|
||||
|
||||
|
||||
Vendored
+413
-415
File diff suppressed because it is too large
Load Diff
Vendored
+133
-131
@@ -30,247 +30,249 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __SDS_H
|
||||
#define __SDS_H
|
||||
#ifndef HIREDIS_SDS_H
|
||||
#define HIREDIS_SDS_H
|
||||
|
||||
#define SDS_MAX_PREALLOC (1024*1024)
|
||||
#define HI_SDS_MAX_PREALLOC (1024*1024)
|
||||
#ifdef _MSC_VER
|
||||
#define __attribute__(x)
|
||||
typedef long long ssize_t;
|
||||
#define SSIZE_MAX (LLONG_MAX >> 1)
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef char *sds;
|
||||
typedef char *hisds;
|
||||
|
||||
/* Note: sdshdr5 is never used, we just access the flags byte directly.
|
||||
* However is here to document the layout of type 5 SDS strings. */
|
||||
struct __attribute__ ((__packed__)) sdshdr5 {
|
||||
struct __attribute__ ((__packed__)) hisdshdr5 {
|
||||
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr8 {
|
||||
struct __attribute__ ((__packed__)) hisdshdr8 {
|
||||
uint8_t len; /* used */
|
||||
uint8_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr16 {
|
||||
struct __attribute__ ((__packed__)) hisdshdr16 {
|
||||
uint16_t len; /* used */
|
||||
uint16_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr32 {
|
||||
struct __attribute__ ((__packed__)) hisdshdr32 {
|
||||
uint32_t len; /* used */
|
||||
uint32_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr64 {
|
||||
struct __attribute__ ((__packed__)) hisdshdr64 {
|
||||
uint64_t len; /* used */
|
||||
uint64_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
char buf[];
|
||||
};
|
||||
|
||||
#define SDS_TYPE_5 0
|
||||
#define SDS_TYPE_8 1
|
||||
#define SDS_TYPE_16 2
|
||||
#define SDS_TYPE_32 3
|
||||
#define SDS_TYPE_64 4
|
||||
#define SDS_TYPE_MASK 7
|
||||
#define SDS_TYPE_BITS 3
|
||||
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));
|
||||
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
|
||||
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
|
||||
#define HI_SDS_TYPE_5 0
|
||||
#define HI_SDS_TYPE_8 1
|
||||
#define HI_SDS_TYPE_16 2
|
||||
#define HI_SDS_TYPE_32 3
|
||||
#define HI_SDS_TYPE_64 4
|
||||
#define HI_SDS_TYPE_MASK 7
|
||||
#define HI_SDS_TYPE_BITS 3
|
||||
#define HI_SDS_HDR_VAR(T,s) struct hisdshdr##T *sh = (struct hisdshdr##T *)((s)-(sizeof(struct hisdshdr##T)));
|
||||
#define HI_SDS_HDR(T,s) ((struct hisdshdr##T *)((s)-(sizeof(struct hisdshdr##T))))
|
||||
#define HI_SDS_TYPE_5_LEN(f) ((f)>>HI_SDS_TYPE_BITS)
|
||||
|
||||
static inline size_t sdslen(const sds s) {
|
||||
static inline size_t hi_sdslen(const hisds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
return SDS_TYPE_5_LEN(flags);
|
||||
case SDS_TYPE_8:
|
||||
return SDS_HDR(8,s)->len;
|
||||
case SDS_TYPE_16:
|
||||
return SDS_HDR(16,s)->len;
|
||||
case SDS_TYPE_32:
|
||||
return SDS_HDR(32,s)->len;
|
||||
case SDS_TYPE_64:
|
||||
return SDS_HDR(64,s)->len;
|
||||
switch(flags & HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
return HI_SDS_TYPE_5_LEN(flags);
|
||||
case HI_SDS_TYPE_8:
|
||||
return HI_SDS_HDR(8,s)->len;
|
||||
case HI_SDS_TYPE_16:
|
||||
return HI_SDS_HDR(16,s)->len;
|
||||
case HI_SDS_TYPE_32:
|
||||
return HI_SDS_HDR(32,s)->len;
|
||||
case HI_SDS_TYPE_64:
|
||||
return HI_SDS_HDR(64,s)->len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline size_t sdsavail(const sds s) {
|
||||
static inline size_t hi_sdsavail(const hisds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5: {
|
||||
switch(flags&HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5: {
|
||||
return 0;
|
||||
}
|
||||
case SDS_TYPE_8: {
|
||||
SDS_HDR_VAR(8,s);
|
||||
case HI_SDS_TYPE_8: {
|
||||
HI_SDS_HDR_VAR(8,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case SDS_TYPE_16: {
|
||||
SDS_HDR_VAR(16,s);
|
||||
case HI_SDS_TYPE_16: {
|
||||
HI_SDS_HDR_VAR(16,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case SDS_TYPE_32: {
|
||||
SDS_HDR_VAR(32,s);
|
||||
case HI_SDS_TYPE_32: {
|
||||
HI_SDS_HDR_VAR(32,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case SDS_TYPE_64: {
|
||||
SDS_HDR_VAR(64,s);
|
||||
case HI_SDS_TYPE_64: {
|
||||
HI_SDS_HDR_VAR(64,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sdssetlen(sds s, size_t newlen) {
|
||||
static inline void hi_sdssetlen(hisds s, size_t newlen) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
switch(flags&HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
*fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));
|
||||
*fp = (unsigned char)(HI_SDS_TYPE_5 | (newlen << HI_SDS_TYPE_BITS));
|
||||
}
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->len = (uint8_t)newlen;
|
||||
case HI_SDS_TYPE_8:
|
||||
HI_SDS_HDR(8,s)->len = (uint8_t)newlen;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->len = (uint16_t)newlen;
|
||||
case HI_SDS_TYPE_16:
|
||||
HI_SDS_HDR(16,s)->len = (uint16_t)newlen;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->len = (uint32_t)newlen;
|
||||
case HI_SDS_TYPE_32:
|
||||
HI_SDS_HDR(32,s)->len = (uint32_t)newlen;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->len = (uint64_t)newlen;
|
||||
case HI_SDS_TYPE_64:
|
||||
HI_SDS_HDR(64,s)->len = (uint64_t)newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sdsinclen(sds s, size_t inc) {
|
||||
static inline void hi_sdsinclen(hisds s, size_t inc) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
switch(flags&HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
|
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
|
||||
unsigned char newlen = HI_SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
|
||||
*fp = HI_SDS_TYPE_5 | (newlen << HI_SDS_TYPE_BITS);
|
||||
}
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->len += (uint8_t)inc;
|
||||
case HI_SDS_TYPE_8:
|
||||
HI_SDS_HDR(8,s)->len += (uint8_t)inc;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->len += (uint16_t)inc;
|
||||
case HI_SDS_TYPE_16:
|
||||
HI_SDS_HDR(16,s)->len += (uint16_t)inc;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->len += (uint32_t)inc;
|
||||
case HI_SDS_TYPE_32:
|
||||
HI_SDS_HDR(32,s)->len += (uint32_t)inc;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->len += (uint64_t)inc;
|
||||
case HI_SDS_TYPE_64:
|
||||
HI_SDS_HDR(64,s)->len += (uint64_t)inc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* sdsalloc() = sdsavail() + sdslen() */
|
||||
static inline size_t sdsalloc(const sds s) {
|
||||
/* hi_sdsalloc() = hi_sdsavail() + hi_sdslen() */
|
||||
static inline size_t hi_sdsalloc(const hisds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
return SDS_TYPE_5_LEN(flags);
|
||||
case SDS_TYPE_8:
|
||||
return SDS_HDR(8,s)->alloc;
|
||||
case SDS_TYPE_16:
|
||||
return SDS_HDR(16,s)->alloc;
|
||||
case SDS_TYPE_32:
|
||||
return SDS_HDR(32,s)->alloc;
|
||||
case SDS_TYPE_64:
|
||||
return SDS_HDR(64,s)->alloc;
|
||||
switch(flags & HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
return HI_SDS_TYPE_5_LEN(flags);
|
||||
case HI_SDS_TYPE_8:
|
||||
return HI_SDS_HDR(8,s)->alloc;
|
||||
case HI_SDS_TYPE_16:
|
||||
return HI_SDS_HDR(16,s)->alloc;
|
||||
case HI_SDS_TYPE_32:
|
||||
return HI_SDS_HDR(32,s)->alloc;
|
||||
case HI_SDS_TYPE_64:
|
||||
return HI_SDS_HDR(64,s)->alloc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sdssetalloc(sds s, size_t newlen) {
|
||||
static inline void hi_sdssetalloc(hisds s, size_t newlen) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
switch(flags&HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
/* Nothing to do, this type has no total allocation info. */
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->alloc = (uint8_t)newlen;
|
||||
case HI_SDS_TYPE_8:
|
||||
HI_SDS_HDR(8,s)->alloc = (uint8_t)newlen;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->alloc = (uint16_t)newlen;
|
||||
case HI_SDS_TYPE_16:
|
||||
HI_SDS_HDR(16,s)->alloc = (uint16_t)newlen;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->alloc = (uint32_t)newlen;
|
||||
case HI_SDS_TYPE_32:
|
||||
HI_SDS_HDR(32,s)->alloc = (uint32_t)newlen;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->alloc = (uint64_t)newlen;
|
||||
case HI_SDS_TYPE_64:
|
||||
HI_SDS_HDR(64,s)->alloc = (uint64_t)newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen);
|
||||
sds sdsnew(const char *init);
|
||||
sds sdsempty(void);
|
||||
sds sdsdup(const sds s);
|
||||
void sdsfree(sds s);
|
||||
sds sdsgrowzero(sds s, size_t len);
|
||||
sds sdscatlen(sds s, const void *t, size_t len);
|
||||
sds sdscat(sds s, const char *t);
|
||||
sds sdscatsds(sds s, const sds t);
|
||||
sds sdscpylen(sds s, const char *t, size_t len);
|
||||
sds sdscpy(sds s, const char *t);
|
||||
hisds hi_sdsnewlen(const void *init, size_t initlen);
|
||||
hisds hi_sdsnew(const char *init);
|
||||
hisds hi_sdsempty(void);
|
||||
hisds hi_sdsdup(const hisds s);
|
||||
void hi_sdsfree(hisds s);
|
||||
hisds hi_sdsgrowzero(hisds s, size_t len);
|
||||
hisds hi_sdscatlen(hisds s, const void *t, size_t len);
|
||||
hisds hi_sdscat(hisds s, const char *t);
|
||||
hisds hi_sdscatsds(hisds s, const hisds t);
|
||||
hisds hi_sdscpylen(hisds s, const char *t, size_t len);
|
||||
hisds hi_sdscpy(hisds s, const char *t);
|
||||
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
|
||||
hisds hi_sdscatvprintf(hisds s, const char *fmt, va_list ap);
|
||||
#ifdef __GNUC__
|
||||
sds sdscatprintf(sds s, const char *fmt, ...)
|
||||
hisds hi_sdscatprintf(hisds s, const char *fmt, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
#else
|
||||
sds sdscatprintf(sds s, const char *fmt, ...);
|
||||
hisds hi_sdscatprintf(hisds s, const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
sds sdscatfmt(sds s, char const *fmt, ...);
|
||||
sds sdstrim(sds s, const char *cset);
|
||||
void sdsrange(sds s, int start, int end);
|
||||
void sdsupdatelen(sds s);
|
||||
void sdsclear(sds s);
|
||||
int sdscmp(const sds s1, const sds s2);
|
||||
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
|
||||
void sdsfreesplitres(sds *tokens, int count);
|
||||
void sdstolower(sds s);
|
||||
void sdstoupper(sds s);
|
||||
sds sdsfromlonglong(long long value);
|
||||
sds sdscatrepr(sds s, const char *p, size_t len);
|
||||
sds *sdssplitargs(const char *line, int *argc);
|
||||
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
|
||||
sds sdsjoin(char **argv, int argc, char *sep);
|
||||
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
|
||||
hisds hi_sdscatfmt(hisds s, char const *fmt, ...);
|
||||
hisds hi_sdstrim(hisds s, const char *cset);
|
||||
int hi_sdsrange(hisds s, ssize_t start, ssize_t end);
|
||||
void hi_sdsupdatelen(hisds s);
|
||||
void hi_sdsclear(hisds s);
|
||||
int hi_sdscmp(const hisds s1, const hisds s2);
|
||||
hisds *hi_sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
|
||||
void hi_sdsfreesplitres(hisds *tokens, int count);
|
||||
void hi_sdstolower(hisds s);
|
||||
void hi_sdstoupper(hisds s);
|
||||
hisds hi_sdsfromlonglong(long long value);
|
||||
hisds hi_sdscatrepr(hisds s, const char *p, size_t len);
|
||||
hisds *hi_sdssplitargs(const char *line, int *argc);
|
||||
hisds hi_sdsmapchars(hisds s, const char *from, const char *to, size_t setlen);
|
||||
hisds hi_sdsjoin(char **argv, int argc, char *sep);
|
||||
hisds hi_sdsjoinsds(hisds *argv, int argc, const char *sep, size_t seplen);
|
||||
|
||||
/* Low level functions exposed to the user API */
|
||||
sds sdsMakeRoomFor(sds s, size_t addlen);
|
||||
void sdsIncrLen(sds s, int incr);
|
||||
sds sdsRemoveFreeSpace(sds s);
|
||||
size_t sdsAllocSize(sds s);
|
||||
void *sdsAllocPtr(sds s);
|
||||
hisds hi_sdsMakeRoomFor(hisds s, size_t addlen);
|
||||
void hi_sdsIncrLen(hisds s, int incr);
|
||||
hisds hi_sdsRemoveFreeSpace(hisds s);
|
||||
size_t hi_sdsAllocSize(hisds s);
|
||||
void *hi_sdsAllocPtr(hisds s);
|
||||
|
||||
/* Export the allocator used by SDS to the program using SDS.
|
||||
* Sometimes the program SDS is linked to, may use a different set of
|
||||
* allocators, but may want to allocate or free things that SDS will
|
||||
* respectively free or allocate. */
|
||||
void *sds_malloc(size_t size);
|
||||
void *sds_realloc(void *ptr, size_t size);
|
||||
void sds_free(void *ptr);
|
||||
void *hi_sds_malloc(size_t size);
|
||||
void *hi_sds_realloc(void *ptr, size_t size);
|
||||
void hi_sds_free(void *ptr);
|
||||
|
||||
#ifdef REDIS_TEST
|
||||
int sdsTest(int argc, char *argv[]);
|
||||
int hi_sdsTest(int argc, char *argv[]);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif /* HIREDIS_SDS_H */
|
||||
|
||||
Vendored
+5
-3
@@ -37,6 +37,8 @@
|
||||
* the include of your alternate allocator if needed (not needed in order
|
||||
* to use the default libc allocator). */
|
||||
|
||||
#define s_malloc malloc
|
||||
#define s_realloc realloc
|
||||
#define s_free free
|
||||
#include "alloc.h"
|
||||
|
||||
#define hi_s_malloc hi_malloc
|
||||
#define hi_s_realloc hi_realloc
|
||||
#define hi_s_free hi_free
|
||||
|
||||
Vendored
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SDS compatibility header.
|
||||
*
|
||||
* This simple file maps sds types and calls to their unique hiredis symbol names.
|
||||
* It's useful when we build Hiredis as a dependency of Redis and want to call
|
||||
* Hiredis' sds symbols rather than the ones built into Redis, as the libraries
|
||||
* have slightly diverged and could cause hard to track down ABI incompatibility
|
||||
* bugs.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HIREDIS_SDS_COMPAT
|
||||
#define HIREDIS_SDS_COMPAT
|
||||
|
||||
#define sds hisds
|
||||
|
||||
#define sdslen hi_sdslen
|
||||
#define sdsavail hi_sdsavail
|
||||
#define sdssetlen hi_sdssetlen
|
||||
#define sdsinclen hi_sdsinclen
|
||||
#define sdsalloc hi_sdsalloc
|
||||
#define sdssetalloc hi_sdssetalloc
|
||||
|
||||
#define sdsAllocPtr hi_sdsAllocPtr
|
||||
#define sdsAllocSize hi_sdsAllocSize
|
||||
#define sdscat hi_sdscat
|
||||
#define sdscatfmt hi_sdscatfmt
|
||||
#define sdscatlen hi_sdscatlen
|
||||
#define sdscatprintf hi_sdscatprintf
|
||||
#define sdscatrepr hi_sdscatrepr
|
||||
#define sdscatsds hi_sdscatsds
|
||||
#define sdscatvprintf hi_sdscatvprintf
|
||||
#define sdsclear hi_sdsclear
|
||||
#define sdscmp hi_sdscmp
|
||||
#define sdscpy hi_sdscpy
|
||||
#define sdscpylen hi_sdscpylen
|
||||
#define sdsdup hi_sdsdup
|
||||
#define sdsempty hi_sdsempty
|
||||
#define sds_free hi_sds_free
|
||||
#define sdsfree hi_sdsfree
|
||||
#define sdsfreesplitres hi_sdsfreesplitres
|
||||
#define sdsfromlonglong hi_sdsfromlonglong
|
||||
#define sdsgrowzero hi_sdsgrowzero
|
||||
#define sdsIncrLen hi_sdsIncrLen
|
||||
#define sdsjoin hi_sdsjoin
|
||||
#define sdsjoinsds hi_sdsjoinsds
|
||||
#define sdsll2str hi_sdsll2str
|
||||
#define sdsMakeRoomFor hi_sdsMakeRoomFor
|
||||
#define sds_malloc hi_sds_malloc
|
||||
#define sdsmapchars hi_sdsmapchars
|
||||
#define sdsnew hi_sdsnew
|
||||
#define sdsnewlen hi_sdsnewlen
|
||||
#define sdsrange hi_sdsrange
|
||||
#define sds_realloc hi_sds_realloc
|
||||
#define sdsRemoveFreeSpace hi_sdsRemoveFreeSpace
|
||||
#define sdssplitargs hi_sdssplitargs
|
||||
#define sdssplitlen hi_sdssplitlen
|
||||
#define sdstolower hi_sdstolower
|
||||
#define sdstoupper hi_sdstoupper
|
||||
#define sdstrim hi_sdstrim
|
||||
#define sdsull2str hi_sdsull2str
|
||||
#define sdsupdatelen hi_sdsupdatelen
|
||||
|
||||
#endif /* HIREDIS_SDS_COMPAT */
|
||||
Vendored
+1
-1
@@ -212,7 +212,7 @@ int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, sockle
|
||||
int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) {
|
||||
int ret = 0;
|
||||
if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {
|
||||
struct timeval *tv = optval;
|
||||
const struct timeval *tv = optval;
|
||||
DWORD timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;
|
||||
ret = setsockopt(sockfd, level, optname, (const char*)&timeout, sizeof(DWORD));
|
||||
} else {
|
||||
|
||||
Vendored
+2
-1
@@ -49,9 +49,10 @@
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
typedef signed long ssize_t;
|
||||
typedef long long ssize_t;
|
||||
#endif
|
||||
|
||||
/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */
|
||||
|
||||
Vendored
+198
-120
@@ -34,25 +34,33 @@
|
||||
#include "async.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "win32.h"
|
||||
#include "async_private.h"
|
||||
#include "hiredis_ssl.h"
|
||||
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
/* The SSL context is attached to SSL/TLS connections as a privdata. */
|
||||
typedef struct redisSSLContext {
|
||||
/**
|
||||
* OpenSSL SSL_CTX; It is optional and will not be set when using
|
||||
* user-supplied SSL.
|
||||
*/
|
||||
struct redisSSLContext {
|
||||
/* Associated OpenSSL SSL_CTX as created by redisCreateSSLContext() */
|
||||
SSL_CTX *ssl_ctx;
|
||||
|
||||
/* Requested SNI, or NULL */
|
||||
char *server_name;
|
||||
};
|
||||
|
||||
/* The SSL connection context is attached to SSL/TLS connections as a privdata. */
|
||||
typedef struct redisSSL {
|
||||
/**
|
||||
* OpenSSL SSL object.
|
||||
*/
|
||||
@@ -72,43 +80,11 @@ typedef struct redisSSLContext {
|
||||
* should resume whenever a read takes place, if possible
|
||||
*/
|
||||
int pendingWrite;
|
||||
} redisSSLContext;
|
||||
} redisSSL;
|
||||
|
||||
/* Forward declaration */
|
||||
redisContextFuncs redisContextSSLFuncs;
|
||||
|
||||
#ifdef HIREDIS_SSL_TRACE
|
||||
/**
|
||||
* Callback used for debugging
|
||||
*/
|
||||
static void sslLogCallback(const SSL *ssl, int where, int ret) {
|
||||
const char *retstr = "";
|
||||
int should_log = 1;
|
||||
/* Ignore low-level SSL stuff */
|
||||
|
||||
if (where & SSL_CB_ALERT) {
|
||||
should_log = 1;
|
||||
}
|
||||
if (where == SSL_CB_HANDSHAKE_START || where == SSL_CB_HANDSHAKE_DONE) {
|
||||
should_log = 1;
|
||||
}
|
||||
if ((where & SSL_CB_EXIT) && ret == 0) {
|
||||
should_log = 1;
|
||||
}
|
||||
|
||||
if (!should_log) {
|
||||
return;
|
||||
}
|
||||
|
||||
retstr = SSL_alert_type_string(ret);
|
||||
printf("ST(0x%x). %s. R(0x%x)%s\n", where, SSL_state_string_long(ssl), ret, retstr);
|
||||
|
||||
if (where == SSL_CB_HANDSHAKE_DONE) {
|
||||
printf("Using SSL version %s. Cipher=%s\n", SSL_get_version(ssl), SSL_get_cipher_name(ssl));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* OpenSSL global initialization and locking handling callbacks.
|
||||
* Note that this is only required for OpenSSL < 1.1.0.
|
||||
@@ -119,6 +95,18 @@ static void sslLogCallback(const SSL *ssl, int where, int ret) {
|
||||
#endif
|
||||
|
||||
#ifdef HIREDIS_USE_CRYPTO_LOCKS
|
||||
#ifdef _WIN32
|
||||
typedef CRITICAL_SECTION sslLockType;
|
||||
static void sslLockInit(sslLockType* l) {
|
||||
InitializeCriticalSection(l);
|
||||
}
|
||||
static void sslLockAcquire(sslLockType* l) {
|
||||
EnterCriticalSection(l);
|
||||
}
|
||||
static void sslLockRelease(sslLockType* l) {
|
||||
LeaveCriticalSection(l);
|
||||
}
|
||||
#else
|
||||
typedef pthread_mutex_t sslLockType;
|
||||
static void sslLockInit(sslLockType *l) {
|
||||
pthread_mutex_init(l, NULL);
|
||||
@@ -129,7 +117,9 @@ static void sslLockAcquire(sslLockType *l) {
|
||||
static void sslLockRelease(sslLockType *l) {
|
||||
pthread_mutex_unlock(l);
|
||||
}
|
||||
static pthread_mutex_t *ossl_locks;
|
||||
#endif
|
||||
|
||||
static sslLockType* ossl_locks;
|
||||
|
||||
static void opensslDoLock(int mode, int lkid, const char *f, int line) {
|
||||
sslLockType *l = ossl_locks + lkid;
|
||||
@@ -144,36 +134,151 @@ static void opensslDoLock(int mode, int lkid, const char *f, int line) {
|
||||
(void)line;
|
||||
}
|
||||
|
||||
static void initOpensslLocks(void) {
|
||||
static int initOpensslLocks(void) {
|
||||
unsigned ii, nlocks;
|
||||
if (CRYPTO_get_locking_callback() != NULL) {
|
||||
/* Someone already set the callback before us. Don't destroy it! */
|
||||
return;
|
||||
return REDIS_OK;
|
||||
}
|
||||
nlocks = CRYPTO_num_locks();
|
||||
ossl_locks = malloc(sizeof(*ossl_locks) * nlocks);
|
||||
ossl_locks = hi_malloc(sizeof(*ossl_locks) * nlocks);
|
||||
if (ossl_locks == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
for (ii = 0; ii < nlocks; ii++) {
|
||||
sslLockInit(ossl_locks + ii);
|
||||
}
|
||||
CRYPTO_set_locking_callback(opensslDoLock);
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif /* HIREDIS_USE_CRYPTO_LOCKS */
|
||||
|
||||
int redisInitOpenSSL(void)
|
||||
{
|
||||
SSL_library_init();
|
||||
#ifdef HIREDIS_USE_CRYPTO_LOCKS
|
||||
initOpensslLocks();
|
||||
#endif
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* redisSSLContext helper context destruction.
|
||||
*/
|
||||
|
||||
const char *redisSSLContextGetError(redisSSLContextError error)
|
||||
{
|
||||
switch (error) {
|
||||
case REDIS_SSL_CTX_NONE:
|
||||
return "No Error";
|
||||
case REDIS_SSL_CTX_CREATE_FAILED:
|
||||
return "Failed to create OpenSSL SSL_CTX";
|
||||
case REDIS_SSL_CTX_CERT_KEY_REQUIRED:
|
||||
return "Client cert and key must both be specified or skipped";
|
||||
case REDIS_SSL_CTX_CA_CERT_LOAD_FAILED:
|
||||
return "Failed to load CA Certificate or CA Path";
|
||||
case REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED:
|
||||
return "Failed to load client certificate";
|
||||
case REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED:
|
||||
return "Failed to load private key";
|
||||
default:
|
||||
return "Unknown error code";
|
||||
}
|
||||
}
|
||||
|
||||
void redisFreeSSLContext(redisSSLContext *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
if (ctx->server_name) {
|
||||
hi_free(ctx->server_name);
|
||||
ctx->server_name = NULL;
|
||||
}
|
||||
|
||||
if (ctx->ssl_ctx) {
|
||||
SSL_CTX_free(ctx->ssl_ctx);
|
||||
ctx->ssl_ctx = NULL;
|
||||
}
|
||||
|
||||
hi_free(ctx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* redisSSLContext helper context initialization.
|
||||
*/
|
||||
|
||||
redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath,
|
||||
const char *cert_filename, const char *private_key_filename,
|
||||
const char *server_name, redisSSLContextError *error)
|
||||
{
|
||||
redisSSLContext *ctx = hi_calloc(1, sizeof(redisSSLContext));
|
||||
if (ctx == NULL)
|
||||
goto error;
|
||||
|
||||
ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if (!ctx->ssl_ctx) {
|
||||
if (error) *error = REDIS_SSL_CTX_CREATE_FAILED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
|
||||
|
||||
if ((cert_filename != NULL && private_key_filename == NULL) ||
|
||||
(private_key_filename != NULL && cert_filename == NULL)) {
|
||||
if (error) *error = REDIS_SSL_CTX_CERT_KEY_REQUIRED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (capath || cacert_filename) {
|
||||
if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, cacert_filename, capath)) {
|
||||
if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (cert_filename) {
|
||||
if (!SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, cert_filename)) {
|
||||
if (error) *error = REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED;
|
||||
goto error;
|
||||
}
|
||||
if (!SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, private_key_filename, SSL_FILETYPE_PEM)) {
|
||||
if (error) *error = REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (server_name)
|
||||
ctx->server_name = hi_strdup(server_name);
|
||||
|
||||
return ctx;
|
||||
|
||||
error:
|
||||
redisFreeSSLContext(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSL Connection initialization.
|
||||
*/
|
||||
|
||||
static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
|
||||
if (c->privdata) {
|
||||
|
||||
static int redisSSLConnect(redisContext *c, SSL *ssl) {
|
||||
if (c->privctx) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
c->privdata = calloc(1, sizeof(redisSSLContext));
|
||||
|
||||
redisSSL *rssl = hi_calloc(1, sizeof(redisSSL));
|
||||
if (rssl == NULL) {
|
||||
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
c->funcs = &redisContextSSLFuncs;
|
||||
redisSSLContext *rssl = c->privdata;
|
||||
|
||||
rssl->ssl_ctx = ssl_ctx;
|
||||
rssl->ssl = ssl;
|
||||
|
||||
SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
@@ -183,12 +288,14 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
|
||||
ERR_clear_error();
|
||||
int rv = SSL_connect(rssl->ssl);
|
||||
if (rv == 1) {
|
||||
c->privctx = rssl;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
rv = SSL_get_error(rssl->ssl, rv);
|
||||
if (((c->flags & REDIS_BLOCK) == 0) &&
|
||||
(rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
|
||||
c->privctx = rssl;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
@@ -203,83 +310,58 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
|
||||
}
|
||||
__redisSetError(c, REDIS_ERR_IO, err);
|
||||
}
|
||||
|
||||
hi_free(rssl);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around redisSSLConnect() for users who manage their own context and
|
||||
* create their own SSL object.
|
||||
*/
|
||||
|
||||
int redisInitiateSSL(redisContext *c, SSL *ssl) {
|
||||
return redisSSLConnect(c, NULL, ssl);
|
||||
return redisSSLConnect(c, ssl);
|
||||
}
|
||||
|
||||
int redisSecureConnection(redisContext *c, const char *capath,
|
||||
const char *certpath, const char *keypath, const char *servername) {
|
||||
/**
|
||||
* A wrapper around redisSSLConnect() for users who use redisSSLContext and don't
|
||||
* manage their own SSL objects.
|
||||
*/
|
||||
|
||||
SSL_CTX *ssl_ctx = NULL;
|
||||
SSL *ssl = NULL;
|
||||
int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx)
|
||||
{
|
||||
if (!c || !redis_ssl_ctx)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Initialize global OpenSSL stuff */
|
||||
static int isInit = 0;
|
||||
if (!isInit) {
|
||||
isInit = 1;
|
||||
SSL_library_init();
|
||||
#ifdef HIREDIS_USE_CRYPTO_LOCKS
|
||||
initOpensslLocks();
|
||||
#endif
|
||||
}
|
||||
/* We want to verify that redisSSLConnect() won't fail on this, as it will
|
||||
* not own the SSL object in that case and we'll end up leaking.
|
||||
*/
|
||||
if (c->privctx)
|
||||
return REDIS_ERR;
|
||||
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if (!ssl_ctx) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Failed to create SSL_CTX");
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifdef HIREDIS_SSL_TRACE
|
||||
SSL_CTX_set_info_callback(ssl_ctx, sslLogCallback);
|
||||
#endif
|
||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
|
||||
if ((certpath != NULL && keypath == NULL) || (keypath != NULL && certpath == NULL)) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "certpath and keypath must be specified together");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (capath) {
|
||||
if (!SSL_CTX_load_verify_locations(ssl_ctx, capath, NULL)) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Invalid CA certificate");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (certpath) {
|
||||
if (!SSL_CTX_use_certificate_chain_file(ssl_ctx, certpath)) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Invalid client certificate");
|
||||
goto error;
|
||||
}
|
||||
if (!SSL_CTX_use_PrivateKey_file(ssl_ctx, keypath, SSL_FILETYPE_PEM)) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Invalid client key");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
ssl = SSL_new(ssl_ctx);
|
||||
SSL *ssl = SSL_new(redis_ssl_ctx->ssl_ctx);
|
||||
if (!ssl) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Couldn't create new SSL instance");
|
||||
goto error;
|
||||
}
|
||||
if (servername) {
|
||||
if (!SSL_set_tlsext_host_name(ssl, servername)) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Couldn't set server name indication");
|
||||
|
||||
if (redis_ssl_ctx->server_name) {
|
||||
if (!SSL_set_tlsext_host_name(ssl, redis_ssl_ctx->server_name)) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Failed to set server_name/SNI");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return redisSSLConnect(c, ssl_ctx, ssl);
|
||||
return redisSSLConnect(c, ssl);
|
||||
|
||||
error:
|
||||
if (ssl) SSL_free(ssl);
|
||||
if (ssl_ctx) SSL_CTX_free(ssl_ctx);
|
||||
if (ssl)
|
||||
SSL_free(ssl);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static int maybeCheckWant(redisSSLContext *rssl, int rv) {
|
||||
static int maybeCheckWant(redisSSL *rssl, int rv) {
|
||||
/**
|
||||
* If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
|
||||
* and true is returned. False is returned otherwise
|
||||
@@ -299,23 +381,19 @@ static int maybeCheckWant(redisSSLContext *rssl, int rv) {
|
||||
* Implementation of redisContextFuncs for SSL connections.
|
||||
*/
|
||||
|
||||
static void redisSSLFreeContext(void *privdata){
|
||||
redisSSLContext *rsc = privdata;
|
||||
static void redisSSLFree(void *privctx){
|
||||
redisSSL *rsc = privctx;
|
||||
|
||||
if (!rsc) return;
|
||||
if (rsc->ssl) {
|
||||
SSL_free(rsc->ssl);
|
||||
rsc->ssl = NULL;
|
||||
}
|
||||
if (rsc->ssl_ctx) {
|
||||
SSL_CTX_free(rsc->ssl_ctx);
|
||||
rsc->ssl_ctx = NULL;
|
||||
}
|
||||
free(rsc);
|
||||
hi_free(rsc);
|
||||
}
|
||||
|
||||
static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
redisSSLContext *rssl = c->privdata;
|
||||
static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
redisSSL *rssl = c->privctx;
|
||||
|
||||
int nread = SSL_read(rssl->ssl, buf, bufcap);
|
||||
if (nread > 0) {
|
||||
@@ -356,10 +434,10 @@ static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
}
|
||||
}
|
||||
|
||||
static int redisSSLWrite(redisContext *c) {
|
||||
redisSSLContext *rssl = c->privdata;
|
||||
static ssize_t redisSSLWrite(redisContext *c) {
|
||||
redisSSL *rssl = c->privctx;
|
||||
|
||||
size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf);
|
||||
size_t len = rssl->lastLen ? rssl->lastLen : hi_sdslen(c->obuf);
|
||||
int rv = SSL_write(rssl->ssl, c->obuf, len);
|
||||
|
||||
if (rv > 0) {
|
||||
@@ -380,7 +458,7 @@ static int redisSSLWrite(redisContext *c) {
|
||||
|
||||
static void redisSSLAsyncRead(redisAsyncContext *ac) {
|
||||
int rv;
|
||||
redisSSLContext *rssl = ac->c.privdata;
|
||||
redisSSL *rssl = ac->c.privctx;
|
||||
redisContext *c = &ac->c;
|
||||
|
||||
rssl->wantRead = 0;
|
||||
@@ -410,7 +488,7 @@ static void redisSSLAsyncRead(redisAsyncContext *ac) {
|
||||
|
||||
static void redisSSLAsyncWrite(redisAsyncContext *ac) {
|
||||
int rv, done = 0;
|
||||
redisSSLContext *rssl = ac->c.privdata;
|
||||
redisSSL *rssl = ac->c.privctx;
|
||||
redisContext *c = &ac->c;
|
||||
|
||||
rssl->pendingWrite = 0;
|
||||
@@ -439,7 +517,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
|
||||
}
|
||||
|
||||
redisContextFuncs redisContextSSLFuncs = {
|
||||
.free_privdata = redisSSLFreeContext,
|
||||
.free_privctx = redisSSLFree,
|
||||
.async_read = redisSSLAsyncRead,
|
||||
.async_write = redisSSLAsyncWrite,
|
||||
.read = redisSSLRead,
|
||||
|
||||
Vendored
+504
-91
@@ -1,22 +1,24 @@
|
||||
#include "fmacros.h"
|
||||
#include "sockcompat.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef _WIN32
|
||||
#include <strings.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "hiredis.h"
|
||||
#include "async.h"
|
||||
#ifdef HIREDIS_TEST_SSL
|
||||
#include "hiredis_ssl.h"
|
||||
#endif
|
||||
#include "net.h"
|
||||
#include "win32.h"
|
||||
|
||||
enum connection_type {
|
||||
CONN_TCP,
|
||||
@@ -47,15 +49,35 @@ struct config {
|
||||
} ssl;
|
||||
};
|
||||
|
||||
struct privdata {
|
||||
int dtor_counter;
|
||||
};
|
||||
|
||||
struct pushCounters {
|
||||
int nil;
|
||||
int str;
|
||||
};
|
||||
|
||||
#ifdef HIREDIS_TEST_SSL
|
||||
redisSSLContext *_ssl_ctx = NULL;
|
||||
#endif
|
||||
|
||||
/* The following lines make up our testing "framework" :) */
|
||||
static int tests = 0, fails = 0;
|
||||
static int tests = 0, fails = 0, skips = 0;
|
||||
#define test(_s) { printf("#%02d ", ++tests); printf(_s); }
|
||||
#define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;}
|
||||
#define test_skipped() { printf("\033[01;33mSKIPPED\033[0;0m\n"); skips++; }
|
||||
|
||||
static long long usec(void) {
|
||||
#ifndef _MSC_VER
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
|
||||
#else
|
||||
FILETIME ft;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
return (((long long)ft.dwHighDateTime << 32) | ft.dwLowDateTime) / 10;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* The assert() calls below have side effects, so we need assert()
|
||||
@@ -65,6 +87,43 @@ static long long usec(void) {
|
||||
#define assert(e) (void)(e)
|
||||
#endif
|
||||
|
||||
/* Helper to extract Redis version information. Aborts on any failure. */
|
||||
#define REDIS_VERSION_FIELD "redis_version:"
|
||||
void get_redis_version(redisContext *c, int *majorptr, int *minorptr) {
|
||||
redisReply *reply;
|
||||
char *eptr, *s, *e;
|
||||
int major, minor;
|
||||
|
||||
reply = redisCommand(c, "INFO");
|
||||
if (reply == NULL || c->err || reply->type != REDIS_REPLY_STRING)
|
||||
goto abort;
|
||||
if ((s = strstr(reply->str, REDIS_VERSION_FIELD)) == NULL)
|
||||
goto abort;
|
||||
|
||||
s += strlen(REDIS_VERSION_FIELD);
|
||||
|
||||
/* We need a field terminator and at least 'x.y.z' (5) bytes of data */
|
||||
if ((e = strstr(s, "\r\n")) == NULL || (e - s) < 5)
|
||||
goto abort;
|
||||
|
||||
/* Extract version info */
|
||||
major = strtol(s, &eptr, 10);
|
||||
if (*eptr != '.') goto abort;
|
||||
minor = strtol(eptr+1, NULL, 10);
|
||||
|
||||
/* Push info the caller wants */
|
||||
if (majorptr) *majorptr = major;
|
||||
if (minorptr) *minorptr = minor;
|
||||
|
||||
freeReplyObject(reply);
|
||||
return;
|
||||
|
||||
abort:
|
||||
freeReplyObject(reply);
|
||||
fprintf(stderr, "Error: Cannot determine Redis version, aborting\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static redisContext *select_database(redisContext *c) {
|
||||
redisReply *reply;
|
||||
|
||||
@@ -87,6 +146,26 @@ static redisContext *select_database(redisContext *c) {
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Switch protocol */
|
||||
static void send_hello(redisContext *c, int version) {
|
||||
redisReply *reply;
|
||||
int expected;
|
||||
|
||||
reply = redisCommand(c, "HELLO %d", version);
|
||||
expected = version == 3 ? REDIS_REPLY_MAP : REDIS_REPLY_ARRAY;
|
||||
assert(reply != NULL && reply->type == expected);
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
/* Togggle client tracking */
|
||||
static void send_client_tracking(redisContext *c, const char *str) {
|
||||
redisReply *reply;
|
||||
|
||||
reply = redisCommand(c, "CLIENT TRACKING %s", str);
|
||||
assert(reply != NULL && reply->type == REDIS_REPLY_STATUS);
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
static int disconnect(redisContext *c, int keep_fd) {
|
||||
redisReply *reply;
|
||||
|
||||
@@ -105,9 +184,9 @@ static int disconnect(redisContext *c, int keep_fd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void do_ssl_handshake(redisContext *c, struct config config) {
|
||||
static void do_ssl_handshake(redisContext *c) {
|
||||
#ifdef HIREDIS_TEST_SSL
|
||||
redisSecureConnection(c, config.ssl.ca_cert, config.ssl.cert, config.ssl.key, NULL);
|
||||
redisInitiateSSLWithContext(c, _ssl_ctx);
|
||||
if (c->err) {
|
||||
printf("SSL error: %s\n", c->errstr);
|
||||
redisFree(c);
|
||||
@@ -115,7 +194,6 @@ static void do_ssl_handshake(redisContext *c, struct config config) {
|
||||
}
|
||||
#else
|
||||
(void) c;
|
||||
(void) config;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -150,7 +228,7 @@ static redisContext *do_connect(struct config config) {
|
||||
}
|
||||
|
||||
if (config.type == CONN_SSL) {
|
||||
do_ssl_handshake(c, config);
|
||||
do_ssl_handshake(c);
|
||||
}
|
||||
|
||||
return select_database(c);
|
||||
@@ -160,7 +238,7 @@ static void do_reconnect(redisContext *c, struct config config) {
|
||||
redisReconnect(c);
|
||||
|
||||
if (config.type == CONN_SSL) {
|
||||
do_ssl_handshake(c, config);
|
||||
do_ssl_handshake(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,43 +250,43 @@ static void test_format_commands(void) {
|
||||
len = redisFormatCommand(&cmd,"SET foo bar");
|
||||
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
|
||||
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
|
||||
test("Format command with %%s string interpolation: ");
|
||||
len = redisFormatCommand(&cmd,"SET %s %s","foo","bar");
|
||||
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
|
||||
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
|
||||
test("Format command with %%s and an empty string: ");
|
||||
len = redisFormatCommand(&cmd,"SET %s %s","foo","");
|
||||
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
|
||||
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
|
||||
test("Format command with an empty string in between proper interpolations: ");
|
||||
len = redisFormatCommand(&cmd,"SET %s %s","","foo");
|
||||
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 &&
|
||||
len == 4+4+(3+2)+4+(0+2)+4+(3+2));
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
|
||||
test("Format command with %%b string interpolation: ");
|
||||
len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"b\0r",(size_t)3);
|
||||
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 &&
|
||||
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
|
||||
test("Format command with %%b and an empty string: ");
|
||||
len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"",(size_t)0);
|
||||
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
|
||||
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
|
||||
test("Format command with literal %%: ");
|
||||
len = redisFormatCommand(&cmd,"SET %% %%");
|
||||
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 &&
|
||||
len == 4+4+(3+2)+4+(1+2)+4+(1+2));
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
|
||||
/* Vararg width depends on the type. These tests make sure that the
|
||||
* width is correctly determined using the format and subsequent varargs
|
||||
@@ -219,7 +297,7 @@ static void test_format_commands(void) {
|
||||
len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \
|
||||
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \
|
||||
len == 4+5+(12+2)+4+(9+2)); \
|
||||
free(cmd); \
|
||||
hi_free(cmd); \
|
||||
} while(0)
|
||||
|
||||
#define FLOAT_WIDTH_TEST(type) do { \
|
||||
@@ -228,7 +306,7 @@ static void test_format_commands(void) {
|
||||
len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \
|
||||
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \
|
||||
len == 4+5+(12+2)+4+(9+2)); \
|
||||
free(cmd); \
|
||||
hi_free(cmd); \
|
||||
} while(0)
|
||||
|
||||
INTEGER_WIDTH_TEST("d", int);
|
||||
@@ -259,29 +337,29 @@ static void test_format_commands(void) {
|
||||
len = redisFormatCommandArgv(&cmd,argc,argv,NULL);
|
||||
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
|
||||
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
|
||||
test("Format command by passing argc/argv with lengths: ");
|
||||
len = redisFormatCommandArgv(&cmd,argc,argv,lens);
|
||||
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
|
||||
len == 4+4+(3+2)+4+(7+2)+4+(3+2));
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
|
||||
sds sds_cmd;
|
||||
hisds sds_cmd;
|
||||
|
||||
sds_cmd = sdsempty();
|
||||
test("Format command into sds by passing argc/argv without lengths: ");
|
||||
sds_cmd = NULL;
|
||||
test("Format command into hisds by passing argc/argv without lengths: ");
|
||||
len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL);
|
||||
test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
|
||||
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
|
||||
sdsfree(sds_cmd);
|
||||
hi_sdsfree(sds_cmd);
|
||||
|
||||
sds_cmd = sdsempty();
|
||||
test("Format command into sds by passing argc/argv with lengths: ");
|
||||
sds_cmd = NULL;
|
||||
test("Format command into hisds by passing argc/argv with lengths: ");
|
||||
len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens);
|
||||
test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
|
||||
len == 4+4+(3+2)+4+(7+2)+4+(3+2));
|
||||
sdsfree(sds_cmd);
|
||||
hi_sdsfree(sds_cmd);
|
||||
}
|
||||
|
||||
static void test_append_formatted_commands(struct config config) {
|
||||
@@ -300,7 +378,7 @@ static void test_append_formatted_commands(struct config config) {
|
||||
|
||||
assert(redisGetReply(c, (void*)&reply) == REDIS_OK);
|
||||
|
||||
free(cmd);
|
||||
hi_free(cmd);
|
||||
freeReplyObject(reply);
|
||||
|
||||
disconnect(c, 0);
|
||||
@@ -308,7 +386,7 @@ static void test_append_formatted_commands(struct config config) {
|
||||
|
||||
static void test_reply_reader(void) {
|
||||
redisReader *reader;
|
||||
void *reply;
|
||||
void *reply, *root;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
@@ -332,16 +410,26 @@ static void test_reply_reader(void) {
|
||||
strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Set error on nested multi bulks with depth > 7: ");
|
||||
reader = redisReaderCreate();
|
||||
|
||||
for (i = 0; i < 9; i++) {
|
||||
redisReaderFeed(reader,(char*)"*1\r\n",4);
|
||||
test("Can handle arbitrarily nested multi-bulks: ");
|
||||
for (i = 0; i < 128; i++) {
|
||||
redisReaderFeed(reader,(char*)"*1\r\n", 4);
|
||||
}
|
||||
redisReaderFeed(reader,(char*)"$6\r\nLOLWUT\r\n",12);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
root = reply; /* Keep track of the root reply */
|
||||
test_cond(ret == REDIS_OK &&
|
||||
((redisReply*)reply)->type == REDIS_REPLY_ARRAY &&
|
||||
((redisReply*)reply)->elements == 1);
|
||||
|
||||
ret = redisReaderGetReply(reader,NULL);
|
||||
test_cond(ret == REDIS_ERR &&
|
||||
strncasecmp(reader->errstr,"No support for",14) == 0);
|
||||
test("Can parse arbitrarily nested multi-bulks correctly: ");
|
||||
while(i--) {
|
||||
assert(reply != NULL && ((redisReply*)reply)->type == REDIS_REPLY_ARRAY);
|
||||
reply = ((redisReply*)reply)->element[0];
|
||||
}
|
||||
test_cond(((redisReply*)reply)->type == REDIS_REPLY_STRING &&
|
||||
!memcmp(((redisReply*)reply)->str, "LOLWUT", 6));
|
||||
freeReplyObject(root);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Correctly parses LLONG_MAX: ");
|
||||
@@ -400,6 +488,30 @@ static void test_reply_reader(void) {
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Can configure maximum multi-bulk elements: ");
|
||||
reader = redisReaderCreate();
|
||||
reader->maxelements = 1024;
|
||||
redisReaderFeed(reader, "*1025\r\n", 7);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_ERR &&
|
||||
strcasecmp(reader->errstr, "Multi-bulk length out of range") == 0);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Multi-bulk never overflows regardless of maxelements: ");
|
||||
size_t bad_mbulk_len = (SIZE_MAX / sizeof(void *)) + 3;
|
||||
char bad_mbulk_reply[100];
|
||||
snprintf(bad_mbulk_reply, sizeof(bad_mbulk_reply), "*%llu\r\n+asdf\r\n",
|
||||
(unsigned long long) bad_mbulk_len);
|
||||
|
||||
reader = redisReaderCreate();
|
||||
reader->maxelements = 0; /* Don't rely on default limit */
|
||||
redisReaderFeed(reader, bad_mbulk_reply, strlen(bad_mbulk_reply));
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_ERR && strcasecmp(reader->errstr, "Out of memory") == 0);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
#if LLONG_MAX > SIZE_MAX
|
||||
test("Set error when array > SIZE_MAX: ");
|
||||
reader = redisReaderCreate();
|
||||
@@ -459,6 +571,32 @@ static void test_reply_reader(void) {
|
||||
((redisReply*)reply)->elements == 0);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
/* RESP3 verbatim strings (GitHub issue #802) */
|
||||
test("Can parse RESP3 verbatim strings: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader,(char*)"=10\r\ntxt:LOLWUT\r\n",17);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_OK &&
|
||||
((redisReply*)reply)->type == REDIS_REPLY_VERB &&
|
||||
!memcmp(((redisReply*)reply)->str,"LOLWUT", 6));
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
/* RESP3 push messages (Github issue #815) */
|
||||
test("Can parse RESP3 push messages: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader,(char*)">2\r\n$6\r\nLOLWUT\r\n:42\r\n",21);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_OK &&
|
||||
((redisReply*)reply)->type == REDIS_REPLY_PUSH &&
|
||||
((redisReply*)reply)->elements == 2 &&
|
||||
((redisReply*)reply)->element[0]->type == REDIS_REPLY_STRING &&
|
||||
!memcmp(((redisReply*)reply)->element[0]->str,"LOLWUT",6) &&
|
||||
((redisReply*)reply)->element[1]->type == REDIS_REPLY_INTEGER &&
|
||||
((redisReply*)reply)->element[1]->integer == 42);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
}
|
||||
|
||||
static void test_free_null(void) {
|
||||
@@ -474,6 +612,47 @@ static void test_free_null(void) {
|
||||
test_cond(reply == NULL);
|
||||
}
|
||||
|
||||
static void *hi_malloc_fail(size_t size) {
|
||||
(void)size;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *hi_calloc_fail(size_t nmemb, size_t size) {
|
||||
(void)nmemb;
|
||||
(void)size;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *hi_realloc_fail(void *ptr, size_t size) {
|
||||
(void)ptr;
|
||||
(void)size;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void test_allocator_injection(void) {
|
||||
hiredisAllocFuncs ha = {
|
||||
.mallocFn = hi_malloc_fail,
|
||||
.callocFn = hi_calloc_fail,
|
||||
.reallocFn = hi_realloc_fail,
|
||||
.strdupFn = strdup,
|
||||
.freeFn = free,
|
||||
};
|
||||
|
||||
// Override hiredis allocators
|
||||
hiredisSetAllocators(&ha);
|
||||
|
||||
test("redisContext uses injected allocators: ");
|
||||
redisContext *c = redisConnect("localhost", 6379);
|
||||
test_cond(c == NULL);
|
||||
|
||||
test("redisReader uses injected allocators: ");
|
||||
redisReader *reader = redisReaderCreate();
|
||||
test_cond(reader == NULL);
|
||||
|
||||
// Return allocators to default
|
||||
hiredisResetAllocators();
|
||||
}
|
||||
|
||||
#define HIREDIS_BAD_DOMAIN "idontexist-noreally.com"
|
||||
static void test_blocking_connection_errors(void) {
|
||||
redisContext *c;
|
||||
@@ -491,19 +670,19 @@ static void test_blocking_connection_errors(void) {
|
||||
(strcmp(c->errstr, "Name or service not known") == 0 ||
|
||||
strcmp(c->errstr, "Can't resolve: " HIREDIS_BAD_DOMAIN) == 0 ||
|
||||
strcmp(c->errstr, "Name does not resolve") == 0 ||
|
||||
strcmp(c->errstr,
|
||||
"nodename nor servname provided, or not known") == 0 ||
|
||||
strcmp(c->errstr, "nodename nor servname provided, or not known") == 0 ||
|
||||
strcmp(c->errstr, "No address associated with hostname") == 0 ||
|
||||
strcmp(c->errstr, "Temporary failure in name resolution") == 0 ||
|
||||
strcmp(c->errstr,
|
||||
"hostname nor servname provided, or not known") == 0 ||
|
||||
strcmp(c->errstr, "no address associated with name") == 0));
|
||||
strcmp(c->errstr, "hostname nor servname provided, or not known") == 0 ||
|
||||
strcmp(c->errstr, "no address associated with name") == 0 ||
|
||||
strcmp(c->errstr, "No such host is known. ") == 0));
|
||||
redisFree(c);
|
||||
} else {
|
||||
printf("Skipping NXDOMAIN test. Found evil ISP!\n");
|
||||
freeaddrinfo(ai_tmp);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
test("Returns error when the port is not open: ");
|
||||
c = redisConnect((char*)"localhost", 1);
|
||||
test_cond(c->err == REDIS_ERR_IO &&
|
||||
@@ -514,11 +693,166 @@ static void test_blocking_connection_errors(void) {
|
||||
c = redisConnectUnix((char*)"/tmp/idontexist.sock");
|
||||
test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */
|
||||
redisFree(c);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Test push handler */
|
||||
void push_handler(void *privdata, void *r) {
|
||||
struct pushCounters *pcounts = privdata;
|
||||
redisReply *reply = r, *payload;
|
||||
|
||||
assert(reply && reply->type == REDIS_REPLY_PUSH && reply->elements == 2);
|
||||
|
||||
payload = reply->element[1];
|
||||
if (payload->type == REDIS_REPLY_ARRAY) {
|
||||
payload = payload->element[0];
|
||||
}
|
||||
|
||||
if (payload->type == REDIS_REPLY_STRING) {
|
||||
pcounts->str++;
|
||||
} else if (payload->type == REDIS_REPLY_NIL) {
|
||||
pcounts->nil++;
|
||||
}
|
||||
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
/* Dummy function just to test setting a callback with redisOptions */
|
||||
void push_handler_async(redisAsyncContext *ac, void *reply) {
|
||||
(void)ac;
|
||||
(void)reply;
|
||||
}
|
||||
|
||||
static void test_resp3_push_handler(redisContext *c) {
|
||||
struct pushCounters pc = {0};
|
||||
redisPushFn *old = NULL;
|
||||
redisReply *reply;
|
||||
void *privdata;
|
||||
|
||||
/* Switch to RESP3 and turn on client tracking */
|
||||
send_hello(c, 3);
|
||||
send_client_tracking(c, "ON");
|
||||
privdata = c->privdata;
|
||||
c->privdata = &pc;
|
||||
|
||||
reply = redisCommand(c, "GET key:0");
|
||||
assert(reply != NULL);
|
||||
freeReplyObject(reply);
|
||||
|
||||
test("RESP3 PUSH messages are handled out of band by default: ");
|
||||
reply = redisCommand(c, "SET key:0 val:0");
|
||||
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS);
|
||||
freeReplyObject(reply);
|
||||
|
||||
assert((reply = redisCommand(c, "GET key:0")) != NULL);
|
||||
freeReplyObject(reply);
|
||||
|
||||
old = redisSetPushCallback(c, push_handler);
|
||||
test("We can set a custom RESP3 PUSH handler: ");
|
||||
reply = redisCommand(c, "SET key:0 val:0");
|
||||
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && pc.str == 1);
|
||||
freeReplyObject(reply);
|
||||
|
||||
test("We properly handle a NIL invalidation payload: ");
|
||||
reply = redisCommand(c, "FLUSHDB");
|
||||
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && pc.nil == 1);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Unset the push callback and generate an invalidate message making
|
||||
* sure it is not handled out of band. */
|
||||
test("With no handler, PUSH replies come in-band: ");
|
||||
redisSetPushCallback(c, NULL);
|
||||
assert((reply = redisCommand(c, "GET key:0")) != NULL);
|
||||
freeReplyObject(reply);
|
||||
assert((reply = redisCommand(c, "SET key:0 invalid")) != NULL);
|
||||
test_cond(reply->type == REDIS_REPLY_PUSH);
|
||||
freeReplyObject(reply);
|
||||
|
||||
test("With no PUSH handler, no replies are lost: ");
|
||||
assert(redisGetReply(c, (void**)&reply) == REDIS_OK);
|
||||
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Return to the originally set PUSH handler */
|
||||
assert(old != NULL);
|
||||
redisSetPushCallback(c, old);
|
||||
|
||||
/* Switch back to RESP2 and disable tracking */
|
||||
c->privdata = privdata;
|
||||
send_client_tracking(c, "OFF");
|
||||
send_hello(c, 2);
|
||||
}
|
||||
|
||||
redisOptions get_redis_tcp_options(struct config config) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, config.tcp.host, config.tcp.port);
|
||||
return options;
|
||||
}
|
||||
|
||||
static void test_resp3_push_options(struct config config) {
|
||||
redisAsyncContext *ac;
|
||||
redisContext *c;
|
||||
redisOptions options;
|
||||
|
||||
test("We set a default RESP3 handler for redisContext: ");
|
||||
options = get_redis_tcp_options(config);
|
||||
assert((c = redisConnectWithOptions(&options)) != NULL);
|
||||
test_cond(c->push_cb != NULL);
|
||||
redisFree(c);
|
||||
|
||||
test("We don't set a default RESP3 push handler for redisAsyncContext: ");
|
||||
options = get_redis_tcp_options(config);
|
||||
assert((ac = redisAsyncConnectWithOptions(&options)) != NULL);
|
||||
test_cond(ac->c.push_cb == NULL);
|
||||
redisAsyncFree(ac);
|
||||
|
||||
test("Our REDIS_OPT_NO_PUSH_AUTOFREE flag works: ");
|
||||
options = get_redis_tcp_options(config);
|
||||
options.options |= REDIS_OPT_NO_PUSH_AUTOFREE;
|
||||
assert((c = redisConnectWithOptions(&options)) != NULL);
|
||||
test_cond(c->push_cb == NULL);
|
||||
redisFree(c);
|
||||
|
||||
test("We can use redisOptions to set a custom PUSH handler for redisContext: ");
|
||||
options = get_redis_tcp_options(config);
|
||||
options.push_cb = push_handler;
|
||||
assert((c = redisConnectWithOptions(&options)) != NULL);
|
||||
test_cond(c->push_cb == push_handler);
|
||||
redisFree(c);
|
||||
|
||||
test("We can use redisOptions to set a custom PUSH handler for redisAsyncContext: ");
|
||||
options = get_redis_tcp_options(config);
|
||||
options.async_push_cb = push_handler_async;
|
||||
assert((ac = redisAsyncConnectWithOptions(&options)) != NULL);
|
||||
test_cond(ac->push_cb == push_handler_async);
|
||||
redisAsyncFree(ac);
|
||||
}
|
||||
|
||||
void free_privdata(void *privdata) {
|
||||
struct privdata *data = privdata;
|
||||
data->dtor_counter++;
|
||||
}
|
||||
|
||||
static void test_privdata_hooks(struct config config) {
|
||||
struct privdata data = {0};
|
||||
redisOptions options;
|
||||
redisContext *c;
|
||||
|
||||
test("We can use redisOptions to set privdata: ");
|
||||
options = get_redis_tcp_options(config);
|
||||
REDIS_OPTIONS_SET_PRIVDATA(&options, &data, free_privdata);
|
||||
assert((c = redisConnectWithOptions(&options)) != NULL);
|
||||
test_cond(c->privdata == &data);
|
||||
|
||||
test("Our privdata destructor fires when we free the context: ");
|
||||
redisFree(c);
|
||||
test_cond(data.dtor_counter == 1);
|
||||
}
|
||||
|
||||
static void test_blocking_connection(struct config config) {
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
int major;
|
||||
|
||||
c = do_connect(config);
|
||||
|
||||
@@ -591,14 +925,42 @@ static void test_blocking_connection(struct config config) {
|
||||
strcasecmp(reply->element[1]->str,"pong") == 0);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Make sure passing NULL to redisGetReply is safe */
|
||||
test("Can pass NULL to redisGetReply: ");
|
||||
assert(redisAppendCommand(c, "PING") == REDIS_OK);
|
||||
test_cond(redisGetReply(c, NULL) == REDIS_OK);
|
||||
|
||||
get_redis_version(c, &major, NULL);
|
||||
if (major >= 6) test_resp3_push_handler(c);
|
||||
test_resp3_push_options(config);
|
||||
|
||||
test_privdata_hooks(config);
|
||||
|
||||
disconnect(c, 0);
|
||||
}
|
||||
|
||||
/* Send DEBUG SLEEP 0 to detect if we have this command */
|
||||
static int detect_debug_sleep(redisContext *c) {
|
||||
int detected;
|
||||
redisReply *reply = redisCommand(c, "DEBUG SLEEP 0\r\n");
|
||||
|
||||
if (reply == NULL || c->err) {
|
||||
const char *cause = c->err ? c->errstr : "(none)";
|
||||
fprintf(stderr, "Error testing for DEBUG SLEEP (Redis error: %s), exiting\n", cause);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
detected = reply->type == REDIS_REPLY_STATUS;
|
||||
freeReplyObject(reply);
|
||||
|
||||
return detected;
|
||||
}
|
||||
|
||||
static void test_blocking_connection_timeouts(struct config config) {
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
ssize_t s;
|
||||
const char *cmd = "DEBUG SLEEP 3\r\n";
|
||||
const char *sleep_cmd = "DEBUG SLEEP 3\r\n";
|
||||
struct timeval tv;
|
||||
|
||||
c = do_connect(config);
|
||||
@@ -615,14 +977,24 @@ static void test_blocking_connection_timeouts(struct config config) {
|
||||
|
||||
c = do_connect(config);
|
||||
test("Does not return a reply when the command times out: ");
|
||||
redisAppendFormattedCommand(c, cmd, strlen(cmd));
|
||||
s = c->funcs->write(c);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10000;
|
||||
redisSetTimeout(c, tv);
|
||||
reply = redisCommand(c, "GET foo");
|
||||
test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, "Resource temporarily unavailable") == 0);
|
||||
freeReplyObject(reply);
|
||||
if (detect_debug_sleep(c)) {
|
||||
redisAppendFormattedCommand(c, sleep_cmd, strlen(sleep_cmd));
|
||||
s = c->funcs->write(c);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10000;
|
||||
redisSetTimeout(c, tv);
|
||||
reply = redisCommand(c, "GET foo");
|
||||
#ifndef _WIN32
|
||||
test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO &&
|
||||
strcmp(c->errstr, "Resource temporarily unavailable") == 0);
|
||||
#else
|
||||
test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_TIMEOUT &&
|
||||
strcmp(c->errstr, "recv timeout") == 0);
|
||||
#endif
|
||||
freeReplyObject(reply);
|
||||
} else {
|
||||
test_skipped();
|
||||
}
|
||||
|
||||
test("Reconnect properly reconnects after a timeout: ");
|
||||
do_reconnect(c, config);
|
||||
@@ -649,18 +1021,7 @@ static void test_blocking_io_errors(struct config config) {
|
||||
|
||||
/* Connect to target given by config. */
|
||||
c = do_connect(config);
|
||||
{
|
||||
/* Find out Redis version to determine the path for the next test */
|
||||
const char *field = "redis_version:";
|
||||
char *p, *eptr;
|
||||
|
||||
reply = redisCommand(c,"INFO");
|
||||
p = strstr(reply->str,field);
|
||||
major = strtol(p+strlen(field),&eptr,10);
|
||||
p = eptr+1; /* char next to the first "." */
|
||||
minor = strtol(p,&eptr,10);
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
get_redis_version(c, &major, &minor);
|
||||
|
||||
test("Returns I/O error when the connection is lost: ");
|
||||
reply = redisCommand(c,"QUIT");
|
||||
@@ -674,6 +1035,7 @@ static void test_blocking_io_errors(struct config config) {
|
||||
test_cond(reply == NULL);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
/* On 2.0, QUIT will cause the connection to be closed immediately and
|
||||
* the read(2) for the reply on QUIT will set the error to EOF.
|
||||
* On >2.0, QUIT will return with OK and another read(2) needed to be
|
||||
@@ -681,14 +1043,19 @@ static void test_blocking_io_errors(struct config config) {
|
||||
* conditions, the error will be set to EOF. */
|
||||
assert(c->err == REDIS_ERR_EOF &&
|
||||
strcmp(c->errstr,"Server closed the connection") == 0);
|
||||
#endif
|
||||
redisFree(c);
|
||||
|
||||
c = do_connect(config);
|
||||
test("Returns I/O error on socket timeout: ");
|
||||
struct timeval tv = { 0, 1000 };
|
||||
assert(redisSetTimeout(c,tv) == REDIS_OK);
|
||||
test_cond(redisGetReply(c,&_reply) == REDIS_ERR &&
|
||||
c->err == REDIS_ERR_IO && errno == EAGAIN);
|
||||
int respcode = redisGetReply(c,&_reply);
|
||||
#ifndef _WIN32
|
||||
test_cond(respcode == REDIS_ERR && c->err == REDIS_ERR_IO && errno == EAGAIN);
|
||||
#else
|
||||
test_cond(respcode == REDIS_ERR && c->err == REDIS_ERR_TIMEOUT);
|
||||
#endif
|
||||
redisFree(c);
|
||||
}
|
||||
|
||||
@@ -716,6 +1083,18 @@ static void test_invalid_timeout_errors(struct config config) {
|
||||
redisFree(c);
|
||||
}
|
||||
|
||||
/* Wrap malloc to abort on failure so OOM checks don't make the test logic
|
||||
* harder to follow. */
|
||||
void *hi_malloc_safe(size_t size) {
|
||||
void *ptr = hi_malloc(size);
|
||||
if (ptr == NULL) {
|
||||
fprintf(stderr, "Error: Out of memory\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void test_throughput(struct config config) {
|
||||
redisContext *c = do_connect(config);
|
||||
redisReply **replies;
|
||||
@@ -727,7 +1106,7 @@ static void test_throughput(struct config config) {
|
||||
freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
|
||||
|
||||
num = 1000;
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
replies = hi_malloc_safe(sizeof(redisReply*)*num);
|
||||
t1 = usec();
|
||||
for (i = 0; i < num; i++) {
|
||||
replies[i] = redisCommand(c,"PING");
|
||||
@@ -735,10 +1114,10 @@ static void test_throughput(struct config config) {
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
free(replies);
|
||||
hi_free(replies);
|
||||
printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
replies = hi_malloc_safe(sizeof(redisReply*)*num);
|
||||
t1 = usec();
|
||||
for (i = 0; i < num; i++) {
|
||||
replies[i] = redisCommand(c,"LRANGE mylist 0 499");
|
||||
@@ -747,10 +1126,10 @@ static void test_throughput(struct config config) {
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
free(replies);
|
||||
hi_free(replies);
|
||||
printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
replies = hi_malloc_safe(sizeof(redisReply*)*num);
|
||||
t1 = usec();
|
||||
for (i = 0; i < num; i++) {
|
||||
replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000);
|
||||
@@ -758,11 +1137,11 @@ static void test_throughput(struct config config) {
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
free(replies);
|
||||
hi_free(replies);
|
||||
printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
num = 10000;
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
replies = hi_malloc_safe(sizeof(redisReply*)*num);
|
||||
for (i = 0; i < num; i++)
|
||||
redisAppendCommand(c,"PING");
|
||||
t1 = usec();
|
||||
@@ -772,10 +1151,10 @@ static void test_throughput(struct config config) {
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
free(replies);
|
||||
hi_free(replies);
|
||||
printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
replies = hi_malloc_safe(sizeof(redisReply*)*num);
|
||||
for (i = 0; i < num; i++)
|
||||
redisAppendCommand(c,"LRANGE mylist 0 499");
|
||||
t1 = usec();
|
||||
@@ -786,10 +1165,10 @@ static void test_throughput(struct config config) {
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
free(replies);
|
||||
hi_free(replies);
|
||||
printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
replies = hi_malloc_safe(sizeof(redisReply*)*num);
|
||||
for (i = 0; i < num; i++)
|
||||
redisAppendCommand(c,"INCRBY incrkey %d", 1000000);
|
||||
t1 = usec();
|
||||
@@ -799,7 +1178,7 @@ static void test_throughput(struct config config) {
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
free(replies);
|
||||
hi_free(replies);
|
||||
printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
disconnect(c, 0);
|
||||
@@ -916,9 +1295,8 @@ int main(int argc, char **argv) {
|
||||
};
|
||||
int throughput = 1;
|
||||
int test_inherit_fd = 1;
|
||||
|
||||
/* Ignore broken pipe signal (for I/O error tests). */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
int skips_as_fails = 0;
|
||||
int test_unix_socket;
|
||||
|
||||
/* Parse command line options. */
|
||||
argv++; argc--;
|
||||
@@ -936,6 +1314,8 @@ int main(int argc, char **argv) {
|
||||
throughput = 0;
|
||||
} else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) {
|
||||
test_inherit_fd = 0;
|
||||
} else if (argc >= 1 && !strcmp(argv[0],"--skips-as-fails")) {
|
||||
skips_as_fails = 1;
|
||||
#ifdef HIREDIS_TEST_SSL
|
||||
} else if (argc >= 2 && !strcmp(argv[0],"--ssl-port")) {
|
||||
argv++; argc--;
|
||||
@@ -960,6 +1340,19 @@ int main(int argc, char **argv) {
|
||||
argv++; argc--;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
/* Ignore broken pipe signal (for I/O error tests). */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
test_unix_socket = access(cfg.unix_sock.path, F_OK) == 0;
|
||||
|
||||
#else
|
||||
/* Unix sockets don't exist in Windows */
|
||||
test_unix_socket = 0;
|
||||
#endif
|
||||
|
||||
test_allocator_injection();
|
||||
|
||||
test_format_commands();
|
||||
test_reply_reader();
|
||||
test_blocking_connection_errors();
|
||||
@@ -974,15 +1367,25 @@ int main(int argc, char **argv) {
|
||||
test_append_formatted_commands(cfg);
|
||||
if (throughput) test_throughput(cfg);
|
||||
|
||||
printf("\nTesting against Unix socket connection (%s):\n", cfg.unix_sock.path);
|
||||
cfg.type = CONN_UNIX;
|
||||
test_blocking_connection(cfg);
|
||||
test_blocking_connection_timeouts(cfg);
|
||||
test_blocking_io_errors(cfg);
|
||||
if (throughput) test_throughput(cfg);
|
||||
printf("\nTesting against Unix socket connection (%s): ", cfg.unix_sock.path);
|
||||
if (test_unix_socket) {
|
||||
printf("\n");
|
||||
cfg.type = CONN_UNIX;
|
||||
test_blocking_connection(cfg);
|
||||
test_blocking_connection_timeouts(cfg);
|
||||
test_blocking_io_errors(cfg);
|
||||
if (throughput) test_throughput(cfg);
|
||||
} else {
|
||||
test_skipped();
|
||||
}
|
||||
|
||||
#ifdef HIREDIS_TEST_SSL
|
||||
if (cfg.ssl.port && cfg.ssl.host) {
|
||||
|
||||
redisInitOpenSSL();
|
||||
_ssl_ctx = redisCreateSSLContext(cfg.ssl.ca_cert, NULL, cfg.ssl.cert, cfg.ssl.key, NULL, NULL);
|
||||
assert(_ssl_ctx != NULL);
|
||||
|
||||
printf("\nTesting against SSL connection (%s:%d):\n", cfg.ssl.host, cfg.ssl.port);
|
||||
cfg.type = CONN_SSL;
|
||||
|
||||
@@ -992,21 +1395,31 @@ int main(int argc, char **argv) {
|
||||
test_invalid_timeout_errors(cfg);
|
||||
test_append_formatted_commands(cfg);
|
||||
if (throughput) test_throughput(cfg);
|
||||
|
||||
redisFreeSSLContext(_ssl_ctx);
|
||||
_ssl_ctx = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (test_inherit_fd) {
|
||||
printf("\nTesting against inherited fd (%s):\n", cfg.unix_sock.path);
|
||||
cfg.type = CONN_FD;
|
||||
test_blocking_connection(cfg);
|
||||
printf("\nTesting against inherited fd (%s): ", cfg.unix_sock.path);
|
||||
if (test_unix_socket) {
|
||||
printf("\n");
|
||||
cfg.type = CONN_FD;
|
||||
test_blocking_connection(cfg);
|
||||
} else {
|
||||
test_skipped();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (fails) {
|
||||
if (fails || (skips_as_fails && skips)) {
|
||||
printf("*** %d TESTS FAILED ***\n", fails);
|
||||
if (skips) {
|
||||
printf("*** %d TESTS SKIPPED ***\n", skips);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("ALL TESTS PASSED\n");
|
||||
printf("ALL TESTS PASSED (%d skipped)\n", skips);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
+9
-1
@@ -4,7 +4,9 @@ REDIS_SERVER=${REDIS_SERVER:-redis-server}
|
||||
REDIS_PORT=${REDIS_PORT:-56379}
|
||||
REDIS_SSL_PORT=${REDIS_SSL_PORT:-56443}
|
||||
TEST_SSL=${TEST_SSL:-0}
|
||||
SKIPS_AS_FAILS=${SKIPS_AS_FAILS-:0}
|
||||
SSL_TEST_ARGS=
|
||||
SKIPS_ARG=
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
PID_FILE=${tmpdir}/hiredis-test-redis.pid
|
||||
@@ -67,4 +69,10 @@ fi
|
||||
cat ${tmpdir}/redis.conf
|
||||
${REDIS_SERVER} ${tmpdir}/redis.conf
|
||||
|
||||
${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS}
|
||||
# Wait until we detect the unix socket
|
||||
while [ ! -S "${SOCK_FILE}" ]; do sleep 1; done
|
||||
|
||||
# Treat skips as failures if directed
|
||||
[ "$SKIPS_AS_FAILS" = 1 ] && SKIPS_ARG="--skips-as-fails"
|
||||
|
||||
${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS} ${SKIPS_ARG}
|
||||
|
||||
Vendored
+1
-1
@@ -512,7 +512,7 @@ CTARGET='-o $@'
|
||||
LDTARGET='-o $@'
|
||||
TEST_LD_MODE=
|
||||
EXTRA_LDFLAGS=
|
||||
ARFLAGS='crus'
|
||||
ARFLAGS='crs'
|
||||
AROUT=' $@'
|
||||
CC_MM=1
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ ixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE int
|
||||
iget_defrag_hint(tsdn_t *tsdn, void* ptr, int *bin_util, int *run_util) {
|
||||
iget_defrag_hint(tsdn_t *tsdn, void* ptr) {
|
||||
int defrag = 0;
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
|
||||
@@ -232,11 +232,22 @@ iget_defrag_hint(tsdn_t *tsdn, void* ptr, int *bin_util, int *run_util) {
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
/* don't bother moving allocations from the slab currently used for new allocations */
|
||||
if (slab != bin->slabcur) {
|
||||
const bin_info_t *bin_info = &bin_infos[binind];
|
||||
size_t availregs = bin_info->nregs * bin->stats.curslabs;
|
||||
*bin_util = ((long long)bin->stats.curregs<<16) / availregs;
|
||||
*run_util = ((long long)(bin_info->nregs - extent_nfree_get(slab))<<16) / bin_info->nregs;
|
||||
defrag = 1;
|
||||
int free_in_slab = extent_nfree_get(slab);
|
||||
if (free_in_slab) {
|
||||
const bin_info_t *bin_info = &bin_infos[binind];
|
||||
unsigned long curslabs = bin->stats.curslabs;
|
||||
size_t curregs = bin->stats.curregs;
|
||||
if (bin->slabcur) {
|
||||
/* remove slabcur from the overall utilization */
|
||||
curregs -= bin_info->nregs - extent_nfree_get(bin->slabcur);
|
||||
curslabs -= 1;
|
||||
}
|
||||
/* Compare the utilization ratio of the slab in question to the total average,
|
||||
* to avoid precision lost and division, we do that by extrapolating the usage
|
||||
* of the slab as if all slabs have the same usage. If this slab is less used
|
||||
* than the average, we'll prefer to evict the data to hopefully more used ones */
|
||||
defrag = (bin_info->nregs - free_in_slab) * curslabs <= curregs;
|
||||
}
|
||||
}
|
||||
malloc_mutex_unlock(tsdn, &bin->lock);
|
||||
}
|
||||
|
||||
Vendored
+3
-5
@@ -3326,12 +3326,10 @@ jemalloc_postfork_child(void) {
|
||||
/******************************************************************************/
|
||||
|
||||
/* Helps the application decide if a pointer is worth re-allocating in order to reduce fragmentation.
|
||||
* returns 0 if the allocation is in the currently active run,
|
||||
* or when it is not causing any frag issue (large or huge bin)
|
||||
* returns the bin utilization and run utilization both in fixed point 16:16.
|
||||
* returns 1 if the allocation should be moved, and 0 if the allocation be kept.
|
||||
* If the application decides to re-allocate it should use MALLOCX_TCACHE_NONE when doing so. */
|
||||
JEMALLOC_EXPORT int JEMALLOC_NOTHROW
|
||||
get_defrag_hint(void* ptr, int *bin_util, int *run_util) {
|
||||
get_defrag_hint(void* ptr) {
|
||||
assert(ptr != NULL);
|
||||
return iget_defrag_hint(TSDN_NULL, ptr, bin_util, run_util);
|
||||
return iget_defrag_hint(TSDN_NULL, ptr);
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -362,7 +362,7 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
|
||||
} \
|
||||
} while (0)
|
||||
#define GET_ARG_NUMERIC(val, len) do { \
|
||||
switch (len) { \
|
||||
switch ((unsigned char)len) { \
|
||||
case '?': \
|
||||
val = va_arg(ap, int); \
|
||||
break; \
|
||||
|
||||
Vendored
+24
-1
@@ -21,7 +21,7 @@ So what usually happens is either:
|
||||
|
||||
The result is a pollution of binaries without line editing support.
|
||||
|
||||
So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporing line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to Linenoise if not.
|
||||
So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporting line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to Linenoise if not.
|
||||
|
||||
## Terminals, in 2010.
|
||||
|
||||
@@ -126,6 +126,24 @@ Linenoise has direct support for persisting the history into an history
|
||||
file. The functions `linenoiseHistorySave` and `linenoiseHistoryLoad` do
|
||||
just that. Both functions return -1 on error and 0 on success.
|
||||
|
||||
## Mask mode
|
||||
|
||||
Sometimes it is useful to allow the user to type passwords or other
|
||||
secrets that should not be displayed. For such situations linenoise supports
|
||||
a "mask mode" that will just replace the characters the user is typing
|
||||
with `*` characters, like in the following example:
|
||||
|
||||
$ ./linenoise_example
|
||||
hello> get mykey
|
||||
echo: 'get mykey'
|
||||
hello> /mask
|
||||
hello> *********
|
||||
|
||||
You can enable and disable mask mode using the following two functions:
|
||||
|
||||
void linenoiseMaskModeEnable(void);
|
||||
void linenoiseMaskModeDisable(void);
|
||||
|
||||
## Completion
|
||||
|
||||
Linenoise supports completion, which is the ability to complete the user
|
||||
@@ -222,3 +240,8 @@ Sometimes you may want to clear the screen as a result of something the
|
||||
user typed. You can do this by calling the following function:
|
||||
|
||||
void linenoiseClearScreen(void);
|
||||
|
||||
## Related projects
|
||||
|
||||
* [Linenoise NG](https://github.com/arangodb/linenoise-ng) is a fork of Linenoise that aims to add more advanced features like UTF-8 support, Windows support and other features. Uses C++ instead of C as development language.
|
||||
* [Linenoise-swift](https://github.com/andybest/linenoise-swift) is a reimplementation of Linenoise written in Swift.
|
||||
|
||||
Vendored
+5
@@ -55,6 +55,7 @@ int main(int argc, char **argv) {
|
||||
*
|
||||
* The typed string is returned as a malloc() allocated string by
|
||||
* linenoise, so the user needs to free() it. */
|
||||
|
||||
while((line = linenoise("hello> ")) != NULL) {
|
||||
/* Do something with the string. */
|
||||
if (line[0] != '\0' && line[0] != '/') {
|
||||
@@ -65,6 +66,10 @@ int main(int argc, char **argv) {
|
||||
/* The "/historylen" command will change the history len. */
|
||||
int len = atoi(line+11);
|
||||
linenoiseHistorySetMaxLen(len);
|
||||
} else if (!strncmp(line, "/mask", 5)) {
|
||||
linenoiseMaskModeEnable();
|
||||
} else if (!strncmp(line, "/unmask", 7)) {
|
||||
linenoiseMaskModeDisable();
|
||||
} else if (line[0] == '/') {
|
||||
printf("Unreconized command: %s\n", line);
|
||||
}
|
||||
|
||||
Vendored
+31
-5
@@ -125,6 +125,7 @@ static linenoiseHintsCallback *hintsCallback = NULL;
|
||||
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
|
||||
|
||||
static struct termios orig_termios; /* In order to restore at exit.*/
|
||||
static int maskmode = 0; /* Show "***" instead of input. For passwords. */
|
||||
static int rawmode = 0; /* For atexit() function to check if restore is needed*/
|
||||
static int mlmode = 0; /* Multi line mode. Default is single line. */
|
||||
static int atexit_registered = 0; /* Register atexit just 1 time. */
|
||||
@@ -197,6 +198,19 @@ FILE *lndebug_fp = NULL;
|
||||
|
||||
/* ======================= Low level terminal handling ====================== */
|
||||
|
||||
/* Enable "mask mode". When it is enabled, instead of the input that
|
||||
* the user is typing, the terminal will just display a corresponding
|
||||
* number of asterisks, like "****". This is useful for passwords and other
|
||||
* secrets that should not be displayed. */
|
||||
void linenoiseMaskModeEnable(void) {
|
||||
maskmode = 1;
|
||||
}
|
||||
|
||||
/* Disable mask mode. */
|
||||
void linenoiseMaskModeDisable(void) {
|
||||
maskmode = 0;
|
||||
}
|
||||
|
||||
/* Set if to use or not the multi line mode. */
|
||||
void linenoiseSetMultiLine(int ml) {
|
||||
mlmode = ml;
|
||||
@@ -485,6 +499,8 @@ void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
|
||||
if (bold == 1 && color == -1) color = 37;
|
||||
if (color != -1 || bold != 0)
|
||||
snprintf(seq,64,"\033[%d;%d;49m",bold,color);
|
||||
else
|
||||
seq[0] = '\0';
|
||||
abAppend(ab,seq,strlen(seq));
|
||||
abAppend(ab,hint,hintlen);
|
||||
if (color != -1 || bold != 0)
|
||||
@@ -523,7 +539,11 @@ static void refreshSingleLine(struct linenoiseState *l) {
|
||||
abAppend(&ab,seq,strlen(seq));
|
||||
/* Write the prompt and the current buffer content */
|
||||
abAppend(&ab,l->prompt,strlen(l->prompt));
|
||||
abAppend(&ab,buf,len);
|
||||
if (maskmode == 1) {
|
||||
while (len--) abAppend(&ab,"*",1);
|
||||
} else {
|
||||
abAppend(&ab,buf,len);
|
||||
}
|
||||
/* Show hits if any. */
|
||||
refreshShowHints(&ab,l,plen);
|
||||
/* Erase to right */
|
||||
@@ -577,7 +597,12 @@ static void refreshMultiLine(struct linenoiseState *l) {
|
||||
|
||||
/* Write the prompt and the current buffer content */
|
||||
abAppend(&ab,l->prompt,strlen(l->prompt));
|
||||
abAppend(&ab,l->buf,l->len);
|
||||
if (maskmode == 1) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < l->len; i++) abAppend(&ab,"*",1);
|
||||
} else {
|
||||
abAppend(&ab,l->buf,l->len);
|
||||
}
|
||||
|
||||
/* Show hits if any. */
|
||||
refreshShowHints(&ab,l,plen);
|
||||
@@ -600,7 +625,7 @@ static void refreshMultiLine(struct linenoiseState *l) {
|
||||
rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */
|
||||
lndebug("rpos2 %d", rpos2);
|
||||
|
||||
/* Go up till we reach the expected positon. */
|
||||
/* Go up till we reach the expected position. */
|
||||
if (rows-rpos2 > 0) {
|
||||
lndebug("go-up %d", rows-rpos2);
|
||||
snprintf(seq,64,"\x1b[%dA", rows-rpos2);
|
||||
@@ -645,7 +670,8 @@ int linenoiseEditInsert(struct linenoiseState *l, char c) {
|
||||
if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) {
|
||||
/* Avoid a full update of the line in the
|
||||
* trivial case. */
|
||||
if (write(l->ofd,&c,1) == -1) return -1;
|
||||
char d = (maskmode==1) ? '*' : c;
|
||||
if (write(l->ofd,&d,1) == -1) return -1;
|
||||
} else {
|
||||
refreshLine(l);
|
||||
}
|
||||
@@ -741,7 +767,7 @@ void linenoiseEditBackspace(struct linenoiseState *l) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete the previosu word, maintaining the cursor at the start of the
|
||||
/* Delete the previous word, maintaining the cursor at the start of the
|
||||
* current word. */
|
||||
void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
|
||||
size_t old_pos = l->pos;
|
||||
|
||||
Vendored
+2
@@ -65,6 +65,8 @@ int linenoiseHistoryLoad(const char *filename);
|
||||
void linenoiseClearScreen(void);
|
||||
void linenoiseSetMultiLine(int ml);
|
||||
void linenoisePrintKeyCodes(void);
|
||||
void linenoiseMaskModeEnable(void);
|
||||
void linenoiseMaskModeDisable(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
Vendored
+22
@@ -674,6 +674,8 @@ LUA_API void lua_rawset (lua_State *L, int idx) {
|
||||
api_checknelems(L, 2);
|
||||
t = index2adr(L, idx);
|
||||
api_check(L, ttistable(t));
|
||||
if (hvalue(t)->readonly)
|
||||
luaG_runerror(L, "Attempt to modify a readonly table");
|
||||
setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1);
|
||||
luaC_barriert(L, hvalue(t), L->top-1);
|
||||
L->top -= 2;
|
||||
@@ -687,6 +689,8 @@ LUA_API void lua_rawseti (lua_State *L, int idx, int n) {
|
||||
api_checknelems(L, 1);
|
||||
o = index2adr(L, idx);
|
||||
api_check(L, ttistable(o));
|
||||
if (hvalue(o)->readonly)
|
||||
luaG_runerror(L, "Attempt to modify a readonly table");
|
||||
setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1);
|
||||
luaC_barriert(L, hvalue(o), L->top-1);
|
||||
L->top--;
|
||||
@@ -709,6 +713,8 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
|
||||
}
|
||||
switch (ttype(obj)) {
|
||||
case LUA_TTABLE: {
|
||||
if (hvalue(obj)->readonly)
|
||||
luaG_runerror(L, "Attempt to modify a readonly table");
|
||||
hvalue(obj)->metatable = mt;
|
||||
if (mt)
|
||||
luaC_objbarriert(L, hvalue(obj), mt);
|
||||
@@ -1085,3 +1091,19 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
|
||||
return name;
|
||||
}
|
||||
|
||||
LUA_API void lua_enablereadonlytable (lua_State *L, int objindex, int enabled) {
|
||||
const TValue* o = index2adr(L, objindex);
|
||||
api_check(L, ttistable(o));
|
||||
Table* t = hvalue(o);
|
||||
api_check(L, t != hvalue(registry(L)));
|
||||
t->readonly = enabled;
|
||||
}
|
||||
|
||||
LUA_API int lua_isreadonlytable (lua_State *L, int objindex) {
|
||||
const TValue* o = index2adr(L, objindex);
|
||||
api_check(L, ttistable(o));
|
||||
Table* t = hvalue(o);
|
||||
api_check(L, t != hvalue(registry(L)));
|
||||
return t->readonly;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -575,7 +575,7 @@ LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
|
||||
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
|
||||
/* skip eventual `#!...' */
|
||||
while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;
|
||||
lf.extraline = 0;
|
||||
lf.extraline = 0;
|
||||
}
|
||||
ungetc(c, lf.f);
|
||||
status = lua_load(L, getF, &lf, lua_tostring(L, -1));
|
||||
|
||||
Vendored
-1
@@ -80,7 +80,6 @@ LUA_API int lua_gethookcount (lua_State *L) {
|
||||
return L->basehookcount;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
|
||||
int status;
|
||||
CallInfo *ci;
|
||||
|
||||
Vendored
+2
-2
@@ -274,7 +274,7 @@ int luaD_precall (lua_State *L, StkId func, int nresults) {
|
||||
CallInfo *ci;
|
||||
StkId st, base;
|
||||
Proto *p = cl->p;
|
||||
luaD_checkstack(L, p->maxstacksize);
|
||||
luaD_checkstack(L, p->maxstacksize + p->numparams);
|
||||
func = restorestack(L, funcr);
|
||||
if (!p->is_vararg) { /* no varargs? */
|
||||
base = func + 1;
|
||||
@@ -493,7 +493,7 @@ static void f_parser (lua_State *L, void *ud) {
|
||||
Proto *tf;
|
||||
Closure *cl;
|
||||
struct SParser *p = cast(struct SParser *, ud);
|
||||
int c = luaZ_lookahead(p->z);
|
||||
luaZ_lookahead(p->z);
|
||||
luaC_checkGC(L);
|
||||
tf = (luaY_parser)(L, p->z,
|
||||
&p->buff, p->name);
|
||||
|
||||
Vendored
+2
-1
@@ -337,7 +337,8 @@ typedef struct Node {
|
||||
|
||||
typedef struct Table {
|
||||
CommonHeader;
|
||||
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
|
||||
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
|
||||
int readonly;
|
||||
lu_byte lsizenode; /* log2 of size of `node' array */
|
||||
struct Table *metatable;
|
||||
TValue *array; /* array part */
|
||||
|
||||
Vendored
+1
@@ -364,6 +364,7 @@ Table *luaH_new (lua_State *L, int narray, int nhash) {
|
||||
t->array = NULL;
|
||||
t->sizearray = 0;
|
||||
t->lsizenode = 0;
|
||||
t->readonly = 0;
|
||||
t->node = cast(Node *, dummynode);
|
||||
setarrayvector(L, t, narray);
|
||||
setnodevector(L, t, nhash);
|
||||
|
||||
Vendored
+1
-1
@@ -137,7 +137,7 @@ static void addfield (lua_State *L, luaL_Buffer *b, int i) {
|
||||
if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "invalid value (%s) at index %d in table for "
|
||||
LUA_QL("concat"), luaL_typename(L, -1), i);
|
||||
luaL_addvalue(b);
|
||||
luaL_addvalue(b);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+3
@@ -358,6 +358,9 @@ struct lua_Debug {
|
||||
int i_ci; /* active function */
|
||||
};
|
||||
|
||||
LUA_API void lua_enablereadonlytable (lua_State *L, int index, int enabled);
|
||||
LUA_API int lua_isreadonlytable (lua_State *L, int objindex);
|
||||
|
||||
/* }====================================================================== */
|
||||
|
||||
|
||||
|
||||
Vendored
+1
@@ -131,6 +131,7 @@ static int bit_tohex(lua_State *L)
|
||||
const char *hexdigits = "0123456789abcdef";
|
||||
char buf[8];
|
||||
int i;
|
||||
if (n == INT32_MIN) n = INT32_MIN+1;
|
||||
if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }
|
||||
if (n > 8) n = 8;
|
||||
for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
|
||||
|
||||
Vendored
+6
-3
@@ -39,6 +39,7 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
@@ -141,13 +142,13 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
json_token_type_t type;
|
||||
int index;
|
||||
size_t index;
|
||||
union {
|
||||
const char *string;
|
||||
double number;
|
||||
int boolean;
|
||||
} value;
|
||||
int string_len;
|
||||
size_t string_len;
|
||||
} json_token_t;
|
||||
|
||||
static const char *char2escape[256] = {
|
||||
@@ -473,6 +474,8 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
|
||||
* This buffer is reused constantly for small strings
|
||||
* If there are any excess pages, they won't be hit anyway.
|
||||
* This gains ~5% speedup. */
|
||||
if (len > SIZE_MAX / 6 - 3)
|
||||
abort(); /* Overflow check */
|
||||
strbuf_ensure_empty_length(json, len * 6 + 2);
|
||||
|
||||
strbuf_append_char_unsafe(json, '\"');
|
||||
@@ -706,7 +709,7 @@ static int json_encode(lua_State *l)
|
||||
strbuf_t local_encode_buf;
|
||||
strbuf_t *encode_buf;
|
||||
char *json;
|
||||
int len;
|
||||
size_t len;
|
||||
|
||||
luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument");
|
||||
|
||||
|
||||
Vendored
+17
-14
@@ -117,7 +117,9 @@ mp_buf *mp_buf_new(lua_State *L) {
|
||||
|
||||
void mp_buf_append(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) {
|
||||
if (buf->free < len) {
|
||||
size_t newsize = (buf->len+len)*2;
|
||||
size_t newsize = buf->len+len;
|
||||
if (newsize < buf->len || newsize >= SIZE_MAX/2) abort();
|
||||
newsize *= 2;
|
||||
|
||||
buf->b = (unsigned char*)mp_realloc(L, buf->b, buf->len + buf->free, newsize);
|
||||
buf->free = newsize - buf->len;
|
||||
@@ -173,7 +175,7 @@ void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {
|
||||
|
||||
void mp_encode_bytes(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) {
|
||||
unsigned char hdr[5];
|
||||
int hdrlen;
|
||||
size_t hdrlen;
|
||||
|
||||
if (len < 32) {
|
||||
hdr[0] = 0xa0 | (len&0xff); /* fix raw */
|
||||
@@ -220,7 +222,7 @@ void mp_encode_double(lua_State *L, mp_buf *buf, double d) {
|
||||
|
||||
void mp_encode_int(lua_State *L, mp_buf *buf, int64_t n) {
|
||||
unsigned char b[9];
|
||||
int enclen;
|
||||
size_t enclen;
|
||||
|
||||
if (n >= 0) {
|
||||
if (n <= 127) {
|
||||
@@ -290,9 +292,9 @@ void mp_encode_int(lua_State *L, mp_buf *buf, int64_t n) {
|
||||
mp_buf_append(L,buf,b,enclen);
|
||||
}
|
||||
|
||||
void mp_encode_array(lua_State *L, mp_buf *buf, int64_t n) {
|
||||
void mp_encode_array(lua_State *L, mp_buf *buf, uint64_t n) {
|
||||
unsigned char b[5];
|
||||
int enclen;
|
||||
size_t enclen;
|
||||
|
||||
if (n <= 15) {
|
||||
b[0] = 0x90 | (n & 0xf); /* fix array */
|
||||
@@ -313,7 +315,7 @@ void mp_encode_array(lua_State *L, mp_buf *buf, int64_t n) {
|
||||
mp_buf_append(L,buf,b,enclen);
|
||||
}
|
||||
|
||||
void mp_encode_map(lua_State *L, mp_buf *buf, int64_t n) {
|
||||
void mp_encode_map(lua_State *L, mp_buf *buf, uint64_t n) {
|
||||
unsigned char b[5];
|
||||
int enclen;
|
||||
|
||||
@@ -790,7 +792,7 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
|
||||
}
|
||||
}
|
||||
|
||||
int mp_unpack_full(lua_State *L, int limit, int offset) {
|
||||
int mp_unpack_full(lua_State *L, lua_Integer limit, lua_Integer offset) {
|
||||
size_t len;
|
||||
const char *s;
|
||||
mp_cur c;
|
||||
@@ -802,10 +804,10 @@ int mp_unpack_full(lua_State *L, int limit, int offset) {
|
||||
if (offset < 0 || limit < 0) /* requesting negative off or lim is invalid */
|
||||
return luaL_error(L,
|
||||
"Invalid request to unpack with offset of %d and limit of %d.",
|
||||
offset, len);
|
||||
(int) offset, (int) len);
|
||||
else if (offset > len)
|
||||
return luaL_error(L,
|
||||
"Start offset %d greater than input length %d.", offset, len);
|
||||
"Start offset %d greater than input length %d.", (int) offset, (int) len);
|
||||
|
||||
if (decode_all) limit = INT_MAX;
|
||||
|
||||
@@ -827,12 +829,13 @@ int mp_unpack_full(lua_State *L, int limit, int offset) {
|
||||
/* c->left is the remaining size of the input buffer.
|
||||
* subtract the entire buffer size from the unprocessed size
|
||||
* to get our next start offset */
|
||||
int offset = len - c.left;
|
||||
size_t new_offset = len - c.left;
|
||||
if (new_offset > LONG_MAX) abort();
|
||||
|
||||
luaL_checkstack(L, 1, "in function mp_unpack_full");
|
||||
|
||||
/* Return offset -1 when we have have processed the entire buffer. */
|
||||
lua_pushinteger(L, c.left == 0 ? -1 : offset);
|
||||
lua_pushinteger(L, c.left == 0 ? -1 : (lua_Integer) new_offset);
|
||||
/* Results are returned with the arg elements still
|
||||
* in place. Lua takes care of only returning
|
||||
* elements above the args for us.
|
||||
@@ -851,15 +854,15 @@ int mp_unpack(lua_State *L) {
|
||||
}
|
||||
|
||||
int mp_unpack_one(lua_State *L) {
|
||||
int offset = luaL_optinteger(L, 2, 0);
|
||||
lua_Integer offset = luaL_optinteger(L, 2, 0);
|
||||
/* Variable pop because offset may not exist */
|
||||
lua_pop(L, lua_gettop(L)-1);
|
||||
return mp_unpack_full(L, 1, offset);
|
||||
}
|
||||
|
||||
int mp_unpack_limit(lua_State *L) {
|
||||
int limit = luaL_checkinteger(L, 2);
|
||||
int offset = luaL_optinteger(L, 3, 0);
|
||||
lua_Integer limit = luaL_checkinteger(L, 2);
|
||||
lua_Integer offset = luaL_optinteger(L, 3, 0);
|
||||
/* Variable pop because offset may not exist */
|
||||
lua_pop(L, lua_gettop(L)-1);
|
||||
|
||||
|
||||
Vendored
+6
-4
@@ -89,12 +89,14 @@ typedef struct Header {
|
||||
} Header;
|
||||
|
||||
|
||||
static int getnum (const char **fmt, int df) {
|
||||
static int getnum (lua_State *L, const char **fmt, int df) {
|
||||
if (!isdigit(**fmt)) /* no number? */
|
||||
return df; /* return default value */
|
||||
else {
|
||||
int a = 0;
|
||||
do {
|
||||
if (a > (INT_MAX / 10) || a * 10 > (INT_MAX - (**fmt - '0')))
|
||||
luaL_error(L, "integral size overflow");
|
||||
a = a*10 + *((*fmt)++) - '0';
|
||||
} while (isdigit(**fmt));
|
||||
return a;
|
||||
@@ -115,9 +117,9 @@ static size_t optsize (lua_State *L, char opt, const char **fmt) {
|
||||
case 'f': return sizeof(float);
|
||||
case 'd': return sizeof(double);
|
||||
case 'x': return 1;
|
||||
case 'c': return getnum(fmt, 1);
|
||||
case 'c': return getnum(L, fmt, 1);
|
||||
case 'i': case 'I': {
|
||||
int sz = getnum(fmt, sizeof(int));
|
||||
int sz = getnum(L, fmt, sizeof(int));
|
||||
if (sz > MAXINTSIZE)
|
||||
luaL_error(L, "integral size %d is larger than limit of %d",
|
||||
sz, MAXINTSIZE);
|
||||
@@ -150,7 +152,7 @@ static void controloptions (lua_State *L, int opt, const char **fmt,
|
||||
case '>': h->endian = BIG; return;
|
||||
case '<': h->endian = LITTLE; return;
|
||||
case '!': {
|
||||
int a = getnum(fmt, MAXALIGN);
|
||||
int a = getnum(L, fmt, MAXALIGN);
|
||||
if (!isp2(a))
|
||||
luaL_error(L, "alignment %d is not a power of 2", a);
|
||||
h->align = a;
|
||||
|
||||
Vendored
+2
@@ -138,6 +138,8 @@ void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
|
||||
const TValue *tm;
|
||||
if (ttistable(t)) { /* `t' is a table? */
|
||||
Table *h = hvalue(t);
|
||||
if (h->readonly)
|
||||
luaG_runerror(L, "Attempt to modify a readonly table");
|
||||
TValue *oldval = luaH_set(L, h, key); /* do a primitive set */
|
||||
if (!ttisnil(oldval) || /* result is no nil? */
|
||||
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
|
||||
|
||||
Vendored
+28
-81
@@ -26,6 +26,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "strbuf.h"
|
||||
|
||||
@@ -38,22 +39,22 @@ static void die(const char *fmt, ...)
|
||||
va_end(arg);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
exit(-1);
|
||||
abort();
|
||||
}
|
||||
|
||||
void strbuf_init(strbuf_t *s, int len)
|
||||
void strbuf_init(strbuf_t *s, size_t len)
|
||||
{
|
||||
int size;
|
||||
size_t size;
|
||||
|
||||
if (len <= 0)
|
||||
if (!len)
|
||||
size = STRBUF_DEFAULT_SIZE;
|
||||
else
|
||||
size = len + 1; /* \0 terminator */
|
||||
|
||||
size = len + 1;
|
||||
if (size < len)
|
||||
die("Overflow, len: %zu", len);
|
||||
s->buf = NULL;
|
||||
s->size = size;
|
||||
s->length = 0;
|
||||
s->increment = STRBUF_DEFAULT_INCREMENT;
|
||||
s->dynamic = 0;
|
||||
s->reallocs = 0;
|
||||
s->debug = 0;
|
||||
@@ -65,7 +66,7 @@ void strbuf_init(strbuf_t *s, int len)
|
||||
strbuf_ensure_null(s);
|
||||
}
|
||||
|
||||
strbuf_t *strbuf_new(int len)
|
||||
strbuf_t *strbuf_new(size_t len)
|
||||
{
|
||||
strbuf_t *s;
|
||||
|
||||
@@ -81,20 +82,10 @@ strbuf_t *strbuf_new(int len)
|
||||
return s;
|
||||
}
|
||||
|
||||
void strbuf_set_increment(strbuf_t *s, int increment)
|
||||
{
|
||||
/* Increment > 0: Linear buffer growth rate
|
||||
* Increment < -1: Exponential buffer growth rate */
|
||||
if (increment == 0 || increment == -1)
|
||||
die("BUG: Invalid string increment");
|
||||
|
||||
s->increment = increment;
|
||||
}
|
||||
|
||||
static inline void debug_stats(strbuf_t *s)
|
||||
{
|
||||
if (s->debug) {
|
||||
fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
|
||||
fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %zd, size: %zd\n",
|
||||
(long)s, s->reallocs, s->length, s->size);
|
||||
}
|
||||
}
|
||||
@@ -113,7 +104,7 @@ void strbuf_free(strbuf_t *s)
|
||||
free(s);
|
||||
}
|
||||
|
||||
char *strbuf_free_to_string(strbuf_t *s, int *len)
|
||||
char *strbuf_free_to_string(strbuf_t *s, size_t *len)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
@@ -131,57 +122,62 @@ char *strbuf_free_to_string(strbuf_t *s, int *len)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int calculate_new_size(strbuf_t *s, int len)
|
||||
static size_t calculate_new_size(strbuf_t *s, size_t len)
|
||||
{
|
||||
int reqsize, newsize;
|
||||
size_t reqsize, newsize;
|
||||
|
||||
if (len <= 0)
|
||||
die("BUG: Invalid strbuf length requested");
|
||||
|
||||
/* Ensure there is room for optional NULL termination */
|
||||
reqsize = len + 1;
|
||||
if (reqsize < len)
|
||||
die("Overflow, len: %zu", len);
|
||||
|
||||
/* If the user has requested to shrink the buffer, do it exactly */
|
||||
if (s->size > reqsize)
|
||||
return reqsize;
|
||||
|
||||
newsize = s->size;
|
||||
if (s->increment < 0) {
|
||||
if (reqsize >= SIZE_MAX / 2) {
|
||||
newsize = reqsize;
|
||||
} else {
|
||||
/* Exponential sizing */
|
||||
while (newsize < reqsize)
|
||||
newsize *= -s->increment;
|
||||
} else {
|
||||
/* Linear sizing */
|
||||
newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
|
||||
newsize *= 2;
|
||||
}
|
||||
|
||||
if (newsize < reqsize)
|
||||
die("BUG: strbuf length would overflow, len: %zu", len);
|
||||
|
||||
return newsize;
|
||||
}
|
||||
|
||||
|
||||
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
|
||||
* optional termination). */
|
||||
void strbuf_resize(strbuf_t *s, int len)
|
||||
void strbuf_resize(strbuf_t *s, size_t len)
|
||||
{
|
||||
int newsize;
|
||||
size_t newsize;
|
||||
|
||||
newsize = calculate_new_size(s, len);
|
||||
|
||||
if (s->debug > 1) {
|
||||
fprintf(stderr, "strbuf(%lx) resize: %d => %d\n",
|
||||
fprintf(stderr, "strbuf(%lx) resize: %zd => %zd\n",
|
||||
(long)s, s->size, newsize);
|
||||
}
|
||||
|
||||
s->size = newsize;
|
||||
s->buf = realloc(s->buf, s->size);
|
||||
if (!s->buf)
|
||||
die("Out of memory");
|
||||
die("Out of memory, len: %zu", len);
|
||||
s->reallocs++;
|
||||
}
|
||||
|
||||
void strbuf_append_string(strbuf_t *s, const char *str)
|
||||
{
|
||||
int space, i;
|
||||
int i;
|
||||
size_t space;
|
||||
|
||||
space = strbuf_empty_length(s);
|
||||
|
||||
@@ -197,55 +193,6 @@ void strbuf_append_string(strbuf_t *s, const char *str)
|
||||
}
|
||||
}
|
||||
|
||||
/* strbuf_append_fmt() should only be used when an upper bound
|
||||
* is known for the output string. */
|
||||
void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int fmt_len;
|
||||
|
||||
strbuf_ensure_empty_length(s, len);
|
||||
|
||||
va_start(arg, fmt);
|
||||
fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (fmt_len < 0)
|
||||
die("BUG: Unable to convert number"); /* This should never happen.. */
|
||||
|
||||
s->length += fmt_len;
|
||||
}
|
||||
|
||||
/* strbuf_append_fmt_retry() can be used when the there is no known
|
||||
* upper bound for the output string. */
|
||||
void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int fmt_len, try;
|
||||
int empty_len;
|
||||
|
||||
/* If the first attempt to append fails, resize the buffer appropriately
|
||||
* and try again */
|
||||
for (try = 0; ; try++) {
|
||||
va_start(arg, fmt);
|
||||
/* Append the new formatted string */
|
||||
/* fmt_len is the length of the string required, excluding the
|
||||
* trailing NULL */
|
||||
empty_len = strbuf_empty_length(s);
|
||||
/* Add 1 since there is also space to store the terminating NULL. */
|
||||
fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (fmt_len <= empty_len)
|
||||
break; /* SUCCESS */
|
||||
if (try > 0)
|
||||
die("BUG: length of formatted string changed");
|
||||
|
||||
strbuf_resize(s, s->length + fmt_len);
|
||||
}
|
||||
|
||||
s->length += fmt_len;
|
||||
}
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
||||
|
||||
Vendored
+19
-27
@@ -27,15 +27,13 @@
|
||||
|
||||
/* Size: Total bytes allocated to *buf
|
||||
* Length: String length, excluding optional NULL terminator.
|
||||
* Increment: Allocation increments when resizing the string buffer.
|
||||
* Dynamic: True if created via strbuf_new()
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
char *buf;
|
||||
int size;
|
||||
int length;
|
||||
int increment;
|
||||
size_t size;
|
||||
size_t length;
|
||||
int dynamic;
|
||||
int reallocs;
|
||||
int debug;
|
||||
@@ -44,32 +42,26 @@ typedef struct {
|
||||
#ifndef STRBUF_DEFAULT_SIZE
|
||||
#define STRBUF_DEFAULT_SIZE 1023
|
||||
#endif
|
||||
#ifndef STRBUF_DEFAULT_INCREMENT
|
||||
#define STRBUF_DEFAULT_INCREMENT -2
|
||||
#endif
|
||||
|
||||
/* Initialise */
|
||||
extern strbuf_t *strbuf_new(int len);
|
||||
extern void strbuf_init(strbuf_t *s, int len);
|
||||
extern void strbuf_set_increment(strbuf_t *s, int increment);
|
||||
extern strbuf_t *strbuf_new(size_t len);
|
||||
extern void strbuf_init(strbuf_t *s, size_t len);
|
||||
|
||||
/* Release */
|
||||
extern void strbuf_free(strbuf_t *s);
|
||||
extern char *strbuf_free_to_string(strbuf_t *s, int *len);
|
||||
extern char *strbuf_free_to_string(strbuf_t *s, size_t *len);
|
||||
|
||||
/* Management */
|
||||
extern void strbuf_resize(strbuf_t *s, int len);
|
||||
static int strbuf_empty_length(strbuf_t *s);
|
||||
static int strbuf_length(strbuf_t *s);
|
||||
static char *strbuf_string(strbuf_t *s, int *len);
|
||||
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
|
||||
extern void strbuf_resize(strbuf_t *s, size_t len);
|
||||
static size_t strbuf_empty_length(strbuf_t *s);
|
||||
static size_t strbuf_length(strbuf_t *s);
|
||||
static char *strbuf_string(strbuf_t *s, size_t *len);
|
||||
static void strbuf_ensure_empty_length(strbuf_t *s, size_t len);
|
||||
static char *strbuf_empty_ptr(strbuf_t *s);
|
||||
static void strbuf_extend_length(strbuf_t *s, int len);
|
||||
static void strbuf_extend_length(strbuf_t *s, size_t len);
|
||||
|
||||
/* Update */
|
||||
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
|
||||
extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);
|
||||
static void strbuf_append_mem(strbuf_t *s, const char *c, int len);
|
||||
static void strbuf_append_mem(strbuf_t *s, const char *c, size_t len);
|
||||
extern void strbuf_append_string(strbuf_t *s, const char *str);
|
||||
static void strbuf_append_char(strbuf_t *s, const char c);
|
||||
static void strbuf_ensure_null(strbuf_t *s);
|
||||
@@ -87,12 +79,12 @@ static inline int strbuf_allocated(strbuf_t *s)
|
||||
|
||||
/* Return bytes remaining in the string buffer
|
||||
* Ensure there is space for a NULL terminator. */
|
||||
static inline int strbuf_empty_length(strbuf_t *s)
|
||||
static inline size_t strbuf_empty_length(strbuf_t *s)
|
||||
{
|
||||
return s->size - s->length - 1;
|
||||
}
|
||||
|
||||
static inline void strbuf_ensure_empty_length(strbuf_t *s, int len)
|
||||
static inline void strbuf_ensure_empty_length(strbuf_t *s, size_t len)
|
||||
{
|
||||
if (len > strbuf_empty_length(s))
|
||||
strbuf_resize(s, s->length + len);
|
||||
@@ -103,12 +95,12 @@ static inline char *strbuf_empty_ptr(strbuf_t *s)
|
||||
return s->buf + s->length;
|
||||
}
|
||||
|
||||
static inline void strbuf_extend_length(strbuf_t *s, int len)
|
||||
static inline void strbuf_extend_length(strbuf_t *s, size_t len)
|
||||
{
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline int strbuf_length(strbuf_t *s)
|
||||
static inline size_t strbuf_length(strbuf_t *s)
|
||||
{
|
||||
return s->length;
|
||||
}
|
||||
@@ -124,14 +116,14 @@ static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
|
||||
s->buf[s->length++] = c;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len)
|
||||
static inline void strbuf_append_mem(strbuf_t *s, const char *c, size_t len)
|
||||
{
|
||||
strbuf_ensure_empty_length(s, len);
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len)
|
||||
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, size_t len)
|
||||
{
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
@@ -142,7 +134,7 @@ static inline void strbuf_ensure_null(strbuf_t *s)
|
||||
s->buf[s->length] = 0;
|
||||
}
|
||||
|
||||
static inline char *strbuf_string(strbuf_t *s, int *len)
|
||||
static inline char *strbuf_string(strbuf_t *s, size_t *len)
|
||||
{
|
||||
if (len)
|
||||
*len = s->length;
|
||||
|
||||
+493
-142
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
TCL_VERSIONS="8.5 8.6"
|
||||
TCL_VERSIONS="8.5 8.6 8.7"
|
||||
TCLSH=""
|
||||
|
||||
for VERSION in $TCL_VERSIONS; do
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
TCL_VERSIONS="8.5 8.6"
|
||||
TCL_VERSIONS="8.5 8.6 8.7"
|
||||
TCLSH=""
|
||||
|
||||
for VERSION in $TCL_VERSIONS; do
|
||||
@@ -8,7 +8,7 @@ done
|
||||
|
||||
if [ -z $TCLSH ]
|
||||
then
|
||||
echo "You need tcl 8.5 or newer in order to run the Redis Sentinel test"
|
||||
echo "You need tcl 8.5 or newer in order to run the Redis Cluster test"
|
||||
exit 1
|
||||
fi
|
||||
$TCLSH tests/cluster/run.tcl $*
|
||||
|
||||
+15
-3
@@ -1,6 +1,7 @@
|
||||
#!/bin/sh
|
||||
TCL_VERSIONS="8.5 8.6"
|
||||
TCL_VERSIONS="8.5 8.6 8.7"
|
||||
TCLSH=""
|
||||
[ -z "$MAKE" ] && MAKE=make
|
||||
|
||||
for VERSION in $TCL_VERSIONS; do
|
||||
TCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL
|
||||
@@ -8,13 +9,14 @@ done
|
||||
|
||||
if [ -z $TCLSH ]
|
||||
then
|
||||
echo "You need tcl 8.5 or newer in order to run the Redis test"
|
||||
echo "You need tcl 8.5 or newer in order to run the Redis ModuleApi test"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
make -C tests/modules && \
|
||||
$MAKE -C tests/modules && \
|
||||
$TCLSH tests/test_helper.tcl \
|
||||
--single unit/moduleapi/commandfilter \
|
||||
--single unit/moduleapi/basics \
|
||||
--single unit/moduleapi/fork \
|
||||
--single unit/moduleapi/testrdb \
|
||||
--single unit/moduleapi/infotest \
|
||||
@@ -22,7 +24,17 @@ $TCLSH tests/test_helper.tcl \
|
||||
--single unit/moduleapi/hooks \
|
||||
--single unit/moduleapi/misc \
|
||||
--single unit/moduleapi/blockonkeys \
|
||||
--single unit/moduleapi/blockonbackground \
|
||||
--single unit/moduleapi/scan \
|
||||
--single unit/moduleapi/datatype \
|
||||
--single unit/moduleapi/auth \
|
||||
--single unit/moduleapi/keyspace_events \
|
||||
--single unit/moduleapi/blockedclient \
|
||||
--single unit/moduleapi/getkeys \
|
||||
--single unit/moduleapi/test_lazyfree \
|
||||
--single unit/moduleapi/defrag \
|
||||
--single unit/moduleapi/hash \
|
||||
--single unit/moduleapi/zset \
|
||||
--single unit/moduleapi/stream \
|
||||
--single unit/moduleapi/cluster \
|
||||
"${@}"
|
||||
|
||||
+98
-1
@@ -102,6 +102,18 @@ sentinel monitor mymaster 127.0.0.1 6379 2
|
||||
#
|
||||
# sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
|
||||
|
||||
# sentinel auth-user <master-name> <username>
|
||||
#
|
||||
# This is useful in order to authenticate to instances having ACL capabilities,
|
||||
# that is, running Redis 6.0 or greater. When just auth-pass is provided the
|
||||
# Sentinel instance will authenticate to Redis using the old "AUTH <pass>"
|
||||
# method. When also an username is provided, it will use "AUTH <user> <pass>".
|
||||
# In the Redis servers side, the ACL to provide just minimal access to
|
||||
# Sentinel instances, should be configured along the following lines:
|
||||
#
|
||||
# user sentinel-user >somepassword +client +subscribe +publish \
|
||||
# +ping +info +multi +slaveof +config +client +exec on
|
||||
|
||||
# sentinel down-after-milliseconds <master-name> <milliseconds>
|
||||
#
|
||||
# Number of milliseconds the master (or any attached replica or sentinel) should
|
||||
@@ -112,6 +124,73 @@ sentinel monitor mymaster 127.0.0.1 6379 2
|
||||
# Default is 30 seconds.
|
||||
sentinel down-after-milliseconds mymaster 30000
|
||||
|
||||
# IMPORTANT NOTE: starting with Redis 6.2 ACL capability is supported for
|
||||
# Sentinel mode, please refer to the Redis website https://redis.io/topics/acl
|
||||
# for more details.
|
||||
|
||||
# Sentinel's ACL users are defined in the following format:
|
||||
#
|
||||
# user <username> ... acl rules ...
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# user worker +@admin +@connection ~* on >ffa9203c493aa99
|
||||
#
|
||||
# For more information about ACL configuration please refer to the Redis
|
||||
# website at https://redis.io/topics/acl and redis server configuration
|
||||
# template redis.conf.
|
||||
|
||||
# ACL LOG
|
||||
#
|
||||
# The ACL Log tracks failed commands and authentication events associated
|
||||
# with ACLs. The ACL Log is useful to troubleshoot failed commands blocked
|
||||
# by ACLs. The ACL Log is stored in memory. You can reclaim memory with
|
||||
# ACL LOG RESET. Define the maximum entry length of the ACL Log below.
|
||||
acllog-max-len 128
|
||||
|
||||
# Using an external ACL file
|
||||
#
|
||||
# Instead of configuring users here in this file, it is possible to use
|
||||
# a stand-alone file just listing users. The two methods cannot be mixed:
|
||||
# if you configure users here and at the same time you activate the external
|
||||
# ACL file, the server will refuse to start.
|
||||
#
|
||||
# The format of the external ACL user file is exactly the same as the
|
||||
# format that is used inside redis.conf to describe users.
|
||||
#
|
||||
# aclfile /etc/redis/sentinel-users.acl
|
||||
|
||||
# requirepass <password>
|
||||
#
|
||||
# You can configure Sentinel itself to require a password, however when doing
|
||||
# so Sentinel will try to authenticate with the same password to all the
|
||||
# other Sentinels. So you need to configure all your Sentinels in a given
|
||||
# group with the same "requirepass" password. Check the following documentation
|
||||
# for more info: https://redis.io/topics/sentinel
|
||||
#
|
||||
# IMPORTANT NOTE: starting with Redis 6.2 "requirepass" is a compatibility
|
||||
# layer on top of the ACL system. The option effect will be just setting
|
||||
# the password for the default user. Clients will still authenticate using
|
||||
# AUTH <password> as usually, or more explicitly with AUTH default <password>
|
||||
# if they follow the new protocol: both will work.
|
||||
#
|
||||
# New config files are advised to use separate authentication control for
|
||||
# incoming connections (via ACL), and for outgoing connections (via
|
||||
# sentinel-user and sentinel-pass)
|
||||
#
|
||||
# The requirepass is not compatable with aclfile option and the ACL LOAD
|
||||
# command, these will cause requirepass to be ignored.
|
||||
|
||||
# sentinel sentinel-user <username>
|
||||
#
|
||||
# You can configure Sentinel to authenticate with other Sentinels with specific
|
||||
# user name.
|
||||
|
||||
# sentinel sentinel-pass <password>
|
||||
#
|
||||
# The password for Sentinel to authenticate with other Sentinels. If sentinel-user
|
||||
# is not configured, Sentinel will use 'default' user with sentinel-pass to authenticate.
|
||||
|
||||
# sentinel parallel-syncs <master-name> <numreplicas>
|
||||
#
|
||||
# How many replicas we can reconfigure to point to the new replica simultaneously
|
||||
@@ -239,6 +318,24 @@ sentinel deny-scripts-reconfig yes
|
||||
# SENTINEL SET can also be used in order to perform this configuration at runtime.
|
||||
#
|
||||
# In order to set a command back to its original name (undo the renaming), it
|
||||
# is possible to just rename a command to itsef:
|
||||
# is possible to just rename a command to itself:
|
||||
#
|
||||
# SENTINEL rename-command mymaster CONFIG CONFIG
|
||||
|
||||
# HOSTNAMES SUPPORT
|
||||
#
|
||||
# Normally Sentinel uses only IP addresses and requires SENTINEL MONITOR
|
||||
# to specify an IP address. Also, it requires the Redis replica-announce-ip
|
||||
# keyword to specify only IP addresses.
|
||||
#
|
||||
# You may enable hostnames support by enabling resolve-hostnames. Note
|
||||
# that you must make sure your DNS is configured properly and that DNS
|
||||
# resolution does not introduce very long delays.
|
||||
#
|
||||
SENTINEL resolve-hostnames no
|
||||
|
||||
# When resolve-hostnames is enabled, Sentinel still uses IP addresses
|
||||
# when exposing instances to users, configuration files, etc. If you want
|
||||
# to retain the hostnames when announced, enable announce-hostnames below.
|
||||
#
|
||||
SENTINEL announce-hostnames no
|
||||
|
||||
+136
-48
@@ -16,12 +16,17 @@ release_hdr := $(shell sh -c './mkreleasehdr.sh')
|
||||
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
|
||||
OPTIMIZATION?=-O2
|
||||
DEPENDENCY_TARGETS=hiredis linenoise lua
|
||||
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram
|
||||
NODEPS:=clean distclean
|
||||
|
||||
# Default settings
|
||||
STD=-std=c11 -pedantic -DREDIS_STATIC=''
|
||||
STD=-pedantic -DREDIS_STATIC=''
|
||||
|
||||
# Use -Wno-c11-extensions on clang, either where explicitly used or on
|
||||
# platforms we can assume it's being used.
|
||||
ifneq (,$(findstring clang,$(CC)))
|
||||
STD+=-Wno-c11-extensions
|
||||
else
|
||||
ifneq (,$(findstring FreeBSD,$(uname_S)))
|
||||
STD+=-Wno-c11-extensions
|
||||
endif
|
||||
@@ -29,6 +34,18 @@ endif
|
||||
WARN=-Wall -W -Wno-missing-field-initializers
|
||||
OPT=$(OPTIMIZATION)
|
||||
|
||||
# Detect if the compiler supports C11 _Atomic.
|
||||
# NUMBER_SIGN_CHAR is a workaround to support both GNU Make 4.3 and older versions.
|
||||
NUMBER_SIGN_CHAR := \#
|
||||
C11_ATOMIC := $(shell sh -c 'echo "$(NUMBER_SIGN_CHAR)include <stdatomic.h>" > foo.c; \
|
||||
$(CC) -std=c11 -c foo.c -o foo.o > /dev/null 2>&1; \
|
||||
if [ -f foo.o ]; then echo "yes"; rm foo.o; fi; rm foo.c')
|
||||
ifeq ($(C11_ATOMIC),yes)
|
||||
STD+=-std=c11
|
||||
else
|
||||
STD+=-std=c99
|
||||
endif
|
||||
|
||||
PREFIX?=/usr/local
|
||||
INSTALL_BIN=$(PREFIX)/bin
|
||||
INSTALL=install
|
||||
@@ -78,20 +95,18 @@ FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG)
|
||||
FINAL_LIBS=-lm
|
||||
DEBUG=-g -ggdb
|
||||
|
||||
# Linux ARM needs -latomic at linking time
|
||||
ifneq (,$(filter aarch64 armv,$(uname_M)))
|
||||
FINAL_LIBS+=-latomic
|
||||
else
|
||||
# Linux ARM32 needs -latomic at linking time
|
||||
ifneq (,$(findstring armv,$(uname_M)))
|
||||
FINAL_LIBS+=-latomic
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(uname_S),SunOS)
|
||||
# SunOS
|
||||
ifneq ($(@@),32bit)
|
||||
CFLAGS+= -m64
|
||||
LDFLAGS+= -m64
|
||||
ifeq ($(findstring -m32,$(FINAL_CFLAGS)),)
|
||||
CFLAGS+=-m64
|
||||
endif
|
||||
ifeq ($(findstring -m32,$(FINAL_LDFLAGS)),)
|
||||
LDFLAGS+=-m64
|
||||
endif
|
||||
DEBUG=-g
|
||||
DEBUG_FLAGS=-g
|
||||
@@ -103,8 +118,16 @@ else
|
||||
ifeq ($(uname_S),Darwin)
|
||||
# Darwin
|
||||
FINAL_LIBS+= -ldl
|
||||
OPENSSL_CFLAGS=-I/usr/local/opt/openssl/include
|
||||
OPENSSL_LDFLAGS=-L/usr/local/opt/openssl/lib
|
||||
# Homebrew's OpenSSL is not linked to /usr/local to avoid
|
||||
# conflicts with the system's LibreSSL installation so it
|
||||
# must be referenced explicitly during build.
|
||||
ifeq ($(uname_M),arm64)
|
||||
# Homebrew arm64 uses /opt/homebrew as HOMEBREW_PREFIX
|
||||
OPENSSL_PREFIX?=/opt/homebrew/opt/openssl
|
||||
else
|
||||
# Homebrew x86/ppc uses /usr/local as HOMEBREW_PREFIX
|
||||
OPENSSL_PREFIX?=/usr/local/opt/openssl
|
||||
endif
|
||||
else
|
||||
ifeq ($(uname_S),AIX)
|
||||
# AIX
|
||||
@@ -121,13 +144,36 @@ ifeq ($(uname_S),OpenBSD)
|
||||
endif
|
||||
|
||||
else
|
||||
ifeq ($(uname_S),NetBSD)
|
||||
# NetBSD
|
||||
FINAL_LIBS+= -lpthread
|
||||
ifeq ($(USE_BACKTRACE),yes)
|
||||
FINAL_CFLAGS+= -DUSE_BACKTRACE -I/usr/pkg/include
|
||||
FINAL_LDFLAGS+= -L/usr/pkg/lib
|
||||
FINAL_LIBS+= -lexecinfo
|
||||
endif
|
||||
else
|
||||
ifeq ($(uname_S),FreeBSD)
|
||||
# FreeBSD
|
||||
FINAL_LIBS+= -lpthread -lexecinfo
|
||||
else
|
||||
ifeq ($(uname_S),DragonFly)
|
||||
# FreeBSD
|
||||
# DragonFly
|
||||
FINAL_LIBS+= -lpthread -lexecinfo
|
||||
else
|
||||
ifeq ($(uname_S),OpenBSD)
|
||||
# OpenBSD
|
||||
FINAL_LIBS+= -lpthread -lexecinfo
|
||||
else
|
||||
ifeq ($(uname_S),NetBSD)
|
||||
# NetBSD
|
||||
FINAL_LIBS+= -lpthread -lexecinfo
|
||||
else
|
||||
ifeq ($(uname_S),Haiku)
|
||||
# Haiku
|
||||
FINAL_CFLAGS+= -DBSD_SOURCE
|
||||
FINAL_LDFLAGS+= -lbsd -lnetwork
|
||||
FINAL_LIBS+= -lpthread
|
||||
else
|
||||
# All the other OSes (notably Linux)
|
||||
FINAL_LDFLAGS+= -rdynamic
|
||||
@@ -138,11 +184,25 @@ endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef OPENSSL_PREFIX
|
||||
OPENSSL_CFLAGS=-I$(OPENSSL_PREFIX)/include
|
||||
OPENSSL_LDFLAGS=-L$(OPENSSL_PREFIX)/lib
|
||||
# Also export OPENSSL_PREFIX so it ends up in deps sub-Makefiles
|
||||
export OPENSSL_PREFIX
|
||||
endif
|
||||
|
||||
# Include paths to dependencies
|
||||
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src
|
||||
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram
|
||||
|
||||
# Determine systemd support and/or build preference (defaulting to auto-detection)
|
||||
BUILD_WITH_SYSTEMD=no
|
||||
LIBSYSTEMD_LIBS=-lsystemd
|
||||
|
||||
# If 'USE_SYSTEMD' in the environment is neither "no" nor "yes", try to
|
||||
# auto-detect libsystemd's presence and link accordingly.
|
||||
ifneq ($(USE_SYSTEMD),no)
|
||||
@@ -151,17 +211,18 @@ ifneq ($(USE_SYSTEMD),no)
|
||||
# (unless a later check tells us otherwise)
|
||||
ifeq ($(LIBSYSTEMD_PKGCONFIG),0)
|
||||
BUILD_WITH_SYSTEMD=yes
|
||||
LIBSYSTEMD_LIBS=$(shell $(PKG_CONFIG) --libs libsystemd)
|
||||
endif
|
||||
endif
|
||||
|
||||
# If 'USE_SYSTEMD' is set to "yes" use pkg-config if available or fall back to
|
||||
# default -lsystemd.
|
||||
ifeq ($(USE_SYSTEMD),yes)
|
||||
ifneq ($(LIBSYSTEMD_PKGCONFIG),0)
|
||||
$(error USE_SYSTEMD is set to "$(USE_SYSTEMD)", but $(PKG_CONFIG) cannot find libsystemd)
|
||||
endif
|
||||
# Force building with libsystemd
|
||||
BUILD_WITH_SYSTEMD=yes
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_WITH_SYSTEMD),yes)
|
||||
FINAL_LIBS+=$(shell $(PKG_CONFIG) --libs libsystemd)
|
||||
FINAL_LIBS+=$(LIBSYSTEMD_LIBS)
|
||||
FINAL_CFLAGS+= -DHAVE_LIBSYSTEMD
|
||||
endif
|
||||
|
||||
@@ -182,9 +243,32 @@ ifeq ($(MALLOC),jemalloc)
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_TLS),yes)
|
||||
FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CFLAGS)
|
||||
FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS)
|
||||
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a -lssl -lcrypto
|
||||
FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CFLAGS)
|
||||
FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS)
|
||||
LIBSSL_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libssl && echo $$?)
|
||||
ifeq ($(LIBSSL_PKGCONFIG),0)
|
||||
LIBSSL_LIBS=$(shell $(PKG_CONFIG) --libs libssl)
|
||||
else
|
||||
LIBSSL_LIBS=-lssl
|
||||
endif
|
||||
LIBCRYPTO_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libcrypto && echo $$?)
|
||||
ifeq ($(LIBCRYPTO_PKGCONFIG),0)
|
||||
LIBCRYPTO_LIBS=$(shell $(PKG_CONFIG) --libs libcrypto)
|
||||
else
|
||||
LIBCRYPTO_LIBS=-lcrypto
|
||||
endif
|
||||
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS)
|
||||
endif
|
||||
|
||||
ifndef V
|
||||
define MAKE_INSTALL
|
||||
@printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$(1)$(ENDCOLOR) 1>&2
|
||||
@$(INSTALL) $(1) $(2)
|
||||
endef
|
||||
else
|
||||
define MAKE_INSTALL
|
||||
$(INSTALL) $(1) $(2)
|
||||
endef
|
||||
endif
|
||||
|
||||
REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)
|
||||
@@ -204,15 +288,15 @@ QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(EN
|
||||
QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;
|
||||
endif
|
||||
|
||||
REDIS_SERVER_NAME=redis-server
|
||||
REDIS_SENTINEL_NAME=redis-sentinel
|
||||
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o
|
||||
REDIS_CLI_NAME=redis-cli
|
||||
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o siphash.o crc16.o
|
||||
REDIS_BENCHMARK_NAME=redis-benchmark
|
||||
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o redis-benchmark.o
|
||||
REDIS_CHECK_RDB_NAME=redis-check-rdb
|
||||
REDIS_CHECK_AOF_NAME=redis-check-aof
|
||||
REDIS_SERVER_NAME=redis-server$(PROG_SUFFIX)
|
||||
REDIS_SENTINEL_NAME=redis-sentinel$(PROG_SUFFIX)
|
||||
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o
|
||||
REDIS_CLI_NAME=redis-cli$(PROG_SUFFIX)
|
||||
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o
|
||||
REDIS_BENCHMARK_NAME=redis-benchmark$(PROG_SUFFIX)
|
||||
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o release.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o
|
||||
REDIS_CHECK_RDB_NAME=redis-check-rdb$(PROG_SUFFIX)
|
||||
REDIS_CHECK_AOF_NAME=redis-check-aof$(PROG_SUFFIX)
|
||||
|
||||
all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME)
|
||||
@echo ""
|
||||
@@ -233,6 +317,8 @@ persist-settings: distclean
|
||||
echo WARN=$(WARN) >> .make-settings
|
||||
echo OPT=$(OPT) >> .make-settings
|
||||
echo MALLOC=$(MALLOC) >> .make-settings
|
||||
echo BUILD_TLS=$(BUILD_TLS) >> .make-settings
|
||||
echo USE_SYSTEMD=$(USE_SYSTEMD) >> .make-settings
|
||||
echo CFLAGS=$(CFLAGS) >> .make-settings
|
||||
echo LDFLAGS=$(LDFLAGS) >> .make-settings
|
||||
echo REDIS_CFLAGS=$(REDIS_CFLAGS) >> .make-settings
|
||||
@@ -278,32 +364,38 @@ $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
|
||||
|
||||
# redis-benchmark
|
||||
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/hdr_histogram/hdr_histogram.o $(FINAL_LIBS)
|
||||
|
||||
dict-benchmark: dict.c zmalloc.c sds.c siphash.c
|
||||
$(REDIS_CC) $(FINAL_CFLAGS) $^ -D DICT_BENCHMARK_MAIN -o $@ $(FINAL_LIBS)
|
||||
DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d)
|
||||
-include $(DEP)
|
||||
|
||||
# Because the jemalloc.h header is generated as a part of the jemalloc build,
|
||||
# building it should complete before building any other object. Instead of
|
||||
# depending on a single artifact, build all dependencies first.
|
||||
%.o: %.c .make-prerequisites
|
||||
$(REDIS_CC) -c $<
|
||||
$(REDIS_CC) -MMD -o $@ -c $<
|
||||
|
||||
clean:
|
||||
rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep dict-benchmark
|
||||
rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep
|
||||
rm -f $(DEP)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
distclean: clean
|
||||
-(cd ../deps && $(MAKE) distclean)
|
||||
-(cd modules && $(MAKE) clean)
|
||||
-(cd ../tests/modules && $(MAKE) clean)
|
||||
-(rm -f .make-*)
|
||||
|
||||
.PHONY: distclean
|
||||
|
||||
test: $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME)
|
||||
test: $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME)
|
||||
@(cd ..; ./runtest)
|
||||
|
||||
test-sentinel: $(REDIS_SENTINEL_NAME)
|
||||
test-modules: $(REDIS_SERVER_NAME)
|
||||
@(cd ..; ./runtest-moduleapi)
|
||||
|
||||
test-sentinel: $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME)
|
||||
@(cd ..; ./runtest-sentinel)
|
||||
|
||||
check: test
|
||||
@@ -314,10 +406,6 @@ lcov:
|
||||
@geninfo -o redis.info .
|
||||
@genhtml --legend -o lcov-html redis.info
|
||||
|
||||
test-sds: sds.c sds.h
|
||||
$(REDIS_CC) sds.c zmalloc.c -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test
|
||||
/tmp/sds_test
|
||||
|
||||
.PHONY: lcov
|
||||
|
||||
bench: $(REDIS_BENCHMARK_NAME)
|
||||
@@ -339,18 +427,18 @@ valgrind:
|
||||
$(MAKE) OPTIMIZATION="-O0" MALLOC="libc"
|
||||
|
||||
helgrind:
|
||||
$(MAKE) OPTIMIZATION="-O0" MALLOC="libc" CFLAGS="-D__ATOMIC_VAR_FORCE_SYNC_MACROS"
|
||||
$(MAKE) OPTIMIZATION="-O0" MALLOC="libc" CFLAGS="-D__ATOMIC_VAR_FORCE_SYNC_MACROS" REDIS_CFLAGS="-I/usr/local/include" REDIS_LDFLAGS="-L/usr/local/lib"
|
||||
|
||||
src/help.h:
|
||||
@../utils/generate-command-help.rb > help.h
|
||||
|
||||
install: all
|
||||
@mkdir -p $(INSTALL_BIN)
|
||||
$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN)
|
||||
$(REDIS_INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN)
|
||||
$(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN)
|
||||
$(REDIS_INSTALL) $(REDIS_CHECK_RDB_NAME) $(INSTALL_BIN)
|
||||
$(REDIS_INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN)
|
||||
$(call MAKE_INSTALL,$(REDIS_SERVER_NAME),$(INSTALL_BIN))
|
||||
$(call MAKE_INSTALL,$(REDIS_BENCHMARK_NAME),$(INSTALL_BIN))
|
||||
$(call MAKE_INSTALL,$(REDIS_CLI_NAME),$(INSTALL_BIN))
|
||||
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_RDB_NAME)
|
||||
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_AOF_NAME)
|
||||
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)
|
||||
|
||||
uninstall:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user