Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5895d119b1 | ||
|
|
aabe95125d | ||
|
|
03cb27e8ec | ||
|
|
dde1c975b8 | ||
|
|
5a82df05aa | ||
|
|
12a6b438a3 | ||
|
|
c6ad876774 | ||
|
|
f6a40570fa | ||
|
|
666ed7facf | ||
|
|
6ac3c0b7ab | ||
|
|
5674b0057f | ||
|
|
bb7597f46e | ||
|
|
a30d367a71 | ||
|
|
e0cf85b848 | ||
|
|
5f49f4fb40 | ||
|
|
2cd271e5d5 | ||
|
|
7d9878e4e5 | ||
|
|
240ff70ae6 | ||
|
|
3941d2e062 | ||
|
|
62bc09d92b | ||
|
|
c5446aca4c | ||
|
|
4f59673df5 | ||
|
|
1655576e23 | ||
|
|
8c0f06c2e6 | ||
|
|
32d923f85a | ||
|
|
8df81c0326 | ||
|
|
78c1623553 | ||
|
|
518fd2dcc4 | ||
|
|
d80c87111b | ||
|
|
981953a6ef | ||
|
|
da127ed3dd | ||
|
|
fe417b06d6 | ||
|
|
ea0a376432 | ||
|
|
18429ce594 | ||
|
|
f0a91d5a00 | ||
|
|
941d58d4c9 | ||
|
|
dd27c4e15e | ||
|
|
0506d85122 | ||
|
|
0d05570a82 | ||
|
|
1d4ebefbff | ||
|
|
68e93c22c3 | ||
|
|
adf8f6f63f | ||
|
|
394614a5f9 | ||
|
|
789f101560 | ||
|
|
5c6f0b26c0 | ||
|
|
8bcca06cb0 | ||
|
|
aae2d6e351 | ||
|
|
9469401088 | ||
|
|
1b03a0d754 | ||
|
|
f86385f70b | ||
|
|
d0762100b0 | ||
|
|
40ec7a2796 | ||
|
|
10b240fd31 | ||
|
|
91ffe99e03 | ||
|
|
c992857618 | ||
|
|
4bba84f096 | ||
|
|
17c3ac89e7 | ||
|
|
cde69883a1 | ||
|
|
e6a94aa569 | ||
|
|
840b393c06 | ||
|
|
56ef0aa7c8 | ||
|
|
dcf409f8e7 | ||
|
|
1237cce65d | ||
|
|
d182820fa7 | ||
|
|
0c65342889 | ||
|
|
8d70d498d5 | ||
|
|
5745b469d3 | ||
|
|
a11c842ecd | ||
|
|
615eb0db86 | ||
|
|
203feb6ea2 | ||
|
|
08c3f0bca6 | ||
|
|
3bf02ea7e8 | ||
|
|
acb4cd2135 | ||
|
|
fb86ecb802 | ||
|
|
2c5865cd90 | ||
|
|
ebbcd05e03 | ||
|
|
de4f04a272 | ||
|
|
f2f57eb44f | ||
|
|
8119259bb6 | ||
|
|
e664f38170 | ||
|
|
f464cf2380 | ||
|
|
02c395a179 | ||
|
|
acdccab019 | ||
|
|
e9f674e5fb | ||
|
|
0f8d47c9ed | ||
|
|
f1d56c981d | ||
|
|
ea595784f9 | ||
|
|
549fe1f3af | ||
|
|
cf6b0f9cef | ||
|
|
02e930624a | ||
|
|
ec56906bd6 | ||
|
|
23d299234c | ||
|
|
f84c181eac | ||
|
|
8ff0226361 | ||
|
|
d756b11fa2 | ||
|
|
ec74ae7ec1 | ||
|
|
785851a736 | ||
|
|
2a2d5a7a1f | ||
|
|
a60ed4a599 | ||
|
|
5ed6662145 | ||
|
|
f885e364ba | ||
|
|
3ac666b3e6 | ||
|
|
4b37eb13dc | ||
|
|
3a13c654e6 | ||
|
|
25214bd7dc | ||
|
|
3cc673bdd8 | ||
|
|
de0919cd62 | ||
|
|
5aa0fef2ce | ||
|
|
3988e32f41 | ||
|
|
6f16e52a6d | ||
|
|
c6c2de9567 | ||
|
|
cebc1f2696 | ||
|
|
56d8ad932d | ||
|
|
0103dc09d2 | ||
|
|
71c1427a1b | ||
|
|
30bb0dd595 | ||
|
|
99f39d32ab | ||
|
|
851ec530b4 | ||
|
|
79e6ab31d8 | ||
|
|
b20af834ff | ||
|
|
1453277294 | ||
|
|
e4e24460f9 | ||
|
|
db9cffe7c2 | ||
|
|
9c475272da | ||
|
|
6f2c894d69 | ||
|
|
4147a2202e | ||
|
|
28d1fe6718 | ||
|
|
116a2042a7 | ||
|
|
945983ac52 | ||
|
|
e7c8002b56 | ||
|
|
86b5e1a72f | ||
|
|
bab834574c | ||
|
|
76f3a63de1 | ||
|
|
93b4c6b31e | ||
|
|
12055ed8c2 | ||
|
|
c80f6219d5 | ||
|
|
801c7307fb | ||
|
|
a7b95b7c72 | ||
|
|
919580a228 | ||
|
|
e83d6b0cfa | ||
|
|
cb070accb4 | ||
|
|
9ce6fc7322 | ||
|
|
a7b9cfbe98 | ||
|
|
219bab2d85 | ||
|
|
d51bebd4d7 | ||
|
|
120c6b43e1 | ||
|
|
63075b81e6 | ||
|
|
9418083770 | ||
|
|
5d1ac2df64 | ||
|
|
90555566ed | ||
|
|
bb7e00b699 | ||
|
|
faa431c98d | ||
|
|
757ad7cdf2 | ||
|
|
31491202aa | ||
|
|
26c1c60187 | ||
|
|
2ccbba650c | ||
|
|
c73406c2bd | ||
|
|
c1423c016c | ||
|
|
ecf51b0b3d | ||
|
|
9d0388a043 | ||
|
|
6a4da4958e | ||
|
|
e8f772efff | ||
|
|
59866fcd30 | ||
|
|
4832cf4fd6 | ||
|
|
fd610ae025 | ||
|
|
88441019a5 | ||
|
|
aa1feec746 | ||
|
|
e5e353c81d | ||
|
|
0bdddd3c89 | ||
|
|
7bc0726016 | ||
|
|
ea4bf91abc | ||
|
|
5d9332266d | ||
|
|
7601ed344c | ||
|
|
405f041e90 | ||
|
|
aa6b869c15 | ||
|
|
00a767db38 | ||
|
|
5106a144f0 | ||
|
|
3efdbbc79c | ||
|
|
2ae7f49115 | ||
|
|
03b59cd5f0 | ||
|
|
5380716a70 | ||
|
|
18b30467f7 | ||
|
|
0052d5196d | ||
|
|
b55b0ea0f2 | ||
|
|
25e211bcf0 | ||
|
|
635704d424 | ||
|
|
47364bce69 | ||
|
|
3cd6c26056 | ||
|
|
8b0d797ecd | ||
|
|
3c8b394511 | ||
|
|
84ba2ec82b | ||
|
|
cf3375bf7c | ||
|
|
643d3e0b79 | ||
|
|
874c3a9978 | ||
|
|
781e50d41f | ||
|
|
57dbd6e90d | ||
|
|
e9fef49e12 | ||
|
|
a227bc8053 | ||
|
|
d410dc3162 | ||
|
|
41c7c7919c | ||
|
|
db6c763d8b | ||
|
|
72d6f966ac | ||
|
|
28e074608c | ||
|
|
5b8de5b7f2 | ||
|
|
bce350c666 | ||
|
|
332aa2f919 | ||
|
|
e4db67ea57 | ||
|
|
b983015dd4 | ||
|
|
9b718d41f0 | ||
|
|
da33a18588 | ||
|
|
dbea5f7a8d | ||
|
|
4bb40a9688 | ||
|
|
d2532d1335 | ||
|
|
00d0d870d2 | ||
|
|
f38e2802b6 | ||
|
|
1b100a167a | ||
|
|
d4e1c88052 | ||
|
|
2c0c5a809c | ||
|
|
f6d04d01b9 | ||
|
|
2fc915f509 | ||
|
|
e3cfc92180 | ||
|
|
7fcf29e615 | ||
|
|
e81bac32fd | ||
|
|
2257f38b68 | ||
|
|
2165a78d10 | ||
|
|
e6f6731c66 | ||
|
|
5a9a653f3e | ||
|
|
ff0846cbb7 | ||
|
|
3e2ed279fd | ||
|
|
19ef1f371d | ||
|
|
8d79702d8a | ||
|
|
6cfcf76e0b | ||
|
|
c9478a3b4e | ||
|
|
113d5ae872 | ||
|
|
1bd75aafdb | ||
|
|
ba48e7e9cd | ||
|
|
4e19358d65 | ||
|
|
43bc1ea065 | ||
|
|
3842bfd899 | ||
|
|
6dcd641681 | ||
|
|
1180537589 | ||
|
|
2e7ad58918 | ||
|
|
39ffc3702f | ||
|
|
31415cb27a | ||
|
|
840a9951b5 | ||
|
|
3c87d572b9 | ||
|
|
d3831559cf | ||
|
|
44ef19743e | ||
|
|
171f83306e | ||
|
|
0314429c8f | ||
|
|
086707315f | ||
|
|
9fd0c027e7 | ||
|
|
0e581e0044 | ||
|
|
e8801f7eaa | ||
|
|
3a677b45ac | ||
|
|
dc46f6f041 | ||
|
|
916b215fc5 | ||
|
|
9f0e8f7849 | ||
|
|
a5294c4e52 | ||
|
|
e511ab2cc6 | ||
|
|
7200b3aa0f | ||
|
|
e8efcdee0a | ||
|
|
2bcc056a41 | ||
|
|
b2bd3c0653 | ||
|
|
4238df5436 | ||
|
|
c68b3908a6 | ||
|
|
1c05b87e30 | ||
|
|
67750ce3b3 | ||
|
|
096285ab64 | ||
|
|
0b2f7c6407 | ||
|
|
8a4240b1fa | ||
|
|
395d339234 | ||
|
|
6daa8b9adb | ||
|
|
e00b6248c9 | ||
|
|
e8aa5583d0 | ||
|
|
f1d5d5d28e | ||
|
|
cc7f4ba67e | ||
|
|
af907e4b6d | ||
|
|
d2f09a054c | ||
|
|
cef46edebc | ||
|
|
9e997cd0d1 | ||
|
|
171aa22b0b | ||
|
|
7bf665f125 | ||
|
|
a5696bdf4f | ||
|
|
e15528bf1d | ||
|
|
e28aa99af1 | ||
|
|
3051430040 | ||
|
|
29b20fd528 | ||
|
|
5b36681213 | ||
|
|
0f75036c07 | ||
|
|
b1a01fda91 | ||
|
|
83f55f61a6 | ||
|
|
f89f50dbd0 | ||
|
|
05f8975d21 | ||
|
|
4780cc5efa | ||
|
|
9bc6af2449 | ||
|
|
c4b428a388 | ||
|
|
aea4db2f5a | ||
|
|
b5c5f870a4 | ||
|
|
23bf3d1cb9 | ||
|
|
1be6dbbdf6 | ||
|
|
1acdc84d10 | ||
|
|
52929b61f8 | ||
|
|
b0faf6113e | ||
|
|
f838df92d2 | ||
|
|
7a536c2912 | ||
|
|
6f1e828454 | ||
|
|
2d3687655b | ||
|
|
b057ff81ee | ||
|
|
a9d1014fb2 | ||
|
|
183bdc11c0 | ||
|
|
95ba01b538 | ||
|
|
7b21b8c3fb | ||
|
|
33ca884cc5 | ||
|
|
2b5f23197c | ||
|
|
1104113c07 | ||
|
|
62f8583445 | ||
|
|
3155adb299 | ||
|
|
14a59d4ce7 | ||
|
|
17aaf5ec97 | ||
|
|
3f5cf24da9 | ||
|
|
05e483cbb3 | ||
|
|
c8f250f8dd | ||
|
|
9412094f25 | ||
|
|
8312aa27d4 | ||
|
|
d3aa3791fe | ||
|
|
2b189f098e | ||
|
|
3328b7a514 | ||
|
|
88e1f80eee | ||
|
|
0ebbc36059 | ||
|
|
43ed3c3589 | ||
|
|
8095daea4a | ||
|
|
51e178454d | ||
|
|
82b2bfd20b | ||
|
|
ec1faeec72 | ||
|
|
c1326d7b10 | ||
|
|
845fb2d1c1 | ||
|
|
930fac8058 | ||
|
|
0cae0900cf | ||
|
|
e0022d8cfe | ||
|
|
495327c020 | ||
|
|
afb8b3c030 | ||
|
|
31c9cd8604 | ||
|
|
51efb7fe25 | ||
|
|
a7d3670da5 | ||
|
|
ee8dd01bbc | ||
|
|
a4a856d532 | ||
|
|
e2046b3003 | ||
|
|
b35fdf1de1 | ||
|
|
1d7bf208ce | ||
|
|
676445ad95 | ||
|
|
4846c0c8af | ||
|
|
f33de403ed | ||
|
|
49af4d07e4 | ||
|
|
329fddbda1 | ||
|
|
925a2cd5aa | ||
|
|
84a7a90586 | ||
|
|
cd63359a17 | ||
|
|
886d287c40 | ||
|
|
59cd4c9f65 | ||
|
|
6c1bb7b194 | ||
|
|
1aee695e52 | ||
|
|
a2ae463520 | ||
|
|
86e562d691 | ||
|
|
ab2984b1e2 | ||
|
|
1ff5a222de | ||
|
|
cc549b46a4 | ||
|
|
377dd0515c | ||
|
|
3f8d113f1b | ||
|
|
d4541349dc | ||
|
|
2112a57028 | ||
|
|
d2eb6e0b47 | ||
|
|
9c1df3b769 | ||
|
|
115ed1911e | ||
|
|
11c748aaca | ||
|
|
8f10137227 | ||
|
|
2e591fc4a4 | ||
|
|
cbb51fb8f9 | ||
|
|
e0fc88b4d9 | ||
|
|
e3f864b5fd | ||
|
|
8af1e513f6 | ||
|
|
3c21418cd8 | ||
|
|
e201f83cee | ||
|
|
58fc456cbd | ||
|
|
3febc5c29e | ||
|
|
8d67385593 | ||
|
|
7a35eec540 | ||
|
|
f93e1417b9 | ||
|
|
d0c9e44545 | ||
|
|
1cde6a0603 | ||
|
|
436be34986 | ||
|
|
f9d2ffdc5d | ||
|
|
d7968ee920 | ||
|
|
a902e6b251 | ||
|
|
9564ed7c39 | ||
|
|
2e41827435 | ||
|
|
eb2c8b2c61 | ||
|
|
928e6976bb | ||
|
|
cdcf5af5aa | ||
|
|
e8b09d2203 | ||
|
|
57b4fb0d84 | ||
|
|
29f25e4111 | ||
|
|
00d8b92b89 | ||
|
|
5e17e6276c | ||
|
|
96e7c011e2 | ||
|
|
7803b1148d | ||
|
|
1eab62f7e5 | ||
|
|
ae306a3df6 | ||
|
|
16ba33c05b | ||
|
|
77ae66930c | ||
|
|
389697988a | ||
|
|
2435341d74 | ||
|
|
80c906bd30 | ||
|
|
74249be4a2 | ||
|
|
8bf660af90 | ||
|
|
9da134cd88 | ||
|
|
f7f219a137 | ||
|
|
693629585b | ||
|
|
e3b5648df0 | ||
|
|
5c41802d55 | ||
|
|
a23cdbb94b | ||
|
|
1276058ea8 | ||
|
|
002fcde3da | ||
|
|
960186a71f | ||
|
|
3672875b4c | ||
|
|
24e12641d5 | ||
|
|
c7edffbd56 | ||
|
|
8a9c84f4a5 | ||
|
|
cb683a84f7 | ||
|
|
84d9766d6a | ||
|
|
ec1e106ec5 | ||
|
|
e48c37316e | ||
|
|
1e561cfaaf | ||
|
|
d1af82a883 | ||
|
|
897a360d06 | ||
|
|
ac316d8cc5 | ||
|
|
fc7bc32045 | ||
|
|
a6e55c096d | ||
|
|
b062fd5235 | ||
|
|
4efb25d9c0 | ||
|
|
d6436eb7cf | ||
|
|
3a441c7d95 | ||
|
|
75addb4fe2 | ||
|
|
c57d9146f4 | ||
|
|
3d3861dd88 | ||
|
|
47ff13637e | ||
|
|
db73d0998f | ||
|
|
7c0fe72714 | ||
|
|
8fe25edc75 | ||
|
|
17dfd7cabb | ||
|
|
1f9b82bd56 | ||
|
|
3fcffe7d04 | ||
|
|
455d8a05c1 | ||
|
|
70287bbc94 | ||
|
|
7be21139aa | ||
|
|
e1ee1a49d6 | ||
|
|
d56f058c08 | ||
|
|
96dd5fc932 | ||
|
|
5b59d9c5d7 | ||
|
|
c163d4addc | ||
|
|
aab74b7151 | ||
|
|
606134f9de | ||
|
|
ee627bb667 | ||
|
|
47b8a7f9b8 | ||
|
|
6c0bc608a1 | ||
|
|
5bfc18950a | ||
|
|
56d628f851 | ||
|
|
cac9d7cf7e | ||
|
|
b712fba17c | ||
|
|
ea63aea72d | ||
|
|
64e588bfab | ||
|
|
0d1e8c93bf | ||
|
|
a1bed447b3 | ||
|
|
a75fa3aad1 | ||
|
|
52c75e9db1 | ||
|
|
8110ba8880 | ||
|
|
e4d2bb62b2 | ||
|
|
fea9788cc4 | ||
|
|
2144047e14 | ||
|
|
43329c9b64 | ||
|
|
1e17d3de79 | ||
|
|
3722f89f49 | ||
|
|
373ae6061a | ||
|
|
f03f1fad67 | ||
|
|
374ffdf1c1 | ||
|
|
4db38d2efd | ||
|
|
f0a261448c | ||
|
|
fbdef6a9bd | ||
|
|
05a41da75b | ||
|
|
345c3768de | ||
|
|
639c8a1d91 | ||
|
|
321acea038 | ||
|
|
b0920e6e86 | ||
|
|
149b658b56 | ||
|
|
06917e581c | ||
|
|
e434b2ce4f | ||
|
|
f3a1728873 | ||
|
|
e63bb7ec8c | ||
|
|
6791ff0525 | ||
|
|
725b8cc680 | ||
|
|
3575b8706d | ||
|
|
a76c67578c | ||
|
|
3a27064c41 | ||
|
|
5c4c73e2c2 | ||
|
|
6148f94939 | ||
|
|
51d3012d48 | ||
|
|
c39f16c422 | ||
|
|
7cf0a77d59 | ||
|
|
9f594e2432 | ||
|
|
48781dd95c | ||
|
|
371ab0cff8 | ||
|
|
b86140ac5f | ||
|
|
a7ee3c3e77 | ||
|
|
e5b9eb8171 | ||
|
|
0f31bb5c1a | ||
|
|
b92d9a895f | ||
|
|
8f896e57ae | ||
|
|
8f5157058b | ||
|
|
ddeda9ceb7 | ||
|
|
bde1f0a8e2 | ||
|
|
bec68bff29 | ||
|
|
0f239e51b0 | ||
|
|
f855db61b0 | ||
|
|
23094ba015 | ||
|
|
8161a7a3ef | ||
|
|
61b153073f | ||
|
|
414debfd04 | ||
|
|
cbcd07777d | ||
|
|
2437455f27 | ||
|
|
35c64b898c | ||
|
|
c3ac717487 | ||
|
|
c8dbcff9db | ||
|
|
abd5156f25 | ||
|
|
5719b3054a | ||
|
|
c89e1f2937 | ||
|
|
0b16f8d444 | ||
|
|
9254a805d7 | ||
|
|
a4c4907035 | ||
|
|
cb92c23de0 | ||
|
|
56a52e804c | ||
|
|
ebb09a5c3b | ||
|
|
a9f8a8cba0 | ||
|
|
4aa24e62a4 | ||
|
|
2b67b6b87e | ||
|
|
420aac7274 | ||
|
|
a518a9a766 | ||
|
|
026cc11b05 | ||
|
|
02b594f6ad | ||
|
|
f695d18306 | ||
|
|
0e42cfc365 | ||
|
|
91ed9b3c47 | ||
|
|
3e0d209625 | ||
|
|
240094c9b0 | ||
|
|
209f3a1eba | ||
|
|
024c380b9d | ||
|
|
a38ff404bd | ||
|
|
814874d68b | ||
|
|
061616c1b1 | ||
|
|
7764996bec | ||
|
|
eba28e2cea | ||
|
|
15c9e79a7d | ||
|
|
cd2b5df971 | ||
|
|
8cdc153f58 | ||
|
|
b35407fa7c | ||
|
|
4395889c9e | ||
|
|
1907e0f18f | ||
|
|
c35a53169f | ||
|
|
6fe66e0969 | ||
|
|
193fc241ca | ||
|
|
957e917a84 | ||
|
|
ef1b1f01a8 | ||
|
|
9f347fabba | ||
|
|
a509400d58 | ||
|
|
805c8c94a9 | ||
|
|
97b80b57e9 | ||
|
|
4f6b6b8008 | ||
|
|
616b1cb7ac | ||
|
|
08fdef4bf6 | ||
|
|
26b79ca18c | ||
|
|
fa41863752 | ||
|
|
bbbc80acae | ||
|
|
c3b268a0bc | ||
|
|
0f7dfc378c | ||
|
|
67643ead69 | ||
|
|
ad94066ec8 | ||
|
|
a443ec2e9a | ||
|
|
6862fd70da | ||
|
|
30f1df8c48 | ||
|
|
7add0f2490 | ||
|
|
9d6d177994 | ||
|
|
316a8f1545 | ||
|
|
11db53f875 | ||
|
|
e257f121e1 | ||
|
|
5f72f69688 | ||
|
|
8caa271476 | ||
|
|
e43cd8316f | ||
|
|
34b8983220 | ||
|
|
70a98a43ea | ||
|
|
8783304a2d | ||
|
|
ec9cf002d5 | ||
|
|
b3e4abf06e | ||
|
|
50f8f9504b | ||
|
|
f2f3dc5e73 | ||
|
|
eb80887936 | ||
|
|
cdcab0e820 | ||
|
|
50dcd9f96d | ||
|
|
87dbd8f54c | ||
|
|
81e8686cc7 | ||
|
|
c7524a7e44 | ||
|
|
2dd1ca6af0 | ||
|
|
95324b8190 | ||
|
|
04c53fa145 | ||
|
|
0578157d56 | ||
|
|
299f1d0258 | ||
|
|
61b98f32a2 | ||
|
|
34ea2f4e1a | ||
|
|
cbbf9b3931 | ||
|
|
9c2e42ddfc | ||
|
|
d387f67dcb | ||
|
|
7c07841632 | ||
|
|
9cc7038e54 | ||
|
|
da8c7c49bf | ||
|
|
bdb338cf77 | ||
|
|
b3e0305448 | ||
|
|
e628f94436 | ||
|
|
5d4c4df3ef | ||
|
|
da14982d1e | ||
|
|
dc8885a1ca | ||
|
|
bfb18e5519 | ||
|
|
76d57161d9 | ||
|
|
0e5820d893 | ||
|
|
916dd79f19 | ||
|
|
c0c67c9be3 | ||
|
|
e1c29434b2 | ||
|
|
e5a063bcb4 | ||
|
|
d28cbaf770 | ||
|
|
23d5e8b8e4 | ||
|
|
6173815408 | ||
|
|
70e0e49952 | ||
|
|
b3e4aa6797 | ||
|
|
e74e68c84f | ||
|
|
4af0d7fd94 | ||
|
|
6ef018785b | ||
|
|
fe81d5c8a9 | ||
|
|
c2f01d7f9e | ||
|
|
127e09bca1 | ||
|
|
a20303c623 | ||
|
|
7a23b94559 | ||
|
|
baaf869fc3 | ||
|
|
be4bc1a5be | ||
|
|
07dc1b42fb | ||
|
|
10e71b3d01 | ||
|
|
edc0ed1415 | ||
|
|
c5319612b4 | ||
|
|
fff6b26ae3 | ||
|
|
650484604c | ||
|
|
3144a278dd | ||
|
|
afe0b16c02 | ||
|
|
df152b0ce7 | ||
|
|
e3c1f43952 | ||
|
|
141c0679a0 | ||
|
|
fe902461f4 | ||
|
|
9d4219ebac | ||
|
|
8424306433 | ||
|
|
dafb94db79 | ||
|
|
ea697b6345 | ||
|
|
dd4798802c | ||
|
|
ecf3b2ef32 | ||
|
|
2966132c52 | ||
|
|
7277e5d8a8 | ||
|
|
1bb5ee9c68 | ||
|
|
973297336f | ||
|
|
15ea13245a | ||
|
|
4d12c37c54 | ||
|
|
12626ce9bb | ||
|
|
2ecab0b63a | ||
|
|
635321d47e | ||
|
|
0b988fa9ec | ||
|
|
60096bc1a1 | ||
|
|
b439542604 | ||
|
|
ef3551d149 | ||
|
|
72c053519c | ||
|
|
294c9af469 | ||
|
|
ba0270799e | ||
|
|
5d0890c0a8 | ||
|
|
d1f22eaca4 | ||
|
|
762fbcb687 | ||
|
|
349aa24511 | ||
|
|
b4ddc7b7ba | ||
|
|
73806f74ca | ||
|
|
b6129f86c1 | ||
|
|
f15fb727a0 | ||
|
|
e374573f30 | ||
|
|
73d47d5749 | ||
|
|
b7cb28d501 | ||
|
|
1db7257107 | ||
|
|
fe96e29d5f | ||
|
|
f21be1ec55 | ||
|
|
6fb1aa2381 | ||
|
|
d4fe79a174 | ||
|
|
abb81c6351 | ||
|
|
77da960815 | ||
|
|
3e8c69a9de | ||
|
|
a788c373e6 | ||
|
|
df83892760 | ||
|
|
0517da3618 | ||
|
|
3c16d6b32d | ||
|
|
3b4f147775 | ||
|
|
5e762d847c | ||
|
|
54f5499aee | ||
|
|
0c952b13d0 | ||
|
|
dd34f70368 | ||
|
|
5e042dbc05 | ||
|
|
dcbe8bfad1 | ||
|
|
36caf2e42b | ||
|
|
3067352a8d | ||
|
|
9baaf858f9 | ||
|
|
4440133e9a | ||
|
|
c957794144 | ||
|
|
1333a46b7e | ||
|
|
8e7282eb3e | ||
|
|
a678390e52 | ||
|
|
919fbf421d | ||
|
|
22e45d46fe | ||
|
|
ba28924418 | ||
|
|
f42ce57d0f | ||
|
|
df096bc96b | ||
|
|
a55e584706 | ||
|
|
5a6cfbf4ca | ||
|
|
fae306b374 | ||
|
|
736309660f | ||
|
|
488e194787 | ||
|
|
c82ccf0670 | ||
|
|
51c1a9f8fb | ||
|
|
ea1e1b12c9 | ||
|
|
7379c78a9b | ||
|
|
9f6e84f6be | ||
|
|
82790e510f | ||
|
|
943008ebac | ||
|
|
e271a61103 | ||
|
|
f1974d5d67 | ||
|
|
d9b153c9f6 | ||
|
|
577fc4388b | ||
|
|
f7a94526dd | ||
|
|
eecfa9793e | ||
|
|
d2509811b7 | ||
|
|
29d4a1502a | ||
|
|
ec0c61da05 | ||
|
|
6fe55c2f29 | ||
|
|
bbce3ba974 | ||
|
|
40295fb3fe | ||
|
|
8e9d19bc65 | ||
|
|
ba146d4c09 | ||
|
|
721a39ddff | ||
|
|
5be3a15a82 | ||
|
|
cbabf779c2 | ||
|
|
658749cc54 | ||
|
|
47988c9666 | ||
|
|
cc90f79baf | ||
|
|
ecd17e819c | ||
|
|
1927932b43 | ||
|
|
f2df5773b1 | ||
|
|
b3ff8a4b6d | ||
|
|
b1f2c51037 | ||
|
|
7bbafc5639 | ||
|
|
c7f752667e | ||
|
|
ed7ea13a2a | ||
|
|
3be7762388 | ||
|
|
15f6b74814 | ||
|
|
1b5bf40c20 | ||
|
|
954c20edd9 | ||
|
|
11c3afd711 | ||
|
|
b752e83db5 | ||
|
|
e16eb87465 | ||
|
|
35ea9d231b | ||
|
|
3872912679 | ||
|
|
b9a1530345 | ||
|
|
5e7e5e6b61 | ||
|
|
e61dde8806 | ||
|
|
cddf1da2e9 | ||
|
|
52ea44e53b | ||
|
|
0f28ea16a7 | ||
|
|
00e5fefe6d | ||
|
|
baafd30ba6 |
@@ -1,24 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
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)
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
name: Other
|
||||
about: Can't find the right issue type? Use this one!
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
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 :)
|
||||
@@ -9,9 +9,8 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
# Fail build if there are warnings
|
||||
# build with TLS just for compilation coverage
|
||||
run: make REDIS_CFLAGS='-Werror' BUILD_TLS=yes
|
||||
# build with TLS just for compilatoin coverage
|
||||
run: make BUILD_TLS=yes
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6
|
||||
@@ -19,22 +18,19 @@ jobs:
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
|
||||
build-debian-old:
|
||||
runs-on: ubuntu-latest
|
||||
container: debian:oldoldstable
|
||||
build-ubuntu-old:
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update && apt-get install -y build-essential
|
||||
make REDIS_CFLAGS='-Werror'
|
||||
run: make
|
||||
|
||||
build-macos-latest:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror'
|
||||
run: make
|
||||
|
||||
build-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -43,29 +39,12 @@ jobs:
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386
|
||||
make REDIS_CFLAGS='-Werror' 32bit
|
||||
make 32bit
|
||||
|
||||
build-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror' MALLOC=libc
|
||||
run: make MALLOC=libc
|
||||
|
||||
build-old-chain-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
container: ubuntu:20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y gnupg2
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial main" >> /etc/apt/sources.list
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial universe" >> /etc/apt/sources.list
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
|
||||
apt-get update
|
||||
apt-get install -y make gcc-4.8
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
|
||||
make CC=gcc REDIS_CFLAGS='-Werror'
|
||||
|
||||
+31
-125
@@ -17,19 +17,17 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror -DREDIS_TEST'
|
||||
run: make
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
./runtest --accurate --verbose
|
||||
- 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
|
||||
@@ -42,26 +40,7 @@ jobs:
|
||||
- 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
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
@@ -78,11 +57,11 @@ jobs:
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386
|
||||
make 32bit REDIS_CFLAGS='-Werror -DREDIS_TEST'
|
||||
make 32bit
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
./runtest --accurate --verbose
|
||||
- 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
|
||||
@@ -91,8 +70,6 @@ jobs:
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
- name: unittest
|
||||
run: ./src/redis-server test all
|
||||
|
||||
test-ubuntu-tls:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -107,8 +84,8 @@ jobs:
|
||||
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
|
||||
./runtest --accurate --verbose --tls
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --tls
|
||||
@@ -122,23 +99,6 @@ jobs:
|
||||
./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'
|
||||
@@ -146,58 +106,31 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make valgrind REDIS_CFLAGS='-Werror -DREDIS_TEST'
|
||||
run: make valgrind
|
||||
- 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
|
||||
./runtest --valgrind --verbose --clients 1
|
||||
- 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
|
||||
run: ./runtest-moduleapi --valgrind --verbose --clients 1
|
||||
|
||||
test-valgrind-no-malloc-usable-size:
|
||||
test-centos7-jemalloc:
|
||||
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-old-chain-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
container: ubuntu:20.04
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y gnupg2
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial main" >> /etc/apt/sources.list
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial universe" >> /etc/apt/sources.list
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
|
||||
apt-get update
|
||||
apt-get install -y make gcc-4.8
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
|
||||
make CC=gcc REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: apt-get install -y tcl tcltls tclx
|
||||
yum -y install centos-release-scl
|
||||
yum -y install devtoolset-7
|
||||
scl enable devtoolset-7 "make"
|
||||
- name: test
|
||||
run: ./runtest --accurate --verbose --dump-logs
|
||||
run: |
|
||||
yum -y install tcl
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
@@ -205,33 +138,24 @@ jobs:
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-old-chain-tls:
|
||||
test-centos7-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
container: ubuntu:20.04
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y gnupg2
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial main" >> /etc/apt/sources.list
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial universe" >> /etc/apt/sources.list
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
|
||||
apt-get update
|
||||
apt-get install -y make gcc-4.8 openssl libssl-dev
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
|
||||
make CC=gcc BUILD_TLS=yes REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: |
|
||||
apt-get install -y tcl tcltls tclx
|
||||
./utils/gen-test-certs.sh
|
||||
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: |
|
||||
./runtest --accurate --verbose --tls --dump-logs
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
yum -y install tcl tcltls
|
||||
./utils/gen-test-certs.sh
|
||||
./runtest --accurate --verbose --tls
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --tls
|
||||
@@ -255,7 +179,7 @@ jobs:
|
||||
run: make
|
||||
- name: test
|
||||
run: |
|
||||
./runtest --accurate --verbose --no-latency --dump-logs
|
||||
./runtest --accurate --verbose --no-latency
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
@@ -263,24 +187,6 @@ jobs:
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-freebsd:
|
||||
runs-on: macos-13
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: test
|
||||
uses: cross-platform-actions/action@v0.22.0
|
||||
with:
|
||||
operating_system: freebsd
|
||||
environment_variables: MAKE
|
||||
version: 13.2
|
||||
shell: bash
|
||||
run: |
|
||||
sudo pkg install -y bash gmake lang/tcl86 lang/tclx gcc
|
||||
gmake
|
||||
./runtest --single unit/keyspace --single unit/auth --single unit/networking --single unit/protocol
|
||||
|
||||
test-alpine-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
@@ -294,7 +200,7 @@ jobs:
|
||||
- name: test
|
||||
run: |
|
||||
apk add tcl procps
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
@@ -311,11 +217,11 @@ jobs:
|
||||
- name: make
|
||||
run: |
|
||||
apk add build-base
|
||||
make REDIS_CFLAGS='-Werror' USE_JEMALLOC=no CFLAGS=-DUSE_MALLOC_USABLE_SIZE
|
||||
make REDIS_CFLAGS='-Werror' USE_JEMALLOC=no
|
||||
- name: test
|
||||
run: |
|
||||
apk add tcl procps
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
|
||||
@@ -37,4 +37,3 @@ Makefile.dep
|
||||
.ccls
|
||||
.ccls-cache/*
|
||||
compile_commands.json
|
||||
redis.code-workspace
|
||||
|
||||
+3224
-737
File diff suppressed because it is too large
Load Diff
@@ -1,96 +0,0 @@
|
||||
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.
|
||||
+8
-28
@@ -7,38 +7,18 @@ 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
|
||||
|
||||
# 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!)
|
||||
http://stackoverflow.com/questions/tagged/redis
|
||||
|
||||
Issues and pull requests for documentation belong on the redis-doc repo:
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@ Another good example is to think of Redis as a more complex version of memcached
|
||||
|
||||
If you want to know more, this is a list of selected starting points:
|
||||
|
||||
* Introduction to Redis data types. https://redis.io/topics/data-types-intro
|
||||
* Introduction to Redis data types. http://redis.io/topics/data-types-intro
|
||||
* Try Redis directly inside your browser. http://try.redis.io
|
||||
* The full list of Redis commands. https://redis.io/commands
|
||||
* There is much more inside the official Redis documentation. https://redis.io/documentation
|
||||
* The full list of Redis commands. http://redis.io/commands
|
||||
* There is much more inside the official Redis documentation. http://redis.io/documentation
|
||||
|
||||
Building Redis
|
||||
--------------
|
||||
@@ -49,7 +49,7 @@ To append a suffix to Redis program names, use:
|
||||
|
||||
% make PROG_SUFFIX="-alt"
|
||||
|
||||
You can build a 32 bit Redis binary using:
|
||||
You can run a 32 bit Redis binary using:
|
||||
|
||||
% make 32bit
|
||||
|
||||
@@ -115,18 +115,6 @@ 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
|
||||
-------------
|
||||
|
||||
@@ -184,7 +172,7 @@ then in another terminal try the following:
|
||||
(integer) 2
|
||||
redis>
|
||||
|
||||
You can find the list of all the available commands at https://redis.io/commands.
|
||||
You can find the list of all the available commands at http://redis.io/commands.
|
||||
|
||||
Installing Redis
|
||||
-----------------
|
||||
@@ -224,7 +212,7 @@ of the BSD license that you can find in the [COPYING][1] file included in the Re
|
||||
source distribution.
|
||||
|
||||
Please see the [CONTRIBUTING][2] file in this source distribution for more
|
||||
information, including details on our process for security bugs/vulnerabilities.
|
||||
information.
|
||||
|
||||
[1]: https://github.com/redis/redis/blob/unstable/COPYING
|
||||
[2]: https://github.com/redis/redis/blob/unstable/CONTRIBUTING
|
||||
@@ -294,19 +282,19 @@ 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:
|
||||
```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 ...
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -357,8 +345,8 @@ There are two special functions called periodically by 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 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.
|
||||
* `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.
|
||||
* 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
|
||||
@@ -453,7 +441,7 @@ Other C files
|
||||
* `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]: https://redis.io/topics/cluster-spec
|
||||
[3]: http://redis.io/topics/cluster-spec
|
||||
|
||||
Anatomy of a Redis command
|
||||
---
|
||||
|
||||
Vendored
+3
-17
@@ -2,8 +2,6 @@
|
||||
|
||||
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"
|
||||
@@ -39,7 +37,6 @@ 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
|
||||
@@ -60,29 +57,18 @@ 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='' -DLUA_USE_MKSTEMP $(CFLAGS)
|
||||
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI -DENABLE_CJSON_GLOBAL -DREDIS_STATIC='' $(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=rc
|
||||
ARFLAGS=rcu
|
||||
|
||||
lua: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
@@ -95,7 +81,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_ CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)"
|
||||
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 && $(MAKE) CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)" lib/libjemalloc.a
|
||||
|
||||
.PHONY: jemalloc
|
||||
|
||||
Vendored
+1
-1
@@ -17,7 +17,7 @@ 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 these steps:
|
||||
just following tose steps:
|
||||
|
||||
1. Remove the jemalloc directory.
|
||||
2. Substitute it with the new jemalloc source tree.
|
||||
|
||||
Vendored
-121
@@ -1,121 +0,0 @@
|
||||
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
@@ -1,41 +0,0 @@
|
||||
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
@@ -1,20 +0,0 @@
|
||||
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
@@ -1,10 +0,0 @@
|
||||
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
@@ -1,146 +0,0 @@
|
||||
/**
|
||||
* 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
@@ -1,509 +0,0 @@
|
||||
/**
|
||||
* 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,4 +6,3 @@
|
||||
/*.a
|
||||
/*.pc
|
||||
*.dSYM
|
||||
tags
|
||||
|
||||
Vendored
+9
-43
@@ -1,4 +1,5 @@
|
||||
language: c
|
||||
sudo: false
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
@@ -7,34 +8,17 @@ 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
|
||||
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;
|
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:chris-lea/redis-server'
|
||||
packages:
|
||||
- libc6-dbg
|
||||
- libc6-dev
|
||||
@@ -43,20 +27,14 @@ 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 -DENABLE_SSL:BOOL=ON";
|
||||
if [ "$BITS" == "64" ]; then
|
||||
EXTRA_CMAKE_OPTS="$EXTRA_CMAKE_OPTS -DENABLE_SSL_TESTS:BOOL=ON";
|
||||
fi;
|
||||
- EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DHIREDIS_SSL:BOOL=ON";
|
||||
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
if [ "$BITS" == "32" ]; then
|
||||
CFLAGS="-m32 -Werror";
|
||||
@@ -80,24 +58,12 @@ 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
|
||||
- if [ "$BITS" == "64" ]; then
|
||||
TEST_SSL=1 SKIPS_AS_FAILS=1 ctest -V;
|
||||
else
|
||||
SKIPS_AS_FAILS=1 ctest -V;
|
||||
fi;
|
||||
- ctest -V
|
||||
|
||||
jobs:
|
||||
matrix:
|
||||
include:
|
||||
# Windows MinGW cross compile on Linux
|
||||
- os: linux
|
||||
@@ -123,9 +89,9 @@ jobs:
|
||||
- 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 -DENABLE_EXAMPLES=ON '&&' ninja -v
|
||||
- ./hiredis-test.exe
|
||||
- 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
|
||||
|
||||
Vendored
+16
-162
@@ -1,175 +1,28 @@
|
||||
## [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)
|
||||
### 1.0.0 (unreleased)
|
||||
|
||||
**BREAKING CHANGES**:
|
||||
|
||||
* `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))
|
||||
* 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`.
|
||||
|
||||
* 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
|
||||
|
||||
* `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter.
|
||||
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.
|
||||
|
||||
**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)
|
||||
### 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.
|
||||
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.
|
||||
|
||||
* `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter.
|
||||
|
||||
### 0.14.0 (2018-09-25)
|
||||
|
||||
* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])
|
||||
* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537])
|
||||
@@ -343,3 +196,4 @@ The parser, standalone since v0.12.0, can now be compiled on Windows
|
||||
### 0.10.0
|
||||
|
||||
* See commit log.
|
||||
|
||||
|
||||
Vendored
+9
-84
@@ -3,8 +3,6 @@ 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} (.+)$")
|
||||
@@ -24,8 +22,7 @@ PROJECT(hiredis VERSION "${VERSION}")
|
||||
|
||||
SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples")
|
||||
|
||||
SET(hiredis_sources
|
||||
alloc.c
|
||||
ADD_LIBRARY(hiredis SHARED
|
||||
async.c
|
||||
dict.c
|
||||
hiredis.c
|
||||
@@ -34,32 +31,20 @@ SET(hiredis_sources
|
||||
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 WINDOWS_EXPORT_ALL_SYMBOLS TRUE
|
||||
PROPERTIES
|
||||
VERSION "${HIREDIS_SONAME}")
|
||||
IF(WIN32 OR MINGW)
|
||||
TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32)
|
||||
ENDIF()
|
||||
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC $<INSTALL_INTERFACE:.> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC .)
|
||||
|
||||
CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY)
|
||||
|
||||
INSTALL(TARGETS hiredis
|
||||
EXPORT hiredis-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
||||
|
||||
INSTALL(FILES hiredis.h read.h sds.h async.h alloc.h
|
||||
INSTALL(FILES hiredis.h read.h sds.h async.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
||||
|
||||
INSTALL(DIRECTORY adapters
|
||||
@@ -68,26 +53,6 @@ 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)
|
||||
@@ -95,66 +60,26 @@ IF(ENABLE_SSL)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
FIND_PACKAGE(OpenSSL REQUIRED)
|
||||
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}")
|
||||
|
||||
ssl.c)
|
||||
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
|
||||
EXPORT hiredis_ssl-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
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 DISABLE_TESTS)
|
||||
IF(NOT (WIN32 OR MINGW))
|
||||
ENABLE_TESTING()
|
||||
ADD_EXECUTABLE(hiredis-test test.c)
|
||||
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()
|
||||
TARGET_LINK_LIBRARIES(hiredis-test hiredis)
|
||||
ADD_TEST(NAME hiredis-test
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh)
|
||||
ENDIF()
|
||||
|
||||
Vendored
+19
-59
@@ -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=alloc.o net.o hiredis.o sds.o async.o read.o sockcompat.o
|
||||
OBJ=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 hiredis-example-push
|
||||
EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib
|
||||
ifeq ($(USE_SSL),1)
|
||||
EXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl
|
||||
endif
|
||||
TESTS=hiredis-test
|
||||
LIBNAME=libhiredis
|
||||
PKGCONFNAME=hiredis.pc
|
||||
SSL_LIBNAME=libhiredis_ssl
|
||||
PKGCONFNAME=hiredis.pc
|
||||
SSL_PKGCONFNAME=hiredis_ssl.pc
|
||||
|
||||
HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}')
|
||||
@@ -55,16 +55,11 @@ 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)
|
||||
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)
|
||||
STLIB_MAKE_CMD=$(AR) rcs
|
||||
|
||||
# Platform-specific overrides
|
||||
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
@@ -77,12 +72,7 @@ ifeq ($(USE_SSL),1)
|
||||
endif
|
||||
|
||||
ifeq ($(uname_S),Linux)
|
||||
ifdef OPENSSL_PREFIX
|
||||
CFLAGS+=-I$(OPENSSL_PREFIX)/include
|
||||
SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto
|
||||
else
|
||||
SSL_LDFLAGS=-lssl -lcrypto
|
||||
endif
|
||||
SSL_LDFLAGS=-lssl -lcrypto
|
||||
else
|
||||
OPENSSL_PREFIX?=/usr/local/opt/openssl
|
||||
CFLAGS+=-I$(OPENSSL_PREFIX)/include
|
||||
@@ -90,22 +80,13 @@ 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) $(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)
|
||||
DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(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)
|
||||
@@ -114,16 +95,15 @@ all: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)
|
||||
endif
|
||||
|
||||
# Deps (use make dep to generate this)
|
||||
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
|
||||
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
|
||||
sockcompat.o: sockcompat.c sockcompat.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
|
||||
ssl.o: ssl.c hiredis.h
|
||||
test.o: test.c fmacros.h hiredis.h read.h sds.h
|
||||
|
||||
$(DYLIBNAME): $(OBJ)
|
||||
$(DYLIB_MAKE_CMD) -o $(DYLIBNAME) $(OBJ) $(REAL_LDFLAGS)
|
||||
@@ -132,7 +112,7 @@ $(STLIBNAME): $(OBJ)
|
||||
$(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ)
|
||||
|
||||
$(SSL_DYLIBNAME): $(SSL_OBJ)
|
||||
$(SSL_DYLIB_MAKE_CMD) $(DYLIB_PLUGIN) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(LDFLAGS) $(SSL_LDFLAGS)
|
||||
$(DYLIB_MAKE_CMD) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
|
||||
|
||||
$(SSL_STLIBNAME): $(SSL_OBJ)
|
||||
$(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ)
|
||||
@@ -166,7 +146,6 @@ 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)"
|
||||
@@ -201,19 +180,13 @@ 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)
|
||||
TEST_LDFLAGS = $(SSL_LDFLAGS) -lssl -lcrypto -lpthread
|
||||
TEST_LIBS += $(SSL_STLIBNAME) -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)
|
||||
@@ -248,7 +221,7 @@ $(PKGCONFNAME): hiredis.h
|
||||
@echo Libs: -L\$${libdir} -lhiredis >> $@
|
||||
@echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@
|
||||
|
||||
$(SSL_PKGCONFNAME): hiredis_ssl.h
|
||||
$(SSL_PKGCONFNAME): hiredis.h
|
||||
@echo "Generating $@ for pkgconfig..."
|
||||
@echo prefix=$(PREFIX) > $@
|
||||
@echo exec_prefix=\$${prefix} >> $@
|
||||
@@ -264,7 +237,7 @@ $(SSL_PKGCONFNAME): hiredis_ssl.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 alloc.h $(INSTALL_INCLUDE_PATH)
|
||||
$(INSTALL) hiredis.h async.h read.h sds.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)
|
||||
@@ -272,19 +245,6 @@ 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
+13
-259
@@ -1,6 +1,6 @@
|
||||
[](https://travis-ci.org/redis/hiredis)
|
||||
|
||||
**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)).**
|
||||
**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.**
|
||||
|
||||
# HIREDIS
|
||||
|
||||
@@ -24,31 +24,12 @@ The library comes with multiple APIs. There is the
|
||||
|
||||
## Upgrading to `1.0.0`
|
||||
|
||||
Version 1.0.0 marks the first stable release of Hiredis.
|
||||
Version 1.0.0 marks a 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
|
||||
@@ -129,8 +110,6 @@ 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`.
|
||||
@@ -155,51 +134,16 @@ 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 (1.0.0) frees replies when the
|
||||
**Important:** the current version of hiredis (0.10.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. We may introduce a flag to make this configurable in future versions of the library.
|
||||
returns. This behavior will probably change in future releases, so make sure to
|
||||
keep an eye on the changelog when upgrading (see issue #39).
|
||||
|
||||
### Cleaning up
|
||||
|
||||
@@ -261,16 +205,16 @@ a single call to `read(2)`):
|
||||
redisReply *reply;
|
||||
redisAppendCommand(context,"SET foo bar");
|
||||
redisAppendCommand(context,"GET foo");
|
||||
redisGetReply(context,(void *)&reply); // reply for SET
|
||||
redisGetReply(context,&reply); // reply for SET
|
||||
freeReplyObject(reply);
|
||||
redisGetReply(context,(void *)&reply); // reply for GET
|
||||
redisGetReply(context,&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,(void *)&reply) == REDIS_OK) {
|
||||
while(redisGetReply(context,&reply) == REDIS_OK) {
|
||||
// consume message
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
@@ -460,199 +404,9 @@ 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
|
||||
|
||||
Salvatore Sanfilippo (antirez at gmail),\
|
||||
Pieter Noordhuis (pcnoordhuis at gmail)\
|
||||
Michael Grunder (michael dot grunder at gmail)
|
||||
|
||||
_Hiredis is released under the BSD license._
|
||||
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)
|
||||
|
||||
Vendored
+2
-5
@@ -96,7 +96,7 @@ static void redisAeCleanup(void *privdata) {
|
||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
||||
redisAeDelRead(privdata);
|
||||
redisAeDelWrite(privdata);
|
||||
hi_free(e);
|
||||
free(e);
|
||||
}
|
||||
|
||||
static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
|
||||
@@ -108,10 +108,7 @@ static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisAeEvents*)hi_malloc(sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e = (redisAeEvents*)malloc(sizeof(*e));
|
||||
e->context = ac;
|
||||
e->loop = loop;
|
||||
e->fd = c->fd;
|
||||
|
||||
Vendored
-3
@@ -134,9 +134,6 @@ 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
+2
-5
@@ -43,7 +43,7 @@ static void redisIvykisCleanup(void *privdata) {
|
||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||
|
||||
iv_fd_unregister(&e->fd);
|
||||
hi_free(e);
|
||||
free(e);
|
||||
}
|
||||
|
||||
static int redisIvykisAttach(redisAsyncContext *ac) {
|
||||
@@ -55,10 +55,7 @@ static int redisIvykisAttach(redisAsyncContext *ac) {
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisIvykisEvents*)hi_malloc(sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e = (redisIvykisEvents*)malloc(sizeof(*e));
|
||||
e->context = ac;
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
|
||||
Vendored
+3
-35
@@ -41,7 +41,6 @@ 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) {
|
||||
@@ -104,39 +103,11 @@ 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);
|
||||
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);
|
||||
free(e);
|
||||
}
|
||||
|
||||
static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
|
||||
@@ -148,16 +119,14 @@ static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisLibevEvents*)hi_calloc(1, sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e = (redisLibevEvents*)malloc(sizeof(*e));
|
||||
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;
|
||||
|
||||
@@ -167,7 +136,6 @@ 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
+2
-5
@@ -47,7 +47,7 @@ typedef struct redisLibeventEvents {
|
||||
} redisLibeventEvents;
|
||||
|
||||
static void redisLibeventDestroy(redisLibeventEvents *e) {
|
||||
hi_free(e);
|
||||
free(e);
|
||||
}
|
||||
|
||||
static void redisLibeventHandler(int fd, short event, void *arg) {
|
||||
@@ -152,10 +152,7 @@ static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisLibeventEvents*)hi_calloc(1, sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e = (redisLibeventEvents*)calloc(1, sizeof(*e));
|
||||
e->context = ac;
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
|
||||
Vendored
+7
-5
@@ -73,7 +73,7 @@ static void redisLibuvDelWrite(void *privdata) {
|
||||
static void on_close(uv_handle_t* handle) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
|
||||
|
||||
hi_free(p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,13 +98,15 @@ static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {
|
||||
ac->ev.delWrite = redisLibuvDelWrite;
|
||||
ac->ev.cleanup = redisLibuvCleanup;
|
||||
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)hi_malloc(sizeof(*p));
|
||||
if (p == NULL)
|
||||
return REDIS_ERR;
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)malloc(sizeof(*p));
|
||||
|
||||
if (!p) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
if (uv_poll_init_socket(loop, &p->handle, c->fd) != 0) {
|
||||
if (uv_poll_init(loop, &p->handle, c->fd) != 0) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+3
-4
@@ -27,7 +27,7 @@ static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {
|
||||
CFSocketInvalidate(redisRunLoop->socketRef);
|
||||
CFRelease(redisRunLoop->socketRef);
|
||||
}
|
||||
hi_free(redisRunLoop);
|
||||
free(redisRunLoop);
|
||||
}
|
||||
return REDIS_ERR;
|
||||
}
|
||||
@@ -80,9 +80,8 @@ 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*) hi_calloc(1, sizeof(RedisRunLoop));
|
||||
if (redisRunLoop == NULL)
|
||||
return REDIS_ERR;
|
||||
RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop));
|
||||
if( !redisRunLoop ) return REDIS_ERR;
|
||||
|
||||
/* Setup redis stuff */
|
||||
redisRunLoop->context = redisAsyncCtx;
|
||||
|
||||
Vendored
-90
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* 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
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* 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
+48
-169
@@ -30,7 +30,6 @@
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include "alloc.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef _MSC_VER
|
||||
@@ -47,24 +46,18 @@
|
||||
|
||||
#include "async_private.h"
|
||||
|
||||
/* Forward declarations of hiredis.c functions */
|
||||
/* Forward declaration of function in hiredis.c */
|
||||
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,
|
||||
hi_sdslen((const hisds)key));
|
||||
sdslen((const sds)key));
|
||||
}
|
||||
|
||||
static void *callbackValDup(void *privdata, const void *src) {
|
||||
((void) privdata);
|
||||
redisCallback *dup;
|
||||
|
||||
dup = hi_malloc(sizeof(*dup));
|
||||
if (dup == NULL)
|
||||
return NULL;
|
||||
|
||||
redisCallback *dup = malloc(sizeof(*dup));
|
||||
memcpy(dup,src,sizeof(*dup));
|
||||
return dup;
|
||||
}
|
||||
@@ -73,20 +66,20 @@ static int callbackKeyCompare(void *privdata, const void *key1, const void *key2
|
||||
int l1, l2;
|
||||
((void) privdata);
|
||||
|
||||
l1 = hi_sdslen((const hisds)key1);
|
||||
l2 = hi_sdslen((const hisds)key2);
|
||||
l1 = sdslen((const sds)key1);
|
||||
l2 = sdslen((const sds)key2);
|
||||
if (l1 != l2) return 0;
|
||||
return memcmp(key1,key2,l1) == 0;
|
||||
}
|
||||
|
||||
static void callbackKeyDestructor(void *privdata, void *key) {
|
||||
((void) privdata);
|
||||
hi_sdsfree((hisds)key);
|
||||
sdsfree((sds)key);
|
||||
}
|
||||
|
||||
static void callbackValDestructor(void *privdata, void *val) {
|
||||
((void) privdata);
|
||||
hi_free(val);
|
||||
free(val);
|
||||
}
|
||||
|
||||
static dictType callbackDict = {
|
||||
@@ -100,19 +93,10 @@ static dictType callbackDict = {
|
||||
|
||||
static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
||||
redisAsyncContext *ac;
|
||||
dict *channels = NULL, *patterns = NULL;
|
||||
|
||||
channels = dictCreate(&callbackDict,NULL);
|
||||
if (channels == NULL)
|
||||
goto oom;
|
||||
|
||||
patterns = dictCreate(&callbackDict,NULL);
|
||||
if (patterns == NULL)
|
||||
goto oom;
|
||||
|
||||
ac = hi_realloc(c,sizeof(redisAsyncContext));
|
||||
ac = realloc(c,sizeof(redisAsyncContext));
|
||||
if (ac == NULL)
|
||||
goto oom;
|
||||
return NULL;
|
||||
|
||||
c = &(ac->c);
|
||||
|
||||
@@ -124,7 +108,6 @@ 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;
|
||||
@@ -141,14 +124,9 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
||||
ac->replies.tail = NULL;
|
||||
ac->sub.invalid.head = NULL;
|
||||
ac->sub.invalid.tail = NULL;
|
||||
ac->sub.channels = channels;
|
||||
ac->sub.patterns = patterns;
|
||||
|
||||
ac->sub.channels = dictCreate(&callbackDict,NULL);
|
||||
ac->sub.patterns = dictCreate(&callbackDict,NULL);
|
||||
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
|
||||
@@ -167,26 +145,16 @@ 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;
|
||||
}
|
||||
@@ -246,7 +214,7 @@ static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
|
||||
redisCallback *cb;
|
||||
|
||||
/* Copy callback from stack to heap */
|
||||
cb = hi_malloc(sizeof(*cb));
|
||||
cb = malloc(sizeof(*cb));
|
||||
if (cb == NULL)
|
||||
return REDIS_ERR_OOM;
|
||||
|
||||
@@ -274,7 +242,7 @@ static int __redisShiftCallback(redisCallbackList *list, redisCallback *target)
|
||||
/* Copy callback from heap to stack */
|
||||
if (target != NULL)
|
||||
memcpy(target,cb,sizeof(*cb));
|
||||
hi_free(cb);
|
||||
free(cb);
|
||||
return REDIS_OK;
|
||||
}
|
||||
return REDIS_ERR;
|
||||
@@ -289,14 +257,6 @@ 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);
|
||||
@@ -312,28 +272,18 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
|
||||
while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)
|
||||
__redisRunCallback(ac,&cb,NULL);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
/* 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);
|
||||
|
||||
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);
|
||||
}
|
||||
it = dictGetIterator(ac->sub.patterns);
|
||||
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);
|
||||
@@ -348,10 +298,6 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ac->dataCleanup) {
|
||||
ac->dataCleanup(ac->data);
|
||||
}
|
||||
|
||||
/* Cleanup self */
|
||||
redisFree(c);
|
||||
}
|
||||
@@ -418,11 +364,11 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
|
||||
dictEntry *de;
|
||||
int pvariant;
|
||||
char *stype;
|
||||
hisds sname;
|
||||
sds 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 || reply->type == REDIS_REPLY_PUSH) {
|
||||
if (reply->type == REDIS_REPLY_ARRAY) {
|
||||
assert(reply->elements >= 2);
|
||||
assert(reply->element[0]->type == REDIS_REPLY_STRING);
|
||||
stype = reply->element[0]->str;
|
||||
@@ -435,10 +381,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
|
||||
|
||||
/* Locate the right callback */
|
||||
assert(reply->element[1]->type == REDIS_REPLY_STRING);
|
||||
sname = hi_sdsnewlen(reply->element[1]->str,reply->element[1]->len);
|
||||
if (sname == NULL)
|
||||
goto oom;
|
||||
|
||||
sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
|
||||
de = dictFind(callbacks,sname);
|
||||
if (de != NULL) {
|
||||
cb = dictGetEntryVal(de);
|
||||
@@ -466,39 +409,12 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
|
||||
c->flags &= ~REDIS_SUBSCRIBED;
|
||||
}
|
||||
}
|
||||
hi_sdsfree(sname);
|
||||
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) {
|
||||
@@ -511,7 +427,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 && hi_sdslen(c->obuf) == 0
|
||||
if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0
|
||||
&& ac->replies.head == NULL) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
return;
|
||||
@@ -527,18 +443,8 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
/* 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
|
||||
@@ -591,31 +497,20 @@ 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);
|
||||
__redisAsyncHandleConnectFailure(ac);
|
||||
if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);
|
||||
__redisAsyncDisconnect(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;
|
||||
@@ -687,6 +582,8 @@ 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;
|
||||
@@ -744,7 +641,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
||||
const char *cstr, *astr;
|
||||
size_t clen, alen;
|
||||
const char *p;
|
||||
hisds sname;
|
||||
sds sname;
|
||||
int ret;
|
||||
|
||||
/* Don't accept new commands when the connection is about to be closed. */
|
||||
@@ -768,10 +665,7 @@ 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 = hi_sdsnewlen(astr,alen);
|
||||
if (sname == NULL)
|
||||
goto oom;
|
||||
|
||||
sname = sdsnewlen(astr,alen);
|
||||
if (pvariant)
|
||||
cbdict = ac->sub.patterns;
|
||||
else
|
||||
@@ -786,7 +680,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
||||
|
||||
ret = dictReplace(cbdict,sname,&cb);
|
||||
|
||||
if (ret == 0) hi_sdsfree(sname);
|
||||
if (ret == 0) sdsfree(sname);
|
||||
}
|
||||
} else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
|
||||
/* It is only useful to call (P)UNSUBSCRIBE when the context is
|
||||
@@ -815,9 +709,6 @@ 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) {
|
||||
@@ -831,7 +722,7 @@ int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdat
|
||||
return REDIS_ERR;
|
||||
|
||||
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
||||
hi_free(cmd);
|
||||
free(cmd);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -845,14 +736,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) {
|
||||
hisds cmd;
|
||||
sds cmd;
|
||||
int len;
|
||||
int status;
|
||||
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
|
||||
if (len < 0)
|
||||
return REDIS_ERR;
|
||||
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
||||
hi_sdsfree(cmd);
|
||||
sdsfree(cmd);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -861,27 +752,15 @@ int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
||||
return status;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
void redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {
|
||||
if (!ac->c.timeout) {
|
||||
ac->c.timeout = calloc(1, sizeof(tv));
|
||||
}
|
||||
|
||||
if (tv.tv_sec != ac->c.command_timeout->tv_sec ||
|
||||
tv.tv_usec != ac->c.command_timeout->tv_usec)
|
||||
{
|
||||
*ac->c.command_timeout = tv;
|
||||
if (tv.tv_sec == ac->c.timeout->tv_sec &&
|
||||
tv.tv_usec == ac->c.timeout->tv_usec) {
|
||||
return;
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
*ac->c.timeout = tv;
|
||||
}
|
||||
|
||||
Vendored
+1
-6
@@ -70,7 +70,6 @@ typedef struct redisAsyncContext {
|
||||
|
||||
/* Not used by hiredis */
|
||||
void *data;
|
||||
void (*dataCleanup)(void *privdata);
|
||||
|
||||
/* Event library data and hooks */
|
||||
struct {
|
||||
@@ -106,9 +105,6 @@ typedef struct redisAsyncContext {
|
||||
struct dict *channels;
|
||||
struct dict *patterns;
|
||||
} sub;
|
||||
|
||||
/* Any configured RESP3 PUSH handler */
|
||||
redisAsyncPushFn *push_cb;
|
||||
} redisAsyncContext;
|
||||
|
||||
/* Functions that proxy to hiredis */
|
||||
@@ -121,8 +117,7 @@ redisAsyncContext *redisAsyncConnectUnix(const char *path);
|
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
|
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
||||
|
||||
redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn);
|
||||
int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);
|
||||
void redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);
|
||||
void redisAsyncDisconnect(redisAsyncContext *ac);
|
||||
void redisAsyncFree(redisAsyncContext *ac);
|
||||
|
||||
|
||||
Vendored
+10
-13
@@ -51,21 +51,18 @@
|
||||
#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) {
|
||||
#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);
|
||||
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);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+11
-25
@@ -34,7 +34,6 @@
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include "alloc.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
@@ -72,10 +71,7 @@ static void _dictReset(dict *ht) {
|
||||
|
||||
/* Create a new hash table */
|
||||
static dict *dictCreate(dictType *type, void *privDataPtr) {
|
||||
dict *ht = hi_malloc(sizeof(*ht));
|
||||
if (ht == NULL)
|
||||
return NULL;
|
||||
|
||||
dict *ht = malloc(sizeof(*ht));
|
||||
_dictInit(ht,type,privDataPtr);
|
||||
return ht;
|
||||
}
|
||||
@@ -101,9 +97,7 @@ static int dictExpand(dict *ht, unsigned long size) {
|
||||
_dictInit(&n, ht->type, ht->privdata);
|
||||
n.size = realsize;
|
||||
n.sizemask = realsize-1;
|
||||
n.table = hi_calloc(realsize,sizeof(dictEntry*));
|
||||
if (n.table == NULL)
|
||||
return DICT_ERR;
|
||||
n.table = calloc(realsize,sizeof(dictEntry*));
|
||||
|
||||
/* Copy all the elements from the old to the new table:
|
||||
* note that if the old hash table is empty ht->size is zero,
|
||||
@@ -130,7 +124,7 @@ static int dictExpand(dict *ht, unsigned long size) {
|
||||
}
|
||||
}
|
||||
assert(ht->used == 0);
|
||||
hi_free(ht->table);
|
||||
free(ht->table);
|
||||
|
||||
/* Remap the new hashtable in the old */
|
||||
*ht = n;
|
||||
@@ -148,10 +142,7 @@ static int dictAdd(dict *ht, void *key, void *val) {
|
||||
return DICT_ERR;
|
||||
|
||||
/* Allocates the memory and stores key */
|
||||
entry = hi_malloc(sizeof(*entry));
|
||||
if (entry == NULL)
|
||||
return DICT_ERR;
|
||||
|
||||
entry = malloc(sizeof(*entry));
|
||||
entry->next = ht->table[index];
|
||||
ht->table[index] = entry;
|
||||
|
||||
@@ -175,9 +166,6 @@ 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
|
||||
@@ -211,7 +199,7 @@ static int dictDelete(dict *ht, const void *key) {
|
||||
|
||||
dictFreeEntryKey(ht,de);
|
||||
dictFreeEntryVal(ht,de);
|
||||
hi_free(de);
|
||||
free(de);
|
||||
ht->used--;
|
||||
return DICT_OK;
|
||||
}
|
||||
@@ -234,13 +222,13 @@ static int _dictClear(dict *ht) {
|
||||
nextHe = he->next;
|
||||
dictFreeEntryKey(ht, he);
|
||||
dictFreeEntryVal(ht, he);
|
||||
hi_free(he);
|
||||
free(he);
|
||||
ht->used--;
|
||||
he = nextHe;
|
||||
}
|
||||
}
|
||||
/* Free the table and the allocated cache structure */
|
||||
hi_free(ht->table);
|
||||
free(ht->table);
|
||||
/* Re-initialize the table */
|
||||
_dictReset(ht);
|
||||
return DICT_OK; /* never fails */
|
||||
@@ -249,7 +237,7 @@ static int _dictClear(dict *ht) {
|
||||
/* Clear & Release the hash table */
|
||||
static void dictRelease(dict *ht) {
|
||||
_dictClear(ht);
|
||||
hi_free(ht);
|
||||
free(ht);
|
||||
}
|
||||
|
||||
static dictEntry *dictFind(dict *ht, const void *key) {
|
||||
@@ -268,9 +256,7 @@ static dictEntry *dictFind(dict *ht, const void *key) {
|
||||
}
|
||||
|
||||
static dictIterator *dictGetIterator(dict *ht) {
|
||||
dictIterator *iter = hi_malloc(sizeof(*iter));
|
||||
if (iter == NULL)
|
||||
return NULL;
|
||||
dictIterator *iter = malloc(sizeof(*iter));
|
||||
|
||||
iter->ht = ht;
|
||||
iter->index = -1;
|
||||
@@ -300,7 +286,7 @@ static dictEntry *dictNext(dictIterator *iter) {
|
||||
}
|
||||
|
||||
static void dictReleaseIterator(dictIterator *iter) {
|
||||
hi_free(iter);
|
||||
free(iter);
|
||||
}
|
||||
|
||||
/* ------------------------- private functions ------------------------------ */
|
||||
@@ -308,7 +294,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" double its size. */
|
||||
* if the table is "full" dobule its size. */
|
||||
if (ht->size == 0)
|
||||
return dictExpand(ht, DICT_HT_INITIAL_SIZE);
|
||||
if (ht->used == ht->size)
|
||||
|
||||
Vendored
-3
@@ -44,6 +44,3 @@ 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,9 +33,7 @@ void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
iv_init();
|
||||
|
||||
|
||||
-2
@@ -33,9 +33,7 @@ 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) {
|
||||
|
||||
+1
-18
@@ -34,10 +34,7 @@ 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,
|
||||
@@ -55,25 +52,13 @@ 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 (redisInitiateSSLWithContext(&c->c, ssl) != REDIS_OK) {
|
||||
if (redisSecureConnection(&c->c, caCert, cert, certKey, "sni") != REDIS_OK) {
|
||||
printf("SSL Error!\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -84,7 +69,5 @@ 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;
|
||||
}
|
||||
|
||||
+1
-4
@@ -38,16 +38,13 @@ 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.connect_timeout = &tv;
|
||||
options.timeout = &tv;
|
||||
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnectWithOptions(&options);
|
||||
|
||||
-3
@@ -33,10 +33,7 @@ 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
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
* 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
+2
-15
@@ -4,12 +4,9 @@
|
||||
|
||||
#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) {
|
||||
@@ -22,18 +19,10 @@ 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.connect_timeout = &tv;
|
||||
options.timeout = &tv;
|
||||
c = redisConnectWithOptions(&options);
|
||||
|
||||
if (c == NULL || c->err) {
|
||||
@@ -46,7 +35,7 @@ int main(int argc, char **argv) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) {
|
||||
if (redisSecureConnection(c, ca, cert, key, "sni") != REDIS_OK) {
|
||||
printf("Couldn't initialize SSL!\n");
|
||||
printf("Error: %s\n", c->errstr);
|
||||
redisFree(c);
|
||||
@@ -104,7 +93,5 @@ 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
@@ -1,13 +0,0 @@
|
||||
@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
+111
-198
@@ -44,11 +44,8 @@
|
||||
#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_privctx = NULL,
|
||||
.free_privdata = NULL,
|
||||
.async_read = redisAsyncRead,
|
||||
.async_write = redisAsyncWrite,
|
||||
.read = redisNetRead,
|
||||
@@ -77,7 +74,7 @@ static redisReplyObjectFunctions defaultFunctions = {
|
||||
|
||||
/* Create a reply object */
|
||||
static redisReply *createReplyObject(int type) {
|
||||
redisReply *r = hi_calloc(1,sizeof(*r));
|
||||
redisReply *r = calloc(1,sizeof(*r));
|
||||
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
@@ -100,22 +97,20 @@ 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]);
|
||||
hi_free(r->element);
|
||||
free(r->element);
|
||||
}
|
||||
break;
|
||||
case REDIS_REPLY_ERROR:
|
||||
case REDIS_REPLY_STATUS:
|
||||
case REDIS_REPLY_STRING:
|
||||
case REDIS_REPLY_DOUBLE:
|
||||
case REDIS_REPLY_VERB:
|
||||
hi_free(r->str);
|
||||
free(r->str);
|
||||
break;
|
||||
}
|
||||
hi_free(r);
|
||||
free(r);
|
||||
}
|
||||
|
||||
static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
|
||||
@@ -133,18 +128,22 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
|
||||
|
||||
/* Copy string value */
|
||||
if (task->type == REDIS_REPLY_VERB) {
|
||||
buf = hi_malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */
|
||||
if (buf == NULL) goto oom;
|
||||
|
||||
buf = malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */
|
||||
if (buf == NULL) {
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
}
|
||||
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 = hi_malloc(len+1);
|
||||
if (buf == NULL) goto oom;
|
||||
|
||||
buf = malloc(len+1);
|
||||
if (buf == NULL) {
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(buf,str,len);
|
||||
buf[len] = '\0';
|
||||
r->len = len;
|
||||
@@ -155,15 +154,10 @@ 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_PUSH);
|
||||
parent->type == REDIS_REPLY_SET);
|
||||
parent->element[task->idx] = r;
|
||||
}
|
||||
return r;
|
||||
|
||||
oom:
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *createArrayObject(const redisReadTask *task, size_t elements) {
|
||||
@@ -174,7 +168,8 @@ static void *createArrayObject(const redisReadTask *task, size_t elements) {
|
||||
return NULL;
|
||||
|
||||
if (elements > 0) {
|
||||
r->element = hi_calloc(elements,sizeof(redisReply*));
|
||||
if (SIZE_MAX / sizeof(redisReply*) < elements) return NULL; /* Don't overflow */
|
||||
r->element = calloc(elements,sizeof(redisReply*));
|
||||
if (r->element == NULL) {
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
@@ -187,8 +182,7 @@ 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_PUSH);
|
||||
parent->type == REDIS_REPLY_SET);
|
||||
parent->element[task->idx] = r;
|
||||
}
|
||||
return r;
|
||||
@@ -207,8 +201,7 @@ 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_PUSH);
|
||||
parent->type == REDIS_REPLY_SET);
|
||||
parent->element[task->idx] = r;
|
||||
}
|
||||
return r;
|
||||
@@ -222,7 +215,7 @@ static void *createDoubleObject(const redisReadTask *task, double value, char *s
|
||||
return NULL;
|
||||
|
||||
r->dval = value;
|
||||
r->str = hi_malloc(len+1);
|
||||
r->str = malloc(len+1);
|
||||
if (r->str == NULL) {
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
@@ -257,8 +250,7 @@ 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_PUSH);
|
||||
parent->type == REDIS_REPLY_SET);
|
||||
parent->element[task->idx] = r;
|
||||
}
|
||||
return r;
|
||||
@@ -306,7 +298,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 */
|
||||
hisds curarg, newarg; /* current argument */
|
||||
sds curarg, newarg; /* current argument */
|
||||
int touched = 0; /* was the current argument touched? */
|
||||
char **curargv = NULL, **newargv = NULL;
|
||||
int argc = 0;
|
||||
@@ -319,7 +311,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
return -1;
|
||||
|
||||
/* Build the command string accordingly to protocol */
|
||||
curarg = hi_sdsempty();
|
||||
curarg = sdsempty();
|
||||
if (curarg == NULL)
|
||||
return -1;
|
||||
|
||||
@@ -327,19 +319,19 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
if (*c != '%' || c[1] == '\0') {
|
||||
if (*c == ' ') {
|
||||
if (touched) {
|
||||
newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));
|
||||
newargv = realloc(curargv,sizeof(char*)*(argc+1));
|
||||
if (newargv == NULL) goto memory_err;
|
||||
curargv = newargv;
|
||||
curargv[argc++] = curarg;
|
||||
totlen += bulklen(hi_sdslen(curarg));
|
||||
totlen += bulklen(sdslen(curarg));
|
||||
|
||||
/* curarg is put in argv so it can be overwritten. */
|
||||
curarg = hi_sdsempty();
|
||||
curarg = sdsempty();
|
||||
if (curarg == NULL) goto memory_err;
|
||||
touched = 0;
|
||||
}
|
||||
} else {
|
||||
newarg = hi_sdscatlen(curarg,c,1);
|
||||
newarg = sdscatlen(curarg,c,1);
|
||||
if (newarg == NULL) goto memory_err;
|
||||
curarg = newarg;
|
||||
touched = 1;
|
||||
@@ -356,16 +348,16 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
arg = va_arg(ap,char*);
|
||||
size = strlen(arg);
|
||||
if (size > 0)
|
||||
newarg = hi_sdscatlen(curarg,arg,size);
|
||||
newarg = sdscatlen(curarg,arg,size);
|
||||
break;
|
||||
case 'b':
|
||||
arg = va_arg(ap,char*);
|
||||
size = va_arg(ap,size_t);
|
||||
if (size > 0)
|
||||
newarg = hi_sdscatlen(curarg,arg,size);
|
||||
newarg = sdscatlen(curarg,arg,size);
|
||||
break;
|
||||
case '%':
|
||||
newarg = hi_sdscat(curarg,"%");
|
||||
newarg = sdscat(curarg,"%");
|
||||
break;
|
||||
default:
|
||||
/* Try to detect printf format */
|
||||
@@ -453,7 +445,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
if (_l < sizeof(_format)-2) {
|
||||
memcpy(_format,c,_l);
|
||||
_format[_l] = '\0';
|
||||
newarg = hi_sdscatvprintf(curarg,_format,_cpy);
|
||||
newarg = sdscatvprintf(curarg,_format,_cpy);
|
||||
|
||||
/* Update current position (note: outer blocks
|
||||
* increment c twice so compensate here) */
|
||||
@@ -476,13 +468,13 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
|
||||
/* Add the last argument if needed */
|
||||
if (touched) {
|
||||
newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));
|
||||
newargv = realloc(curargv,sizeof(char*)*(argc+1));
|
||||
if (newargv == NULL) goto memory_err;
|
||||
curargv = newargv;
|
||||
curargv[argc++] = curarg;
|
||||
totlen += bulklen(hi_sdslen(curarg));
|
||||
totlen += bulklen(sdslen(curarg));
|
||||
} else {
|
||||
hi_sdsfree(curarg);
|
||||
sdsfree(curarg);
|
||||
}
|
||||
|
||||
/* Clear curarg because it was put in curargv or was free'd. */
|
||||
@@ -492,22 +484,22 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
totlen += 1+countDigits(argc)+2;
|
||||
|
||||
/* Build the command at protocol level */
|
||||
cmd = hi_malloc(totlen+1);
|
||||
cmd = 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",hi_sdslen(curargv[j]));
|
||||
memcpy(cmd+pos,curargv[j],hi_sdslen(curargv[j]));
|
||||
pos += hi_sdslen(curargv[j]);
|
||||
hi_sdsfree(curargv[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]);
|
||||
cmd[pos++] = '\r';
|
||||
cmd[pos++] = '\n';
|
||||
}
|
||||
assert(pos == totlen);
|
||||
cmd[pos] = '\0';
|
||||
|
||||
hi_free(curargv);
|
||||
free(curargv);
|
||||
*target = cmd;
|
||||
return totlen;
|
||||
|
||||
@@ -522,12 +514,12 @@ memory_err:
|
||||
cleanup:
|
||||
if (curargv) {
|
||||
while(argc--)
|
||||
hi_sdsfree(curargv[argc]);
|
||||
hi_free(curargv);
|
||||
sdsfree(curargv[argc]);
|
||||
free(curargv);
|
||||
}
|
||||
|
||||
hi_sdsfree(curarg);
|
||||
hi_free(cmd);
|
||||
sdsfree(curarg);
|
||||
free(cmd);
|
||||
|
||||
return error_type;
|
||||
}
|
||||
@@ -559,16 +551,16 @@ int redisFormatCommand(char **target, const char *format, ...) {
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Format a command according to the Redis protocol using an hisds string and
|
||||
* hi_sdscatfmt for the processing of arguments. This function takes the
|
||||
/* Format a command according to the Redis protocol using an sds string and
|
||||
* 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(hisds *target, int argc, const char **argv,
|
||||
int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,
|
||||
const size_t *argvlen)
|
||||
{
|
||||
hisds cmd, aux;
|
||||
sds cmd;
|
||||
unsigned long long totlen;
|
||||
int j;
|
||||
size_t len;
|
||||
@@ -585,36 +577,32 @@ int redisFormatSdsCommandArgv(hisds *target, int argc, const char **argv,
|
||||
}
|
||||
|
||||
/* Use an SDS string for command construction */
|
||||
cmd = hi_sdsempty();
|
||||
cmd = sdsempty();
|
||||
if (cmd == NULL)
|
||||
return -1;
|
||||
|
||||
/* We already know how much storage we need */
|
||||
aux = hi_sdsMakeRoomFor(cmd, totlen);
|
||||
if (aux == NULL) {
|
||||
hi_sdsfree(cmd);
|
||||
cmd = sdsMakeRoomFor(cmd, totlen);
|
||||
if (cmd == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmd = aux;
|
||||
|
||||
/* Construct command */
|
||||
cmd = hi_sdscatfmt(cmd, "*%i\r\n", argc);
|
||||
cmd = 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);
|
||||
cmd = sdscatfmt(cmd, "$%u\r\n", len);
|
||||
cmd = sdscatlen(cmd, argv[j], len);
|
||||
cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1);
|
||||
}
|
||||
|
||||
assert(hi_sdslen(cmd)==totlen);
|
||||
assert(sdslen(cmd)==totlen);
|
||||
|
||||
*target = cmd;
|
||||
return totlen;
|
||||
}
|
||||
|
||||
void redisFreeSdsCommand(hisds cmd) {
|
||||
hi_sdsfree(cmd);
|
||||
void redisFreeSdsCommand(sds cmd) {
|
||||
sdsfree(cmd);
|
||||
}
|
||||
|
||||
/* Format a command according to the Redis protocol. This function takes the
|
||||
@@ -640,7 +628,7 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
|
||||
}
|
||||
|
||||
/* Build the command at protocol level */
|
||||
cmd = hi_malloc(totlen+1);
|
||||
cmd = malloc(totlen+1);
|
||||
if (cmd == NULL)
|
||||
return -1;
|
||||
|
||||
@@ -661,7 +649,7 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
|
||||
}
|
||||
|
||||
void redisFreeCommand(char *cmd) {
|
||||
hi_free(cmd);
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
void __redisSetError(redisContext *c, int type, const char *str) {
|
||||
@@ -684,21 +672,15 @@ redisReader *redisReaderCreate(void) {
|
||||
return redisReaderCreateWithFunctions(&defaultFunctions);
|
||||
}
|
||||
|
||||
static void redisPushAutoFree(void *privdata, void *reply) {
|
||||
(void)privdata;
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
static redisContext *redisContextInit(void) {
|
||||
static redisContext *redisContextInit(const redisOptions *options) {
|
||||
redisContext *c;
|
||||
|
||||
c = hi_calloc(1, sizeof(*c));
|
||||
c = calloc(1, sizeof(*c));
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
c->funcs = &redisContextDefaultFuncs;
|
||||
|
||||
c->obuf = hi_sdsempty();
|
||||
c->obuf = sdsempty();
|
||||
c->reader = redisReaderCreate();
|
||||
c->fd = REDIS_INVALID_FD;
|
||||
|
||||
@@ -706,7 +688,7 @@ static redisContext *redisContextInit(void) {
|
||||
redisFree(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(void)options; /* options are used in other functions */
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -715,23 +697,18 @@ void redisFree(redisContext *c) {
|
||||
return;
|
||||
redisNetClose(c);
|
||||
|
||||
hi_sdsfree(c->obuf);
|
||||
sdsfree(c->obuf);
|
||||
redisReaderFree(c->reader);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
memset(c, 0xff, sizeof(*c));
|
||||
hi_free(c);
|
||||
free(c);
|
||||
}
|
||||
|
||||
redisFD redisFreeKeepFd(redisContext *c) {
|
||||
@@ -745,46 +722,35 @@ int redisReconnect(redisContext *c) {
|
||||
c->err = 0;
|
||||
memset(c->errstr, '\0', strlen(c->errstr));
|
||||
|
||||
if (c->privctx && c->funcs->free_privctx) {
|
||||
c->funcs->free_privctx(c->privctx);
|
||||
c->privctx = NULL;
|
||||
if (c->privdata && c->funcs->free_privdata) {
|
||||
c->funcs->free_privdata(c->privdata);
|
||||
c->privdata = NULL;
|
||||
}
|
||||
|
||||
redisNetClose(c);
|
||||
|
||||
hi_sdsfree(c->obuf);
|
||||
sdsfree(c->obuf);
|
||||
redisReaderFree(c->reader);
|
||||
|
||||
c->obuf = hi_sdsempty();
|
||||
c->obuf = 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) {
|
||||
ret = redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port,
|
||||
c->connect_timeout, c->tcp.source_addr);
|
||||
return redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port,
|
||||
c->timeout, c->tcp.source_addr);
|
||||
} else if (c->connection_type == REDIS_CONN_UNIX) {
|
||||
ret = redisContextConnectUnix(c, c->unix_sock.path, c->connect_timeout);
|
||||
return redisContextConnectUnix(c, c->unix_sock.path, c->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;
|
||||
}
|
||||
|
||||
if (c->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
|
||||
redisContextSetTimeout(c, *c->command_timeout);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
redisContext *redisConnectWithOptions(const redisOptions *options) {
|
||||
redisContext *c = redisContextInit();
|
||||
redisContext *c = redisContextInit(options);
|
||||
if (c == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -795,32 +761,16 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
|
||||
c->flags |= REDIS_REUSEADDR;
|
||||
}
|
||||
if (options->options & REDIS_OPT_NOAUTOFREE) {
|
||||
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;
|
||||
c->flags |= REDIS_NO_AUTO_FREE;
|
||||
}
|
||||
|
||||
if (options->type == REDIS_CONN_TCP) {
|
||||
redisContextConnectBindTcp(c, options->endpoint.tcp.ip,
|
||||
options->endpoint.tcp.port, options->connect_timeout,
|
||||
options->endpoint.tcp.port, options->timeout,
|
||||
options->endpoint.tcp.source_addr);
|
||||
} else if (options->type == REDIS_CONN_UNIX) {
|
||||
redisContextConnectUnix(c, options->endpoint.unix_socket,
|
||||
options->connect_timeout);
|
||||
options->timeout);
|
||||
} else if (options->type == REDIS_CONN_USERFD) {
|
||||
c->fd = options->endpoint.fd;
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
@@ -828,11 +778,9 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
|
||||
// Unknown type - FIXME - FREE
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
|
||||
redisContextSetTimeout(c, *options->command_timeout);
|
||||
if (options->timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
|
||||
redisContextSetTimeout(c, *options->timeout);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -848,7 +796,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.connect_timeout = &tv;
|
||||
options.timeout = &tv;
|
||||
return redisConnectWithOptions(&options);
|
||||
}
|
||||
|
||||
@@ -886,7 +834,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.connect_timeout = &tv;
|
||||
options.timeout = &tv;
|
||||
return redisConnectWithOptions(&options);
|
||||
}
|
||||
|
||||
@@ -918,13 +866,6 @@ 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.
|
||||
*
|
||||
@@ -966,27 +907,21 @@ int redisBufferWrite(redisContext *c, int *done) {
|
||||
if (c->err)
|
||||
return REDIS_ERR;
|
||||
|
||||
if (hi_sdslen(c->obuf) > 0) {
|
||||
ssize_t nwritten = c->funcs->write(c);
|
||||
if (sdslen(c->obuf) > 0) {
|
||||
int nwritten = c->funcs->write(c);
|
||||
if (nwritten < 0) {
|
||||
return REDIS_ERR;
|
||||
} else if (nwritten > 0) {
|
||||
if (nwritten == (ssize_t)hi_sdslen(c->obuf)) {
|
||||
hi_sdsfree(c->obuf);
|
||||
c->obuf = hi_sdsempty();
|
||||
if (c->obuf == NULL)
|
||||
goto oom;
|
||||
if (nwritten == (signed)sdslen(c->obuf)) {
|
||||
sdsfree(c->obuf);
|
||||
c->obuf = sdsempty();
|
||||
} else {
|
||||
if (hi_sdsrange(c->obuf,nwritten,-1) < 0) goto oom;
|
||||
sdsrange(c->obuf,nwritten,-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (done != NULL) *done = (hi_sdslen(c->obuf) == 0);
|
||||
if (done != NULL) *done = (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,
|
||||
@@ -996,21 +931,9 @@ 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;
|
||||
@@ -1031,23 +954,13 @@ int redisGetReply(redisContext *c, void **reply) {
|
||||
do {
|
||||
if (redisBufferRead(c) == 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));
|
||||
if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
|
||||
return REDIS_ERR;
|
||||
} while (aux == NULL);
|
||||
}
|
||||
|
||||
/* Set reply or free it if we were passed NULL */
|
||||
if (reply != NULL) {
|
||||
*reply = aux;
|
||||
} else {
|
||||
freeReplyObject(aux);
|
||||
}
|
||||
|
||||
/* Set reply object */
|
||||
if (reply != NULL) *reply = aux;
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
@@ -1059,9 +972,9 @@ int redisGetReply(redisContext *c, void **reply) {
|
||||
* the reply (or replies in pub/sub).
|
||||
*/
|
||||
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) {
|
||||
hisds newbuf;
|
||||
sds newbuf;
|
||||
|
||||
newbuf = hi_sdscatlen(c->obuf,cmd,len);
|
||||
newbuf = sdscatlen(c->obuf,cmd,len);
|
||||
if (newbuf == NULL) {
|
||||
__redisSetError(c,REDIS_ERR_OOM,"Out of memory");
|
||||
return REDIS_ERR;
|
||||
@@ -1094,11 +1007,11 @@ int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
|
||||
}
|
||||
|
||||
if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
|
||||
hi_free(cmd);
|
||||
free(cmd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
hi_free(cmd);
|
||||
free(cmd);
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
@@ -1113,7 +1026,7 @@ int redisAppendCommand(redisContext *c, const char *format, ...) {
|
||||
}
|
||||
|
||||
int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
|
||||
hisds cmd;
|
||||
sds cmd;
|
||||
int len;
|
||||
|
||||
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
|
||||
@@ -1123,11 +1036,11 @@ int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const s
|
||||
}
|
||||
|
||||
if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
|
||||
hi_sdsfree(cmd);
|
||||
sdsfree(cmd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
hi_sdsfree(cmd);
|
||||
sdsfree(cmd);
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+16
-53
@@ -39,16 +39,14 @@
|
||||
#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 hisds */
|
||||
#include "alloc.h" /* for allocation wrappers */
|
||||
#include "sds.h" /* for sds */
|
||||
|
||||
#define HIREDIS_MAJOR 1
|
||||
#define HIREDIS_MINOR 0
|
||||
#define HIREDIS_MAJOR 0
|
||||
#define HIREDIS_MINOR 14
|
||||
#define HIREDIS_PATCH 0
|
||||
#define HIREDIS_SONAME 1.0.0
|
||||
#define HIREDIS_SONAME 0.14
|
||||
|
||||
/* Connection type can be blocking or non-blocking and is set in the
|
||||
* least significant bit of the flags field in redisContext. */
|
||||
@@ -92,15 +90,6 @@ typedef long long ssize_t;
|
||||
* 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
|
||||
@@ -112,7 +101,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
|
||||
REDIS_REPLY_VERB, and REDIS_REPLY_DOUBLE (in additional to dval). */
|
||||
and REDIS_REPLY_DOUBLE (in additionl 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 */
|
||||
@@ -128,9 +117,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(hisds *target, int argc, const char ** argv, const size_t *argvlen);
|
||||
int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
|
||||
void redisFreeCommand(char *cmd);
|
||||
void redisFreeSdsCommand(hisds cmd);
|
||||
void redisFreeSdsCommand(sds cmd);
|
||||
|
||||
enum redisConnectionType {
|
||||
REDIS_CONN_TCP,
|
||||
@@ -149,9 +138,6 @@ 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
|
||||
@@ -176,11 +162,8 @@ typedef struct {
|
||||
int type;
|
||||
/* bit field of REDIS_OPT_xxx */
|
||||
int options;
|
||||
/* 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;
|
||||
/* timeout value. if NULL, no timeout is used */
|
||||
const struct timeval *timeout;
|
||||
union {
|
||||
/** use this field for tcp/ip connections */
|
||||
struct {
|
||||
@@ -195,14 +178,6 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -217,16 +192,15 @@ typedef struct {
|
||||
(opts)->type = REDIS_CONN_UNIX; \
|
||||
(opts)->endpoint.unix_socket = path;
|
||||
|
||||
#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \
|
||||
(opts)->privdata = data; \
|
||||
(opts)->free_privdata = dtor; \
|
||||
struct redisAsyncContext;
|
||||
struct redisContext;
|
||||
|
||||
typedef struct redisContextFuncs {
|
||||
void (*free_privctx)(void *);
|
||||
void (*free_privdata)(void *);
|
||||
void (*async_read)(struct redisAsyncContext *);
|
||||
void (*async_write)(struct redisAsyncContext *);
|
||||
ssize_t (*read)(struct redisContext *, char *, size_t);
|
||||
ssize_t (*write)(struct redisContext *);
|
||||
int (*read)(struct redisContext *, char *, size_t);
|
||||
int (*write)(struct redisContext *);
|
||||
} redisContextFuncs;
|
||||
|
||||
/* Context for a connection to Redis */
|
||||
@@ -241,8 +215,7 @@ typedef struct redisContext {
|
||||
redisReader *reader; /* Protocol reader */
|
||||
|
||||
enum redisConnectionType connection_type;
|
||||
struct timeval *connect_timeout;
|
||||
struct timeval *command_timeout;
|
||||
struct timeval *timeout;
|
||||
|
||||
struct {
|
||||
char *host;
|
||||
@@ -258,17 +231,8 @@ typedef struct redisContext {
|
||||
struct sockadr *saddr;
|
||||
size_t addrlen;
|
||||
|
||||
/* Optional data and corresponding destructor users can use to provide
|
||||
* context to a given redisContext. Not used by hiredis. */
|
||||
/* Additional private data for hiredis addons such as SSL */
|
||||
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);
|
||||
@@ -295,7 +259,6 @@ 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
+1
-2
@@ -1,7 +1,6 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
install_libdir=@CMAKE_INSTALL_LIBDIR@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/${install_libdir}
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
pkgincludedir=${includedir}/hiredis
|
||||
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
@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
+6
-80
@@ -32,96 +32,22 @@
|
||||
#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;
|
||||
|
||||
/* A wrapper around OpenSSL SSL_CTX to allow easy SSL use without directly
|
||||
* calling OpenSSL.
|
||||
/**
|
||||
* Secure the connection using SSL. This should be done before any command is
|
||||
* executed on the connection.
|
||||
*/
|
||||
typedef struct redisSSLContext redisSSLContext;
|
||||
int redisSecureConnection(redisContext *c, const char *capath, const char *certpath,
|
||||
const char *keypath, const char *servername);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Initiate SSL/TLS negotiation on a provided context.
|
||||
*/
|
||||
|
||||
int redisInitiateSSL(redisContext *c, struct ssl_st *ssl);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __HIREDIS_SSL_H */
|
||||
|
||||
Vendored
+39
-80
@@ -57,8 +57,8 @@ void redisNetClose(redisContext *c) {
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
ssize_t nread = recv(c->fd, buf, bufcap, 0);
|
||||
int redisNetRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
int 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 @@ ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t redisNetWrite(redisContext *c) {
|
||||
ssize_t nwritten = send(c->fd, c->obuf, hi_sdslen(c->obuf), 0);
|
||||
int redisNetWrite(redisContext *c) {
|
||||
int nwritten = send(c->fd, c->obuf, 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;
|
||||
}
|
||||
|
||||
int redisSetTcpNoDelay(redisContext *c) {
|
||||
static 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 @@ int redisSetTcpNoDelay(redisContext *c) {
|
||||
|
||||
static int redisContextTimeoutMsec(redisContext *c, long *result)
|
||||
{
|
||||
const struct timeval *timeout = c->connect_timeout;
|
||||
const struct timeval *timeout = c->timeout;
|
||||
long msec = -1;
|
||||
|
||||
/* Only use timeout when not NULL. */
|
||||
@@ -316,7 +316,11 @@ 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;
|
||||
@@ -328,38 +332,6 @@ 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) {
|
||||
@@ -384,19 +356,21 @@ 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) {
|
||||
hi_free(c->tcp.host);
|
||||
free(c->tcp.host);
|
||||
|
||||
c->tcp.host = hi_strdup(addr);
|
||||
if (c->tcp.host == NULL)
|
||||
goto oom;
|
||||
c->tcp.host = strdup(addr);
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
|
||||
goto oom;
|
||||
if (c->timeout != timeout) {
|
||||
if (c->timeout == NULL)
|
||||
c->timeout = malloc(sizeof(struct timeval));
|
||||
|
||||
memcpy(c->timeout, timeout, sizeof(struct timeval));
|
||||
}
|
||||
} else {
|
||||
hi_free(c->connect_timeout);
|
||||
c->connect_timeout = NULL;
|
||||
free(c->timeout);
|
||||
c->timeout = NULL;
|
||||
}
|
||||
|
||||
if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
|
||||
@@ -405,11 +379,11 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
||||
}
|
||||
|
||||
if (source_addr == NULL) {
|
||||
hi_free(c->tcp.source_addr);
|
||||
free(c->tcp.source_addr);
|
||||
c->tcp.source_addr = NULL;
|
||||
} else if (c->tcp.source_addr != source_addr) {
|
||||
hi_free(c->tcp.source_addr);
|
||||
c->tcp.source_addr = hi_strdup(source_addr);
|
||||
free(c->tcp.source_addr);
|
||||
c->tcp.source_addr = strdup(source_addr);
|
||||
}
|
||||
|
||||
snprintf(_port, 6, "%d", port);
|
||||
@@ -472,11 +446,8 @@ addrretry:
|
||||
}
|
||||
|
||||
/* For repeat connection */
|
||||
hi_free(c->saddr);
|
||||
c->saddr = hi_malloc(p->ai_addrlen);
|
||||
if (c->saddr == NULL)
|
||||
goto oom;
|
||||
|
||||
free(c->saddr);
|
||||
c->saddr = malloc(p->ai_addrlen);
|
||||
memcpy(c->saddr, p->ai_addr, p->ai_addrlen);
|
||||
c->addrlen = p->ai_addrlen;
|
||||
|
||||
@@ -503,12 +474,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;
|
||||
@@ -521,8 +492,6 @@ addrretry:
|
||||
goto error;
|
||||
}
|
||||
|
||||
oom:
|
||||
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||
error:
|
||||
rv = REDIS_ERR;
|
||||
end:
|
||||
@@ -556,32 +525,25 @@ 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) {
|
||||
hi_free(c->unix_sock.path);
|
||||
|
||||
c->unix_sock.path = hi_strdup(path);
|
||||
if (c->unix_sock.path == NULL)
|
||||
goto oom;
|
||||
}
|
||||
if (c->unix_sock.path != path)
|
||||
c->unix_sock.path = strdup(path);
|
||||
|
||||
if (timeout) {
|
||||
if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
|
||||
goto oom;
|
||||
if (c->timeout != timeout) {
|
||||
if (c->timeout == NULL)
|
||||
c->timeout = malloc(sizeof(struct timeval));
|
||||
|
||||
memcpy(c->timeout, timeout, sizeof(struct timeval));
|
||||
}
|
||||
} else {
|
||||
hi_free(c->connect_timeout);
|
||||
c->connect_timeout = NULL;
|
||||
free(c->timeout);
|
||||
c->timeout = NULL;
|
||||
}
|
||||
|
||||
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* 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;
|
||||
|
||||
sa = (struct sockaddr_un*)(c->saddr = malloc(sizeof(struct sockaddr_un)));
|
||||
c->addrlen = sizeof(struct sockaddr_un);
|
||||
sa->sun_family = AF_UNIX;
|
||||
strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1);
|
||||
@@ -606,7 +568,4 @@ 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
+2
-4
@@ -38,8 +38,8 @@
|
||||
#include "hiredis.h"
|
||||
|
||||
void redisNetClose(redisContext *c);
|
||||
ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap);
|
||||
ssize_t redisNetWrite(redisContext *c);
|
||||
int redisNetRead(redisContext *c, char *buf, size_t bufcap);
|
||||
int redisNetWrite(redisContext *c);
|
||||
|
||||
int redisCheckSocketError(redisContext *c);
|
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv);
|
||||
@@ -51,6 +51,4 @@ 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
+50
-108
@@ -42,14 +42,10 @@
|
||||
#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;
|
||||
|
||||
@@ -59,7 +55,7 @@ static void __redisReaderSetError(redisReader *r, int type, const char *str) {
|
||||
}
|
||||
|
||||
/* Clear input buffer on errors. */
|
||||
hi_sdsfree(r->buf);
|
||||
sdsfree(r->buf);
|
||||
r->buf = NULL;
|
||||
r->pos = r->len = 0;
|
||||
|
||||
@@ -247,12 +243,11 @@ static void moveToNextTask(redisReader *r) {
|
||||
return;
|
||||
}
|
||||
|
||||
cur = r->task[r->ridx];
|
||||
prv = r->task[r->ridx-1];
|
||||
cur = &(r->rstack[r->ridx]);
|
||||
prv = &(r->rstack[r->ridx-1]);
|
||||
assert(prv->type == REDIS_REPLY_ARRAY ||
|
||||
prv->type == REDIS_REPLY_MAP ||
|
||||
prv->type == REDIS_REPLY_SET ||
|
||||
prv->type == REDIS_REPLY_PUSH);
|
||||
prv->type == REDIS_REPLY_SET);
|
||||
if (cur->idx == prv->elements-1) {
|
||||
r->ridx--;
|
||||
} else {
|
||||
@@ -267,7 +262,7 @@ static void moveToNextTask(redisReader *r) {
|
||||
}
|
||||
|
||||
static int processLineItem(redisReader *r) {
|
||||
redisReadTask *cur = r->task[r->ridx];
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
void *obj;
|
||||
char *p;
|
||||
int len;
|
||||
@@ -302,7 +297,7 @@ static int processLineItem(redisReader *r) {
|
||||
if (strcasecmp(buf,",inf") == 0) {
|
||||
d = INFINITY; /* Positive infinite. */
|
||||
} else if (strcasecmp(buf,",-inf") == 0) {
|
||||
d = -INFINITY; /* Negative infinite. */
|
||||
d = -INFINITY; /* Nevative infinite. */
|
||||
} else {
|
||||
d = strtod((char*)buf,&eptr);
|
||||
if (buf[0] == '\0' || eptr[0] != '\0' || isnan(d)) {
|
||||
@@ -349,7 +344,7 @@ static int processLineItem(redisReader *r) {
|
||||
}
|
||||
|
||||
static int processBulkItem(redisReader *r) {
|
||||
redisReadTask *cur = r->task[r->ridx];
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
void *obj = NULL;
|
||||
char *p, *s;
|
||||
long long len;
|
||||
@@ -420,43 +415,19 @@ 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->task[r->ridx];
|
||||
redisReadTask *cur = &(r->rstack[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 == r->tasks - 1) {
|
||||
if (redisReaderGrow(r) == REDIS_ERR)
|
||||
return REDIS_ERR;
|
||||
if (r->ridx == 8) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"No support for nested multi bulk replies with depth > 7");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if ((p = readLine(r,&len)) != NULL) {
|
||||
@@ -468,9 +439,7 @@ static int processAggregateItem(redisReader *r) {
|
||||
|
||||
root = (r->ridx == 0);
|
||||
|
||||
if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) ||
|
||||
(r->maxelements > 0 && elements > r->maxelements))
|
||||
{
|
||||
if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX)) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Multi-bulk length out of range");
|
||||
return REDIS_ERR;
|
||||
@@ -506,12 +475,12 @@ static int processAggregateItem(redisReader *r) {
|
||||
cur->elements = elements;
|
||||
cur->obj = obj;
|
||||
r->ridx++;
|
||||
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;
|
||||
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;
|
||||
} else {
|
||||
moveToNextTask(r);
|
||||
}
|
||||
@@ -526,7 +495,7 @@ static int processAggregateItem(redisReader *r) {
|
||||
}
|
||||
|
||||
static int processItem(redisReader *r) {
|
||||
redisReadTask *cur = r->task[r->ridx];
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
char *p;
|
||||
|
||||
/* check if we need to read type */
|
||||
@@ -566,9 +535,6 @@ 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;
|
||||
@@ -594,7 +560,6 @@ 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);
|
||||
@@ -605,57 +570,33 @@ static int processItem(redisReader *r) {
|
||||
redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
|
||||
redisReader *r;
|
||||
|
||||
r = hi_calloc(1,sizeof(redisReader));
|
||||
r = calloc(1,sizeof(redisReader));
|
||||
if (r == NULL)
|
||||
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->buf = sdsempty();
|
||||
r->maxbuf = REDIS_READER_MAX_BUF;
|
||||
if (r->buf == NULL) {
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
sdsfree(r->buf);
|
||||
free(r);
|
||||
}
|
||||
|
||||
int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
|
||||
hisds newbuf;
|
||||
sds newbuf;
|
||||
|
||||
/* Return early when this reader is in an erroneous state. */
|
||||
if (r->err)
|
||||
@@ -664,25 +605,26 @@ 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 && hi_sdsavail(r->buf) > r->maxbuf) {
|
||||
hi_sdsfree(r->buf);
|
||||
r->buf = hi_sdsempty();
|
||||
if (r->buf == 0) goto oom;
|
||||
|
||||
if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {
|
||||
sdsfree(r->buf);
|
||||
r->buf = sdsempty();
|
||||
r->pos = 0;
|
||||
|
||||
/* r->buf should not be NULL since we just free'd a larger one. */
|
||||
assert(r->buf != NULL);
|
||||
}
|
||||
|
||||
newbuf = hi_sdscatlen(r->buf,buf,len);
|
||||
if (newbuf == NULL) goto oom;
|
||||
newbuf = sdscatlen(r->buf,buf,len);
|
||||
if (newbuf == NULL) {
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
r->buf = newbuf;
|
||||
r->len = hi_sdslen(r->buf);
|
||||
r->len = sdslen(r->buf);
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
oom:
|
||||
__redisReaderSetErrorOOM(r);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int redisReaderGetReply(redisReader *r, void **reply) {
|
||||
@@ -700,12 +642,12 @@ int redisReaderGetReply(redisReader *r, void **reply) {
|
||||
|
||||
/* Set first item to process when the stack is empty. */
|
||||
if (r->ridx == -1) {
|
||||
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->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->ridx = 0;
|
||||
}
|
||||
|
||||
@@ -721,9 +663,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) {
|
||||
if (hi_sdsrange(r->buf,r->pos,-1) < 0) return REDIS_ERR;
|
||||
sdsrange(r->buf,r->pos,-1);
|
||||
r->pos = 0;
|
||||
r->len = hi_sdslen(r->buf);
|
||||
r->len = sdslen(r->buf);
|
||||
}
|
||||
|
||||
/* Emit a reply when there is one. */
|
||||
|
||||
Vendored
+3
-10
@@ -63,11 +63,7 @@
|
||||
#define REDIS_REPLY_BIGNUM 13
|
||||
#define REDIS_REPLY_VERB 14
|
||||
|
||||
/* 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)
|
||||
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -75,7 +71,7 @@ extern "C" {
|
||||
|
||||
typedef struct redisReadTask {
|
||||
int type;
|
||||
long long elements; /* number of elements in multibulk container */
|
||||
int 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 */
|
||||
@@ -100,11 +96,8 @@ 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
+415
-413
File diff suppressed because it is too large
Load Diff
Vendored
+131
-133
@@ -30,249 +30,247 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef HIREDIS_SDS_H
|
||||
#define HIREDIS_SDS_H
|
||||
#ifndef __SDS_H
|
||||
#define __SDS_H
|
||||
|
||||
#define HI_SDS_MAX_PREALLOC (1024*1024)
|
||||
#define 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 *hisds;
|
||||
typedef char *sds;
|
||||
|
||||
/* 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__)) hisdshdr5 {
|
||||
struct __attribute__ ((__packed__)) sdshdr5 {
|
||||
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
|
||||
char buf[];
|
||||
};
|
||||
struct __attribute__ ((__packed__)) hisdshdr8 {
|
||||
struct __attribute__ ((__packed__)) sdshdr8 {
|
||||
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__)) hisdshdr16 {
|
||||
struct __attribute__ ((__packed__)) sdshdr16 {
|
||||
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__)) hisdshdr32 {
|
||||
struct __attribute__ ((__packed__)) sdshdr32 {
|
||||
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__)) hisdshdr64 {
|
||||
struct __attribute__ ((__packed__)) sdshdr64 {
|
||||
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 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)
|
||||
#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)
|
||||
|
||||
static inline size_t hi_sdslen(const hisds s) {
|
||||
static inline size_t sdslen(const sds s) {
|
||||
unsigned char flags = s[-1];
|
||||
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;
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline size_t hi_sdsavail(const hisds s) {
|
||||
static inline size_t sdsavail(const sds s) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5: {
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5: {
|
||||
return 0;
|
||||
}
|
||||
case HI_SDS_TYPE_8: {
|
||||
HI_SDS_HDR_VAR(8,s);
|
||||
case SDS_TYPE_8: {
|
||||
SDS_HDR_VAR(8,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case HI_SDS_TYPE_16: {
|
||||
HI_SDS_HDR_VAR(16,s);
|
||||
case SDS_TYPE_16: {
|
||||
SDS_HDR_VAR(16,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case HI_SDS_TYPE_32: {
|
||||
HI_SDS_HDR_VAR(32,s);
|
||||
case SDS_TYPE_32: {
|
||||
SDS_HDR_VAR(32,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
case HI_SDS_TYPE_64: {
|
||||
HI_SDS_HDR_VAR(64,s);
|
||||
case SDS_TYPE_64: {
|
||||
SDS_HDR_VAR(64,s);
|
||||
return sh->alloc - sh->len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void hi_sdssetlen(hisds s, size_t newlen) {
|
||||
static inline void sdssetlen(sds s, size_t newlen) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
*fp = (unsigned char)(HI_SDS_TYPE_5 | (newlen << HI_SDS_TYPE_BITS));
|
||||
*fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));
|
||||
}
|
||||
break;
|
||||
case HI_SDS_TYPE_8:
|
||||
HI_SDS_HDR(8,s)->len = (uint8_t)newlen;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->len = (uint8_t)newlen;
|
||||
break;
|
||||
case HI_SDS_TYPE_16:
|
||||
HI_SDS_HDR(16,s)->len = (uint16_t)newlen;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->len = (uint16_t)newlen;
|
||||
break;
|
||||
case HI_SDS_TYPE_32:
|
||||
HI_SDS_HDR(32,s)->len = (uint32_t)newlen;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->len = (uint32_t)newlen;
|
||||
break;
|
||||
case HI_SDS_TYPE_64:
|
||||
HI_SDS_HDR(64,s)->len = (uint64_t)newlen;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->len = (uint64_t)newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hi_sdsinclen(hisds s, size_t inc) {
|
||||
static inline void sdsinclen(sds s, size_t inc) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
unsigned char newlen = HI_SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
|
||||
*fp = HI_SDS_TYPE_5 | (newlen << HI_SDS_TYPE_BITS);
|
||||
unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
|
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
|
||||
}
|
||||
break;
|
||||
case HI_SDS_TYPE_8:
|
||||
HI_SDS_HDR(8,s)->len += (uint8_t)inc;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->len += (uint8_t)inc;
|
||||
break;
|
||||
case HI_SDS_TYPE_16:
|
||||
HI_SDS_HDR(16,s)->len += (uint16_t)inc;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->len += (uint16_t)inc;
|
||||
break;
|
||||
case HI_SDS_TYPE_32:
|
||||
HI_SDS_HDR(32,s)->len += (uint32_t)inc;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->len += (uint32_t)inc;
|
||||
break;
|
||||
case HI_SDS_TYPE_64:
|
||||
HI_SDS_HDR(64,s)->len += (uint64_t)inc;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->len += (uint64_t)inc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* hi_sdsalloc() = hi_sdsavail() + hi_sdslen() */
|
||||
static inline size_t hi_sdsalloc(const hisds s) {
|
||||
/* sdsalloc() = sdsavail() + sdslen() */
|
||||
static inline size_t sdsalloc(const sds s) {
|
||||
unsigned char flags = s[-1];
|
||||
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;
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void hi_sdssetalloc(hisds s, size_t newlen) {
|
||||
static inline void sdssetalloc(sds s, size_t newlen) {
|
||||
unsigned char flags = s[-1];
|
||||
switch(flags&HI_SDS_TYPE_MASK) {
|
||||
case HI_SDS_TYPE_5:
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
case SDS_TYPE_5:
|
||||
/* Nothing to do, this type has no total allocation info. */
|
||||
break;
|
||||
case HI_SDS_TYPE_8:
|
||||
HI_SDS_HDR(8,s)->alloc = (uint8_t)newlen;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->alloc = (uint8_t)newlen;
|
||||
break;
|
||||
case HI_SDS_TYPE_16:
|
||||
HI_SDS_HDR(16,s)->alloc = (uint16_t)newlen;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->alloc = (uint16_t)newlen;
|
||||
break;
|
||||
case HI_SDS_TYPE_32:
|
||||
HI_SDS_HDR(32,s)->alloc = (uint32_t)newlen;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->alloc = (uint32_t)newlen;
|
||||
break;
|
||||
case HI_SDS_TYPE_64:
|
||||
HI_SDS_HDR(64,s)->alloc = (uint64_t)newlen;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->alloc = (uint64_t)newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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 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_sdscatvprintf(hisds s, const char *fmt, va_list ap);
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
|
||||
#ifdef __GNUC__
|
||||
hisds hi_sdscatprintf(hisds s, const char *fmt, ...)
|
||||
sds sdscatprintf(sds s, const char *fmt, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
#else
|
||||
hisds hi_sdscatprintf(hisds s, const char *fmt, ...);
|
||||
sds sdscatprintf(sds s, const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
/* Low level functions exposed to the user API */
|
||||
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);
|
||||
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);
|
||||
|
||||
/* 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 *hi_sds_malloc(size_t size);
|
||||
void *hi_sds_realloc(void *ptr, size_t size);
|
||||
void hi_sds_free(void *ptr);
|
||||
void *sds_malloc(size_t size);
|
||||
void *sds_realloc(void *ptr, size_t size);
|
||||
void sds_free(void *ptr);
|
||||
|
||||
#ifdef REDIS_TEST
|
||||
int hi_sdsTest(int argc, char *argv[]);
|
||||
int sdsTest(int argc, char *argv[]);
|
||||
#endif
|
||||
|
||||
#endif /* HIREDIS_SDS_H */
|
||||
#endif
|
||||
|
||||
Vendored
+3
-5
@@ -37,8 +37,6 @@
|
||||
* the include of your alternate allocator if needed (not needed in order
|
||||
* to use the default libc allocator). */
|
||||
|
||||
#include "alloc.h"
|
||||
|
||||
#define hi_s_malloc hi_malloc
|
||||
#define hi_s_realloc hi_realloc
|
||||
#define hi_s_free hi_free
|
||||
#define s_malloc malloc
|
||||
#define s_realloc realloc
|
||||
#define s_free free
|
||||
|
||||
Vendored
-94
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* 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))) {
|
||||
const struct timeval *tv = optval;
|
||||
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
+1
-2
@@ -49,10 +49,9 @@
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
typedef long long ssize_t;
|
||||
typedef signed long ssize_t;
|
||||
#endif
|
||||
|
||||
/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */
|
||||
|
||||
Vendored
+121
-199
@@ -34,33 +34,25 @@
|
||||
#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);
|
||||
|
||||
struct redisSSLContext {
|
||||
/* Associated OpenSSL SSL_CTX as created by redisCreateSSLContext() */
|
||||
/* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@@ -80,11 +72,43 @@ typedef struct redisSSL {
|
||||
* should resume whenever a read takes place, if possible
|
||||
*/
|
||||
int pendingWrite;
|
||||
} redisSSL;
|
||||
} redisSSLContext;
|
||||
|
||||
/* 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.
|
||||
@@ -95,18 +119,6 @@ redisContextFuncs redisContextSSLFuncs;
|
||||
#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);
|
||||
@@ -117,9 +129,7 @@ static void sslLockAcquire(sslLockType *l) {
|
||||
static void sslLockRelease(sslLockType *l) {
|
||||
pthread_mutex_unlock(l);
|
||||
}
|
||||
#endif
|
||||
|
||||
static sslLockType* ossl_locks;
|
||||
static pthread_mutex_t *ossl_locks;
|
||||
|
||||
static void opensslDoLock(int mode, int lkid, const char *f, int line) {
|
||||
sslLockType *l = ossl_locks + lkid;
|
||||
@@ -134,151 +144,36 @@ static void opensslDoLock(int mode, int lkid, const char *f, int line) {
|
||||
(void)line;
|
||||
}
|
||||
|
||||
static int initOpensslLocks(void) {
|
||||
static void initOpensslLocks(void) {
|
||||
unsigned ii, nlocks;
|
||||
if (CRYPTO_get_locking_callback() != NULL) {
|
||||
/* Someone already set the callback before us. Don't destroy it! */
|
||||
return REDIS_OK;
|
||||
return;
|
||||
}
|
||||
nlocks = CRYPTO_num_locks();
|
||||
ossl_locks = hi_malloc(sizeof(*ossl_locks) * nlocks);
|
||||
if (ossl_locks == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
ossl_locks = malloc(sizeof(*ossl_locks) * nlocks);
|
||||
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 *ssl) {
|
||||
if (c->privctx) {
|
||||
static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
|
||||
if (c->privdata) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
redisSSL *rssl = hi_calloc(1, sizeof(redisSSL));
|
||||
if (rssl == NULL) {
|
||||
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
c->privdata = calloc(1, sizeof(redisSSLContext));
|
||||
|
||||
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);
|
||||
@@ -288,14 +183,12 @@ static int redisSSLConnect(redisContext *c, 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;
|
||||
}
|
||||
|
||||
@@ -310,58 +203,83 @@ static int redisSSLConnect(redisContext *c, 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, ssl);
|
||||
return redisSSLConnect(c, NULL, ssl);
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around redisSSLConnect() for users who use redisSSLContext and don't
|
||||
* manage their own SSL objects.
|
||||
*/
|
||||
int redisSecureConnection(redisContext *c, const char *capath,
|
||||
const char *certpath, const char *keypath, const char *servername) {
|
||||
|
||||
int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx)
|
||||
{
|
||||
if (!c || !redis_ssl_ctx)
|
||||
return REDIS_ERR;
|
||||
SSL_CTX *ssl_ctx = NULL;
|
||||
SSL *ssl = NULL;
|
||||
|
||||
/* 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;
|
||||
/* Initialize global OpenSSL stuff */
|
||||
static int isInit = 0;
|
||||
if (!isInit) {
|
||||
isInit = 1;
|
||||
SSL_library_init();
|
||||
#ifdef HIREDIS_USE_CRYPTO_LOCKS
|
||||
initOpensslLocks();
|
||||
#endif
|
||||
}
|
||||
|
||||
SSL *ssl = SSL_new(redis_ssl_ctx->ssl_ctx);
|
||||
if (!ssl) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Couldn't create new SSL instance");
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if (!ssl_ctx) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Failed to create SSL_CTX");
|
||||
goto error;
|
||||
}
|
||||
|
||||
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");
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
return redisSSLConnect(c, ssl);
|
||||
ssl = SSL_new(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");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return redisSSLConnect(c, ssl_ctx, ssl);
|
||||
|
||||
error:
|
||||
if (ssl)
|
||||
SSL_free(ssl);
|
||||
if (ssl) SSL_free(ssl);
|
||||
if (ssl_ctx) SSL_CTX_free(ssl_ctx);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static int maybeCheckWant(redisSSL *rssl, int rv) {
|
||||
static int maybeCheckWant(redisSSLContext *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
|
||||
@@ -381,19 +299,23 @@ static int maybeCheckWant(redisSSL *rssl, int rv) {
|
||||
* Implementation of redisContextFuncs for SSL connections.
|
||||
*/
|
||||
|
||||
static void redisSSLFree(void *privctx){
|
||||
redisSSL *rsc = privctx;
|
||||
static void redisSSLFreeContext(void *privdata){
|
||||
redisSSLContext *rsc = privdata;
|
||||
|
||||
if (!rsc) return;
|
||||
if (rsc->ssl) {
|
||||
SSL_free(rsc->ssl);
|
||||
rsc->ssl = NULL;
|
||||
}
|
||||
hi_free(rsc);
|
||||
if (rsc->ssl_ctx) {
|
||||
SSL_CTX_free(rsc->ssl_ctx);
|
||||
rsc->ssl_ctx = NULL;
|
||||
}
|
||||
free(rsc);
|
||||
}
|
||||
|
||||
static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
redisSSL *rssl = c->privctx;
|
||||
static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
redisSSLContext *rssl = c->privdata;
|
||||
|
||||
int nread = SSL_read(rssl->ssl, buf, bufcap);
|
||||
if (nread > 0) {
|
||||
@@ -434,10 +356,10 @@ static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t redisSSLWrite(redisContext *c) {
|
||||
redisSSL *rssl = c->privctx;
|
||||
static int redisSSLWrite(redisContext *c) {
|
||||
redisSSLContext *rssl = c->privdata;
|
||||
|
||||
size_t len = rssl->lastLen ? rssl->lastLen : hi_sdslen(c->obuf);
|
||||
size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf);
|
||||
int rv = SSL_write(rssl->ssl, c->obuf, len);
|
||||
|
||||
if (rv > 0) {
|
||||
@@ -458,7 +380,7 @@ static ssize_t redisSSLWrite(redisContext *c) {
|
||||
|
||||
static void redisSSLAsyncRead(redisAsyncContext *ac) {
|
||||
int rv;
|
||||
redisSSL *rssl = ac->c.privctx;
|
||||
redisSSLContext *rssl = ac->c.privdata;
|
||||
redisContext *c = &ac->c;
|
||||
|
||||
rssl->wantRead = 0;
|
||||
@@ -488,7 +410,7 @@ static void redisSSLAsyncRead(redisAsyncContext *ac) {
|
||||
|
||||
static void redisSSLAsyncWrite(redisAsyncContext *ac) {
|
||||
int rv, done = 0;
|
||||
redisSSL *rssl = ac->c.privctx;
|
||||
redisSSLContext *rssl = ac->c.privdata;
|
||||
redisContext *c = &ac->c;
|
||||
|
||||
rssl->pendingWrite = 0;
|
||||
@@ -517,7 +439,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
|
||||
}
|
||||
|
||||
redisContextFuncs redisContextSSLFuncs = {
|
||||
.free_privctx = redisSSLFree,
|
||||
.free_privdata = redisSSLFreeContext,
|
||||
.async_read = redisSSLAsyncRead,
|
||||
.async_write = redisSSLAsyncWrite,
|
||||
.read = redisSSLRead,
|
||||
|
||||
Vendored
+91
-490
@@ -1,24 +1,22 @@
|
||||
#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>
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
#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,
|
||||
@@ -49,35 +47,15 @@ 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, skips = 0;
|
||||
static int tests = 0, fails = 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()
|
||||
@@ -87,43 +65,6 @@ 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;
|
||||
|
||||
@@ -146,26 +87,6 @@ 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;
|
||||
|
||||
@@ -184,9 +105,9 @@ static int disconnect(redisContext *c, int keep_fd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void do_ssl_handshake(redisContext *c) {
|
||||
static void do_ssl_handshake(redisContext *c, struct config config) {
|
||||
#ifdef HIREDIS_TEST_SSL
|
||||
redisInitiateSSLWithContext(c, _ssl_ctx);
|
||||
redisSecureConnection(c, config.ssl.ca_cert, config.ssl.cert, config.ssl.key, NULL);
|
||||
if (c->err) {
|
||||
printf("SSL error: %s\n", c->errstr);
|
||||
redisFree(c);
|
||||
@@ -194,6 +115,7 @@ static void do_ssl_handshake(redisContext *c) {
|
||||
}
|
||||
#else
|
||||
(void) c;
|
||||
(void) config;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -228,7 +150,7 @@ static redisContext *do_connect(struct config config) {
|
||||
}
|
||||
|
||||
if (config.type == CONN_SSL) {
|
||||
do_ssl_handshake(c);
|
||||
do_ssl_handshake(c, config);
|
||||
}
|
||||
|
||||
return select_database(c);
|
||||
@@ -238,7 +160,7 @@ static void do_reconnect(redisContext *c, struct config config) {
|
||||
redisReconnect(c);
|
||||
|
||||
if (config.type == CONN_SSL) {
|
||||
do_ssl_handshake(c);
|
||||
do_ssl_handshake(c, config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,43 +172,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));
|
||||
hi_free(cmd);
|
||||
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));
|
||||
hi_free(cmd);
|
||||
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));
|
||||
hi_free(cmd);
|
||||
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));
|
||||
hi_free(cmd);
|
||||
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));
|
||||
hi_free(cmd);
|
||||
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));
|
||||
hi_free(cmd);
|
||||
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));
|
||||
hi_free(cmd);
|
||||
free(cmd);
|
||||
|
||||
/* Vararg width depends on the type. These tests make sure that the
|
||||
* width is correctly determined using the format and subsequent varargs
|
||||
@@ -297,7 +219,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)); \
|
||||
hi_free(cmd); \
|
||||
free(cmd); \
|
||||
} while(0)
|
||||
|
||||
#define FLOAT_WIDTH_TEST(type) do { \
|
||||
@@ -306,7 +228,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)); \
|
||||
hi_free(cmd); \
|
||||
free(cmd); \
|
||||
} while(0)
|
||||
|
||||
INTEGER_WIDTH_TEST("d", int);
|
||||
@@ -337,29 +259,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));
|
||||
hi_free(cmd);
|
||||
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));
|
||||
hi_free(cmd);
|
||||
free(cmd);
|
||||
|
||||
hisds sds_cmd;
|
||||
sds sds_cmd;
|
||||
|
||||
sds_cmd = NULL;
|
||||
test("Format command into hisds by passing argc/argv without lengths: ");
|
||||
sds_cmd = sdsempty();
|
||||
test("Format command into sds 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));
|
||||
hi_sdsfree(sds_cmd);
|
||||
sdsfree(sds_cmd);
|
||||
|
||||
sds_cmd = NULL;
|
||||
test("Format command into hisds by passing argc/argv with lengths: ");
|
||||
sds_cmd = sdsempty();
|
||||
test("Format command into sds 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));
|
||||
hi_sdsfree(sds_cmd);
|
||||
sdsfree(sds_cmd);
|
||||
}
|
||||
|
||||
static void test_append_formatted_commands(struct config config) {
|
||||
@@ -378,7 +300,7 @@ static void test_append_formatted_commands(struct config config) {
|
||||
|
||||
assert(redisGetReply(c, (void*)&reply) == REDIS_OK);
|
||||
|
||||
hi_free(cmd);
|
||||
free(cmd);
|
||||
freeReplyObject(reply);
|
||||
|
||||
disconnect(c, 0);
|
||||
@@ -386,7 +308,7 @@ static void test_append_formatted_commands(struct config config) {
|
||||
|
||||
static void test_reply_reader(void) {
|
||||
redisReader *reader;
|
||||
void *reply, *root;
|
||||
void *reply;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
@@ -410,26 +332,16 @@ 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();
|
||||
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);
|
||||
|
||||
test("Can parse arbitrarily nested multi-bulks correctly: ");
|
||||
while(i--) {
|
||||
assert(reply != NULL && ((redisReply*)reply)->type == REDIS_REPLY_ARRAY);
|
||||
reply = ((redisReply*)reply)->element[0];
|
||||
for (i = 0; i < 9; i++) {
|
||||
redisReaderFeed(reader,(char*)"*1\r\n",4);
|
||||
}
|
||||
test_cond(((redisReply*)reply)->type == REDIS_REPLY_STRING &&
|
||||
!memcmp(((redisReply*)reply)->str, "LOLWUT", 6));
|
||||
freeReplyObject(root);
|
||||
|
||||
ret = redisReaderGetReply(reader,NULL);
|
||||
test_cond(ret == REDIS_ERR &&
|
||||
strncasecmp(reader->errstr,"No support for",14) == 0);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Correctly parses LLONG_MAX: ");
|
||||
@@ -488,16 +400,6 @@ 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];
|
||||
@@ -571,32 +473,6 @@ 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) {
|
||||
@@ -612,47 +488,6 @@ 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;
|
||||
@@ -670,19 +505,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, "No such host is known. ") == 0));
|
||||
strcmp(c->errstr,
|
||||
"hostname nor servname provided, or not known") == 0 ||
|
||||
strcmp(c->errstr, "no address associated with name") == 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 &&
|
||||
@@ -693,166 +528,11 @@ 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);
|
||||
|
||||
@@ -925,42 +605,14 @@ 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 *sleep_cmd = "DEBUG SLEEP 3\r\n";
|
||||
const char *cmd = "DEBUG SLEEP 3\r\n";
|
||||
struct timeval tv;
|
||||
|
||||
c = do_connect(config);
|
||||
@@ -977,24 +629,14 @@ static void test_blocking_connection_timeouts(struct config config) {
|
||||
|
||||
c = do_connect(config);
|
||||
test("Does not return a reply when the command times out: ");
|
||||
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();
|
||||
}
|
||||
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);
|
||||
|
||||
test("Reconnect properly reconnects after a timeout: ");
|
||||
do_reconnect(c, config);
|
||||
@@ -1021,7 +663,18 @@ static void test_blocking_io_errors(struct config config) {
|
||||
|
||||
/* Connect to target given by config. */
|
||||
c = do_connect(config);
|
||||
get_redis_version(c, &major, &minor);
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
|
||||
test("Returns I/O error when the connection is lost: ");
|
||||
reply = redisCommand(c,"QUIT");
|
||||
@@ -1035,7 +688,6 @@ 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
|
||||
@@ -1043,19 +695,14 @@ 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);
|
||||
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
|
||||
test_cond(redisGetReply(c,&_reply) == REDIS_ERR &&
|
||||
c->err == REDIS_ERR_IO && errno == EAGAIN);
|
||||
redisFree(c);
|
||||
}
|
||||
|
||||
@@ -1083,18 +730,6 @@ 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;
|
||||
@@ -1106,7 +741,7 @@ static void test_throughput(struct config config) {
|
||||
freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
|
||||
|
||||
num = 1000;
|
||||
replies = hi_malloc_safe(sizeof(redisReply*)*num);
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
t1 = usec();
|
||||
for (i = 0; i < num; i++) {
|
||||
replies[i] = redisCommand(c,"PING");
|
||||
@@ -1114,10 +749,10 @@ static void test_throughput(struct config config) {
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
hi_free(replies);
|
||||
free(replies);
|
||||
printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
replies = hi_malloc_safe(sizeof(redisReply*)*num);
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
t1 = usec();
|
||||
for (i = 0; i < num; i++) {
|
||||
replies[i] = redisCommand(c,"LRANGE mylist 0 499");
|
||||
@@ -1126,10 +761,10 @@ static void test_throughput(struct config config) {
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
hi_free(replies);
|
||||
free(replies);
|
||||
printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
replies = hi_malloc_safe(sizeof(redisReply*)*num);
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
t1 = usec();
|
||||
for (i = 0; i < num; i++) {
|
||||
replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000);
|
||||
@@ -1137,11 +772,11 @@ static void test_throughput(struct config config) {
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
hi_free(replies);
|
||||
free(replies);
|
||||
printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
num = 10000;
|
||||
replies = hi_malloc_safe(sizeof(redisReply*)*num);
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
for (i = 0; i < num; i++)
|
||||
redisAppendCommand(c,"PING");
|
||||
t1 = usec();
|
||||
@@ -1151,10 +786,10 @@ static void test_throughput(struct config config) {
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
hi_free(replies);
|
||||
free(replies);
|
||||
printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
replies = hi_malloc_safe(sizeof(redisReply*)*num);
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
for (i = 0; i < num; i++)
|
||||
redisAppendCommand(c,"LRANGE mylist 0 499");
|
||||
t1 = usec();
|
||||
@@ -1165,10 +800,10 @@ static void test_throughput(struct config config) {
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
hi_free(replies);
|
||||
free(replies);
|
||||
printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
replies = hi_malloc_safe(sizeof(redisReply*)*num);
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
for (i = 0; i < num; i++)
|
||||
redisAppendCommand(c,"INCRBY incrkey %d", 1000000);
|
||||
t1 = usec();
|
||||
@@ -1178,7 +813,7 @@ static void test_throughput(struct config config) {
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
hi_free(replies);
|
||||
free(replies);
|
||||
printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
disconnect(c, 0);
|
||||
@@ -1295,8 +930,9 @@ int main(int argc, char **argv) {
|
||||
};
|
||||
int throughput = 1;
|
||||
int test_inherit_fd = 1;
|
||||
int skips_as_fails = 0;
|
||||
int test_unix_socket;
|
||||
|
||||
/* Ignore broken pipe signal (for I/O error tests). */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
/* Parse command line options. */
|
||||
argv++; argc--;
|
||||
@@ -1314,8 +950,6 @@ 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--;
|
||||
@@ -1340,19 +974,6 @@ 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();
|
||||
@@ -1367,25 +988,15 @@ int main(int argc, char **argv) {
|
||||
test_append_formatted_commands(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();
|
||||
}
|
||||
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);
|
||||
|
||||
#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;
|
||||
|
||||
@@ -1395,31 +1006,21 @@ 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): ", cfg.unix_sock.path);
|
||||
if (test_unix_socket) {
|
||||
printf("\n");
|
||||
cfg.type = CONN_FD;
|
||||
test_blocking_connection(cfg);
|
||||
} else {
|
||||
test_skipped();
|
||||
}
|
||||
printf("\nTesting against inherited fd (%s):\n", cfg.unix_sock.path);
|
||||
cfg.type = CONN_FD;
|
||||
test_blocking_connection(cfg);
|
||||
}
|
||||
|
||||
if (fails || (skips_as_fails && skips)) {
|
||||
|
||||
if (fails) {
|
||||
printf("*** %d TESTS FAILED ***\n", fails);
|
||||
if (skips) {
|
||||
printf("*** %d TESTS SKIPPED ***\n", skips);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("ALL TESTS PASSED (%d skipped)\n", skips);
|
||||
printf("ALL TESTS PASSED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
+1
-9
@@ -4,9 +4,7 @@ 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
|
||||
@@ -69,10 +67,4 @@ fi
|
||||
cat ${tmpdir}/redis.conf
|
||||
${REDIS_SERVER} ${tmpdir}/redis.conf
|
||||
|
||||
# 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}
|
||||
${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS}
|
||||
|
||||
Vendored
+1
-1
@@ -512,7 +512,7 @@ CTARGET='-o $@'
|
||||
LDTARGET='-o $@'
|
||||
TEST_LD_MODE=
|
||||
EXTRA_LDFLAGS=
|
||||
ARFLAGS='crs'
|
||||
ARFLAGS='crus'
|
||||
AROUT=' $@'
|
||||
CC_MM=1
|
||||
|
||||
|
||||
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 ((unsigned char)len) { \
|
||||
switch (len) { \
|
||||
case '?': \
|
||||
val = va_arg(ap, int); \
|
||||
break; \
|
||||
|
||||
Vendored
-22
@@ -674,8 +674,6 @@ 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;
|
||||
@@ -689,8 +687,6 @@ 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--;
|
||||
@@ -713,8 +709,6 @@ 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);
|
||||
@@ -1091,19 +1085,3 @@ 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,6 +80,7 @@ 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
+1
-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);
|
||||
luaZ_lookahead(p->z);
|
||||
int c = luaZ_lookahead(p->z);
|
||||
luaC_checkGC(L);
|
||||
tf = (luaY_parser)(L, p->z,
|
||||
&p->buff, p->name);
|
||||
|
||||
Vendored
+1
-2
@@ -337,8 +337,7 @@ typedef struct Node {
|
||||
|
||||
typedef struct Table {
|
||||
CommonHeader;
|
||||
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
|
||||
int readonly;
|
||||
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
|
||||
lu_byte lsizenode; /* log2 of size of `node' array */
|
||||
struct Table *metatable;
|
||||
TValue *array; /* array part */
|
||||
|
||||
Vendored
-1
@@ -364,7 +364,6 @@ 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,9 +358,6 @@ 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,7 +131,6 @@ 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
+3
-6
@@ -39,7 +39,6 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
@@ -142,13 +141,13 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
json_token_type_t type;
|
||||
size_t index;
|
||||
int index;
|
||||
union {
|
||||
const char *string;
|
||||
double number;
|
||||
int boolean;
|
||||
} value;
|
||||
size_t string_len;
|
||||
int string_len;
|
||||
} json_token_t;
|
||||
|
||||
static const char *char2escape[256] = {
|
||||
@@ -474,8 +473,6 @@ 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, '\"');
|
||||
@@ -709,7 +706,7 @@ static int json_encode(lua_State *l)
|
||||
strbuf_t local_encode_buf;
|
||||
strbuf_t *encode_buf;
|
||||
char *json;
|
||||
size_t len;
|
||||
int len;
|
||||
|
||||
luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument");
|
||||
|
||||
|
||||
Vendored
+14
-17
@@ -117,9 +117,7 @@ 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;
|
||||
if (newsize < buf->len || newsize >= SIZE_MAX/2) abort();
|
||||
newsize *= 2;
|
||||
size_t newsize = (buf->len+len)*2;
|
||||
|
||||
buf->b = (unsigned char*)mp_realloc(L, buf->b, buf->len + buf->free, newsize);
|
||||
buf->free = newsize - buf->len;
|
||||
@@ -175,7 +173,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];
|
||||
size_t hdrlen;
|
||||
int hdrlen;
|
||||
|
||||
if (len < 32) {
|
||||
hdr[0] = 0xa0 | (len&0xff); /* fix raw */
|
||||
@@ -222,7 +220,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];
|
||||
size_t enclen;
|
||||
int enclen;
|
||||
|
||||
if (n >= 0) {
|
||||
if (n <= 127) {
|
||||
@@ -292,9 +290,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, uint64_t n) {
|
||||
void mp_encode_array(lua_State *L, mp_buf *buf, int64_t n) {
|
||||
unsigned char b[5];
|
||||
size_t enclen;
|
||||
int enclen;
|
||||
|
||||
if (n <= 15) {
|
||||
b[0] = 0x90 | (n & 0xf); /* fix array */
|
||||
@@ -315,7 +313,7 @@ void mp_encode_array(lua_State *L, mp_buf *buf, uint64_t n) {
|
||||
mp_buf_append(L,buf,b,enclen);
|
||||
}
|
||||
|
||||
void mp_encode_map(lua_State *L, mp_buf *buf, uint64_t n) {
|
||||
void mp_encode_map(lua_State *L, mp_buf *buf, int64_t n) {
|
||||
unsigned char b[5];
|
||||
int enclen;
|
||||
|
||||
@@ -792,7 +790,7 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
|
||||
}
|
||||
}
|
||||
|
||||
int mp_unpack_full(lua_State *L, lua_Integer limit, lua_Integer offset) {
|
||||
int mp_unpack_full(lua_State *L, int limit, int offset) {
|
||||
size_t len;
|
||||
const char *s;
|
||||
mp_cur c;
|
||||
@@ -804,10 +802,10 @@ int mp_unpack_full(lua_State *L, lua_Integer limit, lua_Integer 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.",
|
||||
(int) offset, (int) len);
|
||||
offset, len);
|
||||
else if (offset > len)
|
||||
return luaL_error(L,
|
||||
"Start offset %d greater than input length %d.", (int) offset, (int) len);
|
||||
"Start offset %d greater than input length %d.", offset, len);
|
||||
|
||||
if (decode_all) limit = INT_MAX;
|
||||
|
||||
@@ -829,13 +827,12 @@ int mp_unpack_full(lua_State *L, lua_Integer limit, lua_Integer 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 */
|
||||
size_t new_offset = len - c.left;
|
||||
if (new_offset > LONG_MAX) abort();
|
||||
int offset = len - c.left;
|
||||
|
||||
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 : (lua_Integer) new_offset);
|
||||
lua_pushinteger(L, c.left == 0 ? -1 : offset);
|
||||
/* Results are returned with the arg elements still
|
||||
* in place. Lua takes care of only returning
|
||||
* elements above the args for us.
|
||||
@@ -854,15 +851,15 @@ int mp_unpack(lua_State *L) {
|
||||
}
|
||||
|
||||
int mp_unpack_one(lua_State *L) {
|
||||
lua_Integer offset = luaL_optinteger(L, 2, 0);
|
||||
int 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) {
|
||||
lua_Integer limit = luaL_checkinteger(L, 2);
|
||||
lua_Integer offset = luaL_optinteger(L, 3, 0);
|
||||
int limit = luaL_checkinteger(L, 2);
|
||||
int offset = luaL_optinteger(L, 3, 0);
|
||||
/* Variable pop because offset may not exist */
|
||||
lua_pop(L, lua_gettop(L)-1);
|
||||
|
||||
|
||||
Vendored
-2
@@ -138,8 +138,6 @@ 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
+81
-28
@@ -26,7 +26,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "strbuf.h"
|
||||
|
||||
@@ -39,22 +38,22 @@ static void die(const char *fmt, ...)
|
||||
va_end(arg);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
abort();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void strbuf_init(strbuf_t *s, size_t len)
|
||||
void strbuf_init(strbuf_t *s, int len)
|
||||
{
|
||||
size_t size;
|
||||
int size;
|
||||
|
||||
if (!len)
|
||||
if (len <= 0)
|
||||
size = STRBUF_DEFAULT_SIZE;
|
||||
else
|
||||
size = len + 1;
|
||||
if (size < len)
|
||||
die("Overflow, len: %zu", len);
|
||||
size = len + 1; /* \0 terminator */
|
||||
|
||||
s->buf = NULL;
|
||||
s->size = size;
|
||||
s->length = 0;
|
||||
s->increment = STRBUF_DEFAULT_INCREMENT;
|
||||
s->dynamic = 0;
|
||||
s->reallocs = 0;
|
||||
s->debug = 0;
|
||||
@@ -66,7 +65,7 @@ void strbuf_init(strbuf_t *s, size_t len)
|
||||
strbuf_ensure_null(s);
|
||||
}
|
||||
|
||||
strbuf_t *strbuf_new(size_t len)
|
||||
strbuf_t *strbuf_new(int len)
|
||||
{
|
||||
strbuf_t *s;
|
||||
|
||||
@@ -82,10 +81,20 @@ strbuf_t *strbuf_new(size_t 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: %zd, size: %zd\n",
|
||||
fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
|
||||
(long)s, s->reallocs, s->length, s->size);
|
||||
}
|
||||
}
|
||||
@@ -104,7 +113,7 @@ void strbuf_free(strbuf_t *s)
|
||||
free(s);
|
||||
}
|
||||
|
||||
char *strbuf_free_to_string(strbuf_t *s, size_t *len)
|
||||
char *strbuf_free_to_string(strbuf_t *s, int *len)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
@@ -122,62 +131,57 @@ char *strbuf_free_to_string(strbuf_t *s, size_t *len)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static size_t calculate_new_size(strbuf_t *s, size_t len)
|
||||
static int calculate_new_size(strbuf_t *s, int len)
|
||||
{
|
||||
size_t reqsize, newsize;
|
||||
int 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 (reqsize >= SIZE_MAX / 2) {
|
||||
newsize = reqsize;
|
||||
} else {
|
||||
if (s->increment < 0) {
|
||||
/* Exponential sizing */
|
||||
while (newsize < reqsize)
|
||||
newsize *= 2;
|
||||
newsize *= -s->increment;
|
||||
} else {
|
||||
/* Linear sizing */
|
||||
newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
|
||||
}
|
||||
|
||||
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, size_t len)
|
||||
void strbuf_resize(strbuf_t *s, int len)
|
||||
{
|
||||
size_t newsize;
|
||||
int newsize;
|
||||
|
||||
newsize = calculate_new_size(s, len);
|
||||
|
||||
if (s->debug > 1) {
|
||||
fprintf(stderr, "strbuf(%lx) resize: %zd => %zd\n",
|
||||
fprintf(stderr, "strbuf(%lx) resize: %d => %d\n",
|
||||
(long)s, s->size, newsize);
|
||||
}
|
||||
|
||||
s->size = newsize;
|
||||
s->buf = realloc(s->buf, s->size);
|
||||
if (!s->buf)
|
||||
die("Out of memory, len: %zu", len);
|
||||
die("Out of memory");
|
||||
s->reallocs++;
|
||||
}
|
||||
|
||||
void strbuf_append_string(strbuf_t *s, const char *str)
|
||||
{
|
||||
int i;
|
||||
size_t space;
|
||||
int space, i;
|
||||
|
||||
space = strbuf_empty_length(s);
|
||||
|
||||
@@ -193,6 +197,55 @@ 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
+27
-19
@@ -27,13 +27,15 @@
|
||||
|
||||
/* 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;
|
||||
size_t size;
|
||||
size_t length;
|
||||
int size;
|
||||
int length;
|
||||
int increment;
|
||||
int dynamic;
|
||||
int reallocs;
|
||||
int debug;
|
||||
@@ -42,26 +44,32 @@ 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(size_t len);
|
||||
extern void strbuf_init(strbuf_t *s, size_t len);
|
||||
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);
|
||||
|
||||
/* Release */
|
||||
extern void strbuf_free(strbuf_t *s);
|
||||
extern char *strbuf_free_to_string(strbuf_t *s, size_t *len);
|
||||
extern char *strbuf_free_to_string(strbuf_t *s, int *len);
|
||||
|
||||
/* Management */
|
||||
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);
|
||||
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);
|
||||
static char *strbuf_empty_ptr(strbuf_t *s);
|
||||
static void strbuf_extend_length(strbuf_t *s, size_t len);
|
||||
static void strbuf_extend_length(strbuf_t *s, int len);
|
||||
|
||||
/* Update */
|
||||
static void strbuf_append_mem(strbuf_t *s, const char *c, size_t len);
|
||||
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);
|
||||
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);
|
||||
@@ -79,12 +87,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 size_t strbuf_empty_length(strbuf_t *s)
|
||||
static inline int strbuf_empty_length(strbuf_t *s)
|
||||
{
|
||||
return s->size - s->length - 1;
|
||||
}
|
||||
|
||||
static inline void strbuf_ensure_empty_length(strbuf_t *s, size_t len)
|
||||
static inline void strbuf_ensure_empty_length(strbuf_t *s, int len)
|
||||
{
|
||||
if (len > strbuf_empty_length(s))
|
||||
strbuf_resize(s, s->length + len);
|
||||
@@ -95,12 +103,12 @@ static inline char *strbuf_empty_ptr(strbuf_t *s)
|
||||
return s->buf + s->length;
|
||||
}
|
||||
|
||||
static inline void strbuf_extend_length(strbuf_t *s, size_t len)
|
||||
static inline void strbuf_extend_length(strbuf_t *s, int len)
|
||||
{
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline size_t strbuf_length(strbuf_t *s)
|
||||
static inline int strbuf_length(strbuf_t *s)
|
||||
{
|
||||
return s->length;
|
||||
}
|
||||
@@ -116,14 +124,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, size_t len)
|
||||
static inline void strbuf_append_mem(strbuf_t *s, const char *c, int 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, size_t len)
|
||||
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len)
|
||||
{
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
@@ -134,7 +142,7 @@ static inline void strbuf_ensure_null(strbuf_t *s)
|
||||
s->buf[s->length] = 0;
|
||||
}
|
||||
|
||||
static inline char *strbuf_string(strbuf_t *s, size_t *len)
|
||||
static inline char *strbuf_string(strbuf_t *s, int *len)
|
||||
{
|
||||
if (len)
|
||||
*len = s->length;
|
||||
|
||||
+60
-236
@@ -49,30 +49,23 @@
|
||||
# for connections from all available network interfaces on the host machine.
|
||||
# It is possible to listen to just one or multiple selected interfaces using
|
||||
# the "bind" configuration directive, followed by one or more IP addresses.
|
||||
# Each address can be prefixed by "-", which means that redis will not fail to
|
||||
# start if the address is not available. Being not available only refers to
|
||||
# addresses that does not correspond to any network interfece. Addresses that
|
||||
# are already in use will always fail, and unsupported protocols will always BE
|
||||
# silently skipped.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# bind 192.168.1.100 10.0.0.1 # listens on two specific IPv4 addresses
|
||||
# bind 127.0.0.1 ::1 # listens on loopback IPv4 and IPv6
|
||||
# bind * -::* # like the default, all available interfaces
|
||||
# bind 192.168.1.100 10.0.0.1
|
||||
# bind 127.0.0.1 ::1
|
||||
#
|
||||
# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the
|
||||
# internet, binding to all the interfaces is dangerous and will expose the
|
||||
# instance to everybody on the internet. So by default we uncomment the
|
||||
# following bind directive, that will force Redis to listen only on the
|
||||
# IPv4 and IPv6 (if available) loopback interface addresses (this means Redis
|
||||
# will only be able to accept client connections from the same host that it is
|
||||
# running on).
|
||||
# IPv4 loopback interface address (this means Redis will only be able to
|
||||
# accept client connections from the same host that it is running on).
|
||||
#
|
||||
# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES
|
||||
# JUST COMMENT OUT THE FOLLOWING LINE.
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
bind 127.0.0.1 -::1
|
||||
bind 127.0.0.1
|
||||
|
||||
# Protected mode is a layer of security protection, in order to avoid that
|
||||
# Redis instances left open on the internet are accessed and exploited.
|
||||
@@ -112,7 +105,7 @@ tcp-backlog 511
|
||||
# incoming connections. There is no default, so Redis will not listen
|
||||
# on a unix socket when not specified.
|
||||
#
|
||||
# unixsocket /run/redis.sock
|
||||
# unixsocket /tmp/redis.sock
|
||||
# unixsocketperm 700
|
||||
|
||||
# Close the connection after a client is idle for N seconds (0 to disable)
|
||||
@@ -150,32 +143,8 @@ tcp-keepalive 300
|
||||
#
|
||||
# tls-cert-file redis.crt
|
||||
# tls-key-file redis.key
|
||||
#
|
||||
# If the key file is encrypted using a passphrase, it can be included here
|
||||
# as well.
|
||||
#
|
||||
# tls-key-file-pass secret
|
||||
|
||||
# Normally Redis uses the same certificate for both server functions (accepting
|
||||
# connections) and client functions (replicating from a master, establishing
|
||||
# cluster bus connections, etc.).
|
||||
#
|
||||
# Sometimes certificates are issued with attributes that designate them as
|
||||
# client-only or server-only certificates. In that case it may be desired to use
|
||||
# different certificates for incoming (server) and outgoing (client)
|
||||
# connections. To do that, use the following directives:
|
||||
#
|
||||
# tls-client-cert-file client.crt
|
||||
# tls-client-key-file client.key
|
||||
#
|
||||
# If the key file is encrypted using a passphrase, it can be included here
|
||||
# as well.
|
||||
#
|
||||
# tls-client-key-file-pass secret
|
||||
|
||||
# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange,
|
||||
# required by older versions of OpenSSL (<3.0). Newer versions do not require
|
||||
# this configuration and recommend against it.
|
||||
# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange:
|
||||
#
|
||||
# tls-dh-params-file redis.dh
|
||||
|
||||
@@ -208,12 +177,9 @@ tcp-keepalive 300
|
||||
#
|
||||
# tls-cluster yes
|
||||
|
||||
# By default, only TLSv1.2 and TLSv1.3 are enabled and it is highly recommended
|
||||
# that older formally deprecated versions are kept disabled to reduce the attack surface.
|
||||
# You can explicitly specify TLS versions to support.
|
||||
# Allowed values are case insensitive and include "TLSv1", "TLSv1.1", "TLSv1.2",
|
||||
# "TLSv1.3" (OpenSSL >= 1.1.1) or any combination.
|
||||
# To enable only TLSv1.2 and TLSv1.3, use:
|
||||
# Explicitly specify TLS versions to support. Allowed values are case insensitive
|
||||
# and include "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" (OpenSSL >= 1.1.1) or
|
||||
# any combination. To enable only TLSv1.2 and TLSv1.3, use:
|
||||
#
|
||||
# tls-protocols "TLSv1.2 TLSv1.3"
|
||||
|
||||
@@ -255,7 +221,6 @@ tcp-keepalive 300
|
||||
|
||||
# By default Redis does not run as a daemon. Use 'yes' if you need it.
|
||||
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
|
||||
# When Redis is supervised by upstart or systemd, this parameter has no impact.
|
||||
daemonize no
|
||||
|
||||
# If you run Redis from upstart or systemd, Redis can interact with your
|
||||
@@ -264,17 +229,11 @@ daemonize no
|
||||
# supervised upstart - signal upstart by putting Redis into SIGSTOP mode
|
||||
# requires "expect stop" in your upstart job config
|
||||
# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET
|
||||
# on startup, and updating Redis status on a regular
|
||||
# basis.
|
||||
# supervised auto - detect upstart or systemd method based on
|
||||
# UPSTART_JOB or NOTIFY_SOCKET environment variables
|
||||
# Note: these supervision methods only signal "process is ready."
|
||||
# They do not enable continuous pings back to your supervisor.
|
||||
#
|
||||
# The default is "no". To run under upstart/systemd, you can simply uncomment
|
||||
# the line below:
|
||||
#
|
||||
# supervised auto
|
||||
supervised no
|
||||
|
||||
# If a pid file is specified, Redis writes it where specified at startup
|
||||
# and removes it at exit.
|
||||
@@ -285,9 +244,6 @@ daemonize no
|
||||
#
|
||||
# Creating a pid file is best effort: if Redis is not able to create it
|
||||
# nothing bad happens, the server will start and run normally.
|
||||
#
|
||||
# Note that on modern Linux systems "/run/redis.pid" is more conforming
|
||||
# and should be used instead.
|
||||
pidfile /var/run/redis_6379.pid
|
||||
|
||||
# Specify the server verbosity level.
|
||||
@@ -313,76 +269,44 @@ logfile ""
|
||||
# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
|
||||
# syslog-facility local0
|
||||
|
||||
# To disable the built in crash log, which will possibly produce cleaner core
|
||||
# dumps when they are needed, uncomment the following:
|
||||
#
|
||||
# crash-log-enabled no
|
||||
|
||||
# To disable the fast memory check that's run as part of the crash log, which
|
||||
# will possibly let redis terminate sooner, uncomment the following:
|
||||
#
|
||||
# crash-memcheck-enabled no
|
||||
|
||||
# Set the number of databases. The default database is DB 0, you can select
|
||||
# a different one on a per-connection basis using SELECT <dbid> where
|
||||
# dbid is a number between 0 and 'databases'-1
|
||||
databases 16
|
||||
|
||||
# By default Redis shows an ASCII art logo only when started to log to the
|
||||
# standard output and if the standard output is a TTY and syslog logging is
|
||||
# disabled. Basically this means that normally a logo is displayed only in
|
||||
# interactive sessions.
|
||||
# standard output and if the standard output is a TTY. Basically this means
|
||||
# that normally a logo is displayed only in interactive sessions.
|
||||
#
|
||||
# However it is possible to force the pre-4.0 behavior and always show a
|
||||
# ASCII art logo in startup logs by setting the following option to yes.
|
||||
always-show-logo no
|
||||
|
||||
# By default, Redis modifies the process title (as seen in 'top' and 'ps') to
|
||||
# provide some runtime information. It is possible to disable this and leave
|
||||
# the process name as executed by setting the following to no.
|
||||
set-proc-title yes
|
||||
|
||||
# When changing the process title, Redis uses the following template to construct
|
||||
# the modified title.
|
||||
#
|
||||
# Template variables are specified in curly brackets. The following variables are
|
||||
# supported:
|
||||
#
|
||||
# {title} Name of process as executed if parent, or type of child process.
|
||||
# {listen-addr} Bind address or '*' followed by TCP or TLS port listening on, or
|
||||
# Unix socket if only that's available.
|
||||
# {server-mode} Special mode, i.e. "[sentinel]" or "[cluster]".
|
||||
# {port} TCP port listening on, or 0.
|
||||
# {tls-port} TLS port listening on, or 0.
|
||||
# {unixsocket} Unix domain socket listening on, or "".
|
||||
# {config-file} Name of configuration file used.
|
||||
#
|
||||
proc-title-template "{title} {listen-addr} {server-mode}"
|
||||
always-show-logo yes
|
||||
|
||||
################################ SNAPSHOTTING ################################
|
||||
#
|
||||
# Save the DB on disk:
|
||||
#
|
||||
# save <seconds> <changes>
|
||||
#
|
||||
# Will save the DB if both the given number of seconds and the given
|
||||
# number of write operations against the DB occurred.
|
||||
#
|
||||
# In the example below the behavior will be to save:
|
||||
# after 900 sec (15 min) if at least 1 key changed
|
||||
# after 300 sec (5 min) if at least 10 keys changed
|
||||
# after 60 sec if at least 10000 keys changed
|
||||
#
|
||||
# Note: you can disable saving completely by commenting out all "save" lines.
|
||||
#
|
||||
# It is also possible to remove all the previously configured save
|
||||
# points by adding a save directive with a single empty string argument
|
||||
# like in the following example:
|
||||
#
|
||||
# save ""
|
||||
|
||||
# Save the DB to disk.
|
||||
#
|
||||
# save <seconds> <changes>
|
||||
#
|
||||
# Redis will save the DB if both the given number of seconds and the given
|
||||
# number of write operations against the DB occurred.
|
||||
#
|
||||
# Snapshotting can be completely disabled with a single empty string argument
|
||||
# as in following example:
|
||||
#
|
||||
# save ""
|
||||
#
|
||||
# Unless specified otherwise, by default Redis will save the DB:
|
||||
# * After 3600 seconds (an hour) if at least 1 key changed
|
||||
# * After 300 seconds (5 minutes) if at least 100 keys changed
|
||||
# * After 60 seconds if at least 10000 keys changed
|
||||
#
|
||||
# You can set these explicitly by uncommenting the three following lines.
|
||||
#
|
||||
# save 3600 1
|
||||
# save 300 100
|
||||
# save 60 10000
|
||||
save 900 1
|
||||
save 300 10
|
||||
save 60 10000
|
||||
|
||||
# By default Redis will stop accepting writes if RDB snapshots are enabled
|
||||
# (at least one save point) and the latest background save failed.
|
||||
@@ -414,21 +338,6 @@ rdbcompression yes
|
||||
# tell the loading code to skip the check.
|
||||
rdbchecksum yes
|
||||
|
||||
# Enables or disables full sanitation checks for ziplist and listpack etc when
|
||||
# loading an RDB or RESTORE payload. This reduces the chances of a assertion or
|
||||
# crash later on while processing commands.
|
||||
# Options:
|
||||
# no - Never perform full sanitation
|
||||
# yes - Always perform full sanitation
|
||||
# clients - Perform full sanitation only for user connections.
|
||||
# Excludes: RDB files, RESTORE commands received from the master
|
||||
# connection, and client connections which have the
|
||||
# skip-sanitize-payload ACL flag.
|
||||
# The default should be 'clients' but since it currently affects cluster
|
||||
# resharding via MIGRATE, it is temporarily set to 'no' by default.
|
||||
#
|
||||
# sanitize-dump-payload no
|
||||
|
||||
# The filename where to dump the DB
|
||||
dbfilename dump.rdb
|
||||
|
||||
@@ -573,7 +482,7 @@ repl-diskless-sync-delay 5
|
||||
# does not immediately store an RDB on disk, it may cause data loss during
|
||||
# failovers. RDB diskless load + Redis modules not handling I/O reads may also
|
||||
# cause Redis to abort in case of I/O errors during the initial synchronization
|
||||
# stage with the master. Use only if you know what you are doing.
|
||||
# stage with the master. Use only if your do what you are doing.
|
||||
# -----------------------------------------------------------------------------
|
||||
#
|
||||
# Replica can load the RDB it reads from the replication link directly from the
|
||||
@@ -669,18 +578,6 @@ repl-disable-tcp-nodelay no
|
||||
# By default the priority is 100.
|
||||
replica-priority 100
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# By default, Redis Sentinel includes all replicas in its reports. A replica
|
||||
# can be excluded from Redis Sentinel's announcements. An unannounced replica
|
||||
# will be ignored by the 'sentinel replicas <master>' command and won't be
|
||||
# exposed to Redis Sentinel's clients.
|
||||
#
|
||||
# This option does not change the behavior of replica-priority. Even with
|
||||
# replica-announced set to 'no', the replica can be promoted to master. To
|
||||
# prevent this behavior, set replica-priority to 0.
|
||||
#
|
||||
# replica-announced yes
|
||||
|
||||
# It is possible for a master to stop accepting writes if there are less than
|
||||
# N replicas connected, having a lag less or equal than M seconds.
|
||||
#
|
||||
@@ -736,7 +633,7 @@ replica-priority 100
|
||||
|
||||
# Redis implements server assisted support for client side caching of values.
|
||||
# This is implemented using an invalidation table that remembers, using
|
||||
# a radix key indexed by key name, what clients have which keys. In turn
|
||||
# 16 millions of slots, what clients may have certain subsets of keys. In turn
|
||||
# this is used in order to send invalidation messages to clients. Please
|
||||
# check this page to understand more about the feature:
|
||||
#
|
||||
@@ -800,8 +697,6 @@ replica-priority 100
|
||||
# off Disable the user: it's no longer possible to authenticate
|
||||
# with this user, however the already authenticated connections
|
||||
# will still work.
|
||||
# skip-sanitize-payload RESTORE dump-payload sanitation is skipped.
|
||||
# sanitize-payload RESTORE dump-payload is sanitized (default).
|
||||
# +<command> Allow the execution of that command
|
||||
# -<command> Disallow the execution of that command
|
||||
# +@<category> Allow the execution of all the commands in such category
|
||||
@@ -824,11 +719,6 @@ replica-priority 100
|
||||
# It is possible to specify multiple patterns.
|
||||
# allkeys Alias for ~*
|
||||
# resetkeys Flush the list of allowed keys patterns.
|
||||
# &<pattern> Add a glob-style pattern of Pub/Sub channels that can be
|
||||
# accessed by the user. It is possible to specify multiple channel
|
||||
# patterns.
|
||||
# allchannels Alias for &*
|
||||
# resetchannels Flush the list of allowed channel patterns.
|
||||
# ><password> Add this password to the list of valid password for the user.
|
||||
# For example >mypass will add "mypass" to the list.
|
||||
# This directive clears the "nopass" flag (see later).
|
||||
@@ -897,32 +787,8 @@ acllog-max-len 128
|
||||
# AUTH <password> as usually, or more explicitly with AUTH default <password>
|
||||
# if they follow the new protocol: both will work.
|
||||
#
|
||||
# The requirepass is not compatable with aclfile option and the ACL LOAD
|
||||
# command, these will cause requirepass to be ignored.
|
||||
#
|
||||
# requirepass foobared
|
||||
|
||||
# New users are initialized with restrictive permissions by default, via the
|
||||
# equivalent of this ACL rule 'off resetkeys -@all'. Starting with Redis 6.2, it
|
||||
# is possible to manage access to Pub/Sub channels with ACL rules as well. The
|
||||
# default Pub/Sub channels permission if new users is controlled by the
|
||||
# acl-pubsub-default configuration directive, which accepts one of these values:
|
||||
#
|
||||
# allchannels: grants access to all Pub/Sub channels
|
||||
# resetchannels: revokes access to all Pub/Sub channels
|
||||
#
|
||||
# To ensure backward compatibility while upgrading Redis 6.0, acl-pubsub-default
|
||||
# defaults to the 'allchannels' permission.
|
||||
#
|
||||
# Future compatibility note: it is very likely that in a future version of Redis
|
||||
# the directive's default of 'allchannels' will be changed to 'resetchannels' in
|
||||
# order to provide better out-of-the-box Pub/Sub security. Therefore, it is
|
||||
# recommended that you explicitly define Pub/Sub permissions for all users
|
||||
# rather then rely on implicit default values. Once you've set explicit
|
||||
# Pub/Sub for all existing users, you should uncomment the following line.
|
||||
#
|
||||
# acl-pubsub-default resetchannels
|
||||
|
||||
# Command renaming (DEPRECATED).
|
||||
#
|
||||
# ------------------------------------------------------------------------
|
||||
@@ -1011,12 +877,14 @@ acllog-max-len 128
|
||||
# Both LRU, LFU and volatile-ttl are implemented using approximated
|
||||
# randomized algorithms.
|
||||
#
|
||||
# Note: with any of the above policies, when there are no suitable keys for
|
||||
# eviction, Redis will return an error on write operations that require
|
||||
# more memory. These are usually commands that create new keys, add data or
|
||||
# modify existing keys. A few examples are: SET, INCR, HSET, LPUSH, SUNIONSTORE,
|
||||
# SORT (due to the STORE argument), and EXEC (if the transaction includes any
|
||||
# command that requires memory).
|
||||
# Note: with any of the above policies, Redis will return an error on write
|
||||
# operations, when there are no suitable keys for eviction.
|
||||
#
|
||||
# At the date of writing these commands are: set setnx setex append
|
||||
# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
|
||||
# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
|
||||
# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
|
||||
# getset mset msetnx exec sort
|
||||
#
|
||||
# The default is:
|
||||
#
|
||||
@@ -1033,14 +901,6 @@ acllog-max-len 128
|
||||
#
|
||||
# maxmemory-samples 5
|
||||
|
||||
# Eviction processing is designed to function well with the default setting.
|
||||
# If there is an unusually large amount of write traffic, this value may need to
|
||||
# be increased. Decreasing this value may reduce latency at the risk of
|
||||
# eviction processing effectiveness
|
||||
# 0 = minimum latency, 10 = default, 100 = process without regard to latency
|
||||
#
|
||||
# maxmemory-eviction-tenacity 10
|
||||
|
||||
# Starting from Redis 5, by default a replica will ignore its maxmemory setting
|
||||
# (unless it is promoted to master after a failover or manually). It means
|
||||
# that the eviction of keys will be just handled by the master, sending the
|
||||
@@ -1134,13 +994,6 @@ replica-lazy-flush no
|
||||
|
||||
lazyfree-lazy-user-del no
|
||||
|
||||
# FLUSHDB, FLUSHALL, and SCRIPT FLUSH support both asynchronous and synchronous
|
||||
# deletion, which can be controlled by passing the [SYNC|ASYNC] flags into the
|
||||
# commands. When neither flag is passed, this directive will be used to determine
|
||||
# if the data should be deleted asynchronously.
|
||||
|
||||
lazyfree-lazy-user-flush no
|
||||
|
||||
################################ THREADED I/O #################################
|
||||
|
||||
# Redis is mostly single threaded, however there are certain threaded
|
||||
@@ -1218,19 +1071,6 @@ oom-score-adj no
|
||||
# oom-score-adj-values to positive values will always succeed.
|
||||
oom-score-adj-values 0 200 800
|
||||
|
||||
|
||||
#################### KERNEL transparent hugepage CONTROL ######################
|
||||
|
||||
# Usually the kernel Transparent Huge Pages control is set to "madvise" or
|
||||
# or "never" by default (/sys/kernel/mm/transparent_hugepage/enabled), in which
|
||||
# case this config has no effect. On systems in which it is set to "always",
|
||||
# redis will attempt to disable it specifically for the redis process in order
|
||||
# to avoid latency problems specifically with fork(2) and CoW.
|
||||
# If for some reason you prefer to keep it enabled, you can set this config to
|
||||
# "no" and the kernel global to "always".
|
||||
|
||||
disable-thp yes
|
||||
|
||||
############################## APPEND ONLY MODE ###############################
|
||||
|
||||
# By default Redis asynchronously dumps the dataset on disk. This mode is
|
||||
@@ -1249,7 +1089,7 @@ disable-thp yes
|
||||
# If the AOF is enabled on startup Redis will load the AOF, that is the file
|
||||
# with the better durability guarantees.
|
||||
#
|
||||
# Please check https://redis.io/topics/persistence for more information.
|
||||
# Please check http://redis.io/topics/persistence for more information.
|
||||
|
||||
appendonly no
|
||||
|
||||
@@ -1458,21 +1298,12 @@ lua-time-limit 5000
|
||||
# master in your cluster.
|
||||
#
|
||||
# Default is 1 (replicas migrate only if their masters remain with at least
|
||||
# one replica). To disable migration just set it to a very large value or
|
||||
# set cluster-allow-replica-migration to 'no'.
|
||||
# one replica). To disable migration just set it to a very large value.
|
||||
# A value of 0 can be set but is useful only for debugging and dangerous
|
||||
# in production.
|
||||
#
|
||||
# cluster-migration-barrier 1
|
||||
|
||||
# Turning off this option allows to use less automatic cluster configuration.
|
||||
# It both disables migration to orphaned masters and migration from masters
|
||||
# that became empty.
|
||||
#
|
||||
# Default is 'yes' (allow automatic migrations).
|
||||
#
|
||||
# cluster-allow-replica-migration yes
|
||||
|
||||
# By default Redis Cluster nodes stop accepting queries if they detect there
|
||||
# is at least a hash slot uncovered (no available node is serving it).
|
||||
# This way if the cluster is partially down (for example a range of hash slots
|
||||
@@ -1487,7 +1318,7 @@ lua-time-limit 5000
|
||||
# cluster-require-full-coverage yes
|
||||
|
||||
# This option, when set to yes, prevents replicas from trying to failover its
|
||||
# master during master failures. However the replica can still perform a
|
||||
# master during master failures. However the master can still perform a
|
||||
# manual failover, if forced to do so.
|
||||
#
|
||||
# This is useful in different scenarios, especially in the case of multiple
|
||||
@@ -1513,7 +1344,7 @@ lua-time-limit 5000
|
||||
# cluster-allow-reads-when-down no
|
||||
|
||||
# In order to setup your cluster make sure to read the documentation
|
||||
# available at https://redis.io web site.
|
||||
# available at http://redis.io web site.
|
||||
|
||||
########################## CLUSTER DOCKER/NAT support ########################
|
||||
|
||||
@@ -1523,21 +1354,16 @@ lua-time-limit 5000
|
||||
#
|
||||
# In order to make Redis Cluster working in such environments, a static
|
||||
# configuration where each node knows its public address is needed. The
|
||||
# following four options are used for this scope, and are:
|
||||
# following two options are used for this scope, and are:
|
||||
#
|
||||
# * cluster-announce-ip
|
||||
# * cluster-announce-port
|
||||
# * cluster-announce-tls-port
|
||||
# * cluster-announce-bus-port
|
||||
#
|
||||
# Each instructs the node about its address, client ports (for connections
|
||||
# without and with TLS) and cluster message bus port. The information is then
|
||||
# published in the header of the bus packets so that other nodes will be able to
|
||||
# correctly map the address of the node publishing the information.
|
||||
#
|
||||
# If cluster-tls is set to yes and cluster-announce-tls-port is omitted or set
|
||||
# to zero, then cluster-announce-port refers to the TLS port. Note also that
|
||||
# cluster-announce-tls-port has no effect if cluster-tls is set to no.
|
||||
# Each instructs the node about its address, client port, and cluster message
|
||||
# bus port. The information is then published in the header of the bus packets
|
||||
# so that other nodes will be able to correctly map the address of the node
|
||||
# publishing the information.
|
||||
#
|
||||
# If the above options are not used, the normal Redis Cluster auto-detection
|
||||
# will be used instead.
|
||||
@@ -1550,8 +1376,7 @@ lua-time-limit 5000
|
||||
# Example:
|
||||
#
|
||||
# cluster-announce-ip 10.1.1.5
|
||||
# cluster-announce-tls-port 6379
|
||||
# cluster-announce-port 0
|
||||
# cluster-announce-port 6379
|
||||
# cluster-announce-bus-port 6380
|
||||
|
||||
################################## SLOW LOG ###################################
|
||||
@@ -1602,7 +1427,7 @@ latency-monitor-threshold 0
|
||||
############################# EVENT NOTIFICATION ##############################
|
||||
|
||||
# Redis can notify Pub/Sub clients about events happening in the key space.
|
||||
# This feature is documented at https://redis.io/topics/notifications
|
||||
# This feature is documented at http://redis.io/topics/notifications
|
||||
#
|
||||
# For instance if keyspace events notification is enabled, and a client
|
||||
# performs a DEL operation on key "foo" stored in the Database 0, two
|
||||
@@ -1625,9 +1450,8 @@ latency-monitor-threshold 0
|
||||
# x Expired events (events generated every time a key expires)
|
||||
# e Evicted events (events generated when a key is evicted for maxmemory)
|
||||
# t Stream commands
|
||||
# d Module key type events
|
||||
# m Key-miss events (Note: It is not included in the 'A' class)
|
||||
# A Alias for g$lshzxetd, so that the "AKE" string means all the events
|
||||
# A Alias for g$lshzxet, so that the "AKE" string means all the events
|
||||
# (Except key-miss events which are excluded from 'A' due to their
|
||||
# unique nature).
|
||||
#
|
||||
@@ -1780,7 +1604,7 @@ hll-sparse-max-bytes 3000
|
||||
# maximum number of items it may contain before switching to a new node when
|
||||
# appending new stream entries. If any of the following settings are set to
|
||||
# zero, the limit is ignored, so for instance it is possible to set just a
|
||||
# max entries limit by setting max-bytes to 0 and max-entries to the desired
|
||||
# max entires limit by setting max-bytes to 0 and max-entries to the desired
|
||||
# value.
|
||||
stream-node-max-bytes 4096
|
||||
stream-node-max-entries 100
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
TCL_VERSIONS="8.5 8.6 8.7"
|
||||
TCL_VERSIONS="8.5 8.6"
|
||||
TCLSH=""
|
||||
|
||||
for VERSION in $TCL_VERSIONS; do
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
TCL_VERSIONS="8.5 8.6 8.7"
|
||||
TCL_VERSIONS="8.5 8.6"
|
||||
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 Cluster test"
|
||||
echo "You need tcl 8.5 or newer in order to run the Redis Sentinel test"
|
||||
exit 1
|
||||
fi
|
||||
$TCLSH tests/cluster/run.tcl $*
|
||||
|
||||
+3
-11
@@ -1,7 +1,6 @@
|
||||
#!/bin/sh
|
||||
TCL_VERSIONS="8.5 8.6 8.7"
|
||||
TCL_VERSIONS="8.5 8.6"
|
||||
TCLSH=""
|
||||
[ -z "$MAKE" ] && MAKE=make
|
||||
|
||||
for VERSION in $TCL_VERSIONS; do
|
||||
TCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL
|
||||
@@ -9,14 +8,13 @@ done
|
||||
|
||||
if [ -z $TCLSH ]
|
||||
then
|
||||
echo "You need tcl 8.5 or newer in order to run the Redis ModuleApi test"
|
||||
echo "You need tcl 8.5 or newer in order to run the Redis 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 \
|
||||
@@ -24,17 +22,11 @@ $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 \
|
||||
"${@}"
|
||||
|
||||
@@ -124,42 +124,6 @@ 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
|
||||
@@ -167,29 +131,6 @@ acllog-max-len 128
|
||||
# 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>
|
||||
#
|
||||
@@ -321,21 +262,3 @@ sentinel deny-scripts-reconfig yes
|
||||
# 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
|
||||
|
||||
+39
-82
@@ -16,36 +16,19 @@ 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 hdr_histogram
|
||||
DEPENDENCY_TARGETS=hiredis linenoise lua
|
||||
NODEPS:=clean distclean
|
||||
|
||||
# Default settings
|
||||
STD=-pedantic -DREDIS_STATIC=''
|
||||
|
||||
# Use -Wno-c11-extensions on clang, either where explicitly used or on
|
||||
# platforms we can assume it's being used.
|
||||
STD=-std=c11 -pedantic -DREDIS_STATIC=''
|
||||
ifneq (,$(findstring clang,$(CC)))
|
||||
STD+=-Wno-c11-extensions
|
||||
else
|
||||
ifneq (,$(findstring FreeBSD,$(uname_S)))
|
||||
STD+=-Wno-c11-extensions
|
||||
endif
|
||||
endif
|
||||
WARN=-Wall -W -Wno-missing-field-initializers -Wno-strict-prototypes
|
||||
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
|
||||
@@ -95,18 +78,20 @@ FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG)
|
||||
FINAL_LIBS=-lm
|
||||
DEBUG=-g -ggdb
|
||||
|
||||
# Linux ARM32 needs -latomic at linking time
|
||||
# Linux ARM needs -latomic at linking time
|
||||
ifneq (,$(filter aarch64 armv,$(uname_M)))
|
||||
FINAL_LIBS+=-latomic
|
||||
else
|
||||
ifneq (,$(findstring armv,$(uname_M)))
|
||||
FINAL_LIBS+=-latomic
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(uname_S),SunOS)
|
||||
# SunOS
|
||||
ifeq ($(findstring -m32,$(FINAL_CFLAGS)),)
|
||||
CFLAGS+=-m64
|
||||
endif
|
||||
ifeq ($(findstring -m32,$(FINAL_LDFLAGS)),)
|
||||
LDFLAGS+=-m64
|
||||
ifneq ($(@@),32bit)
|
||||
CFLAGS+= -m64
|
||||
LDFLAGS+= -m64
|
||||
endif
|
||||
DEBUG=-g
|
||||
DEBUG_FLAGS=-g
|
||||
@@ -118,16 +103,8 @@ else
|
||||
ifeq ($(uname_S),Darwin)
|
||||
# Darwin
|
||||
FINAL_LIBS+= -ldl
|
||||
# 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
|
||||
OPENSSL_CFLAGS=-I/usr/local/opt/openssl/include
|
||||
OPENSSL_LDFLAGS=-L/usr/local/opt/openssl/lib
|
||||
else
|
||||
ifeq ($(uname_S),AIX)
|
||||
# AIX
|
||||
@@ -188,21 +165,11 @@ 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 -I../deps/hdr_histogram
|
||||
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src
|
||||
|
||||
# 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)
|
||||
@@ -211,18 +178,17 @@ 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+=$(LIBSYSTEMD_LIBS)
|
||||
FINAL_LIBS+=$(shell $(PKG_CONFIG) --libs libsystemd)
|
||||
FINAL_CFLAGS+= -DHAVE_LIBSYSTEMD
|
||||
endif
|
||||
|
||||
@@ -260,17 +226,6 @@ 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)
|
||||
REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS)
|
||||
REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL)
|
||||
@@ -290,11 +245,11 @@ endif
|
||||
|
||||
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_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 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_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 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_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o mt19937-64.o
|
||||
REDIS_CHECK_RDB_NAME=redis-check-rdb$(PROG_SUFFIX)
|
||||
REDIS_CHECK_AOF_NAME=redis-check-aof$(PROG_SUFFIX)
|
||||
|
||||
@@ -364,7 +319,10 @@ $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
|
||||
|
||||
# redis-benchmark
|
||||
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/hdr_histogram/hdr_histogram.o $(FINAL_LIBS)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)
|
||||
|
||||
dict-benchmark: dict.c zmalloc.c sds.c siphash.c mt19937-64.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)
|
||||
@@ -376,26 +334,21 @@ DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ
|
||||
$(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
|
||||
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 -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) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME)
|
||||
test: $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME)
|
||||
@(cd ..; ./runtest)
|
||||
|
||||
test-modules: $(REDIS_SERVER_NAME)
|
||||
@(cd ..; ./runtest-moduleapi)
|
||||
|
||||
test-sentinel: $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME)
|
||||
test-sentinel: $(REDIS_SENTINEL_NAME)
|
||||
@(cd ..; ./runtest-sentinel)
|
||||
|
||||
check: test
|
||||
@@ -406,6 +359,10 @@ 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)
|
||||
@@ -427,18 +384,18 @@ valgrind:
|
||||
$(MAKE) OPTIMIZATION="-O0" MALLOC="libc"
|
||||
|
||||
helgrind:
|
||||
$(MAKE) OPTIMIZATION="-O0" MALLOC="libc" CFLAGS="-D__ATOMIC_VAR_FORCE_SYNC_MACROS" REDIS_CFLAGS="-I/usr/local/include" REDIS_LDFLAGS="-L/usr/local/lib"
|
||||
$(MAKE) OPTIMIZATION="-O0" MALLOC="libc" CFLAGS="-D__ATOMIC_VAR_FORCE_SYNC_MACROS"
|
||||
|
||||
src/help.h:
|
||||
@../utils/generate-command-help.rb > help.h
|
||||
|
||||
install: all
|
||||
@mkdir -p $(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)
|
||||
$(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)
|
||||
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)
|
||||
|
||||
uninstall:
|
||||
|
||||
@@ -53,10 +53,6 @@ list *UsersToLoad; /* This is a list of users found in the configuration file
|
||||
list *ACLLog; /* Our security log, the user is able to inspect that
|
||||
using the ACL LOG command .*/
|
||||
|
||||
static rax *commandId = NULL; /* Command name to id mapping */
|
||||
|
||||
static unsigned long nextid = 0; /* Next command id that has not been assigned */
|
||||
|
||||
struct ACLCategoryItem {
|
||||
const char *name;
|
||||
uint64_t flag;
|
||||
@@ -89,15 +85,11 @@ struct ACLUserFlag {
|
||||
const char *name;
|
||||
uint64_t flag;
|
||||
} ACLUserFlags[] = {
|
||||
/* Note: the order here dictates the emitted order at ACLDescribeUser */
|
||||
{"on", USER_FLAG_ENABLED},
|
||||
{"off", USER_FLAG_DISABLED},
|
||||
{"allkeys", USER_FLAG_ALLKEYS},
|
||||
{"allchannels", USER_FLAG_ALLCHANNELS},
|
||||
{"allcommands", USER_FLAG_ALLCOMMANDS},
|
||||
{"nopass", USER_FLAG_NOPASS},
|
||||
{"skip-sanitize-payload", USER_FLAG_SANITIZE_PAYLOAD_SKIP},
|
||||
{"sanitize-payload", USER_FLAG_SANITIZE_PAYLOAD},
|
||||
{NULL,0} /* Terminator. */
|
||||
};
|
||||
|
||||
@@ -174,15 +166,15 @@ sds ACLHashPassword(unsigned char *cleartext, size_t len) {
|
||||
return sdsnewlen(hex,HASH_PASSWORD_LEN);
|
||||
}
|
||||
|
||||
/* Given a hash and the hash length, returns C_OK if it is a valid password
|
||||
/* Given a hash and the hash length, returns C_OK if it is a valid password
|
||||
* hash, or C_ERR otherwise. */
|
||||
int ACLCheckPasswordHash(unsigned char *hash, int hashlen) {
|
||||
if (hashlen != HASH_PASSWORD_LEN) {
|
||||
return C_ERR;
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
|
||||
/* Password hashes can only be characters that represent
|
||||
* hexadecimal values, which are numbers and lowercase
|
||||
* hexadecimal values, which are numbers and lowercase
|
||||
* characters 'a' through 'f'. */
|
||||
for(int i = 0; i < HASH_PASSWORD_LEN; i++) {
|
||||
char c = hash[i];
|
||||
@@ -245,20 +237,16 @@ user *ACLCreateUser(const char *name, size_t namelen) {
|
||||
if (raxFind(Users,(unsigned char*)name,namelen) != raxNotFound) return NULL;
|
||||
user *u = zmalloc(sizeof(*u));
|
||||
u->name = sdsnewlen(name,namelen);
|
||||
u->flags = USER_FLAG_DISABLED | server.acl_pubsub_default;
|
||||
u->flags = USER_FLAG_DISABLED;
|
||||
u->allowed_subcommands = NULL;
|
||||
u->passwords = listCreate();
|
||||
u->patterns = listCreate();
|
||||
u->channels = listCreate();
|
||||
listSetMatchMethod(u->passwords,ACLListMatchSds);
|
||||
listSetFreeMethod(u->passwords,ACLListFreeSds);
|
||||
listSetDupMethod(u->passwords,ACLListDupSds);
|
||||
listSetMatchMethod(u->patterns,ACLListMatchSds);
|
||||
listSetFreeMethod(u->patterns,ACLListFreeSds);
|
||||
listSetDupMethod(u->patterns,ACLListDupSds);
|
||||
listSetMatchMethod(u->channels,ACLListMatchSds);
|
||||
listSetFreeMethod(u->channels,ACLListFreeSds);
|
||||
listSetDupMethod(u->channels,ACLListDupSds);
|
||||
memset(u->allowed_commands,0,sizeof(u->allowed_commands));
|
||||
raxInsert(Users,(unsigned char*)name,namelen,u,NULL);
|
||||
return u;
|
||||
@@ -287,7 +275,6 @@ void ACLFreeUser(user *u) {
|
||||
sdsfree(u->name);
|
||||
listRelease(u->passwords);
|
||||
listRelease(u->patterns);
|
||||
listRelease(u->channels);
|
||||
ACLResetSubcommands(u);
|
||||
zfree(u);
|
||||
}
|
||||
@@ -328,10 +315,8 @@ void ACLFreeUserAndKillClients(user *u) {
|
||||
void ACLCopyUser(user *dst, user *src) {
|
||||
listRelease(dst->passwords);
|
||||
listRelease(dst->patterns);
|
||||
listRelease(dst->channels);
|
||||
dst->passwords = listDup(src->passwords);
|
||||
dst->patterns = listDup(src->patterns);
|
||||
dst->channels = listDup(src->channels);
|
||||
memcpy(dst->allowed_commands,src->allowed_commands,
|
||||
sizeof(dst->allowed_commands));
|
||||
dst->flags = src->flags;
|
||||
@@ -613,10 +598,9 @@ sds ACLDescribeUser(user *u) {
|
||||
|
||||
/* Flags. */
|
||||
for (int j = 0; ACLUserFlags[j].flag; j++) {
|
||||
/* Skip the allcommands, allkeys and allchannels flags because they'll
|
||||
* be emitted later as +@all, ~* and &*. */
|
||||
/* Skip the allcommands and allkeys flags because they'll be emitted
|
||||
* later as ~* and +@all. */
|
||||
if (ACLUserFlags[j].flag == USER_FLAG_ALLKEYS ||
|
||||
ACLUserFlags[j].flag == USER_FLAG_ALLCHANNELS ||
|
||||
ACLUserFlags[j].flag == USER_FLAG_ALLCOMMANDS) continue;
|
||||
if (u->flags & ACLUserFlags[j].flag) {
|
||||
res = sdscat(res,ACLUserFlags[j].name);
|
||||
@@ -648,20 +632,6 @@ sds ACLDescribeUser(user *u) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Pub/sub channel patterns. */
|
||||
if (u->flags & USER_FLAG_ALLCHANNELS) {
|
||||
res = sdscatlen(res,"&* ",3);
|
||||
} else {
|
||||
res = sdscatlen(res,"resetchannels ",14);
|
||||
listRewind(u->channels,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
sds thispat = listNodeValue(ln);
|
||||
res = sdscatlen(res,"&",1);
|
||||
res = sdscatsds(res,thispat);
|
||||
res = sdscatlen(res," ",1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Command rules. */
|
||||
sds rules = ACLDescribeUserCommandRules(u);
|
||||
res = sdscatsds(res,rules);
|
||||
@@ -707,6 +677,7 @@ void ACLResetSubcommands(user *u) {
|
||||
u->allowed_subcommands = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Add a subcommand to the list of subcommands for the user 'u' and
|
||||
* the command id specified. */
|
||||
void ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub) {
|
||||
@@ -767,12 +738,6 @@ void ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub) {
|
||||
* It is possible to specify multiple patterns.
|
||||
* allkeys Alias for ~*
|
||||
* resetkeys Flush the list of allowed keys patterns.
|
||||
* &<pattern> Add a pattern of channels that can be mentioned as part of
|
||||
* Pub/Sub commands. For instance &* allows all the channels. The
|
||||
* pattern is a glob-style pattern like the one of PSUBSCRIBE.
|
||||
* It is possible to specify multiple patterns.
|
||||
* allchannels Alias for &*
|
||||
* resetchannels Flush the list of allowed keys patterns.
|
||||
* ><password> Add this password to the list of valid password for the user.
|
||||
* For example >mypass will add "mypass" to the list.
|
||||
* This directive clears the "nopass" flag (see later).
|
||||
@@ -811,14 +776,14 @@ void ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub) {
|
||||
*
|
||||
* When an error is returned, errno is set to the following values:
|
||||
*
|
||||
* EINVAL: The specified opcode is not understood or the key/channel pattern is
|
||||
* EINVAL: The specified opcode is not understood or the key pattern is
|
||||
* invalid (contains non allowed characters).
|
||||
* ENOENT: The command name or command category provided with + or - is not
|
||||
* known.
|
||||
* EBUSY: The subcommand you want to add is about a command that is currently
|
||||
* fully added.
|
||||
* EEXIST: You are adding a key pattern after "*" was already added. This is
|
||||
* almost surely an error on the user side.
|
||||
* EISDIR: You are adding a channel pattern after "*" was already added. This is
|
||||
* almost surely an error on the user side.
|
||||
* ENODEV: The password you are trying to remove from the user does not exist.
|
||||
* EBADMSG: The hash you are trying to add is not a valid hash.
|
||||
*/
|
||||
@@ -831,12 +796,6 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
|
||||
} else if (!strcasecmp(op,"off")) {
|
||||
u->flags |= USER_FLAG_DISABLED;
|
||||
u->flags &= ~USER_FLAG_ENABLED;
|
||||
} else if (!strcasecmp(op,"skip-sanitize-payload")) {
|
||||
u->flags |= USER_FLAG_SANITIZE_PAYLOAD_SKIP;
|
||||
u->flags &= ~USER_FLAG_SANITIZE_PAYLOAD;
|
||||
} else if (!strcasecmp(op,"sanitize-payload")) {
|
||||
u->flags &= ~USER_FLAG_SANITIZE_PAYLOAD_SKIP;
|
||||
u->flags |= USER_FLAG_SANITIZE_PAYLOAD;
|
||||
} else if (!strcasecmp(op,"allkeys") ||
|
||||
!strcasecmp(op,"~*"))
|
||||
{
|
||||
@@ -845,14 +804,6 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
|
||||
} else if (!strcasecmp(op,"resetkeys")) {
|
||||
u->flags &= ~USER_FLAG_ALLKEYS;
|
||||
listEmpty(u->patterns);
|
||||
} else if (!strcasecmp(op,"allchannels") ||
|
||||
!strcasecmp(op,"&*"))
|
||||
{
|
||||
u->flags |= USER_FLAG_ALLCHANNELS;
|
||||
listEmpty(u->channels);
|
||||
} else if (!strcasecmp(op,"resetchannels")) {
|
||||
u->flags &= ~USER_FLAG_ALLCHANNELS;
|
||||
listEmpty(u->channels);
|
||||
} else if (!strcasecmp(op,"allcommands") ||
|
||||
!strcasecmp(op,"+@all"))
|
||||
{
|
||||
@@ -920,29 +871,12 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
|
||||
}
|
||||
sds newpat = sdsnewlen(op+1,oplen-1);
|
||||
listNode *ln = listSearchKey(u->patterns,newpat);
|
||||
/* Avoid re-adding the same key pattern multiple times. */
|
||||
/* Avoid re-adding the same pattern multiple times. */
|
||||
if (ln == NULL)
|
||||
listAddNodeTail(u->patterns,newpat);
|
||||
else
|
||||
sdsfree(newpat);
|
||||
u->flags &= ~USER_FLAG_ALLKEYS;
|
||||
} else if (op[0] == '&') {
|
||||
if (u->flags & USER_FLAG_ALLCHANNELS) {
|
||||
errno = EISDIR;
|
||||
return C_ERR;
|
||||
}
|
||||
if (ACLStringHasSpaces(op+1,oplen-1)) {
|
||||
errno = EINVAL;
|
||||
return C_ERR;
|
||||
}
|
||||
sds newpat = sdsnewlen(op+1,oplen-1);
|
||||
listNode *ln = listSearchKey(u->channels,newpat);
|
||||
/* Avoid re-adding the same channel pattern multiple times. */
|
||||
if (ln == NULL)
|
||||
listAddNodeTail(u->channels,newpat);
|
||||
else
|
||||
sdsfree(newpat);
|
||||
u->flags &= ~USER_FLAG_ALLCHANNELS;
|
||||
} else if (op[0] == '+' && op[1] != '@') {
|
||||
if (strchr(op,'|') == NULL) {
|
||||
if (ACLLookupCommand(op+1) == NULL) {
|
||||
@@ -975,12 +909,22 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
/* The command should not be set right now in the command
|
||||
* bitmap, because adding a subcommand of a fully added
|
||||
* command is probably an error on the user side. */
|
||||
unsigned long id = ACLGetCommandID(copy);
|
||||
/* Add the subcommand to the list of valid ones, if the command is not set. */
|
||||
if (ACLGetUserCommandBit(u,id) == 0) {
|
||||
ACLAddAllowedSubcommand(u,id,sub);
|
||||
if (ACLGetUserCommandBit(u,id) == 1) {
|
||||
zfree(copy);
|
||||
errno = EBUSY;
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
/* Add the subcommand to the list of valid ones. */
|
||||
ACLAddAllowedSubcommand(u,id,sub);
|
||||
|
||||
/* We have to clear the command bit so that we force the
|
||||
* subcommand check. */
|
||||
ACLSetUserCommandBit(u,id,0);
|
||||
zfree(copy);
|
||||
}
|
||||
} else if (op[0] == '-' && op[1] != '@') {
|
||||
@@ -1000,11 +944,7 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
|
||||
} else if (!strcasecmp(op,"reset")) {
|
||||
serverAssert(ACLSetUser(u,"resetpass",-1) == C_OK);
|
||||
serverAssert(ACLSetUser(u,"resetkeys",-1) == C_OK);
|
||||
serverAssert(ACLSetUser(u,"resetchannels",-1) == C_OK);
|
||||
if (server.acl_pubsub_default & USER_FLAG_ALLCHANNELS)
|
||||
serverAssert(ACLSetUser(u,"allchannels",-1) == C_OK);
|
||||
serverAssert(ACLSetUser(u,"off",-1) == C_OK);
|
||||
serverAssert(ACLSetUser(u,"sanitize-payload",-1) == C_OK);
|
||||
serverAssert(ACLSetUser(u,"-@all",-1) == C_OK);
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
@@ -1015,22 +955,21 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
|
||||
|
||||
/* Return a description of the error that occurred in ACLSetUser() according to
|
||||
* the errno value set by the function on error. */
|
||||
const char *ACLSetUserStringError(void) {
|
||||
const char *errmsg = "Wrong format";
|
||||
char *ACLSetUserStringError(void) {
|
||||
char *errmsg = "Wrong format";
|
||||
if (errno == ENOENT)
|
||||
errmsg = "Unknown command or category name in ACL";
|
||||
else if (errno == EINVAL)
|
||||
errmsg = "Syntax error";
|
||||
else if (errno == EBUSY)
|
||||
errmsg = "Adding a subcommand of a command already fully "
|
||||
"added is not allowed. Remove the command to start. "
|
||||
"Example: -DEBUG +DEBUG|DIGEST";
|
||||
else if (errno == EEXIST)
|
||||
errmsg = "Adding a pattern after the * pattern (or the "
|
||||
"'allkeys' flag) is not valid and does not have any "
|
||||
"effect. Try 'resetkeys' to start with an empty "
|
||||
"list of patterns";
|
||||
else if (errno == EISDIR)
|
||||
errmsg = "Adding a pattern after the * pattern (or the "
|
||||
"'allchannels' flag) is not valid and does not have any "
|
||||
"effect. Try 'resetchannels' to start with an empty "
|
||||
"list of channels";
|
||||
else if (errno == ENODEV)
|
||||
errmsg = "The password you are trying to remove from the user does "
|
||||
"not exist";
|
||||
@@ -1046,7 +985,6 @@ void ACLInitDefaultUser(void) {
|
||||
DefaultUser = ACLCreateUser("default",7);
|
||||
ACLSetUser(DefaultUser,"+@all",-1);
|
||||
ACLSetUser(DefaultUser,"~*",-1);
|
||||
ACLSetUser(DefaultUser,"&*",-1);
|
||||
ACLSetUser(DefaultUser,"on",-1);
|
||||
ACLSetUser(DefaultUser,"nopass",-1);
|
||||
}
|
||||
@@ -1057,6 +995,7 @@ void ACLInit(void) {
|
||||
UsersToLoad = listCreate();
|
||||
ACLLog = listCreate();
|
||||
ACLInitDefaultUser();
|
||||
server.requirepass = NULL; /* Only used for backward compatibility. */
|
||||
}
|
||||
|
||||
/* Check the username and password pair and return C_OK if they are valid,
|
||||
@@ -1126,16 +1065,18 @@ int ACLAuthenticateUser(client *c, robj *username, robj *password) {
|
||||
* command name, so that a command retains the same ID in case of modules that
|
||||
* are unloaded and later reloaded. */
|
||||
unsigned long ACLGetCommandID(const char *cmdname) {
|
||||
static rax *map = NULL;
|
||||
static unsigned long nextid = 0;
|
||||
|
||||
sds lowername = sdsnew(cmdname);
|
||||
sdstolower(lowername);
|
||||
if (commandId == NULL) commandId = raxNew();
|
||||
void *id = raxFind(commandId,(unsigned char*)lowername,sdslen(lowername));
|
||||
if (map == NULL) map = raxNew();
|
||||
void *id = raxFind(map,(unsigned char*)lowername,sdslen(lowername));
|
||||
if (id != raxNotFound) {
|
||||
sdsfree(lowername);
|
||||
return (unsigned long)id;
|
||||
}
|
||||
raxInsert(commandId,(unsigned char*)lowername,strlen(lowername),
|
||||
raxInsert(map,(unsigned char*)lowername,strlen(lowername),
|
||||
(void*)nextid,NULL);
|
||||
sdsfree(lowername);
|
||||
unsigned long thisid = nextid;
|
||||
@@ -1153,13 +1094,6 @@ unsigned long ACLGetCommandID(const char *cmdname) {
|
||||
return thisid;
|
||||
}
|
||||
|
||||
/* Clear command id table and reset nextid to 0. */
|
||||
void ACLClearCommandID(void) {
|
||||
if (commandId) raxFree(commandId);
|
||||
commandId = NULL;
|
||||
nextid = 0;
|
||||
}
|
||||
|
||||
/* Return an username by its name, or NULL if the user does not exist. */
|
||||
user *ACLGetUserByName(const char *name, size_t namelen) {
|
||||
void *myuser = raxFind(Users,(unsigned char*)name,namelen);
|
||||
@@ -1183,9 +1117,9 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
|
||||
/* If there is no associated user, the connection can run anything. */
|
||||
if (u == NULL) return ACL_OK;
|
||||
|
||||
/* Check if the user can execute this command or if the command
|
||||
* doesn't need to be authenticated (hello, auth). */
|
||||
if (!(u->flags & USER_FLAG_ALLCOMMANDS) && !(c->cmd->flags & CMD_NO_AUTH))
|
||||
/* Check if the user can execute this command. */
|
||||
if (!(u->flags & USER_FLAG_ALLCOMMANDS) &&
|
||||
c->cmd->proc != authCommand)
|
||||
{
|
||||
/* If the bit is not set we have to check further, in case the
|
||||
* command is allowed just with that specific subcommand. */
|
||||
@@ -1250,142 +1184,6 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
|
||||
return ACL_OK;
|
||||
}
|
||||
|
||||
/* Check if the provided channel is whitelisted by the given allowed channels
|
||||
* list. Glob-style pattern matching is employed, unless the literal flag is
|
||||
* set. Returns ACL_OK if access is granted or ACL_DENIED_CHANNEL otherwise. */
|
||||
int ACLCheckPubsubChannelPerm(sds channel, list *allowed, int literal) {
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
size_t clen = sdslen(channel);
|
||||
int match = 0;
|
||||
|
||||
listRewind(allowed,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
sds pattern = listNodeValue(ln);
|
||||
size_t plen = sdslen(pattern);
|
||||
|
||||
if ((literal && !sdscmp(pattern,channel)) ||
|
||||
(!literal && stringmatchlen(pattern,plen,channel,clen,0)))
|
||||
{
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
return ACL_DENIED_CHANNEL;
|
||||
}
|
||||
return ACL_OK;
|
||||
}
|
||||
|
||||
/* Check if the user's existing pub/sub clients violate the ACL pub/sub
|
||||
* permissions specified via the upcoming argument, and kill them if so. */
|
||||
void ACLKillPubsubClientsIfNeeded(user *u, list *upcoming) {
|
||||
listIter li, lpi;
|
||||
listNode *ln, *lpn;
|
||||
robj *o;
|
||||
int kill = 0;
|
||||
|
||||
/* If any of the original rule has the all-channels permission, but the new
|
||||
* one doesn't (verifed by the caller), then the new list is not a strict
|
||||
* superset of the original, and the next loop can be skipped. */
|
||||
if (u->flags & USER_FLAG_ALLCHANNELS) {
|
||||
kill = 1;
|
||||
}
|
||||
|
||||
/* Nothing to kill when the upcoming are a literal super set of the original
|
||||
* permissions. */
|
||||
listRewind(u->channels,&li);
|
||||
while (!kill && ((ln = listNext(&li)) != NULL)) {
|
||||
sds pattern = listNodeValue(ln);
|
||||
kill = (ACLCheckPubsubChannelPerm(pattern,upcoming,1) ==
|
||||
ACL_DENIED_CHANNEL);
|
||||
}
|
||||
if (!kill) return;
|
||||
|
||||
/* Scan all connected clients to find the user's pub/subs. */
|
||||
listRewind(server.clients,&li);
|
||||
while ((ln = listNext(&li)) != NULL) {
|
||||
client *c = listNodeValue(ln);
|
||||
kill = 0;
|
||||
|
||||
if (c->user == u && getClientType(c) == CLIENT_TYPE_PUBSUB) {
|
||||
/* Check for pattern violations. */
|
||||
listRewind(c->pubsub_patterns,&lpi);
|
||||
while (!kill && ((lpn = listNext(&lpi)) != NULL)) {
|
||||
o = lpn->value;
|
||||
kill = (ACLCheckPubsubChannelPerm(o->ptr,upcoming,1) ==
|
||||
ACL_DENIED_CHANNEL);
|
||||
}
|
||||
/* Check for channel violations. */
|
||||
if (!kill) {
|
||||
dictIterator *di = dictGetIterator(c->pubsub_channels);
|
||||
dictEntry *de;
|
||||
while (!kill && ((de = dictNext(di)) != NULL)) {
|
||||
o = dictGetKey(de);
|
||||
kill = (ACLCheckPubsubChannelPerm(o->ptr,upcoming,0) ==
|
||||
ACL_DENIED_CHANNEL);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
|
||||
/* Kill it. */
|
||||
if (kill) {
|
||||
freeClient(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the pub/sub channels of the command, that's ready to be executed in
|
||||
* the client 'c', can be executed by this client according to the ACLs channels
|
||||
* associated to the client user c->user.
|
||||
*
|
||||
* idx and count are the index and count of channel arguments from the
|
||||
* command. The literal argument controls whether the user's ACL channels are
|
||||
* evaluated as literal values or matched as glob-like patterns.
|
||||
*
|
||||
* If the user can execute the command ACL_OK is returned, otherwise
|
||||
* ACL_DENIED_CHANNEL. */
|
||||
int ACLCheckPubsubPerm(client *c, int idx, int count, int literal, int *idxptr) {
|
||||
user *u = c->user;
|
||||
|
||||
/* If there is no associated user, the connection can run anything. */
|
||||
if (u == NULL) return ACL_OK;
|
||||
|
||||
/* Check if the user can access the channels mentioned in the command's
|
||||
* arguments. */
|
||||
if (!(c->user->flags & USER_FLAG_ALLCHANNELS)) {
|
||||
for (int j = idx; j < idx+count; j++) {
|
||||
if (ACLCheckPubsubChannelPerm(c->argv[j]->ptr,u->channels,literal)
|
||||
!= ACL_OK) {
|
||||
if (idxptr) *idxptr = j;
|
||||
return ACL_DENIED_CHANNEL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we survived all the above checks, the user can execute the
|
||||
* command. */
|
||||
return ACL_OK;
|
||||
|
||||
}
|
||||
|
||||
/* Check whether the command is ready to be exceuted by ACLCheckCommandPerm.
|
||||
* If check passes, then check whether pub/sub channels of the command is
|
||||
* ready to be executed by ACLCheckPubsubPerm */
|
||||
int ACLCheckAllPerm(client *c, int *idxptr) {
|
||||
int acl_retval = ACLCheckCommandPerm(c,idxptr);
|
||||
if (acl_retval != ACL_OK)
|
||||
return acl_retval;
|
||||
if (c->cmd->proc == publishCommand)
|
||||
acl_retval = ACLCheckPubsubPerm(c,1,1,0,idxptr);
|
||||
else if (c->cmd->proc == subscribeCommand)
|
||||
acl_retval = ACLCheckPubsubPerm(c,1,c->argc-1,0,idxptr);
|
||||
else if (c->cmd->proc == psubscribeCommand)
|
||||
acl_retval = ACLCheckPubsubPerm(c,1,c->argc-1,1,idxptr);
|
||||
return acl_retval;
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
* ACL loading / saving functions
|
||||
* ==========================================================================*/
|
||||
@@ -1463,7 +1261,7 @@ int ACLLoadConfiguredUsers(void) {
|
||||
/* Load every rule defined for this user. */
|
||||
for (int j = 1; aclrules[j]; j++) {
|
||||
if (ACLSetUser(u,aclrules[j],sdslen(aclrules[j])) != C_OK) {
|
||||
const char *errmsg = ACLSetUserStringError();
|
||||
char *errmsg = ACLSetUserStringError();
|
||||
serverLog(LL_WARNING,"Error loading ACL rule '%s' for "
|
||||
"the user named '%s': %s",
|
||||
aclrules[j],aclrules[0],errmsg);
|
||||
@@ -1596,7 +1394,7 @@ sds ACLLoadFromFile(const char *filename) {
|
||||
for (j = 2; j < argc; j++) {
|
||||
argv[j] = sdstrim(argv[j],"\t\r\n");
|
||||
if (ACLSetUser(fakeuser,argv[j],sdslen(argv[j])) != C_OK) {
|
||||
const char *errmsg = ACLSetUserStringError();
|
||||
char *errmsg = ACLSetUserStringError();
|
||||
errors = sdscatprintf(errors,
|
||||
"%s:%d: %s. ",
|
||||
server.acl_filename, linenum, errmsg);
|
||||
@@ -1801,13 +1599,13 @@ void ACLFreeLogEntry(void *leptr) {
|
||||
* the log entry instead of creating many entries for very similar ACL
|
||||
* rules issues.
|
||||
*
|
||||
* The argpos argument is used when the reason is ACL_DENIED_KEY or
|
||||
* ACL_DENIED_CHANNEL, since it allows the function to log the key or channel
|
||||
* name that caused the problem. Similarly the username is only passed when we
|
||||
* failed to authenticate the user with AUTH or HELLO, for the ACL_DENIED_AUTH
|
||||
* reason. Otherwise it will just be NULL.
|
||||
* The keypos argument is only used when the reason is ACL_DENIED_KEY, since
|
||||
* it allows the function to log the key name that caused the problem.
|
||||
* Similarly the username is only passed when we failed to authenticate the
|
||||
* user with AUTH or HELLO, for the ACL_DENIED_AUTH reason. Otherwise
|
||||
* it will just be NULL.
|
||||
*/
|
||||
void addACLLogEntry(client *c, int reason, int argpos, sds username) {
|
||||
void addACLLogEntry(client *c, int reason, int keypos, sds username) {
|
||||
/* Create a new entry. */
|
||||
struct ACLLogEntry *le = zmalloc(sizeof(*le));
|
||||
le->count = 1;
|
||||
@@ -1817,9 +1615,8 @@ void addACLLogEntry(client *c, int reason, int argpos, sds username) {
|
||||
|
||||
switch(reason) {
|
||||
case ACL_DENIED_CMD: le->object = sdsnew(c->cmd->name); break;
|
||||
case ACL_DENIED_KEY: le->object = sdsdup(c->argv[argpos]->ptr); break;
|
||||
case ACL_DENIED_CHANNEL: le->object = sdsdup(c->argv[argpos]->ptr); break;
|
||||
case ACL_DENIED_AUTH: le->object = sdsdup(c->argv[0]->ptr); break;
|
||||
case ACL_DENIED_KEY: le->object = sdsnew(c->argv[keypos]->ptr); break;
|
||||
case ACL_DENIED_AUTH: le->object = sdsnew(c->argv[0]->ptr); break;
|
||||
default: le->object = sdsempty();
|
||||
}
|
||||
|
||||
@@ -1866,7 +1663,7 @@ void addACLLogEntry(client *c, int reason, int argpos, sds username) {
|
||||
le->cinfo = NULL;
|
||||
ACLFreeLogEntry(le);
|
||||
} else {
|
||||
/* Add it to our list of entries. We'll have to trim the list
|
||||
/* Add it to our list of entires. We'll have to trim the list
|
||||
* to its maximum size. */
|
||||
listAddNodeHead(ACLLog, le);
|
||||
while(listLength(ACLLog) > server.acllog_max_len) {
|
||||
@@ -1899,12 +1696,6 @@ void addACLLogEntry(client *c, int reason, int argpos, sds username) {
|
||||
void aclCommand(client *c) {
|
||||
char *sub = c->argv[1]->ptr;
|
||||
if (!strcasecmp(sub,"setuser") && c->argc >= 3) {
|
||||
/* Initially redact all of the arguments to not leak any information
|
||||
* about the user. */
|
||||
for (int j = 2; j < c->argc; j++) {
|
||||
redactClientCommandArgument(c, j);
|
||||
}
|
||||
|
||||
sds username = c->argv[2]->ptr;
|
||||
/* Check username validity. */
|
||||
if (ACLStringHasSpaces(username,sdslen(username))) {
|
||||
@@ -1923,7 +1714,7 @@ void aclCommand(client *c) {
|
||||
|
||||
for (int j = 3; j < c->argc; j++) {
|
||||
if (ACLSetUser(tempu,c->argv[j]->ptr,sdslen(c->argv[j]->ptr)) != C_OK) {
|
||||
const char *errmsg = ACLSetUserStringError();
|
||||
char *errmsg = ACLSetUserStringError();
|
||||
addReplyErrorFormat(c,
|
||||
"Error in ACL SETUSER modifier '%s': %s",
|
||||
(char*)c->argv[j]->ptr, errmsg);
|
||||
@@ -1933,11 +1724,6 @@ void aclCommand(client *c) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Existing pub/sub clients authenticated with the user may need to be
|
||||
* disconnected if (some of) their channel permissions were revoked. */
|
||||
if (u && !(tempu->flags & USER_FLAG_ALLCHANNELS))
|
||||
ACLKillPubsubClientsIfNeeded(u,tempu->channels);
|
||||
|
||||
/* Overwrite the user with the temporary user we modified above. */
|
||||
if (!u) u = ACLCreateUser(username,sdslen(username));
|
||||
serverAssert(u != NULL);
|
||||
@@ -1973,7 +1759,7 @@ void aclCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
addReplyMapLen(c,5);
|
||||
addReplyMapLen(c,4);
|
||||
|
||||
/* Flags */
|
||||
addReplyBulkCString(c,"flags");
|
||||
@@ -2018,22 +1804,6 @@ void aclCommand(client *c) {
|
||||
addReplyBulkCBuffer(c,thispat,sdslen(thispat));
|
||||
}
|
||||
}
|
||||
|
||||
/* Pub/sub patterns */
|
||||
addReplyBulkCString(c,"channels");
|
||||
if (u->flags & USER_FLAG_ALLCHANNELS) {
|
||||
addReplyArrayLen(c,1);
|
||||
addReplyBulkCBuffer(c,"*",1);
|
||||
} else {
|
||||
addReplyArrayLen(c,listLength(u->channels));
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
listRewind(u->channels,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
sds thispat = listNodeValue(ln);
|
||||
addReplyBulkCBuffer(c,thispat,sdslen(thispat));
|
||||
}
|
||||
}
|
||||
} else if ((!strcasecmp(sub,"list") || !strcasecmp(sub,"users")) &&
|
||||
c->argc == 2)
|
||||
{
|
||||
@@ -2171,7 +1941,6 @@ void aclCommand(client *c) {
|
||||
switch(le->reason) {
|
||||
case ACL_DENIED_CMD: reasonstr="command"; break;
|
||||
case ACL_DENIED_KEY: reasonstr="key"; break;
|
||||
case ACL_DENIED_CHANNEL: reasonstr="channel"; break;
|
||||
case ACL_DENIED_AUTH: reasonstr="auth"; break;
|
||||
default: reasonstr="unknown";
|
||||
}
|
||||
@@ -2199,30 +1968,18 @@ void aclCommand(client *c) {
|
||||
}
|
||||
} else if (c->argc == 2 && !strcasecmp(sub,"help")) {
|
||||
const char *help[] = {
|
||||
"CAT [<category>]",
|
||||
" List all commands that belong to <category>, or all command categories",
|
||||
" when no category is specified.",
|
||||
"DELUSER <username> [<username> ...]",
|
||||
" Delete a list of users.",
|
||||
"GETUSER <username>",
|
||||
" Get the user's details.",
|
||||
"GENPASS [<bits>]",
|
||||
" Generate a secure 256-bit user password. The optional `bits` argument can",
|
||||
" be used to specify a different size.",
|
||||
"LIST",
|
||||
" Show users details in config file format.",
|
||||
"LOAD",
|
||||
" Reload users from the ACL file.",
|
||||
"LOG [<count> | RESET]",
|
||||
" Show the ACL log entries.",
|
||||
"SAVE",
|
||||
" Save the current config to the ACL file.",
|
||||
"SETUSER <username> <attribute> [<attribute> ...]",
|
||||
" Create or modify a user with the specified attributes.",
|
||||
"USERS",
|
||||
" List all the registered usernames.",
|
||||
"WHOAMI",
|
||||
" Return the current connection username.",
|
||||
"LOAD -- Reload users from the ACL file.",
|
||||
"SAVE -- Save the current config to the ACL file.",
|
||||
"LIST -- Show user details in config file format.",
|
||||
"USERS -- List all the registered usernames.",
|
||||
"SETUSER <username> [attribs ...] -- Create or modify a user.",
|
||||
"GETUSER <username> -- Get the user details.",
|
||||
"DELUSER <username> [...] -- Delete a list of users.",
|
||||
"CAT -- List available categories.",
|
||||
"CAT <category> -- List commands inside category.",
|
||||
"GENPASS [<bits>] -- Generate a secure user password.",
|
||||
"WHOAMI -- Return the current connection username.",
|
||||
"LOG [<count> | RESET] -- Show the ACL log entries.",
|
||||
NULL
|
||||
};
|
||||
addReplyHelp(c,help);
|
||||
@@ -2251,11 +2008,9 @@ void addReplyCommandCategories(client *c, struct redisCommand *cmd) {
|
||||
void authCommand(client *c) {
|
||||
/* Only two or three argument forms are allowed. */
|
||||
if (c->argc > 3) {
|
||||
addReplyErrorObject(c,shared.syntaxerr);
|
||||
addReply(c,shared.syntaxerr);
|
||||
return;
|
||||
}
|
||||
/* Always redact the second argument */
|
||||
redactClientCommandArgument(c, 1);
|
||||
|
||||
/* Handle the two different forms here. The form with two arguments
|
||||
* will just use "default" as username. */
|
||||
@@ -2270,30 +2025,21 @@ void authCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
username = shared.default_username;
|
||||
username = createStringObject("default",7);
|
||||
password = c->argv[1];
|
||||
} else {
|
||||
username = c->argv[1];
|
||||
password = c->argv[2];
|
||||
redactClientCommandArgument(c, 2);
|
||||
}
|
||||
|
||||
if (ACLAuthenticateUser(c,username,password) == C_OK) {
|
||||
addReply(c,shared.ok);
|
||||
} else {
|
||||
addReplyError(c,"-WRONGPASS invalid username-password pair or user is disabled.");
|
||||
addReplyError(c,"-WRONGPASS invalid username-password pair");
|
||||
}
|
||||
|
||||
/* Free the "default" string object we created for the two
|
||||
* arguments form. */
|
||||
if (c->argc == 2) decrRefCount(username);
|
||||
}
|
||||
|
||||
/* Set the password for the "default" ACL user. This implements supports for
|
||||
* requirepass config, so passing in NULL will set the user to be nopass. */
|
||||
void ACLUpdateDefaultUserPassword(sds password) {
|
||||
ACLSetUser(DefaultUser,"resetpass",-1);
|
||||
if (password) {
|
||||
sds aclop = sdscatlen(sdsnew(">"), password, sdslen(password));
|
||||
ACLSetUser(DefaultUser,aclop,sdslen(aclop));
|
||||
sdsfree(aclop);
|
||||
} else {
|
||||
ACLSetUser(DefaultUser,"nopass",-1);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -360,16 +360,15 @@ void listRotateHeadToTail(list *list) {
|
||||
/* Add all the elements of the list 'o' at the end of the
|
||||
* list 'l'. The list 'other' remains empty but otherwise valid. */
|
||||
void listJoin(list *l, list *o) {
|
||||
if (o->len == 0) return;
|
||||
|
||||
o->head->prev = l->tail;
|
||||
if (o->head)
|
||||
o->head->prev = l->tail;
|
||||
|
||||
if (l->tail)
|
||||
l->tail->next = o->head;
|
||||
else
|
||||
l->head = o->head;
|
||||
|
||||
l->tail = o->tail;
|
||||
if (o->tail) l->tail = o->tail;
|
||||
l->len += o->len;
|
||||
|
||||
/* Setup other as an empty list. */
|
||||
|
||||
@@ -30,9 +30,6 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "ae.h"
|
||||
#include "anet.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
@@ -43,6 +40,7 @@
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "ae.h"
|
||||
#include "zmalloc.h"
|
||||
#include "config.h"
|
||||
|
||||
@@ -62,18 +60,16 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
aeEventLoop *aeCreateEventLoop(int setsize) {
|
||||
aeEventLoop *eventLoop;
|
||||
int i;
|
||||
|
||||
monotonicInit(); /* just in case the calling app didn't initialize */
|
||||
|
||||
if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;
|
||||
eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);
|
||||
eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);
|
||||
if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
|
||||
eventLoop->setsize = setsize;
|
||||
eventLoop->lastTime = time(NULL);
|
||||
eventLoop->timeEventHead = NULL;
|
||||
eventLoop->timeEventNextId = 0;
|
||||
eventLoop->stop = 0;
|
||||
@@ -203,6 +199,29 @@ int aeGetFileEvents(aeEventLoop *eventLoop, int fd) {
|
||||
return fe->mask;
|
||||
}
|
||||
|
||||
static void aeGetTime(long *seconds, long *milliseconds)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
*seconds = tv.tv_sec;
|
||||
*milliseconds = tv.tv_usec/1000;
|
||||
}
|
||||
|
||||
static void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) {
|
||||
long cur_sec, cur_ms, when_sec, when_ms;
|
||||
|
||||
aeGetTime(&cur_sec, &cur_ms);
|
||||
when_sec = cur_sec + milliseconds/1000;
|
||||
when_ms = cur_ms + milliseconds%1000;
|
||||
if (when_ms >= 1000) {
|
||||
when_sec ++;
|
||||
when_ms -= 1000;
|
||||
}
|
||||
*sec = when_sec;
|
||||
*ms = when_ms;
|
||||
}
|
||||
|
||||
long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
|
||||
aeTimeProc *proc, void *clientData,
|
||||
aeEventFinalizerProc *finalizerProc)
|
||||
@@ -213,7 +232,7 @@ long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
|
||||
te = zmalloc(sizeof(*te));
|
||||
if (te == NULL) return AE_ERR;
|
||||
te->id = id;
|
||||
te->when = getMonotonicUs() + milliseconds * 1000;
|
||||
aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
|
||||
te->timeProc = proc;
|
||||
te->finalizerProc = finalizerProc;
|
||||
te->clientData = clientData;
|
||||
@@ -239,8 +258,10 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
|
||||
return AE_ERR; /* NO event with the specified ID found */
|
||||
}
|
||||
|
||||
/* How many microseconds until the first timer should fire.
|
||||
* If there are no timers, -1 is returned.
|
||||
/* Search the first timer to fire.
|
||||
* This operation is useful to know how many time the select can be
|
||||
* put in sleep without to delay any event.
|
||||
* If there are no timers NULL is returned.
|
||||
*
|
||||
* Note that's O(N) since time events are unsorted.
|
||||
* Possible optimizations (not needed by Redis so far, but...):
|
||||
@@ -248,19 +269,19 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
|
||||
* Much better but still insertion or deletion of timers is O(N).
|
||||
* 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).
|
||||
*/
|
||||
static int64_t usUntilEarliestTimer(aeEventLoop *eventLoop) {
|
||||
static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)
|
||||
{
|
||||
aeTimeEvent *te = eventLoop->timeEventHead;
|
||||
if (te == NULL) return -1;
|
||||
aeTimeEvent *nearest = NULL;
|
||||
|
||||
aeTimeEvent *earliest = NULL;
|
||||
while (te) {
|
||||
if (!earliest || te->when < earliest->when)
|
||||
earliest = te;
|
||||
while(te) {
|
||||
if (!nearest || te->when_sec < nearest->when_sec ||
|
||||
(te->when_sec == nearest->when_sec &&
|
||||
te->when_ms < nearest->when_ms))
|
||||
nearest = te;
|
||||
te = te->next;
|
||||
}
|
||||
|
||||
monotime now = getMonotonicUs();
|
||||
return (now >= earliest->when) ? 0 : earliest->when - now;
|
||||
return nearest;
|
||||
}
|
||||
|
||||
/* Process time events */
|
||||
@@ -268,11 +289,29 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
int processed = 0;
|
||||
aeTimeEvent *te;
|
||||
long long maxId;
|
||||
time_t now = time(NULL);
|
||||
|
||||
/* If the system clock is moved to the future, and then set back to the
|
||||
* right value, time events may be delayed in a random way. Often this
|
||||
* means that scheduled operations will not be performed soon enough.
|
||||
*
|
||||
* Here we try to detect system clock skews, and force all the time
|
||||
* events to be processed ASAP when this happens: the idea is that
|
||||
* processing events earlier is less dangerous than delaying them
|
||||
* indefinitely, and practice suggests it is. */
|
||||
if (now < eventLoop->lastTime) {
|
||||
te = eventLoop->timeEventHead;
|
||||
while(te) {
|
||||
te->when_sec = 0;
|
||||
te = te->next;
|
||||
}
|
||||
}
|
||||
eventLoop->lastTime = now;
|
||||
|
||||
te = eventLoop->timeEventHead;
|
||||
maxId = eventLoop->timeEventNextId-1;
|
||||
monotime now = getMonotonicUs();
|
||||
while(te) {
|
||||
long now_sec, now_ms;
|
||||
long long id;
|
||||
|
||||
/* Remove events scheduled for deletion. */
|
||||
@@ -291,10 +330,8 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
eventLoop->timeEventHead = te->next;
|
||||
if (te->next)
|
||||
te->next->prev = te->prev;
|
||||
if (te->finalizerProc) {
|
||||
if (te->finalizerProc)
|
||||
te->finalizerProc(eventLoop, te->clientData);
|
||||
now = getMonotonicUs();
|
||||
}
|
||||
zfree(te);
|
||||
te = next;
|
||||
continue;
|
||||
@@ -309,8 +346,10 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
te = te->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (te->when <= now) {
|
||||
aeGetTime(&now_sec, &now_ms);
|
||||
if (now_sec > te->when_sec ||
|
||||
(now_sec == te->when_sec && now_ms >= te->when_ms))
|
||||
{
|
||||
int retval;
|
||||
|
||||
id = te->id;
|
||||
@@ -318,9 +357,8 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
retval = te->timeProc(eventLoop, id, te->clientData);
|
||||
te->refcount--;
|
||||
processed++;
|
||||
now = getMonotonicUs();
|
||||
if (retval != AE_NOMORE) {
|
||||
te->when = now + retval * 1000;
|
||||
aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
|
||||
} else {
|
||||
te->id = AE_DELETED_EVENT_ID;
|
||||
}
|
||||
@@ -352,23 +390,37 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
/* Nothing to do? return ASAP */
|
||||
if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;
|
||||
|
||||
/* Note that we want to call select() even if there are no
|
||||
/* Note that we want call select() even if there are no
|
||||
* file events to process as long as we want to process time
|
||||
* events, in order to sleep until the next time event is ready
|
||||
* to fire. */
|
||||
if (eventLoop->maxfd != -1 ||
|
||||
((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
|
||||
int j;
|
||||
aeTimeEvent *shortest = NULL;
|
||||
struct timeval tv, *tvp;
|
||||
int64_t usUntilTimer = -1;
|
||||
|
||||
if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
|
||||
usUntilTimer = usUntilEarliestTimer(eventLoop);
|
||||
shortest = aeSearchNearestTimer(eventLoop);
|
||||
if (shortest) {
|
||||
long now_sec, now_ms;
|
||||
|
||||
if (usUntilTimer >= 0) {
|
||||
tv.tv_sec = usUntilTimer / 1000000;
|
||||
tv.tv_usec = usUntilTimer % 1000000;
|
||||
aeGetTime(&now_sec, &now_ms);
|
||||
tvp = &tv;
|
||||
|
||||
/* How many milliseconds we need to wait for the next
|
||||
* time event to fire? */
|
||||
long long ms =
|
||||
(shortest->when_sec - now_sec)*1000 +
|
||||
shortest->when_ms - now_ms;
|
||||
|
||||
if (ms > 0) {
|
||||
tvp->tv_sec = ms/1000;
|
||||
tvp->tv_usec = (ms % 1000)*1000;
|
||||
} else {
|
||||
tvp->tv_sec = 0;
|
||||
tvp->tv_usec = 0;
|
||||
}
|
||||
} else {
|
||||
/* If we have to check for events but need to return
|
||||
* ASAP because of AE_DONT_WAIT we need to set the timeout
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#ifndef __AE_H__
|
||||
#define __AE_H__
|
||||
|
||||
#include "monotonic.h"
|
||||
#include <time.h>
|
||||
|
||||
#define AE_OK 0
|
||||
#define AE_ERR -1
|
||||
@@ -79,7 +79,8 @@ typedef struct aeFileEvent {
|
||||
/* Time event structure */
|
||||
typedef struct aeTimeEvent {
|
||||
long long id; /* time event identifier. */
|
||||
monotime when;
|
||||
long when_sec; /* seconds */
|
||||
long when_ms; /* milliseconds */
|
||||
aeTimeProc *timeProc;
|
||||
aeEventFinalizerProc *finalizerProc;
|
||||
void *clientData;
|
||||
@@ -100,6 +101,7 @@ typedef struct aeEventLoop {
|
||||
int maxfd; /* highest file descriptor currently registered */
|
||||
int setsize; /* max number of file descriptors tracked */
|
||||
long long timeEventNextId;
|
||||
time_t lastTime; /* Used to detect system clock skew */
|
||||
aeFileEvent *events; /* Registered events */
|
||||
aeFiredEvent *fired; /* Fired events */
|
||||
aeTimeEvent *timeEventHead;
|
||||
|
||||
+1
-2
@@ -51,7 +51,6 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
|
||||
zfree(state);
|
||||
return -1;
|
||||
}
|
||||
anetCloexec(state->epfd);
|
||||
eventLoop->apidata = state;
|
||||
return 0;
|
||||
}
|
||||
@@ -111,7 +110,7 @@ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
|
||||
int retval, numevents = 0;
|
||||
|
||||
retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
|
||||
tvp ? (tvp->tv_sec*1000 + (tvp->tv_usec + 999)/1000) : -1);
|
||||
tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);
|
||||
if (retval > 0) {
|
||||
int j;
|
||||
|
||||
|
||||
+3
-6
@@ -67,7 +67,7 @@ static int evport_debug = 0;
|
||||
|
||||
typedef struct aeApiState {
|
||||
int portfd; /* event port */
|
||||
uint_t npending; /* # of pending fds */
|
||||
int npending; /* # of pending fds */
|
||||
int pending_fds[MAX_EVENT_BATCHSZ]; /* pending fds */
|
||||
int pending_masks[MAX_EVENT_BATCHSZ]; /* pending fds' masks */
|
||||
} aeApiState;
|
||||
@@ -82,7 +82,6 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
|
||||
zfree(state);
|
||||
return -1;
|
||||
}
|
||||
anetCloexec(state->portfd);
|
||||
|
||||
state->npending = 0;
|
||||
|
||||
@@ -96,8 +95,6 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
|
||||
}
|
||||
|
||||
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
|
||||
(void) eventLoop;
|
||||
(void) setsize;
|
||||
/* Nothing to resize here. */
|
||||
return 0;
|
||||
}
|
||||
@@ -110,7 +107,7 @@ static void aeApiFree(aeEventLoop *eventLoop) {
|
||||
}
|
||||
|
||||
static int aeApiLookupPending(aeApiState *state, int fd) {
|
||||
uint_t i;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < state->npending; i++) {
|
||||
if (state->pending_fds[i] == fd)
|
||||
@@ -246,7 +243,7 @@ static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {
|
||||
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
struct timespec timeout, *tsp;
|
||||
uint_t mask, i;
|
||||
int mask, i;
|
||||
uint_t nevents;
|
||||
port_event_t event[MAX_EVENT_BATCHSZ];
|
||||
|
||||
|
||||
@@ -74,7 +74,6 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
|
||||
zfree(state);
|
||||
return -1;
|
||||
}
|
||||
anetCloexec(state->kqfd);
|
||||
state->eventsMask = zmalloc(EVENT_MASK_MALLOC_SIZE(eventLoop->setsize));
|
||||
memset(state->eventsMask, 0, EVENT_MASK_MALLOC_SIZE(eventLoop->setsize));
|
||||
eventLoop->apidata = state;
|
||||
|
||||
+122
-70
@@ -69,11 +69,6 @@ int anetSetBlock(char *err, int fd, int non_block) {
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
/* Check if this flag has been set or unset, if so,
|
||||
* then there is no need to call fcntl to set/unset it again. */
|
||||
if (!!(flags & O_NONBLOCK) == !!non_block)
|
||||
return ANET_OK;
|
||||
|
||||
if (non_block)
|
||||
flags |= O_NONBLOCK;
|
||||
else
|
||||
@@ -94,29 +89,6 @@ int anetBlock(char *err, int fd) {
|
||||
return anetSetBlock(err,fd,0);
|
||||
}
|
||||
|
||||
/* Enable the FD_CLOEXEC on the given fd to avoid fd leaks.
|
||||
* This function should be invoked for fd's on specific places
|
||||
* where fork + execve system calls are called. */
|
||||
int anetCloexec(int fd) {
|
||||
int r;
|
||||
int flags;
|
||||
|
||||
do {
|
||||
r = fcntl(fd, F_GETFD);
|
||||
} while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r == -1 || (r & FD_CLOEXEC))
|
||||
return r;
|
||||
|
||||
flags = r | FD_CLOEXEC;
|
||||
|
||||
do {
|
||||
r = fcntl(fd, F_SETFD, flags);
|
||||
} while (r == -1 && errno == EINTR);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Set TCP keep alive option to detect dead peers. The interval option
|
||||
* is only used for Linux as we are using Linux-specific APIs to set
|
||||
* the probe send time, interval, and count. */
|
||||
@@ -186,6 +158,27 @@ int anetDisableTcpNoDelay(char *err, int fd)
|
||||
return anetSetTcpNoDelay(err, fd, 0);
|
||||
}
|
||||
|
||||
|
||||
int anetSetSendBuffer(char *err, int fd, int buffsize)
|
||||
{
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1)
|
||||
{
|
||||
anetSetError(err, "setsockopt SO_SNDBUF: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetTcpKeepAlive(char *err, int fd)
|
||||
{
|
||||
int yes = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) {
|
||||
anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
/* Set the socket send timeout (SO_SNDTIMEO socket option) to the specified
|
||||
* number of milliseconds, or disable it if the 'ms' argument is zero. */
|
||||
int anetSendTimeout(char *err, int fd, long long ms) {
|
||||
@@ -214,13 +207,14 @@ int anetRecvTimeout(char *err, int fd, long long ms) {
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
/* Resolve the hostname "host" and set the string representation of the
|
||||
* IP address into the buffer pointed by "ipbuf".
|
||||
/* anetGenericResolve() is called by anetResolve() and anetResolveIP() to
|
||||
* do the actual work. It resolves the hostname "host" and set the string
|
||||
* representation of the IP address into the buffer pointed by "ipbuf".
|
||||
*
|
||||
* If flags is set to ANET_IP_ONLY the function only resolves hostnames
|
||||
* that are actually already IPv4 or IPv6 addresses. This turns the function
|
||||
* into a validating / normalizing function. */
|
||||
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
|
||||
int anetGenericResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
|
||||
int flags)
|
||||
{
|
||||
struct addrinfo hints, *info;
|
||||
@@ -247,6 +241,14 @@ int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len) {
|
||||
return anetGenericResolve(err,host,ipbuf,ipbuf_len,ANET_NONE);
|
||||
}
|
||||
|
||||
int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len) {
|
||||
return anetGenericResolve(err,host,ipbuf,ipbuf_len,ANET_IP_ONLY);
|
||||
}
|
||||
|
||||
static int anetSetReuseAddr(char *err, int fd) {
|
||||
int yes = 1;
|
||||
/* Make sure connection-intensive things like the redis benchmark
|
||||
@@ -357,11 +359,23 @@ end:
|
||||
}
|
||||
}
|
||||
|
||||
int anetTcpConnect(char *err, const char *addr, int port)
|
||||
{
|
||||
return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONE);
|
||||
}
|
||||
|
||||
int anetTcpNonBlockConnect(char *err, const char *addr, int port)
|
||||
{
|
||||
return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONBLOCK);
|
||||
}
|
||||
|
||||
int anetTcpNonBlockBindConnect(char *err, const char *addr, int port,
|
||||
const char *source_addr)
|
||||
{
|
||||
return anetTcpGenericConnect(err,addr,port,source_addr,
|
||||
ANET_CONNECT_NONBLOCK);
|
||||
}
|
||||
|
||||
int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port,
|
||||
const char *source_addr)
|
||||
{
|
||||
@@ -397,16 +411,53 @@ int anetUnixGenericConnect(char *err, const char *path, int flags)
|
||||
return s;
|
||||
}
|
||||
|
||||
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog, mode_t perm) {
|
||||
int anetUnixConnect(char *err, const char *path)
|
||||
{
|
||||
return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE);
|
||||
}
|
||||
|
||||
int anetUnixNonBlockConnect(char *err, const char *path)
|
||||
{
|
||||
return anetUnixGenericConnect(err,path,ANET_CONNECT_NONBLOCK);
|
||||
}
|
||||
|
||||
/* Like read(2) but make sure 'count' is read before to return
|
||||
* (unless error or EOF condition is encountered) */
|
||||
int anetRead(int fd, char *buf, int count)
|
||||
{
|
||||
ssize_t nread, totlen = 0;
|
||||
while(totlen != count) {
|
||||
nread = read(fd,buf,count-totlen);
|
||||
if (nread == 0) return totlen;
|
||||
if (nread == -1) return -1;
|
||||
totlen += nread;
|
||||
buf += nread;
|
||||
}
|
||||
return totlen;
|
||||
}
|
||||
|
||||
/* Like write(2) but make sure 'count' is written before to return
|
||||
* (unless error is encountered) */
|
||||
int anetWrite(int fd, char *buf, int count)
|
||||
{
|
||||
ssize_t nwritten, totlen = 0;
|
||||
while(totlen != count) {
|
||||
nwritten = write(fd,buf,count-totlen);
|
||||
if (nwritten == 0) return totlen;
|
||||
if (nwritten == -1) return -1;
|
||||
totlen += nwritten;
|
||||
buf += nwritten;
|
||||
}
|
||||
return totlen;
|
||||
}
|
||||
|
||||
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) {
|
||||
if (bind(s,sa,len) == -1) {
|
||||
anetSetError(err, "bind: %s", strerror(errno));
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
if (sa->sa_family == AF_LOCAL && perm)
|
||||
chmod(((struct sockaddr_un *) sa)->sun_path, perm);
|
||||
|
||||
if (listen(s, backlog) == -1) {
|
||||
anetSetError(err, "listen: %s", strerror(errno));
|
||||
close(s);
|
||||
@@ -419,6 +470,7 @@ static int anetV6Only(char *err, int s) {
|
||||
int yes = 1;
|
||||
if (setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,&yes,sizeof(yes)) == -1) {
|
||||
anetSetError(err, "setsockopt: %s", strerror(errno));
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
@@ -435,10 +487,6 @@ static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backl
|
||||
hints.ai_family = af;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE; /* No effect if bindaddr != NULL */
|
||||
if (bindaddr && !strcmp("*", bindaddr))
|
||||
bindaddr = NULL;
|
||||
if (af == AF_INET6 && bindaddr && !strcmp("::*", bindaddr))
|
||||
bindaddr = NULL;
|
||||
|
||||
if ((rv = getaddrinfo(bindaddr,_port,&hints,&servinfo)) != 0) {
|
||||
anetSetError(err, "%s", gai_strerror(rv));
|
||||
@@ -450,7 +498,7 @@ static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backl
|
||||
|
||||
if (af == AF_INET6 && anetV6Only(err,s) == ANET_ERR) goto error;
|
||||
if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;
|
||||
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog,0) == ANET_ERR) s = ANET_ERR;
|
||||
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog) == ANET_ERR) s = ANET_ERR;
|
||||
goto end;
|
||||
}
|
||||
if (p == NULL) {
|
||||
@@ -487,8 +535,10 @@ int anetUnixServer(char *err, char *path, mode_t perm, int backlog)
|
||||
memset(&sa,0,sizeof(sa));
|
||||
sa.sun_family = AF_LOCAL;
|
||||
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
|
||||
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog,perm) == ANET_ERR)
|
||||
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog) == ANET_ERR)
|
||||
return ANET_ERR;
|
||||
if (perm)
|
||||
chmod(sa.sun_path, perm);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -538,15 +588,11 @@ int anetUnixAccept(char *err, int s) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
int anetFdToString(int fd, char *ip, size_t ip_len, int *port, int fd_to_str_type) {
|
||||
int anetPeerToString(int fd, char *ip, size_t ip_len, int *port) {
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t salen = sizeof(sa);
|
||||
|
||||
if (fd_to_str_type == FD_TO_PEER_NAME) {
|
||||
if (getpeername(fd, (struct sockaddr *)&sa, &salen) == -1) goto error;
|
||||
} else {
|
||||
if (getsockname(fd, (struct sockaddr *)&sa, &salen) == -1) goto error;
|
||||
}
|
||||
if (getpeername(fd,(struct sockaddr*)&sa,&salen) == -1) goto error;
|
||||
if (ip_len == 0) goto error;
|
||||
|
||||
if (sa.ss_family == AF_INET) {
|
||||
@@ -558,7 +604,7 @@ int anetFdToString(int fd, char *ip, size_t ip_len, int *port, int fd_to_str_typ
|
||||
if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len);
|
||||
if (port) *port = ntohs(s->sin6_port);
|
||||
} else if (sa.ss_family == AF_UNIX) {
|
||||
if (ip) snprintf(ip, ip_len, "/unixsocket");
|
||||
if (ip) strncpy(ip,"/unixsocket",ip_len);
|
||||
if (port) *port = 0;
|
||||
} else {
|
||||
goto error;
|
||||
@@ -586,35 +632,41 @@ int anetFormatAddr(char *buf, size_t buf_len, char *ip, int port) {
|
||||
"[%s]:%d" : "%s:%d", ip, port);
|
||||
}
|
||||
|
||||
/* Like anetFormatAddr() but extract ip and port from the socket's peer/sockname. */
|
||||
int anetFormatFdAddr(int fd, char *buf, size_t buf_len, int fd_to_str_type) {
|
||||
/* Like anetFormatAddr() but extract ip and port from the socket's peer. */
|
||||
int anetFormatPeer(int fd, char *buf, size_t buf_len) {
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
int port;
|
||||
|
||||
anetFdToString(fd,ip,sizeof(ip),&port,fd_to_str_type);
|
||||
anetPeerToString(fd,ip,sizeof(ip),&port);
|
||||
return anetFormatAddr(buf, buf_len, ip, port);
|
||||
}
|
||||
|
||||
/* This function must be called after accept4() fails. It returns 1 if 'err'
|
||||
* indicates accepted connection faced an error, and it's okay to continue
|
||||
* accepting next connection by calling accept4() again. Other errors either
|
||||
* indicate programming errors, e.g. calling accept() on a closed fd or indicate
|
||||
* a resource limit has been reached, e.g. -EMFILE, open fd limit has been
|
||||
* reached. In the latter case, caller might wait until resources are available.
|
||||
* See accept4() documentation for details. */
|
||||
int anetAcceptFailureNeedsRetry(int err) {
|
||||
if (err == ECONNABORTED)
|
||||
return 1;
|
||||
int anetSockName(int fd, char *ip, size_t ip_len, int *port) {
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t salen = sizeof(sa);
|
||||
|
||||
#if defined(__linux__)
|
||||
/* For details, see 'Error Handling' section on
|
||||
* https://man7.org/linux/man-pages/man2/accept.2.html */
|
||||
if (err == ENETDOWN || err == EPROTO || err == ENOPROTOOPT ||
|
||||
err == EHOSTDOWN || err == ENONET || err == EHOSTUNREACH ||
|
||||
err == EOPNOTSUPP || err == ENETUNREACH)
|
||||
{
|
||||
return 1;
|
||||
if (getsockname(fd,(struct sockaddr*)&sa,&salen) == -1) {
|
||||
if (port) *port = 0;
|
||||
ip[0] = '?';
|
||||
ip[1] = '\0';
|
||||
return -1;
|
||||
}
|
||||
if (sa.ss_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&sa;
|
||||
if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len);
|
||||
if (port) *port = ntohs(s->sin_port);
|
||||
} else {
|
||||
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;
|
||||
if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len);
|
||||
if (port) *port = ntohs(s->sin6_port);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int anetFormatSock(int fd, char *fmt, size_t fmt_len) {
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
int port;
|
||||
|
||||
anetSockName(fd,ip,sizeof(ip),&port);
|
||||
return anetFormatAddr(fmt, fmt_len, ip, port);
|
||||
}
|
||||
|
||||
+14
-9
@@ -49,28 +49,33 @@
|
||||
#undef ip_len
|
||||
#endif
|
||||
|
||||
/* FD to address string conversion types */
|
||||
#define FD_TO_PEER_NAME 0
|
||||
#define FD_TO_SOCK_NAME 1
|
||||
|
||||
int anetTcpConnect(char *err, const char *addr, int port);
|
||||
int anetTcpNonBlockConnect(char *err, const char *addr, int port);
|
||||
int anetTcpNonBlockBindConnect(char *err, const char *addr, int port, const char *source_addr);
|
||||
int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port, const char *source_addr);
|
||||
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len, int flags);
|
||||
int anetUnixConnect(char *err, const char *path);
|
||||
int anetUnixNonBlockConnect(char *err, const char *path);
|
||||
int anetRead(int fd, char *buf, int count);
|
||||
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len);
|
||||
int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len);
|
||||
int anetTcpServer(char *err, int port, char *bindaddr, int backlog);
|
||||
int anetTcp6Server(char *err, int port, char *bindaddr, int backlog);
|
||||
int anetUnixServer(char *err, char *path, mode_t perm, int backlog);
|
||||
int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port);
|
||||
int anetUnixAccept(char *err, int serversock);
|
||||
int anetWrite(int fd, char *buf, int count);
|
||||
int anetNonBlock(char *err, int fd);
|
||||
int anetBlock(char *err, int fd);
|
||||
int anetCloexec(int fd);
|
||||
int anetEnableTcpNoDelay(char *err, int fd);
|
||||
int anetDisableTcpNoDelay(char *err, int fd);
|
||||
int anetTcpKeepAlive(char *err, int fd);
|
||||
int anetSendTimeout(char *err, int fd, long long ms);
|
||||
int anetRecvTimeout(char *err, int fd, long long ms);
|
||||
int anetFdToString(int fd, char *ip, size_t ip_len, int *port, int fd_to_str_type);
|
||||
int anetPeerToString(int fd, char *ip, size_t ip_len, int *port);
|
||||
int anetKeepAlive(char *err, int fd, int interval);
|
||||
int anetSockName(int fd, char *ip, size_t ip_len, int *port);
|
||||
int anetFormatAddr(char *fmt, size_t fmt_len, char *ip, int port);
|
||||
int anetFormatFdAddr(int fd, char *buf, size_t buf_len, int fd_to_str_type);
|
||||
int anetAcceptFailureNeedsRetry(int err);
|
||||
int anetFormatPeer(int fd, char *fmt, size_t fmt_len);
|
||||
int anetFormatSock(int fd, char *fmt, size_t fmt_len);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -208,27 +208,29 @@ int aofFsyncInProgress(void) {
|
||||
/* Starts a background task that performs fsync() against the specified
|
||||
* file descriptor (the one of the AOF file) in another thread. */
|
||||
void aof_background_fsync(int fd) {
|
||||
bioCreateFsyncJob(fd);
|
||||
bioCreateBackgroundJob(BIO_AOF_FSYNC,(void*)(long)fd,NULL,NULL);
|
||||
}
|
||||
|
||||
/* Kills an AOFRW child process if exists */
|
||||
void killAppendOnlyChild(void) {
|
||||
int statloc;
|
||||
/* No AOFRW child? return. */
|
||||
if (server.child_type != CHILD_TYPE_AOF) return;
|
||||
if (server.aof_child_pid == -1) return;
|
||||
/* Kill AOFRW child, wait for child exit. */
|
||||
serverLog(LL_NOTICE,"Killing running AOF rewrite child: %ld",
|
||||
(long) server.child_pid);
|
||||
if (kill(server.child_pid,SIGUSR1) != -1) {
|
||||
while(waitpid(-1, &statloc, 0) != server.child_pid);
|
||||
(long) server.aof_child_pid);
|
||||
if (kill(server.aof_child_pid,SIGUSR1) != -1) {
|
||||
while(wait3(&statloc,0,NULL) != server.aof_child_pid);
|
||||
}
|
||||
/* Reset the buffer accumulating changes while the child saves. */
|
||||
aofRewriteBufferReset();
|
||||
aofRemoveTempFile(server.child_pid);
|
||||
resetChildState();
|
||||
aofRemoveTempFile(server.aof_child_pid);
|
||||
server.aof_child_pid = -1;
|
||||
server.aof_rewrite_time_start = -1;
|
||||
/* Close pipes used for IPC between the two processes. */
|
||||
aofClosePipes();
|
||||
closeChildInfoPipe();
|
||||
updateDictResizePolicy();
|
||||
}
|
||||
|
||||
/* Called when the user switches from "appendonly yes" to "appendonly no"
|
||||
@@ -236,12 +238,7 @@ void killAppendOnlyChild(void) {
|
||||
void stopAppendOnly(void) {
|
||||
serverAssert(server.aof_state != AOF_OFF);
|
||||
flushAppendOnlyFile(1);
|
||||
if (redis_fsync(server.aof_fd) == -1) {
|
||||
serverLog(LL_WARNING,"Fail to fsync the AOF file: %s",strerror(errno));
|
||||
} else {
|
||||
server.aof_fsync_offset = server.aof_current_size;
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
}
|
||||
redis_fsync(server.aof_fd);
|
||||
close(server.aof_fd);
|
||||
|
||||
server.aof_fd = -1;
|
||||
@@ -249,8 +246,6 @@ void stopAppendOnly(void) {
|
||||
server.aof_state = AOF_OFF;
|
||||
server.aof_rewrite_scheduled = 0;
|
||||
killAppendOnlyChild();
|
||||
sdsfree(server.aof_buf);
|
||||
server.aof_buf = sdsempty();
|
||||
}
|
||||
|
||||
/* Called when the user switches from "appendonly no" to "appendonly yes"
|
||||
@@ -272,14 +267,14 @@ int startAppendOnly(void) {
|
||||
strerror(errno));
|
||||
return C_ERR;
|
||||
}
|
||||
if (hasActiveChildProcess() && server.child_type != CHILD_TYPE_AOF) {
|
||||
if (hasActiveChildProcess() && server.aof_child_pid == -1) {
|
||||
server.aof_rewrite_scheduled = 1;
|
||||
serverLog(LL_WARNING,"AOF was enabled but there is already another background operation. An AOF background was scheduled to start when possible.");
|
||||
} else {
|
||||
/* If there is a pending AOF rewrite, we need to switch it off and
|
||||
* start a new one: the old one cannot be reused because it is not
|
||||
* accumulating the AOF buffer. */
|
||||
if (server.child_type == CHILD_TYPE_AOF) {
|
||||
if (server.aof_child_pid != -1) {
|
||||
serverLog(LL_WARNING,"AOF was enabled but there is already an AOF rewriting in background. Stopping background AOF and starting a rewrite now.");
|
||||
killAppendOnlyChild();
|
||||
}
|
||||
@@ -294,21 +289,6 @@ int startAppendOnly(void) {
|
||||
server.aof_state = AOF_WAIT_REWRITE;
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
server.aof_fd = newfd;
|
||||
|
||||
/* If AOF fsync error in bio job, we just ignore it and log the event. */
|
||||
int aof_bio_fsync_status;
|
||||
atomicGet(server.aof_bio_fsync_status, aof_bio_fsync_status);
|
||||
if (aof_bio_fsync_status == C_ERR) {
|
||||
serverLog(LL_WARNING,
|
||||
"AOF reopen, just ignore the AOF fsync error in bio job");
|
||||
atomicSet(server.aof_bio_fsync_status,C_OK);
|
||||
}
|
||||
|
||||
/* If AOF was in error state, we just ignore it and log the event. */
|
||||
if (server.aof_last_write_status == C_ERR) {
|
||||
serverLog(LL_WARNING,"AOF reopen, just ignore the last error.");
|
||||
server.aof_last_write_status = C_OK;
|
||||
}
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
@@ -475,11 +455,10 @@ void flushAppendOnlyFile(int force) {
|
||||
|
||||
/* Handle the AOF write error. */
|
||||
if (server.aof_fsync == AOF_FSYNC_ALWAYS) {
|
||||
/* We can't recover when the fsync policy is ALWAYS since the reply
|
||||
* for the client is already in the output buffers (both writes and
|
||||
* reads), and the changes to the db can't be rolled back. Since we
|
||||
* have a contract with the user that on acknowledged or observed
|
||||
* writes are is synced on disk, we must exit. */
|
||||
/* We can't recover when the fsync policy is ALWAYS since the
|
||||
* reply for the client is already in the output buffers, and we
|
||||
* have the contract with the user that on acknowledged write data
|
||||
* is synced on disk. */
|
||||
serverLog(LL_WARNING,"Can't recover from AOF write error when the AOF fsync policy is 'always'. Exiting...");
|
||||
exit(1);
|
||||
} else {
|
||||
@@ -527,14 +506,7 @@ try_fsync:
|
||||
/* redis_fsync is defined as fdatasync() for Linux in order to avoid
|
||||
* flushing metadata. */
|
||||
latencyStartMonitor(latency);
|
||||
/* Let's try to get this data on the disk. To guarantee data safe when
|
||||
* the AOF fsync policy is 'always', we should exit if failed to fsync
|
||||
* AOF (see comment next to the exit(1) after write error above). */
|
||||
if (redis_fsync(server.aof_fd) == -1) {
|
||||
serverLog(LL_WARNING,"Can't persist AOF for fsync error when the "
|
||||
"AOF fsync policy is 'always': %s. Exiting...", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
redis_fsync(server.aof_fd); /* Let's try to get this data on the disk */
|
||||
latencyEndMonitor(latency);
|
||||
latencyAddSampleIfNeeded("aof-fsync-always",latency);
|
||||
server.aof_fsync_offset = server.aof_current_size;
|
||||
@@ -602,16 +574,19 @@ sds catAppendOnlyExpireAtCommand(sds buf, struct redisCommand *cmd, robj *key, r
|
||||
}
|
||||
decrRefCount(seconds);
|
||||
|
||||
argv[0] = shared.pexpireat;
|
||||
argv[0] = createStringObject("PEXPIREAT",9);
|
||||
argv[1] = key;
|
||||
argv[2] = createStringObjectFromLongLong(when);
|
||||
buf = catAppendOnlyGenericCommand(buf, 3, argv);
|
||||
decrRefCount(argv[0]);
|
||||
decrRefCount(argv[2]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) {
|
||||
sds buf = sdsempty();
|
||||
robj *tmpargv[3];
|
||||
|
||||
/* The DB this command was targeting is not the same as the last command
|
||||
* we appended. To issue a SELECT command is needed. */
|
||||
if (dictid != server.aof_selected_db) {
|
||||
@@ -627,31 +602,32 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
|
||||
cmd->proc == expireatCommand) {
|
||||
/* Translate EXPIRE/PEXPIRE/EXPIREAT into PEXPIREAT */
|
||||
buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);
|
||||
} else if (cmd->proc == setexCommand || cmd->proc == psetexCommand) {
|
||||
/* Translate SETEX/PSETEX to SET and PEXPIREAT */
|
||||
tmpargv[0] = createStringObject("SET",3);
|
||||
tmpargv[1] = argv[1];
|
||||
tmpargv[2] = argv[3];
|
||||
buf = catAppendOnlyGenericCommand(buf,3,tmpargv);
|
||||
decrRefCount(tmpargv[0]);
|
||||
buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);
|
||||
} else if (cmd->proc == setCommand && argc > 3) {
|
||||
robj *pxarg = NULL;
|
||||
/* When SET is used with EX/PX argument setGenericCommand propagates them with PX millisecond argument.
|
||||
* So since the command arguments are re-written there, we can rely here on the index of PX being 3. */
|
||||
if (!strcasecmp(argv[3]->ptr, "px")) {
|
||||
pxarg = argv[4];
|
||||
int i;
|
||||
robj *exarg = NULL, *pxarg = NULL;
|
||||
for (i = 3; i < argc; i ++) {
|
||||
if (!strcasecmp(argv[i]->ptr, "ex")) exarg = argv[i+1];
|
||||
if (!strcasecmp(argv[i]->ptr, "px")) pxarg = argv[i+1];
|
||||
}
|
||||
/* For AOF we convert SET key value relative time in milliseconds to SET key value absolute time in
|
||||
* millisecond. Whenever the condition is true it implies that original SET has been transformed
|
||||
* to SET PX with millisecond time argument so we do not need to worry about unit here.*/
|
||||
if (pxarg) {
|
||||
robj *millisecond = getDecodedObject(pxarg);
|
||||
long long when = strtoll(millisecond->ptr,NULL,10);
|
||||
when += mstime();
|
||||
serverAssert(!(exarg && pxarg));
|
||||
|
||||
decrRefCount(millisecond);
|
||||
|
||||
robj *newargs[5];
|
||||
newargs[0] = argv[0];
|
||||
newargs[1] = argv[1];
|
||||
newargs[2] = argv[2];
|
||||
newargs[3] = shared.pxat;
|
||||
newargs[4] = createStringObjectFromLongLong(when);
|
||||
buf = catAppendOnlyGenericCommand(buf,5,newargs);
|
||||
decrRefCount(newargs[4]);
|
||||
if (exarg || pxarg) {
|
||||
/* Translate SET [EX seconds][PX milliseconds] to SET and PEXPIREAT */
|
||||
buf = catAppendOnlyGenericCommand(buf,3,argv);
|
||||
if (exarg)
|
||||
buf = catAppendOnlyExpireAtCommand(buf,server.expireCommand,argv[1],
|
||||
exarg);
|
||||
if (pxarg)
|
||||
buf = catAppendOnlyExpireAtCommand(buf,server.pexpireCommand,argv[1],
|
||||
pxarg);
|
||||
} else {
|
||||
buf = catAppendOnlyGenericCommand(buf,argc,argv);
|
||||
}
|
||||
@@ -672,7 +648,7 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
|
||||
* accumulate the differences between the child DB and the current one
|
||||
* in a buffer, so that when the child process will do its work we
|
||||
* can append the differences to the new append only file. */
|
||||
if (server.child_type == CHILD_TYPE_AOF)
|
||||
if (server.aof_child_pid != -1)
|
||||
aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));
|
||||
|
||||
sdsfree(buf);
|
||||
@@ -695,22 +671,9 @@ struct client *createAOFClient(void) {
|
||||
c->querybuf_peak = 0;
|
||||
c->argc = 0;
|
||||
c->argv = NULL;
|
||||
c->original_argc = 0;
|
||||
c->original_argv = NULL;
|
||||
c->argv_len_sum = 0;
|
||||
c->bufpos = 0;
|
||||
|
||||
/*
|
||||
* The AOF client should never be blocked (unlike master
|
||||
* replication connection).
|
||||
* This is because blocking the AOF client might cause
|
||||
* deadlock (because potentially no one will unblock it).
|
||||
* Also, if the AOF client will be blocked just for
|
||||
* background processing there is a chance that the
|
||||
* command execution order will be violated.
|
||||
*/
|
||||
c->flags = CLIENT_DENY_BLOCKING;
|
||||
|
||||
c->flags = 0;
|
||||
c->btype = BLOCKED_NONE;
|
||||
/* We set the fake client as a slave waiting for the synchronization
|
||||
* so that Redis will not try to send replies to this client. */
|
||||
@@ -720,7 +683,6 @@ struct client *createAOFClient(void) {
|
||||
c->obuf_soft_limit_reached_time = 0;
|
||||
c->watched_keys = listCreate();
|
||||
c->peerid = NULL;
|
||||
c->sockname = NULL;
|
||||
c->resp = 2;
|
||||
c->user = NULL;
|
||||
listSetFreeMethod(c->reply,freeClientReplyValue);
|
||||
@@ -743,7 +705,6 @@ void freeFakeClient(struct client *c) {
|
||||
listRelease(c->reply);
|
||||
listRelease(c->watched_keys);
|
||||
freeClientMultiState(c);
|
||||
freeClientOriginalArgv(c);
|
||||
zfree(c);
|
||||
}
|
||||
|
||||
@@ -901,7 +862,7 @@ int loadAppendOnlyFile(char *filename) {
|
||||
fakeClient->cmd = NULL;
|
||||
if (server.aof_load_truncated) valid_up_to = ftello(fp);
|
||||
if (server.key_load_delay)
|
||||
debugDelay(server.key_load_delay);
|
||||
usleep(server.key_load_delay);
|
||||
}
|
||||
|
||||
/* This point can only be reached when EOF is reached without errors.
|
||||
@@ -1002,25 +963,15 @@ int rewriteListObject(rio *r, robj *key, robj *o) {
|
||||
if (count == 0) {
|
||||
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
|
||||
AOF_REWRITE_ITEMS_PER_CMD : items;
|
||||
if (!rioWriteBulkCount(r,'*',2+cmd_items) ||
|
||||
!rioWriteBulkString(r,"RPUSH",5) ||
|
||||
!rioWriteBulkObject(r,key))
|
||||
{
|
||||
quicklistReleaseIterator(li);
|
||||
return 0;
|
||||
}
|
||||
if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0;
|
||||
if (rioWriteBulkString(r,"RPUSH",5) == 0) return 0;
|
||||
if (rioWriteBulkObject(r,key) == 0) return 0;
|
||||
}
|
||||
|
||||
if (entry.value) {
|
||||
if (!rioWriteBulkString(r,(char*)entry.value,entry.sz)) {
|
||||
quicklistReleaseIterator(li);
|
||||
return 0;
|
||||
}
|
||||
if (rioWriteBulkString(r,(char*)entry.value,entry.sz) == 0) return 0;
|
||||
} else {
|
||||
if (!rioWriteBulkLongLong(r,entry.longval)) {
|
||||
quicklistReleaseIterator(li);
|
||||
return 0;
|
||||
}
|
||||
if (rioWriteBulkLongLong(r,entry.longval) == 0) return 0;
|
||||
}
|
||||
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
|
||||
items--;
|
||||
@@ -1046,14 +997,11 @@ int rewriteSetObject(rio *r, robj *key, robj *o) {
|
||||
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
|
||||
AOF_REWRITE_ITEMS_PER_CMD : items;
|
||||
|
||||
if (!rioWriteBulkCount(r,'*',2+cmd_items) ||
|
||||
!rioWriteBulkString(r,"SADD",4) ||
|
||||
!rioWriteBulkObject(r,key))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0;
|
||||
if (rioWriteBulkString(r,"SADD",4) == 0) return 0;
|
||||
if (rioWriteBulkObject(r,key) == 0) return 0;
|
||||
}
|
||||
if (!rioWriteBulkLongLong(r,llval)) return 0;
|
||||
if (rioWriteBulkLongLong(r,llval) == 0) return 0;
|
||||
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
|
||||
items--;
|
||||
}
|
||||
@@ -1067,18 +1015,11 @@ int rewriteSetObject(rio *r, robj *key, robj *o) {
|
||||
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
|
||||
AOF_REWRITE_ITEMS_PER_CMD : items;
|
||||
|
||||
if (!rioWriteBulkCount(r,'*',2+cmd_items) ||
|
||||
!rioWriteBulkString(r,"SADD",4) ||
|
||||
!rioWriteBulkObject(r,key))
|
||||
{
|
||||
dictReleaseIterator(di);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!rioWriteBulkString(r,ele,sdslen(ele))) {
|
||||
dictReleaseIterator(di);
|
||||
return 0;
|
||||
if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0;
|
||||
if (rioWriteBulkString(r,"SADD",4) == 0) return 0;
|
||||
if (rioWriteBulkObject(r,key) == 0) return 0;
|
||||
}
|
||||
if (rioWriteBulkString(r,ele,sdslen(ele)) == 0) return 0;
|
||||
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
|
||||
items--;
|
||||
}
|
||||
@@ -1115,18 +1056,15 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
|
||||
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
|
||||
AOF_REWRITE_ITEMS_PER_CMD : items;
|
||||
|
||||
if (!rioWriteBulkCount(r,'*',2+cmd_items*2) ||
|
||||
!rioWriteBulkString(r,"ZADD",4) ||
|
||||
!rioWriteBulkObject(r,key))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0;
|
||||
if (rioWriteBulkString(r,"ZADD",4) == 0) return 0;
|
||||
if (rioWriteBulkObject(r,key) == 0) return 0;
|
||||
}
|
||||
if (!rioWriteBulkDouble(r,score)) return 0;
|
||||
if (rioWriteBulkDouble(r,score) == 0) return 0;
|
||||
if (vstr != NULL) {
|
||||
if (!rioWriteBulkString(r,(char*)vstr,vlen)) return 0;
|
||||
if (rioWriteBulkString(r,(char*)vstr,vlen) == 0) return 0;
|
||||
} else {
|
||||
if (!rioWriteBulkLongLong(r,vll)) return 0;
|
||||
if (rioWriteBulkLongLong(r,vll) == 0) return 0;
|
||||
}
|
||||
zzlNext(zl,&eptr,&sptr);
|
||||
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
|
||||
@@ -1145,20 +1083,12 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
|
||||
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
|
||||
AOF_REWRITE_ITEMS_PER_CMD : items;
|
||||
|
||||
if (!rioWriteBulkCount(r,'*',2+cmd_items*2) ||
|
||||
!rioWriteBulkString(r,"ZADD",4) ||
|
||||
!rioWriteBulkObject(r,key))
|
||||
{
|
||||
dictReleaseIterator(di);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!rioWriteBulkDouble(r,*score) ||
|
||||
!rioWriteBulkString(r,ele,sdslen(ele)))
|
||||
{
|
||||
dictReleaseIterator(di);
|
||||
return 0;
|
||||
if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0;
|
||||
if (rioWriteBulkString(r,"ZADD",4) == 0) return 0;
|
||||
if (rioWriteBulkObject(r,key) == 0) return 0;
|
||||
}
|
||||
if (rioWriteBulkDouble(r,*score) == 0) return 0;
|
||||
if (rioWriteBulkString(r,ele,sdslen(ele)) == 0) return 0;
|
||||
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
|
||||
items--;
|
||||
}
|
||||
@@ -1207,21 +1137,13 @@ int rewriteHashObject(rio *r, robj *key, robj *o) {
|
||||
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
|
||||
AOF_REWRITE_ITEMS_PER_CMD : items;
|
||||
|
||||
if (!rioWriteBulkCount(r,'*',2+cmd_items*2) ||
|
||||
!rioWriteBulkString(r,"HMSET",5) ||
|
||||
!rioWriteBulkObject(r,key))
|
||||
{
|
||||
hashTypeReleaseIterator(hi);
|
||||
return 0;
|
||||
}
|
||||
if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0;
|
||||
if (rioWriteBulkString(r,"HMSET",5) == 0) return 0;
|
||||
if (rioWriteBulkObject(r,key) == 0) return 0;
|
||||
}
|
||||
|
||||
if (!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY) ||
|
||||
!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_VALUE))
|
||||
{
|
||||
hashTypeReleaseIterator(hi);
|
||||
return 0;
|
||||
}
|
||||
if (rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY) == 0) return 0;
|
||||
if (rioWriteHashIteratorCursor(r, hi, OBJ_HASH_VALUE) == 0) return 0;
|
||||
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
|
||||
items--;
|
||||
}
|
||||
@@ -1267,20 +1189,6 @@ int rioWriteStreamPendingEntry(rio *r, robj *key, const char *groupname, size_t
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Helper for rewriteStreamObject(): emit the XGROUP CREATECONSUMER is
|
||||
* needed in order to create consumers that do not have any pending entries.
|
||||
* All this in the context of the specified key and group. */
|
||||
int rioWriteStreamEmptyConsumer(rio *r, robj *key, const char *groupname, size_t groupname_len, streamConsumer *consumer) {
|
||||
/* XGROUP CREATECONSUMER <key> <group> <consumer> */
|
||||
if (rioWriteBulkCount(r,'*',5) == 0) return 0;
|
||||
if (rioWriteBulkString(r,"XGROUP",6) == 0) return 0;
|
||||
if (rioWriteBulkString(r,"CREATECONSUMER",14) == 0) return 0;
|
||||
if (rioWriteBulkObject(r,key) == 0) return 0;
|
||||
if (rioWriteBulkString(r,groupname,groupname_len) == 0) return 0;
|
||||
if (rioWriteBulkString(r,consumer->name,sdslen(consumer->name)) == 0) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Emit the commands needed to rebuild a stream object.
|
||||
* The function returns 0 on error, 1 on success. */
|
||||
int rewriteStreamObject(rio *r, robj *key, robj *o) {
|
||||
@@ -1369,25 +1277,13 @@ int rewriteStreamObject(rio *r, robj *key, robj *o) {
|
||||
}
|
||||
|
||||
/* Generate XCLAIMs for each consumer that happens to
|
||||
* have pending entries. Empty consumers would be generated with
|
||||
* XGROUP CREATECONSUMER. */
|
||||
* have pending entries. Empty consumers have no semantical
|
||||
* value so they are discarded. */
|
||||
raxIterator ri_cons;
|
||||
raxStart(&ri_cons,group->consumers);
|
||||
raxSeek(&ri_cons,"^",NULL,0);
|
||||
while(raxNext(&ri_cons)) {
|
||||
streamConsumer *consumer = ri_cons.data;
|
||||
/* If there are no pending entries, just emit XGROUP CREATECONSUMER */
|
||||
if (raxSize(consumer->pel) == 0) {
|
||||
if (rioWriteStreamEmptyConsumer(r,key,(char*)ri.key,
|
||||
ri.key_len,consumer) == 0)
|
||||
{
|
||||
raxStop(&ri_cons);
|
||||
raxStop(&ri);
|
||||
streamIteratorStop(&si);
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* For the current consumer, iterate all the PEL entries
|
||||
* to emit the XCLAIM protocol. */
|
||||
raxIterator ri_pel;
|
||||
@@ -1453,8 +1349,6 @@ int rewriteAppendOnlyFileRio(rio *aof) {
|
||||
dictEntry *de;
|
||||
size_t processed = 0;
|
||||
int j;
|
||||
long key_count = 0;
|
||||
long long updated_time = 0;
|
||||
|
||||
for (j = 0; j < server.dbnum; j++) {
|
||||
char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";
|
||||
@@ -1514,17 +1408,6 @@ int rewriteAppendOnlyFileRio(rio *aof) {
|
||||
processed = aof->processed_bytes;
|
||||
aofReadDiffFromParent();
|
||||
}
|
||||
|
||||
/* Update info every 1 second (approximately).
|
||||
* in order to avoid calling mstime() on each iteration, we will
|
||||
* check the diff every 1024 keys */
|
||||
if ((key_count++ & 1023) == 0) {
|
||||
long long now = mstime();
|
||||
if (now - updated_time >= 1000) {
|
||||
sendChildInfo(CHILD_INFO_TYPE_CURRENT_INFO, key_count, "AOF rewrite");
|
||||
updated_time = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
di = NULL;
|
||||
@@ -1604,7 +1487,7 @@ int rewriteAppendOnlyFile(char *filename) {
|
||||
if (write(server.aof_pipe_write_ack_to_parent,"!",1) != 1) goto werr;
|
||||
if (anetNonBlock(NULL,server.aof_pipe_read_ack_from_parent) != ANET_OK)
|
||||
goto werr;
|
||||
/* We read the ACK from the server using a 5 seconds timeout. Normally
|
||||
/* We read the ACK from the server using a 10 seconds timeout. Normally
|
||||
* it should reply ASAP, but just in case we lose its reply, we are sure
|
||||
* the child will eventually get terminated. */
|
||||
if (syncRead(server.aof_pipe_read_ack_from_parent,&byte,1,5000) != 1 ||
|
||||
@@ -1618,31 +1501,8 @@ int rewriteAppendOnlyFile(char *filename) {
|
||||
serverLog(LL_NOTICE,
|
||||
"Concatenating %.2f MB of AOF diff received from parent.",
|
||||
(double) sdslen(server.aof_child_diff) / (1024*1024));
|
||||
|
||||
/* Now we write the entire AOF buffer we received from the parent
|
||||
* via the pipe during the life of this fork child.
|
||||
* once a second, we'll take a break and send updated COW info to the parent */
|
||||
size_t bytes_to_write = sdslen(server.aof_child_diff);
|
||||
const char *buf = server.aof_child_diff;
|
||||
long long cow_updated_time = mstime();
|
||||
long long key_count = dbTotalServerKeyCount();
|
||||
while (bytes_to_write) {
|
||||
/* We write the AOF buffer in chunk of 8MB so that we can check the time in between them */
|
||||
size_t chunk_size = bytes_to_write < (8<<20) ? bytes_to_write : (8<<20);
|
||||
|
||||
if (rioWrite(&aof,buf,chunk_size) == 0)
|
||||
goto werr;
|
||||
|
||||
bytes_to_write -= chunk_size;
|
||||
buf += chunk_size;
|
||||
|
||||
/* Update COW info */
|
||||
long long now = mstime();
|
||||
if (now - cow_updated_time >= 1000) {
|
||||
sendChildInfo(CHILD_INFO_TYPE_CURRENT_INFO, key_count, "AOF rewrite");
|
||||
cow_updated_time = now;
|
||||
}
|
||||
}
|
||||
if (rioWrite(&aof,server.aof_child_diff,sdslen(server.aof_child_diff)) == 0)
|
||||
goto werr;
|
||||
|
||||
/* Make sure data will not remain on the OS's output buffers */
|
||||
if (fflush(fp)) goto werr;
|
||||
@@ -1765,6 +1625,7 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
|
||||
if (hasActiveChildProcess()) return C_ERR;
|
||||
if (aofCreatePipes() != C_OK) return C_ERR;
|
||||
openChildInfoPipe();
|
||||
if ((childpid = redisFork(CHILD_TYPE_AOF)) == 0) {
|
||||
char tmpfile[256];
|
||||
|
||||
@@ -1773,7 +1634,7 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
redisSetCpuAffinity(server.aof_rewrite_cpulist);
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
|
||||
if (rewriteAppendOnlyFile(tmpfile) == C_OK) {
|
||||
sendChildCowInfo(CHILD_INFO_TYPE_AOF_COW_SIZE, "AOF rewrite");
|
||||
sendChildCOWInfo(CHILD_TYPE_AOF, "AOF rewrite");
|
||||
exitFromChild(0);
|
||||
} else {
|
||||
exitFromChild(1);
|
||||
@@ -1781,6 +1642,7 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
} else {
|
||||
/* Parent */
|
||||
if (childpid == -1) {
|
||||
closeChildInfoPipe();
|
||||
serverLog(LL_WARNING,
|
||||
"Can't rewrite append only file in background: fork: %s",
|
||||
strerror(errno));
|
||||
@@ -1788,10 +1650,11 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
return C_ERR;
|
||||
}
|
||||
serverLog(LL_NOTICE,
|
||||
"Background append only file rewriting started by pid %ld",(long) childpid);
|
||||
"Background append only file rewriting started by pid %d",childpid);
|
||||
server.aof_rewrite_scheduled = 0;
|
||||
server.aof_rewrite_time_start = time(NULL);
|
||||
|
||||
server.aof_child_pid = childpid;
|
||||
updateDictResizePolicy();
|
||||
/* We set appendseldb to -1 in order to force the next call to the
|
||||
* feedAppendOnlyFile() to issue a SELECT command, so the differences
|
||||
* accumulated by the parent into server.aof_rewrite_buf will start
|
||||
@@ -1804,7 +1667,7 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
}
|
||||
|
||||
void bgrewriteaofCommand(client *c) {
|
||||
if (server.child_type == CHILD_TYPE_AOF) {
|
||||
if (server.aof_child_pid != -1) {
|
||||
addReplyError(c,"Background append only file rewriting already in progress");
|
||||
} else if (hasActiveChildProcess()) {
|
||||
server.aof_rewrite_scheduled = 1;
|
||||
@@ -1862,7 +1725,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
|
||||
* rewritten AOF. */
|
||||
latencyStartMonitor(latency);
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof",
|
||||
(int)server.child_pid);
|
||||
(int)server.aof_child_pid);
|
||||
newfd = open(tmpfile,O_WRONLY|O_APPEND);
|
||||
if (newfd == -1) {
|
||||
serverLog(LL_WARNING,
|
||||
@@ -1878,20 +1741,6 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
|
||||
}
|
||||
latencyEndMonitor(latency);
|
||||
latencyAddSampleIfNeeded("aof-rewrite-diff-write",latency);
|
||||
|
||||
if (server.aof_fsync == AOF_FSYNC_EVERYSEC) {
|
||||
aof_background_fsync(newfd);
|
||||
} else if (server.aof_fsync == AOF_FSYNC_ALWAYS) {
|
||||
latencyStartMonitor(latency);
|
||||
if (redis_fsync(newfd) == -1) {
|
||||
serverLog(LL_WARNING,
|
||||
"Error trying to fsync the parent diff to the rewritten AOF: %s", strerror(errno));
|
||||
close(newfd);
|
||||
goto cleanup;
|
||||
}
|
||||
latencyEndMonitor(latency);
|
||||
latencyAddSampleIfNeeded("aof-rewrite-done-fsync",latency);
|
||||
}
|
||||
|
||||
serverLog(LL_NOTICE,
|
||||
"Residual parent diff successfully flushed to the rewritten AOF (%.2f MB)", (double) aofRewriteBufferSize() / (1024*1024));
|
||||
@@ -1959,11 +1808,14 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
|
||||
/* AOF enabled, replace the old fd with the new one. */
|
||||
oldfd = server.aof_fd;
|
||||
server.aof_fd = newfd;
|
||||
if (server.aof_fsync == AOF_FSYNC_ALWAYS)
|
||||
redis_fsync(newfd);
|
||||
else if (server.aof_fsync == AOF_FSYNC_EVERYSEC)
|
||||
aof_background_fsync(newfd);
|
||||
server.aof_selected_db = -1; /* Make sure SELECT is re-issued */
|
||||
aofUpdateCurrentSize();
|
||||
server.aof_rewrite_base_size = server.aof_current_size;
|
||||
server.aof_fsync_offset = server.aof_current_size;
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
|
||||
/* Clear regular AOF buffer since its contents was just written to
|
||||
* the new AOF from the background rewrite buffer. */
|
||||
@@ -1979,7 +1831,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
|
||||
server.aof_state = AOF_ON;
|
||||
|
||||
/* Asynchronously close the overwritten AOF. */
|
||||
if (oldfd != -1) bioCreateCloseJob(oldfd);
|
||||
if (oldfd != -1) bioCreateBackgroundJob(BIO_CLOSE_FILE,(void*)(long)oldfd,NULL,NULL);
|
||||
|
||||
serverLog(LL_VERBOSE,
|
||||
"Background AOF rewrite signal handler took %lldus", ustime()-now);
|
||||
@@ -2001,7 +1853,8 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
|
||||
cleanup:
|
||||
aofClosePipes();
|
||||
aofRewriteBufferReset();
|
||||
aofRemoveTempFile(server.child_pid);
|
||||
aofRemoveTempFile(server.aof_child_pid);
|
||||
server.aof_child_pid = -1;
|
||||
server.aof_rewrite_time_last = time(NULL)-server.aof_rewrite_time_start;
|
||||
server.aof_rewrite_time_start = -1;
|
||||
/* Schedule a new rewrite if we are waiting for it to switch the AOF ON. */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user