Compare commits
691
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
095c131fbb | ||
|
|
89eef40ca2 | ||
|
|
752576ce47 | ||
|
|
84471e238e | ||
|
|
d1df881ec5 | ||
|
|
53949521de | ||
|
|
b704179f15 | ||
|
|
557e0b1c07 | ||
|
|
a39ffc1fe9 | ||
|
|
f364dcca2d | ||
|
|
cb261828bd | ||
|
|
9265234299 | ||
|
|
7939ba031d | ||
|
|
f1d6542b1a | ||
|
|
33f03f6fc8 | ||
|
|
985bf68f34 | ||
|
|
0200e8ada6 | ||
|
|
1848809f66 | ||
|
|
d7a448f9ae | ||
|
|
658424fc83 | ||
|
|
3f06ddfb7b | ||
|
|
1cddd0d3ba | ||
|
|
ee933d9e2b | ||
|
|
1d5e13e121 | ||
|
|
032357ec0f | ||
|
|
695126ccce | ||
|
|
725cd268e6 | ||
|
|
66df58f961 | ||
|
|
b045fe4e17 | ||
|
|
c5f91abaf7 | ||
|
|
6c202f495c | ||
|
|
e2608478b6 | ||
|
|
57807cd338 | ||
|
|
7f5f588232 | ||
|
|
662cb2fe75 | ||
|
|
87124a38b6 | ||
|
|
1583d60cd6 | ||
|
|
1cd622bdca | ||
|
|
d9134f8f95 | ||
|
|
7a40fd630d | ||
|
|
b9361ad5fe | ||
|
|
83c0348553 | ||
|
|
f164012c19 | ||
|
|
98be450f1d | ||
|
|
0f6e3a8273 | ||
|
|
de4e92ac39 | ||
|
|
49455c43ae | ||
|
|
c2694fb696 | ||
|
|
c88f9fe26f | ||
|
|
855ec46a6a | ||
|
|
f7353db7eb | ||
|
|
294492dbf2 | ||
|
|
192799539f | ||
|
|
a8850a8d30 | ||
|
|
f35ad82314 | ||
|
|
a034773497 | ||
|
|
fd5c325886 | ||
|
|
17eb33e0c3 | ||
|
|
0aeb86d78d | ||
|
|
8afb72a326 | ||
|
|
04589f90d7 | ||
|
|
09f8a2f374 | ||
|
|
870b6bd487 | ||
|
|
c688537d49 | ||
|
|
e3b9397dfe | ||
|
|
2bfffe85e9 | ||
|
|
f5e046a730 | ||
|
|
5dbcb3e4ab | ||
|
|
eb50eb20a5 | ||
|
|
c2d3e28540 | ||
|
|
0c1a764074 | ||
|
|
f86575f210 | ||
|
|
dcd0b3d020 | ||
|
|
d88611d36f | ||
|
|
4c5a9076d7 | ||
|
|
8aab7ca84c | ||
|
|
781ccc1bee | ||
|
|
ee96a5a6f5 | ||
|
|
9c81f8bd61 | ||
|
|
0f65806b5b | ||
|
|
5b8b58e472 | ||
|
|
342ee426ad | ||
|
|
4a95b3005a | ||
|
+4 |
73a9b916c9 | ||
|
|
dc0ee51cb1 | ||
|
|
21aee83abd | ||
|
|
08d714d0e5 | ||
|
|
4a12291765 | ||
|
|
8e9f5146dd | ||
|
|
04f63d4af7 | ||
|
|
dc57ee03b1 | ||
|
|
8144019a13 | ||
|
|
7665bdc91a | ||
|
|
64a40b20d9 | ||
|
|
08c2b276fb | ||
|
|
1f09a55eba | ||
|
|
684077682e | ||
|
|
f8942f93a6 | ||
|
|
c51c96656b | ||
|
|
0dd057222b | ||
|
|
59953d2df6 | ||
|
|
ddafac4c6c | ||
|
|
2af69a931a | ||
|
|
06b144aa09 | ||
|
|
db33b67d37 | ||
|
|
a106198878 | ||
|
|
05b99c8f4c | ||
|
|
9ebf80a28c | ||
|
|
155634502d | ||
|
|
79fd255828 | ||
|
|
5b84dc9678 | ||
|
|
701f06657d | ||
|
|
cf83803880 | ||
|
|
54038811c0 | ||
|
|
fdeb97629e | ||
|
|
9906daf5c9 | ||
|
|
ded8d993b7 | ||
|
|
6437d07b03 | ||
|
|
4b29be3f36 | ||
|
|
2ec78d262d | ||
|
|
0a24bfec13 | ||
|
|
611c950293 | ||
|
|
14c48cc5ed | ||
|
|
b08fd9f989 | ||
|
|
dd20aa5160 | ||
|
|
0a8e546957 | ||
|
|
4f8cdc2a1e | ||
|
|
3788a055fe | ||
|
|
b71a610f5c | ||
|
|
efcfffc528 | ||
|
|
99d09c824c | ||
|
|
6c5e263d7b | ||
|
|
3fc7ef8f81 | ||
|
|
a38c29b6c8 | ||
|
|
472d8a0df5 | ||
|
|
8ec5da785b | ||
|
|
3a2669e8ae | ||
|
|
f39e51178e | ||
|
|
5f7d7ce8b0 | ||
|
|
d092d64d7a | ||
|
|
30eb6c3210 | ||
|
|
80458d0490 | ||
|
|
99c40ab53d | ||
|
|
26ef28467a | ||
|
|
5f28bd96db | ||
|
|
438cfed70a | ||
|
|
3a3cacfefa | ||
|
|
617909e943 | ||
|
|
e9cbfccec6 | ||
|
|
7b69183a8d | ||
|
|
af7fca797a | ||
|
|
9146ac050b | ||
|
|
ef3a5f58a8 | ||
|
|
f2f85ba354 | ||
|
|
c115c5230e | ||
|
|
65a87cb773 | ||
|
|
9a89e32a95 | ||
|
|
5fe3e74a38 | ||
|
|
610eb26c11 | ||
|
|
4955375ec7 | ||
|
|
d265a61438 | ||
|
|
406a365f44 | ||
|
|
2dd4cca363 | ||
|
|
bcae770819 | ||
|
|
bf802b0764 | ||
|
|
ac03e3721d | ||
|
|
31227f4faf | ||
|
|
f6f11f3ef1 | ||
|
|
569584d463 | ||
|
|
ea3e8b79a1 | ||
|
|
74609d44cd | ||
|
|
ea05c6ac47 | ||
|
|
05aed4cab9 | ||
|
|
de7f2f87f7 | ||
|
|
fb8755a636 | ||
|
|
d3d94ccf2e | ||
|
|
00a8e72cfc | ||
|
|
a31b516e25 | ||
|
|
c77b8f45e9 | ||
|
|
a7afd1d2b2 | ||
|
|
3fcddfb61f | ||
|
|
3c9f5954b5 | ||
|
|
3b1b1d1486 | ||
|
|
60f22ca830 | ||
|
|
6ceadfb580 | ||
|
|
7f0a7f0a69 | ||
|
|
3264deb24e | ||
|
|
6c6489280c | ||
|
|
2b88db90aa | ||
|
|
b94b714f81 | ||
|
|
806459f481 | ||
|
|
3a08819f51 | ||
|
|
6f0ddc9d92 | ||
|
|
731f2dc5c7 | ||
|
|
bf643a63c8 | ||
|
|
e4ddc34463 | ||
|
|
6d5d754119 | ||
|
|
e3e631f394 | ||
|
|
6263823e54 | ||
|
|
4dd8b1faa9 | ||
|
|
8038eb3147 | ||
|
|
89742a95db | ||
|
|
60e9e630bd | ||
|
|
e750c619b2 | ||
|
|
93fb83b4cb | ||
|
|
94b9072e44 | ||
|
|
e74550dd10 | ||
|
|
d0c64d78d4 | ||
|
|
82f00f5179 | ||
|
|
447ce11a64 | ||
|
|
13d227fa46 | ||
|
|
a331978583 | ||
|
|
fa46aa4d85 | ||
|
|
88af96c7a2 | ||
|
|
76415fa2cf | ||
|
|
7d3545cb16 | ||
|
|
880e147d52 | ||
|
|
b10e19e3d6 | ||
|
|
d39548c854 | ||
|
|
ad9277238f | ||
|
|
915a9e4b93 | ||
|
|
81440a333d | ||
|
|
ffff7fea7c | ||
|
|
69b480cb7a | ||
|
|
b699e8bfe0 | ||
|
|
dcf0229811 | ||
|
|
58edea7385 | ||
|
|
26a2dcb936 | ||
|
|
a84cc20aef | ||
|
|
76d179c64f | ||
|
|
c6ceea9a23 | ||
|
|
799c5e5f51 | ||
|
|
69b7137d32 | ||
|
|
a9267137ee | ||
|
|
52e12d8bac | ||
|
|
5eac99c312 | ||
|
|
e26ea35cd4 | ||
|
|
811c5d7aeb | ||
|
|
a03b6e29a9 | ||
|
|
e18a173a81 | ||
|
|
24c85cc368 | ||
|
|
4aa25d042c | ||
|
|
871c985919 | ||
|
|
ed10f737b8 | ||
|
|
f01fdc3960 | ||
|
|
ce121b9261 | ||
|
|
690ef36330 | ||
|
|
9a2c6ba4e7 | ||
|
|
f36b5a8586 | ||
|
|
293a68afdf | ||
|
|
44352beefa | ||
|
|
56169112e3 | ||
|
|
50569a906c | ||
|
|
5a3534f9b5 | ||
|
|
7b9e960690 | ||
|
|
f0389f2823 | ||
|
|
33fc0fbfae | ||
|
|
6a11d458be | ||
|
|
e2918705c8 | ||
|
|
2d1bb42cba | ||
|
|
2f34f6f0b9 | ||
|
|
60e1582ddb | ||
|
|
6801a3cec8 | ||
|
|
f34f2ade85 | ||
|
|
ae6df30eb1 | ||
|
|
a25b15392c | ||
|
|
95cbe879e5 | ||
|
|
e92363e248 | ||
|
|
9ffc35c98e | ||
|
|
36c3cec6d1 | ||
|
|
323be4d699 | ||
|
|
71676513dd | ||
|
|
f1b0212917 | ||
|
|
ffbdf2f6f3 | ||
|
|
80be2cc291 | ||
|
|
5066e6e9cd | ||
|
|
8a05f0092b | ||
|
|
7010f41c96 | ||
|
|
ca4ed48db6 | ||
|
|
13401f8bc1 | ||
|
|
0e1de78fca | ||
|
|
03cd525ffa | ||
|
|
c33c91dbce | ||
|
|
f95031c473 | ||
|
|
772564fc9e | ||
|
|
c18ff05665 | ||
|
|
804110a487 | ||
|
|
e3550f01dd | ||
|
|
f4481e657f | ||
|
|
4581d43230 | ||
|
|
4df037962d | ||
|
|
ce47834309 | ||
|
|
0b34396924 | ||
|
|
e64d91c371 | ||
|
|
bad33f8738 | ||
|
|
e04d41d78d | ||
|
|
aeada20140 | ||
|
|
7b070423b8 | ||
|
|
98a6e55d4e | ||
|
|
3f725b8619 | ||
|
|
1d77a8e2c5 | ||
|
|
3b3d16f748 | ||
|
|
ad28d222ed | ||
|
|
a8e745117f | ||
|
|
9efc6ad6a6 | ||
|
|
9c065c417d | ||
|
|
da727ad445 | ||
|
|
3c8d15f8c3 | ||
|
|
5fdaa53d20 | ||
|
|
4979cf02ff | ||
|
|
9738ba9841 | ||
|
|
28976a9003 | ||
|
|
ad12730333 | ||
|
|
33ea432585 | ||
|
|
df75153d79 | ||
|
|
a50bbcb656 | ||
|
|
4cae99e785 | ||
|
|
f17381a38d | ||
|
|
cc9fbd270e | ||
|
|
a7abc2f067 | ||
|
|
763827c981 | ||
|
|
0a12f380e8 | ||
|
|
104b207602 | ||
|
|
bfcaa7db0a | ||
|
|
4a265554ae | ||
|
|
165afc5f2a | ||
|
|
820a4e45f1 | ||
|
|
5b9fc46523 | ||
|
|
f6785df663 | ||
|
|
ca5cac998e | ||
|
|
3c2ea1ea95 | ||
|
|
4e3be944fc | ||
|
|
fc3a68d8fb | ||
|
|
6df42df291 | ||
|
|
8876d264ac | ||
|
|
9103ccc398 | ||
|
|
dd92dd8fb5 | ||
|
|
50d6fe8c4b | ||
|
|
c854873746 | ||
|
|
063de675e0 | ||
|
|
32f44da510 | ||
|
|
c1d2ac2a73 | ||
|
|
e9c795e777 | ||
|
|
7ca0b84af6 | ||
|
|
8eeece4ab3 | ||
|
|
676f27acb0 | ||
|
|
493e31e3ad | ||
|
|
813327b231 | ||
|
|
1e8dc1da0d | ||
|
|
81666a6510 | ||
|
|
886b117031 | ||
|
|
1f00c951c2 | ||
|
|
0777dc7896 | ||
|
|
8096515432 | ||
|
|
13bd3643c2 | ||
|
|
87eaf119cd | ||
|
|
84fd745d65 | ||
|
|
8cd62f82ca | ||
|
|
f20774eced | ||
|
|
02a87885e6 | ||
|
|
c1041c2c0d | ||
|
|
62153b3b2f | ||
|
|
9a7d311855 | ||
|
|
6016973ac0 | ||
|
|
74a6e48a3d | ||
|
|
f469dd8ca6 | ||
|
|
24f6d08b3f | ||
|
|
7c9f41b52b | ||
|
|
45a35a79c7 | ||
|
|
76adbf6ff0 | ||
|
|
492021db95 | ||
|
|
af7ceeb765 | ||
|
|
c5273cae18 | ||
|
|
acd9605223 | ||
|
|
5358bd7cdd | ||
|
|
98881f7558 | ||
|
|
4cb5ad85a5 | ||
|
|
85a834bfa2 | ||
|
|
07b292af5e | ||
|
|
685409139b | ||
|
|
85c31e0cff | ||
|
|
628c0dea1b | ||
|
|
f9a0eb60f7 | ||
|
|
12fd752443 | ||
|
|
2bce71b5ff | ||
|
|
e12f2decc1 | ||
|
|
8d0156eb18 | ||
|
|
85a239b363 | ||
|
|
b07174afc2 | ||
|
|
d0640029dc | ||
|
|
f81c3fd89e | ||
|
|
1c7eb0ad37 | ||
|
|
0e5a4a27ea | ||
|
|
29e6245a05 | ||
|
|
14b1edfd99 | ||
|
|
131d95f203 | ||
|
|
ecc31bc697 | ||
|
|
e2b7932b34 | ||
|
|
bb2b6e2927 | ||
|
|
284ef21ea0 | ||
|
|
87786342a5 | ||
|
|
964f4a4576 | ||
|
|
b3aaa0a136 | ||
|
|
b351a04b1e | ||
|
|
f7b1d0287d | ||
|
|
8bb9a2895e | ||
|
|
14e4a9835a | ||
|
|
50b8b99763 | ||
|
|
c452e414a8 | ||
|
|
4730563e93 | ||
|
|
5b0c6a8255 | ||
|
|
ca1f67af80 | ||
|
|
4cae66f5e8 | ||
|
|
38f0234946 | ||
|
|
5189838350 | ||
|
|
068051e378 | ||
|
|
c3f8b542ee | ||
|
|
9d0158bf89 | ||
|
|
2c5b51ad26 | ||
|
|
12b611b374 | ||
|
|
99c468c38c | ||
|
|
5b1fe925f2 | ||
|
|
8527959598 | ||
|
|
fa751f9bef | ||
|
|
bef5715374 | ||
|
|
1aa633d61b | ||
|
|
27a8e3b04e | ||
|
|
baf5699d77 | ||
|
|
71f31da66f | ||
|
|
20214b26a4 | ||
|
|
09e0d338f5 | ||
|
|
23e980e77a | ||
|
|
5dc631d880 | ||
|
|
adbb534f03 | ||
|
|
d8a21c5767 | ||
|
|
967fb3c6e8 | ||
|
|
e95a5d4831 | ||
|
|
3c0fd25201 | ||
|
|
f9cc25c1dd | ||
|
|
c85a9b7896 | ||
|
|
62419c01db | ||
|
|
e6423b7a7e | ||
|
|
a3ae2ed37b | ||
|
|
7410d985bc | ||
|
|
15b993f1ef | ||
|
|
826b39e016 | ||
|
|
f2d59c4f91 | ||
|
|
8e11f84ded | ||
|
|
e2a3f3091f | ||
|
|
2f6d4dabaa | ||
|
|
77e69d8884 | ||
|
|
b730404c2f | ||
|
|
9ee1cc33a3 | ||
|
|
991aff1c0f | ||
|
|
35c8d616cf | ||
|
|
764838d66f | ||
|
|
8a4ccb01b3 | ||
|
|
91309f7981 | ||
|
|
e216c83909 | ||
|
|
423565f784 | ||
|
|
22cc9b5122 | ||
|
|
bdceaf50e4 | ||
|
|
3431b1f156 | ||
|
|
a1c5171c1d | ||
|
|
095d05786f | ||
|
|
d6f19539d2 | ||
|
|
1bd0b54957 | ||
|
|
56ec1ff1ce | ||
|
|
2e854bccc6 | ||
|
|
5e403099bd | ||
|
|
c9aa586b6b | ||
|
|
ae09d4d3ef | ||
|
|
1c48d3dab2 | ||
|
|
157e5d47b5 | ||
|
|
463476933c | ||
|
|
ef1ca6c882 | ||
|
|
dedbf99a80 | ||
|
|
58cb302526 | ||
|
|
eebb025826 | ||
|
|
290f376429 | ||
|
|
13b754853c | ||
|
|
2e5181ef28 | ||
|
|
c6157b3510 | ||
|
|
4afc54ad9b | ||
|
|
33ef6a3003 | ||
|
|
ac1513221b | ||
|
|
040cb6a4aa | ||
|
|
4944eda696 | ||
|
|
d9a0478599 | ||
|
|
98a6c44b75 | ||
|
|
2c41b13505 | ||
|
|
5292adb985 | ||
|
|
6a6ae6ffe8 | ||
|
|
86915775f1 | ||
|
|
4278ed8de5 | ||
|
|
a1f91ffa18 | ||
|
|
5a1f4b9aec | ||
|
|
3d9c427f8c | ||
|
|
eb392c0a6f | ||
|
|
6013122c8e | ||
|
|
9b6dded421 | ||
|
|
4366bbaa61 | ||
|
|
fe36306340 | ||
|
|
a9e73c00bc | ||
|
|
88e83e517b | ||
|
|
9ca8490315 | ||
|
|
6258edebf0 | ||
|
|
cf6ed3feeb | ||
|
|
53294e537c | ||
|
|
0ffb9d2ea9 | ||
|
|
6223355cf3 | ||
|
|
a888503b4f | ||
|
|
282b82e9d2 | ||
|
|
28b6155ba5 | ||
|
|
15a048d4f0 | ||
|
|
0ce74872c4 | ||
|
|
8878817d89 | ||
|
|
e9f312e087 | ||
|
|
8d675950e6 | ||
|
|
4bbb2b0152 | ||
|
|
f7fa481156 | ||
|
|
4145d628b4 | ||
|
|
7d68208a6e | ||
|
|
ba900f6cb8 | ||
|
|
372ea21875 | ||
|
|
3fac869f02 | ||
|
|
26eb4ce397 | ||
|
|
becd50d0da | ||
|
|
f3bf8485d8 | ||
|
|
03345ddc7f | ||
|
|
3c734b8e9d | ||
|
|
d27c7413a9 | ||
|
|
0270abda82 | ||
|
|
f0c1c730d4 | ||
|
|
b784c5375e | ||
|
|
b705049a7a | ||
|
|
77a65e82b2 | ||
|
|
e5ef161374 | ||
|
|
4de4fcf280 | ||
|
|
8d92f7f2b7 | ||
|
|
b810384c62 | ||
|
|
a2b0701d2c | ||
|
|
fe37e4fc87 | ||
|
|
2cf50ddbad | ||
|
|
31c3172d9b | ||
|
|
9d31768cbb | ||
|
|
4ba9e18ef0 | ||
|
|
2e0f6724e0 | ||
|
|
c2a4b78491 | ||
|
|
bfa3931a04 | ||
|
|
f924bebd83 | ||
|
|
9fe63bdc80 | ||
|
|
8cdeddc81c | ||
|
|
24187ed8e3 | ||
|
|
cc2be63997 | ||
|
|
2aad03fa39 | ||
|
|
4031a18732 | ||
|
|
96e9dec419 | ||
|
|
90e9fc387c | ||
|
|
a2046c1eb1 | ||
|
|
044e29dd34 | ||
|
|
4ba144a4eb | ||
|
|
49f7d173b4 | ||
|
|
370d38016f | ||
|
|
b26e8e3213 | ||
|
|
9ce8c54d74 | ||
|
|
7519960527 | ||
|
|
b59f53efb3 | ||
|
|
e3d4b30d09 | ||
|
|
4b281ce519 | ||
|
|
e792653753 | ||
|
|
a6eff389b5 | ||
|
|
1407ac1f3e | ||
|
|
45d3310694 | ||
|
|
c98a28a848 | ||
|
|
16988208bd | ||
|
|
e532c95dfc | ||
|
|
fe47c2027b | ||
|
|
44cc0fcb9d | ||
|
|
ac6bc5d1a8 | ||
|
|
17904780ae | ||
|
|
965dc90b72 | ||
|
|
2b8cde71bb | ||
|
|
f4549d1cf4 | ||
|
|
7c179f9bf4 | ||
|
|
6abfda54c3 | ||
|
|
1b6bdff48d | ||
|
|
8226f39fb2 | ||
|
|
da9c2804a5 | ||
|
|
7af9f4b36e | ||
|
|
2ee1bbb53b | ||
|
|
90ab91f00b | ||
|
|
b653c759cd | ||
|
|
6abb3c4038 | ||
|
|
42985b00ea | ||
|
|
01eb939a06 | ||
|
|
9f512017aa | ||
|
|
34b95f752c | ||
|
|
2495b90a64 | ||
|
|
4b1fbb2338 | ||
|
|
9a3b99cbd1 | ||
|
|
91011100ba | ||
|
|
9b1d4f003d | ||
|
|
0175fe8219 | ||
|
|
8e138ba44f | ||
|
|
67ed3183b5 | ||
|
|
936cfa464f | ||
|
|
14f802b360 | ||
|
|
aefdc57f1e | ||
|
|
1190f25ca7 | ||
|
|
b35e20d1de | ||
|
|
d56a7d9b05 | ||
|
|
b7559d9f3b | ||
|
|
6b57241fa8 | ||
|
|
26174123ee | ||
|
|
6bf9b144ef | ||
|
|
07ed0eafa9 | ||
|
|
b2cdf6bcc3 | ||
|
|
ef4bb4e374 | ||
|
|
f58fd9e6d2 | ||
|
|
1634a0f271 | ||
|
|
22a29935ff | ||
|
|
9600553ef2 | ||
|
|
153f8f082e | ||
|
|
3230199920 | ||
|
|
9e5f45f230 | ||
|
|
47f32bc998 | ||
|
|
d46ef88671 | ||
|
|
73cf0243df | ||
|
|
20fa156067 | ||
|
|
8ad8f0f9d8 | ||
|
|
93708c7f6a | ||
|
|
d9c2ef8a72 | ||
|
|
66ea178cb6 | ||
|
|
d306d86146 | ||
|
|
13e17e94d8 | ||
|
|
813924b4c3 | ||
|
|
07316f16f0 | ||
|
|
cd4f3e20c1 | ||
|
|
b510624978 | ||
|
|
07c14672bf | ||
|
|
070453eef3 | ||
|
|
b00a235186 | ||
|
|
439b0315c8 | ||
|
|
5da9eecdb8 | ||
|
|
9dc6f93e9d | ||
|
|
cefe4566e9 | ||
|
|
254475537a | ||
|
|
789c33bb3e | ||
|
|
1946083410 | ||
|
|
a9e32767f7 | ||
|
|
e7129e43e0 | ||
|
|
f228ec1ea5 | ||
|
|
0bfb6d5582 | ||
|
|
2d7d3911da | ||
|
|
d412269ff8 | ||
|
|
5fd9756d2e | ||
|
|
e4d183afd3 | ||
|
|
e7a3d3d152 | ||
|
|
0bd1a3a4a8 | ||
|
|
032bb2a273 | ||
|
|
2eaf23601d | ||
|
|
fad6c713c2 | ||
|
|
c2f1815bcb | ||
|
|
0dfb0250e6 | ||
|
|
da8f7428fa | ||
|
|
32f45215c3 | ||
|
|
2764dc3768 | ||
|
|
6117f28822 | ||
|
|
da46e926ac | ||
|
|
1a188e4ed6 | ||
|
|
cb78acb865 | ||
|
|
e775b34e81 | ||
|
|
627c610f49 | ||
|
|
3ca451c46f | ||
|
|
c871db24c4 | ||
|
|
d71478a889 | ||
|
|
d664889992 | ||
|
|
d0994c5bca | ||
|
|
ec5721d6ca | ||
|
|
4c74dd986f | ||
|
|
07ea220419 | ||
|
|
71e6abe423 | ||
|
|
006ab26c37 | ||
|
|
38e284f106 | ||
|
|
48757934ff | ||
|
|
37cf1984b9 | ||
|
|
40e6131ba5 | ||
|
|
49845c24b1 | ||
|
|
df1890ef7f | ||
|
|
fd566f4050 |
@@ -1 +1 @@
|
||||
codespell==2.2.4
|
||||
codespell==2.2.5
|
||||
|
||||
+38
-14
@@ -7,7 +7,7 @@ jobs:
|
||||
test-ubuntu-latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: make
|
||||
# Fail build if there are warnings
|
||||
# build with TLS just for compilation coverage
|
||||
@@ -17,27 +17,33 @@ jobs:
|
||||
sudo apt-get install tcl8.6 tclx
|
||||
./runtest --verbose --tags -slow --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose --dump-logs
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs
|
||||
- name: validate commands.def up to date
|
||||
run: |
|
||||
touch src/commands/ping.json
|
||||
make commands.def
|
||||
dirty=$(git diff)
|
||||
if [[ ! -z $dirty ]]; then echo $dirty; exit 1; fi
|
||||
|
||||
test-sanitizer-address:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: make
|
||||
# build with TLS module just for compilation coverage
|
||||
run: make SANITIZER=address REDIS_CFLAGS='-Werror' BUILD_TLS=module
|
||||
run: make SANITIZER=address REDIS_CFLAGS='-Werror -DDEBUG_ASSERTIONS' BUILD_TLS=module
|
||||
- name: testprep
|
||||
run: sudo apt-get install tcl8.6 tclx -y
|
||||
- name: test
|
||||
run: ./runtest --verbose --tags -slow --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose --dump-logs
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs
|
||||
|
||||
build-debian-old:
|
||||
runs-on: ubuntu-latest
|
||||
container: debian:buster
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update && apt-get install -y build-essential
|
||||
@@ -46,33 +52,51 @@ jobs:
|
||||
build-macos-latest:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror'
|
||||
|
||||
build-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386 gcc-multilib g++-multilib
|
||||
make REDIS_CFLAGS='-Werror' 32bit
|
||||
|
||||
build-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror' MALLOC=libc
|
||||
|
||||
build-centos7-jemalloc:
|
||||
build-centos-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
container: centos:7
|
||||
container: quay.io/centos/centos:stream9
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install gcc make
|
||||
dnf -y install which gcc gcc-c++ make
|
||||
make REDIS_CFLAGS='-Werror'
|
||||
|
||||
build-old-chain-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
container: ubuntu:20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- 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 g++-4.8
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
|
||||
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 100
|
||||
make CC=gcc REDIS_CFLAGS='-Werror'
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
name: "Codecov"
|
||||
|
||||
# Enabling on each push is to display the coverage changes in every PR,
|
||||
# where each PR needs to be compared against the coverage of the head commit
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
code-coverage:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install lcov and run test
|
||||
run: |
|
||||
sudo apt-get install lcov
|
||||
make lcov
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: ./src/redis.info
|
||||
@@ -19,15 +19,15 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Creates and uploads a Coverity build on a schedule
|
||||
name: Coverity Scan
|
||||
on:
|
||||
schedule:
|
||||
# Run once daily, since below 500k LOC can have 21 builds per week, per https://scan.coverity.com/faq#frequency
|
||||
- cron: '0 0 * * *'
|
||||
# Support manual execution
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
coverity:
|
||||
if: github.repository == 'redis/redis'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- name: Download and extract the Coverity Build Tool
|
||||
run: |
|
||||
wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=redis-unstable" -O cov-analysis-linux64.tar.gz
|
||||
mkdir cov-analysis-linux64
|
||||
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
|
||||
- name: Install Redis dependencies
|
||||
run: sudo apt install -y gcc tcl8.6 tclx procps libssl-dev
|
||||
- name: Build with cov-build
|
||||
run: cov-analysis-linux64/bin/cov-build --dir cov-int make
|
||||
- name: Upload the result
|
||||
run: |
|
||||
tar czvf cov-int.tgz cov-int
|
||||
curl \
|
||||
--form project=redis-unstable \
|
||||
--form email=${{ secrets.COVERITY_SCAN_EMAIL }} \
|
||||
--form token=${{ secrets.COVERITY_SCAN_TOKEN }} \
|
||||
--form file=@cov-int.tgz \
|
||||
https://scan.coverity.com/builds
|
||||
+270
-146
@@ -11,7 +11,7 @@ on:
|
||||
inputs:
|
||||
skipjobs:
|
||||
description: 'jobs to skip (delete the ones you wanna keep, do not leave empty)'
|
||||
default: 'valgrind,sanitizer,tls,freebsd,macos,alpine,32bit,iothreads,ubuntu,centos,malloc,specific,fortify,reply-schema'
|
||||
default: 'valgrind,sanitizer,tls,freebsd,macos,alpine,32bit,iothreads,ubuntu,centos,malloc,specific,fortify,reply-schema,oldTC'
|
||||
skiptests:
|
||||
description: 'tests to skip (delete the ones you wanna keep, do not leave empty)'
|
||||
default: 'redis,modules,sentinel,cluster,unittest'
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
@@ -76,7 +76,6 @@ jobs:
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'fortify')
|
||||
container: ubuntu:lunar
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -88,23 +87,22 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update && apt-get install -y make gcc-13
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 100
|
||||
apt-get update && apt-get install -y make gcc g++
|
||||
make CC=gcc REDIS_CFLAGS='-Werror -DREDIS_TEST -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3'
|
||||
- name: testprep
|
||||
run: apt-get install -y tcl8.6 tclx procps
|
||||
run: sudo apt-get install -y tcl8.6 tclx procps
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
@@ -131,7 +129,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -144,7 +142,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
@@ -168,7 +166,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -181,7 +179,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
@@ -205,13 +203,13 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386 g++ gcc-multilib g++-multilib
|
||||
make 32bit REDIS_CFLAGS='-Werror -DREDIS_TEST'
|
||||
- name: testprep
|
||||
run: sudo apt-get install tcl8.6 tclx
|
||||
@@ -222,7 +220,7 @@ jobs:
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
make -C tests/modules 32bit # the script below doesn't have an argument, we must build manually ahead of time
|
||||
./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
@@ -249,7 +247,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -267,7 +265,7 @@ jobs:
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --dump-logs --tls --dump-logs ${{github.event.inputs.test_args}}
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs --tls --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
@@ -293,7 +291,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -311,7 +309,7 @@ jobs:
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
@@ -337,7 +335,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -369,7 +367,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -447,7 +445,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -456,7 +454,7 @@ jobs:
|
||||
- name: testprep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install tcl8.6 tclx valgrind -y
|
||||
sudo apt-get install tcl8.6 tclx valgrind g++ -y
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: ./runtest --valgrind --no-latency --verbose --clients 1 --timeout 2400 --dump-logs ${{github.event.inputs.test_args}}
|
||||
@@ -477,7 +475,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -489,7 +487,7 @@ jobs:
|
||||
sudo apt-get install tcl8.6 tclx valgrind -y
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 --timeout 2400 --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 --timeout 2400 --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: unittest
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: |
|
||||
@@ -512,7 +510,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -521,7 +519,7 @@ jobs:
|
||||
- name: testprep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install tcl8.6 tclx valgrind -y
|
||||
sudo apt-get install tcl8.6 tclx valgrind g++ -y
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: ./runtest --valgrind --no-latency --verbose --clients 1 --timeout 2400 --dump-logs ${{github.event.inputs.test_args}}
|
||||
@@ -542,7 +540,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -554,7 +552,7 @@ jobs:
|
||||
sudo apt-get install tcl8.6 tclx valgrind -y
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 --timeout 2400 --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 --timeout 2400 --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: unittest
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: |
|
||||
@@ -582,12 +580,12 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: make SANITIZER=address REDIS_CFLAGS='-DREDIS_TEST -Werror'
|
||||
run: make SANITIZER=address REDIS_CFLAGS='-DREDIS_TEST -Werror -DDEBUG_ASSERTIONS'
|
||||
- name: testprep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@@ -597,7 +595,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
@@ -629,7 +627,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -644,7 +642,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
@@ -655,12 +653,12 @@ jobs:
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: ./src/redis-server test all --accurate
|
||||
|
||||
test-centos7-jemalloc:
|
||||
test-centos-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'centos')
|
||||
container: centos:7
|
||||
container: quay.io/centos/centos:stream9
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -672,22 +670,24 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install gcc make
|
||||
dnf -y install which gcc make g++
|
||||
make REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: yum -y install which tcl tclx
|
||||
run: |
|
||||
dnf -y install epel-release
|
||||
dnf -y install tcl tcltls procps-ng /usr/bin/kill
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
@@ -695,12 +695,12 @@ jobs:
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-centos7-tls-module:
|
||||
test-centos-tls-module:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'tls')
|
||||
container: centos:7
|
||||
container: quay.io/centos/centos:stream9
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -712,18 +712,18 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install centos-release-scl epel-release
|
||||
yum -y install devtoolset-7 openssl-devel openssl
|
||||
scl enable devtoolset-7 "make BUILD_TLS=module REDIS_CFLAGS='-Werror'"
|
||||
dnf -y install which gcc make openssl-devel openssl g++
|
||||
make BUILD_TLS=module REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: |
|
||||
yum -y install tcl tcltls tclx
|
||||
dnf -y install epel-release
|
||||
dnf -y install tcl tcltls procps-ng /usr/bin/kill
|
||||
./utils/gen-test-certs.sh
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
@@ -732,7 +732,7 @@ jobs:
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --dump-logs --tls-module --dump-logs ${{github.event.inputs.test_args}}
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs --tls-module --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
@@ -742,12 +742,12 @@ jobs:
|
||||
run: |
|
||||
./runtest-cluster --tls-module ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-centos7-tls-module-no-tls:
|
||||
test-centos-tls-module-no-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'tls')
|
||||
container: centos:7
|
||||
container: quay.io/centos/centos:stream9
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -759,18 +759,18 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install centos-release-scl epel-release
|
||||
yum -y install devtoolset-7 openssl-devel openssl
|
||||
scl enable devtoolset-7 "make BUILD_TLS=module REDIS_CFLAGS='-Werror'"
|
||||
dnf -y install which gcc make openssl-devel openssl g++
|
||||
make BUILD_TLS=module REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: |
|
||||
yum -y install tcl tcltls tclx
|
||||
dnf -y install epel-release
|
||||
dnf -y install tcl tcltls procps-ng /usr/bin/kill
|
||||
./utils/gen-test-certs.sh
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
@@ -779,7 +779,7 @@ jobs:
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
@@ -805,7 +805,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -813,10 +813,10 @@ jobs:
|
||||
run: make REDIS_CFLAGS='-Werror'
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: ./runtest --accurate --verbose --verbose --clients 1 --no-latency --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: ./runtest --accurate --verbose --clients 1 --no-latency --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --verbose --verbose --clients 1 --no-latency --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --clients 1 --no-latency --dump-logs ${{github.event.inputs.test_args}}
|
||||
|
||||
test-macos-latest-sentinel:
|
||||
runs-on: macos-latest
|
||||
@@ -834,7 +834,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -860,7 +860,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -870,99 +870,65 @@ jobs:
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
build-macos:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-13, macos-15]
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'macos')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror -DREDIS_TEST'
|
||||
|
||||
test-freebsd:
|
||||
runs-on: macos-12
|
||||
runs-on: macos-13
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'freebsd') && !(contains(github.event.inputs.skiptests, 'redis') && contains(github.event.inputs.skiptests, 'modules'))
|
||||
!contains(github.event.inputs.skipjobs, 'freebsd')
|
||||
timeout-minutes: 14400
|
||||
env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: test
|
||||
uses: vmactions/freebsd-vm@v0.3.0
|
||||
uses: cross-platform-actions/action@v0.22.0
|
||||
with:
|
||||
usesh: true
|
||||
sync: rsync
|
||||
copyback: false
|
||||
prepare: pkg install -y bash gmake lang/tcl86 lang/tclx
|
||||
run: >
|
||||
gmake || exit 1 ;
|
||||
if echo "${{github.event.inputs.skiptests}}" | grep -vq redis ; then ./runtest --verbose --timeout 2400 --no-latency --dump-logs ${{github.event.inputs.test_args}} || exit 1 ; fi ;
|
||||
if echo "${{github.event.inputs.skiptests}}" | grep -vq modules ; then MAKE=gmake ./runtest-moduleapi --verbose --timeout 2400 --no-latency --dump-logs ${{github.event.inputs.test_args}} || exit 1 ; fi ;
|
||||
|
||||
test-freebsd-sentinel:
|
||||
runs-on: macos-12
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'freebsd') && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: test
|
||||
uses: vmactions/freebsd-vm@v0.3.0
|
||||
with:
|
||||
usesh: true
|
||||
sync: rsync
|
||||
copyback: false
|
||||
prepare: pkg install -y bash gmake lang/tcl86 lang/tclx
|
||||
run: >
|
||||
gmake || exit 1 ;
|
||||
if echo "${{github.event.inputs.skiptests}}" | grep -vq sentinel ; then ./runtest-sentinel ${{github.event.inputs.cluster_test_args}} || exit 1 ; fi ;
|
||||
|
||||
test-freebsd-cluster:
|
||||
runs-on: macos-12
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'freebsd') && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: test
|
||||
uses: vmactions/freebsd-vm@v0.3.0
|
||||
with:
|
||||
usesh: true
|
||||
sync: rsync
|
||||
copyback: false
|
||||
prepare: pkg install -y bash gmake lang/tcl86 lang/tclx
|
||||
run: >
|
||||
gmake || exit 1 ;
|
||||
if echo "${{github.event.inputs.skiptests}}" | grep -vq cluster ; then ./runtest-cluster ${{github.event.inputs.cluster_test_args}} || exit 1 ; fi ;
|
||||
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
|
||||
@@ -980,7 +946,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -995,7 +961,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
@@ -1019,7 +985,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -1034,7 +1000,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
@@ -1058,7 +1024,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -1071,7 +1037,7 @@ jobs:
|
||||
run: ./runtest --log-req-res --no-latency --dont-clean --force-resp3 --tags -slow --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --log-req-res --no-latency --dont-clean --force-resp3 --dont-pre-clean --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --log-req-res --no-latency --dont-clean --force-resp3 --dont-pre-clean --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel --log-req-res --dont-clean --force-resp3 ${{github.event.inputs.cluster_test_args}}
|
||||
@@ -1085,3 +1051,161 @@ jobs:
|
||||
- name: validator
|
||||
run: ./utils/req-res-log-validator.py --verbose --fail-missing-reply-schemas ${{ (!contains(github.event.inputs.skiptests, 'redis') && !contains(github.event.inputs.skiptests, 'module') && !contains(github.event.inputs.sentinel, 'redis') && !contains(github.event.inputs.skiptests, 'cluster')) && github.event.inputs.test_args == '' && github.event.inputs.cluster_test_args == '' && '--fail-commands-not-all-hit' || '' }}
|
||||
|
||||
test-old-chain-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'oldTC')
|
||||
container: ubuntu:20.04
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- 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 g++-4.8
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
|
||||
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 100
|
||||
make CC=gcc REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: apt-get install -y tcl tcltls tclx
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-old-chain-tls-module:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'tls') && !contains(github.event.inputs.skipjobs, 'oldTC')
|
||||
container: ubuntu:20.04
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- 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 g++-4.8 openssl libssl-dev
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
|
||||
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 100
|
||||
make CC=gcc CXX=g++ BUILD_TLS=module REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: |
|
||||
apt-get install -y tcl tcltls tclx
|
||||
./utils/gen-test-certs.sh
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: |
|
||||
./runtest --accurate --verbose --dump-logs --tls-module --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs --tls-module --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: |
|
||||
./runtest-cluster --tls-module ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-old-chain-tls-module-no-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'tls') && !contains(github.event.inputs.skipjobs, 'oldTC')
|
||||
container: ubuntu:20.04
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- 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 g++-4.8 openssl libssl-dev
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
|
||||
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 100
|
||||
make BUILD_TLS=module CC=gcc REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: |
|
||||
apt-get install -y tcl tcltls tclx
|
||||
./utils/gen-test-certs.sh
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: |
|
||||
./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: |
|
||||
./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
if: github.event_name != 'schedule' || github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: make REDIS_CFLAGS=-Werror
|
||||
- name: Start redis-server
|
||||
@@ -23,10 +23,11 @@ jobs:
|
||||
run: |
|
||||
./runtest \
|
||||
--host 127.0.0.1 --port 6379 \
|
||||
--verbose \
|
||||
--tags -slow
|
||||
- name: Archive redis log
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-external-redis-log
|
||||
path: external-redis.log
|
||||
@@ -36,12 +37,12 @@ jobs:
|
||||
if: github.event_name != 'schedule' || github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: make REDIS_CFLAGS=-Werror
|
||||
- name: Start redis-server
|
||||
run: |
|
||||
./src/redis-server --cluster-enabled yes --daemonize yes --save "" --logfile external-redis.log \
|
||||
./src/redis-server --cluster-enabled yes --daemonize yes --save "" --logfile external-redis-cluster.log \
|
||||
--enable-protected-configs yes --enable-debug-command yes --enable-module-command yes
|
||||
- name: Create a single node cluster
|
||||
run: ./src/redis-cli cluster addslots $(for slot in {0..16383}; do echo $slot; done); sleep 5
|
||||
@@ -49,34 +50,36 @@ jobs:
|
||||
run: |
|
||||
./runtest \
|
||||
--host 127.0.0.1 --port 6379 \
|
||||
--verbose \
|
||||
--cluster-mode \
|
||||
--tags -slow
|
||||
- name: Archive redis log
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-external-cluster-log
|
||||
path: external-redis.log
|
||||
path: external-redis-cluster.log
|
||||
|
||||
test-external-nodebug:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'schedule' || github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: make REDIS_CFLAGS=-Werror
|
||||
- name: Start redis-server
|
||||
run: |
|
||||
./src/redis-server --daemonize yes --save "" --logfile external-redis.log
|
||||
./src/redis-server --daemonize yes --save "" --logfile external-redis-nodebug.log
|
||||
- name: Run external test
|
||||
run: |
|
||||
./runtest \
|
||||
--host 127.0.0.1 --port 6379 \
|
||||
--verbose \
|
||||
--tags "-slow -needs:debug"
|
||||
- name: Archive redis log
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-external-redis-log
|
||||
path: external-redis.log
|
||||
name: test-external-redis-nodebug-log
|
||||
path: external-redis-nodebug.log
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
name: redis_docs_sync
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
redis_docs_sync:
|
||||
if: github.repository == 'redis/redis'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.DOCS_APP_ID }}
|
||||
private-key: ${{ secrets.DOCS_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Invoke workflow on redis/docs
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
RELEASE_NAME: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
LATEST_RELEASE=$(
|
||||
curl -Ls \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${GH_TOKEN}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/redis/redis/releases/latest \
|
||||
| jq -r '.tag_name'
|
||||
)
|
||||
|
||||
if [[ "${LATEST_RELEASE}" == "${RELEASE_NAME}" ]]; then
|
||||
gh workflow run -R redis/docs redis_docs_sync.yaml -f release="${RELEASE_NAME}"
|
||||
fi
|
||||
@@ -12,9 +12,9 @@ jobs:
|
||||
reply-schemas-linter:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup nodejs
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
- name: Install packages
|
||||
run: npm install ajv
|
||||
- name: linter
|
||||
|
||||
@@ -16,10 +16,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: pip cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
|
||||
@@ -30,6 +30,7 @@ deps/lua/src/luac
|
||||
deps/lua/src/liblua.a
|
||||
deps/hdr_histogram/libhdrhistogram.a
|
||||
deps/fpconv/libfpconv.a
|
||||
deps/fast_float/libfast_float.a
|
||||
tests/tls/*
|
||||
.make-*
|
||||
.prerequisites
|
||||
|
||||
+244
-11
@@ -1,16 +1,249 @@
|
||||
Hello! This file is just a placeholder, since this is the "unstable" branch
|
||||
of Redis, the place where all the development happens.
|
||||
Redis Community Edition 8.0 release notes
|
||||
=========================================
|
||||
|
||||
There is no release notes for this branch, it gets forked into another branch
|
||||
every time there is a partial feature freeze in order to eventually create
|
||||
a new stable release.
|
||||
============================================================
|
||||
8.0-M04 (v7.9.227) Committed Sun 16 Mar 2025 11:00:00 IST
|
||||
============================================================
|
||||
|
||||
Usually "unstable" is stable enough for you to use it in development environments
|
||||
however you should never use it in production environments. It is possible
|
||||
to download the latest stable release here:
|
||||
This is the fourth Milestone of Redis Community Edition 8.0.
|
||||
|
||||
https://download.redis.io/redis-stable.tar.gz
|
||||
Milestones are non-feature-complete pre-releases. Pre-releases are not suitable for production use.
|
||||
Once we reach feature-completeness we will release RC1.
|
||||
|
||||
More information is available at https://redis.io
|
||||
### Headlines
|
||||
|
||||
8.0-M04 includes 3 new hash commands, performance improvements, and memory defragmentation improvements.
|
||||
|
||||
### Distributions
|
||||
|
||||
- Alpine and Debian Docker images - https://hub.docker.com/_/redis
|
||||
- Install using snap - see https://github.com/redis/redis-snap
|
||||
- Install using brew - see https://github.com/redis/homebrew-redis
|
||||
- Install using RPM and Debian APT - will be added on the GA release
|
||||
|
||||
### New Features
|
||||
|
||||
- #13798 Hash - new commands:
|
||||
- `HGETDEL` Get and delete the value of one or more fields of a given hash key
|
||||
- `HGETEX` Get the value of one or more fields of a given hash key, and optionally set their expiration
|
||||
- `HSETEX` Set the value of one or more fields of a given hash key, and optionally set their expiration
|
||||
- #13773 Add replication offset to AOF, allowing more robust way to determine which AOF has a more up-to-date data during recovery
|
||||
- #13740, #13763 shared secret - new mechanism to allow sending internal commands between nodes
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- #13804 Overflow on 32-bit systems when calculating idle time for eviction
|
||||
- #13793 `WAITAOF` returns prematurely
|
||||
- #13800 Remove `DENYOOM` from `HEXPIRE`, `HEXPIREAT`, `HPEXPIRE`, and `HPEXPIREAT`
|
||||
- #13632 Streams - wrong behavior of `XREAD +` after last entry
|
||||
|
||||
### Modules API
|
||||
|
||||
- #13788 `RedisModule_LoadDefaultConfigs` - load module configuration values from redis.conf
|
||||
- #13815 `RM_RegisterDefragFunc2` - support for incremental defragmentation of global module data
|
||||
- #13816 `RM_DefragRedisModuleDict` - allow modules to defrag `RedisModuleDict`
|
||||
- #13774 `RM_GetContextFlags` - add a `REDISMODULE_CTX_FLAGS_DEBUG_ENABLED` flag to execute debug commands
|
||||
|
||||
|
||||
### Performance and resource utilization improvements
|
||||
|
||||
- #13752 Reduce defrag CPU usage when defragmentation is ineffective
|
||||
- #13764 Reduce latency when a command is called consecutively
|
||||
- #13787 Optimize parsing data from clients, specifically multi-bulk (array) data
|
||||
- #13792 Optimize dictionary lookup by avoiding duplicate key length calculation during comparisons
|
||||
- #13796 Optimize expiration checks
|
||||
|
||||
|
||||
============================================================
|
||||
8.0-M03 (v7.9.226) Committed Mon 20 Jan 2025 15:00:00 IST
|
||||
============================================================
|
||||
|
||||
This is the third Milestone of Redis Community Edition 8.0.
|
||||
|
||||
Milestones are non-feature-complete pre-releases. Pre-releases are not suitable for production use.
|
||||
Once we reach feature-completeness we will release RC1.
|
||||
|
||||
### Headlines:
|
||||
|
||||
8.0-M03 introduces an improved replication mechanism which is more performant and robust, a new I/O threading implementation which enables throughput increase on multi-core environments, and many additional performance improvements. Both Alpine and Debian Docker images are now available on [Docker Hub](https://hub.docker.com/_/redis). A snap and Homebrew distributions are available as well.
|
||||
|
||||
|
||||
### Security fixes
|
||||
|
||||
- (CVE-2024-46981) Lua script may lead to remote code execution
|
||||
- (CVE-2024-51741) Denial-of-service due to malformed ACL selectors
|
||||
|
||||
### New Features
|
||||
|
||||
- #13695 New I/O threading implementation
|
||||
- #13732 New replication mechanism
|
||||
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- #13653 `MODULE LOADEX` - crash on nonexistent parameter name
|
||||
- #13661 `FUNCTION FLUSH` - memory leak when using jemalloc
|
||||
- #13626 Memory leak on failed RDB loading
|
||||
|
||||
### Other general improvements
|
||||
|
||||
- #13639 When `hide-user-data-from-log` is enabled - also print command tokens on crash
|
||||
- #13660 Add the Lua VM memory to memory overhead
|
||||
|
||||
### New metrics
|
||||
|
||||
- #13592 `INFO` - new `KEYSIZES` section includes key size distributions for basic data types
|
||||
- #13695 `INFO` - new `Threads` section includes I/O threading metrics
|
||||
|
||||
### Modules API
|
||||
|
||||
- #13666 `RedisModule_ACLCheckKeyPrefixPermissions` - check access permissions to any key matching a given prefix
|
||||
- #13676 `RedisModule_HashFieldMinExpire` - query the minimum expiration time over all the hash’s fields
|
||||
- #13676 `RedisModule_HashGet` - new `REDISMODULE_HASH_EXPIRE_TIME` flag - query the field expiration time
|
||||
- #13656 `RedisModule_RegisterXXXConfig` - allow registering unprefixed configuration parameters
|
||||
|
||||
### Configuration parameters
|
||||
|
||||
|
||||
- `replica-full-sync-buffer-limit` - maximum size of accumulated replication stream data on the replica side
|
||||
- `io-threads-do-reads` is no longer effective. The new I/O threading implementation always use threads for both reads and writes
|
||||
|
||||
### Performance and resource utilization improvements
|
||||
|
||||
- #13638 Optimize CRC64 performance
|
||||
- #13521 Optimize commands with large argument count - reuse c->argv after command execution
|
||||
- #13558 Optimize `PFCOUNT` and `PFMERGE` - SIMD acceleration
|
||||
- #13644 Optimize `GET` on high pipeline use-cases
|
||||
- #13646 Optimize `EXISTS` - prefetching and branch prediction hints
|
||||
- #13652 Optimize `LRANGE` - improve listpack handling and decoding efficiency
|
||||
- #13655 Optimize `HSET` - avoid unnecessary hash field creation or deletion
|
||||
- #13721 Optimize `LRANGE` and `HGETALL` - refactor client write preparation and handling
|
||||
|
||||
|
||||
============================================================
|
||||
8.0-M02 (v7.9.225) Committed Mon 28 Oct 2024 14:00:00 IST
|
||||
============================================================
|
||||
|
||||
This is the second Milestone of Redis Community Edition 8.0.
|
||||
|
||||
Milestones are non-feature-complete pre-releases. Pre-releases are not suitable for production use.
|
||||
Once we reach feature-completeness we will release RC1.
|
||||
|
||||
### Headlines:
|
||||
|
||||
8.0-M02 introduces significant performance improvements. Both Alpine and Debian Docker images are now available on [Docker Hub](https://hub.docker.com/_/redis). Additional distributions will be introduced in upcoming pre-releases.
|
||||
|
||||
### Supported upgrade paths (by replication or persistence) to 8.0-M02
|
||||
|
||||
- From previous Redis versions, without modules
|
||||
|
||||
The following upgrade paths (by replication or persistence) to 8.0-M02 are not yet tested and will be introduced in upcoming pre-releases:
|
||||
- From previous Redis versions with modules (RediSearch, RedisJSON, RedisTimeSeries, RedisBloom)
|
||||
- From Redis Stack 7.2 or 7.4
|
||||
|
||||
### Security fixes
|
||||
|
||||
- (CVE-2024-31449) Lua library commands may lead to stack overflow and potential RCE.
|
||||
- (CVE-2024-31227) Potential Denial-of-service due to malformed ACL selectors.
|
||||
- (CVE-2024-31228) Potential Denial-of-service due to unbounded pattern matching.
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- #13539 Hash: Fix key ref for a hash that no longer has fields with expiration on `RENAME`/`MOVE`/`SWAPDB`/`RESTORE`
|
||||
- #13512 Fix `TOUCH` command from a script in no-touch mode
|
||||
- #13468 Cluster: Fix cluster node config corruption caused by mixing shard-id and non-shard-id versions
|
||||
- #13608 Cluster: Fix `GET #` option in `SORT` command
|
||||
|
||||
### Modules API
|
||||
|
||||
- #13526 Extend `RedisModule_OpenKey` to read also expired keys and subkeys
|
||||
|
||||
### Performance and resource utilization improvements
|
||||
|
||||
- #11884 Optimize `ZADD` and `ZRANGE*` commands
|
||||
- #13530 Optimize `SSCAN` command in case of listpack or intset encoding
|
||||
- #13531 Optimize `HSCAN`/`ZSCAN` command in case of listpack encoding
|
||||
- #13520 Optimize commands that heavily rely on bulk/mbulk replies (example of `LRANGE`)
|
||||
- #13566 Optimize `ZUNION[STORE]` by avoiding redundant temporary dict usage
|
||||
- #13567 Optimize `SUNION`/`SDIFF` commands by avoiding redundant temporary dict usage
|
||||
- #11533 Avoid redundant `lpGet` to boost `quicklistCompare`
|
||||
- #13412 Reduce redundant call of `prepareClientToWrite` when call `addReply*` continuously
|
||||
|
||||
|
||||
===========================================================
|
||||
8.0-M01 (v7.9.224) Released Thu 12 Sep 2024 10:00:00 IST
|
||||
===========================================================
|
||||
|
||||
This is the first Milestone of Redis Community Edition 8.0.
|
||||
|
||||
Milestones are non-feature-complete pre-releases. Pre-releases are not suitable for production use.
|
||||
Once we reach feature-completeness we will release RC1.
|
||||
|
||||
### Headlines:
|
||||
|
||||
Redis 8.0 introduces new data structures: JSON, time series, and 5 probabilistic data structures (previously available as separate Redis modules) and incorporates the enhanced Redis Query Enginer (with vector search).
|
||||
|
||||
8.0-M01 is available as a Docker image and can be downloaded from [Docker Hub](https://hub.docker.com/_/redis). Additional distributions will be introduced in upcoming pre-releases.
|
||||
|
||||
### Supported upgrade paths (by replication or persistence) to 8.0-M01
|
||||
|
||||
|
||||
- From previous Redis versions, without modules
|
||||
|
||||
The following upgrade paths (by replication or persistence) to 8.0-M01 are not yet tested and will be introduced in upcoming pre-releases:
|
||||
- From previous Redis versions with modules (RediSearch, RedisJSON, RedisTimeSeries, RedisBloom)
|
||||
- From Redis Stack 7.2 or 7.4
|
||||
|
||||
### New Features in binary distributions
|
||||
|
||||
- 7 new data structures: JSON, Time series, Bloom filter, Cuckoo filter, Count-min sketch, Top-k, t-digest
|
||||
- The enhanced Redis Query Engine (with vector search)
|
||||
|
||||
### Potentially breaking changes
|
||||
|
||||
- #12272 `GETRANGE` returns an empty bulk when the negative end index is out of range
|
||||
- #12395 Optimize `SCAN` command when matching data type
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- #13510 Fix `RM_RdbLoad` to enable AOF after RDB loading is completed
|
||||
- #13489 `ACL CAT` - return module commands
|
||||
- #13476 Fix a race condition in the `cache_memory` of `functionsLibCtx`
|
||||
- #13473 Fix incorrect lag due to trimming stream via `XTRIM` command
|
||||
- #13338 Fix incorrect lag field in `XINFO` when tombstone is after the `last_id` of the consume group
|
||||
- #13470 On `HDEL` of last field - update the global hash field expiration data structure
|
||||
- #13465 Cluster: Pass extensions to node if extension processing is handled by it
|
||||
- #13443 Cluster: Ensure validity of myself when loading cluster config
|
||||
- #13422 Cluster: Fix `CLUSTER SHARDS` command returns empty array
|
||||
|
||||
### Modules API
|
||||
|
||||
- #13509 New API calls: `RM_DefragAllocRaw`, `RM_DefragFreeRaw`, and `RM_RegisterDefragCallbacks` - defrag API to allocate and free raw memory
|
||||
|
||||
### Performance and resource utilization improvements
|
||||
|
||||
- #13503 Avoid overhead of comparison function pointer calls in listpack `lpFind`
|
||||
- #13505 Optimize `STRING` datatype write commands
|
||||
- #13499 Optimize `SMEMBERS` command
|
||||
- #13494 Optimize `GEO*` commands reply
|
||||
- #13490 Optimize `HELLO` command
|
||||
- #13488 Optimize client query buffer
|
||||
- #12395 Optimize `SCAN` command when matching data type
|
||||
- #13529 Optimize `LREM`, `LPOS`, `LINSERT`, and `LINDEX` commands
|
||||
- #13516 Optimize `LRANGE` and other commands that perform several writes to client buffers per call
|
||||
- #13431 Avoid `used_memory` contention when updating from multiple threads
|
||||
|
||||
### Other general improvements
|
||||
|
||||
- #13495 Reply `-LOADING` on replica while flushing the db
|
||||
|
||||
### CLI tools
|
||||
|
||||
- #13411 redis-cli: Fix wrong `dbnum` showed after the client reconnected
|
||||
|
||||
### Notes
|
||||
|
||||
- No backward compatibility for replication or persistence.
|
||||
- Additional distributions, upgrade paths, features, and improvements will be introduced in upcoming pre-releases.
|
||||
- With the GA release of 8.0 we will deprecate Redis Stack.
|
||||
|
||||
Happy hacking!
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@ Examples of unacceptable behavior include:
|
||||
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
|
||||
* 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
|
||||
@@ -89,7 +89,7 @@ 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
|
||||
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
|
||||
|
||||
+80
-18
@@ -1,20 +1,82 @@
|
||||
Note: by contributing code to the Redis project in any form, including sending
|
||||
a pull request via Github, a code fragment or patch via private email or
|
||||
public discussion groups, you agree to release your code under the terms
|
||||
of the BSD license that you can find in the COPYING file included in the Redis
|
||||
source distribution. You will include BSD license in the COPYING file within
|
||||
each source file that you contribute.
|
||||
By contributing code to the Redis project in any form you agree to the Redis Software Grant and
|
||||
Contributor License Agreement attached below. Only contributions made under the Redis Software Grant
|
||||
and Contributor License Agreement may be accepted by Redis, and any contribution is subject to the
|
||||
terms of the Redis dual-license under RSALv2/SSPLv1 as described in the LICENSE.txt file included in
|
||||
the Redis source distribution.
|
||||
|
||||
# REDIS SOFTWARE GRANT AND CONTRIBUTOR LICENSE AGREEMENT
|
||||
|
||||
To specify the intellectual property license granted in any Contribution, Redis Ltd., ("**Redis**")
|
||||
requires a Software Grant and Contributor License Agreement ("**Agreement**"). This Agreement is for
|
||||
your protection as a contributor as well as the protection of Redis and its users; it does not
|
||||
change your rights to use your own Contribution for any other purpose.
|
||||
|
||||
By making any Contribution, You accept and agree to the following terms and conditions for the
|
||||
Contribution. Except for the license granted in this Agreement to Redis and the recipients of the
|
||||
software distributed by Redis, You reserve all right, title, and interest in and to Your
|
||||
Contribution.
|
||||
|
||||
1. **Definitions**
|
||||
|
||||
1.1. "**You**" (or "**Your**") means the copyright owner or legal entity authorized by the
|
||||
copyright owner that is entering into this Agreement with Redis. For legal entities, the entity
|
||||
making a Contribution and all other entities that Control, are Controlled by, or are under
|
||||
common Control with that entity are considered to be a single contributor. For the purposes of
|
||||
this definition, "**Control**" means (i) the power, direct or indirect, to cause the direction
|
||||
or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty
|
||||
percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
1.2. "**Contribution**" means the code, documentation, or any original work of authorship,
|
||||
including any modifications or additions to an existing work described above.
|
||||
|
||||
2. "**Work**" means any software project stewarded by Redis.
|
||||
|
||||
3. **Grant of Copyright License**. Subject to the terms and conditions of this Agreement, You grant
|
||||
to Redis and to the recipients of the software distributed by Redis a perpetual, worldwide,
|
||||
non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
|
||||
derivative works of, publicly display, publicly perform, sublicense, and distribute Your
|
||||
Contribution and such derivative works.
|
||||
|
||||
4. **Grant of Patent License**. Subject to the terms and conditions of this Agreement, You grant to
|
||||
Redis and to the recipients of the software distributed by Redis a perpetual, worldwide,
|
||||
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent
|
||||
license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable by You that are necessarily
|
||||
infringed by Your Contribution alone or by a combination of Your Contribution with the Work to
|
||||
which such Contribution was submitted. If any entity institutes patent litigation against You or
|
||||
any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your
|
||||
Contribution, or the Work to which you have contributed, constitutes a direct or contributory
|
||||
patent infringement, then any patent licenses granted to the claimant entity under this Agreement
|
||||
for that Contribution or Work terminate as of the date such litigation is filed.
|
||||
|
||||
5. **Representations and Warranties**. You represent and warrant that: (i) You are legally entitled
|
||||
to grant the above licenses; and (ii) if You are an entity, each employee or agent designated by
|
||||
You is authorized to submit the Contribution on behalf of You; and (iii) your Contribution is
|
||||
Your original work, and that it will not infringe on any third party's intellectual property
|
||||
right(s).
|
||||
|
||||
6. **Disclaimer**. You are not expected to provide support for Your Contribution, except to the
|
||||
extent You desire to provide support. You may provide support for free, for a fee, or not at all.
|
||||
Unless required by applicable law or agreed to in writing, You provide Your Contribution on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,
|
||||
MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
7. **Enforceability**. Nothing in this Agreement will be construed as creating any joint venture,
|
||||
employment relationship, or partnership between You and Redis. If any provision of this Agreement
|
||||
is held to be unenforceable, the remaining provisions of this Agreement will not be affected.
|
||||
This represents the entire agreement between You and Redis relating to the Contribution.
|
||||
|
||||
# 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 should be asked on Discord:
|
||||
|
||||
https://groups.google.com/forum/m/#!forum/Redis-db
|
||||
https://discord.com/invite/redis
|
||||
|
||||
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.
|
||||
bugs in the GitHub issues system. We'll be delighted to help you and provide
|
||||
all the support on Discord.
|
||||
|
||||
There is also an active community of Redis users at Stack Overflow:
|
||||
|
||||
@@ -33,24 +95,24 @@ straight away: if your feature is not a conceptual fit you'll lose a lot of
|
||||
time writing the code without any reason. Start by posting in the mailing list
|
||||
and creating an issue at Github with the description of, exactly, what you want
|
||||
to accomplish and why. Use cases are important for features to be accepted.
|
||||
Here you'll see if there is consensus about your idea.
|
||||
Here you can see if there is consensus about your idea.
|
||||
|
||||
2. If in step 1 you get an acknowledgment from the project leaders, use the
|
||||
following procedure to submit a patch:
|
||||
|
||||
a. Fork Redis on github ( https://docs.github.com/en/github/getting-started-with-github/fork-a-repo )
|
||||
a. Fork Redis on GitHub ( https://docs.github.com/en/github/getting-started-with-github/fork-a-repo )
|
||||
b. Create a topic branch (git checkout -b my_branch)
|
||||
c. Push to your branch (git push origin my_branch)
|
||||
d. Initiate a pull request on github ( https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request )
|
||||
d. Initiate a pull request on GitHub ( https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request )
|
||||
e. Done :)
|
||||
|
||||
3. Keep in mind that we are very overloaded, so issues and PRs sometimes wait
|
||||
for a *very* long time. However this is not lack of interest, as the project
|
||||
for a *very* long time. However this is not a lack of interest, as the project
|
||||
gets more and more users, we find ourselves in a constant need to prioritize
|
||||
certain issues/PRs over others. If you think your issue/PR is very important
|
||||
try to popularize it, have other users commenting and sharing their point of
|
||||
view and so forth. This helps.
|
||||
view, and so forth. This helps.
|
||||
|
||||
4. For minor fixes just open a pull request on Github.
|
||||
4. For minor fixes - open a pull request on GitHub.
|
||||
|
||||
Thanks!
|
||||
Additional information on the RSALv2/SSPLv1 dual-license is also found in the LICENSE.txt file.
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
Copyright (c) 2006-2020, Salvatore Sanfilippo
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* 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.
|
||||
+733
@@ -0,0 +1,733 @@
|
||||
Starting on March 20th, 2024, Redis follows a dual-licensing model with all Redis project code
|
||||
contributions under version 7.4 and subsequent releases governed by the Redis Software Grant and
|
||||
Contributor License Agreement. After this date, contributions are subject to the user's choice of
|
||||
the Redis Source Available License v2 (RSALv2) or the Server Side Public License v1 (SSPLv1), as
|
||||
follows:
|
||||
|
||||
|
||||
1. Redis Source Available License 2.0 (RSALv2) Agreement
|
||||
========================================================
|
||||
|
||||
Last Update: December 30, 2023
|
||||
|
||||
Acceptance
|
||||
----------
|
||||
|
||||
This Agreement sets forth the terms and conditions on which the Licensor
|
||||
makes available the Software. By installing, downloading, accessing,
|
||||
Using, or distributing any of the Software, You agree to all of the
|
||||
terms and conditions of this Agreement.
|
||||
|
||||
If You are receiving the Software on behalf of Your Company, You
|
||||
represent and warrant that You have the authority to agree to this
|
||||
Agreement on behalf of such entity.
|
||||
|
||||
The Licensor reserves the right to update this Agreement from time to
|
||||
time.
|
||||
|
||||
The terms below have the meanings set forth below for purposes of this
|
||||
Agreement:
|
||||
|
||||
Definitions
|
||||
-----------
|
||||
|
||||
Agreement: this Redis Source Available License 2.0 Agreement.
|
||||
|
||||
Control: ownership, directly or indirectly, of substantially all the
|
||||
assets of an entity, or the power to direct its management and policies
|
||||
by vote, contract, or otherwise.
|
||||
|
||||
License: the License as described in the License paragraph below.
|
||||
|
||||
Licensor: the entity offering these terms, which includes Redis Ltd. on
|
||||
behalf of itself and its subsidiaries and affiliates worldwide.
|
||||
|
||||
Modify, Modified, or Modification: copy from or adapt all or part of the
|
||||
work in a fashion requiring copyright permission other than making an
|
||||
exact copy. The resulting work is called a Modified version of the
|
||||
earlier work.
|
||||
|
||||
Redis: the Redis software as described in redis.com redis.io.
|
||||
|
||||
Software: certain Software components designed to work with Redis and
|
||||
provided to You under this Agreement.
|
||||
|
||||
Trademark: the trademarks, service marks, and any other similar rights.
|
||||
|
||||
Use: anything You do with the Software requiring one of Your Licenses.
|
||||
|
||||
You: the recipient of the Software, the individual or entity on whose
|
||||
behalf You are agreeing to this Agreement.
|
||||
|
||||
Your Company: any legal entity, sole proprietorship, or other kind of
|
||||
organization that You work for, plus all organizations that have control
|
||||
over, are under the control of, or are under common control with that
|
||||
organization.
|
||||
|
||||
Your Licenses: means all the Licenses granted to You for the Software
|
||||
under this Agreement.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
The Licensor grants You a non-exclusive, royalty-free, worldwide,
|
||||
non-sublicensable, non-transferable license to use, copy, distribute,
|
||||
make available, and prepare derivative works of the Software, in each
|
||||
case subject to the limitations and conditions below.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
You may not make the functionality of the Software or a Modified version
|
||||
available to third parties as a service or distribute the Software or a
|
||||
Modified version in a manner that makes the functionality of the
|
||||
Software available to third parties.
|
||||
|
||||
Making the functionality of the Software or Modified version available
|
||||
to third parties includes, without limitation, enabling third parties to
|
||||
interact with the functionality of the Software or Modified version in
|
||||
distributed form or remotely through a computer network, offering a
|
||||
product or service, the value of which entirely or primarily derives
|
||||
from the value of the Software or Modified version, or offering a
|
||||
product or service that accomplishes for users the primary purpose of
|
||||
the Software or Modified version.
|
||||
|
||||
You may not alter, remove, or obscure any licensing, copyright, or other
|
||||
notices of the Licensor in the Software. Any use of the Licensor's
|
||||
Trademarks is subject to applicable law.
|
||||
|
||||
Patents
|
||||
-------
|
||||
|
||||
The Licensor grants You a License, under any patent claims the Licensor
|
||||
can License, or becomes able to License, to make, have made, use, sell,
|
||||
offer for sale, import and have imported the Software, in each case
|
||||
subject to the limitations and conditions in this License. This License
|
||||
does not cover any patent claims that You cause to be infringed by
|
||||
Modifications or additions to the Software. If You or Your Company make
|
||||
any written claim that the Software infringes or contributes to
|
||||
infringement of any patent, your patent License for the Software granted
|
||||
under this Agreement ends immediately. If Your Company makes such a
|
||||
claim, your patent License ends immediately for work on behalf of Your
|
||||
Company.
|
||||
|
||||
Notices
|
||||
-------
|
||||
|
||||
You must ensure that anyone who gets a copy of any part of the Software
|
||||
from You also gets a copy of the terms and conditions in this Agreement.
|
||||
|
||||
If You modify the Software, You must include in any Modified copies of
|
||||
the Software prominent notices stating that You have Modified the
|
||||
Software.
|
||||
|
||||
No Other Rights
|
||||
---------------
|
||||
|
||||
The terms and conditions of this Agreement do not imply any Licenses
|
||||
other than those expressly granted in this Agreement.
|
||||
|
||||
Termination
|
||||
-----------
|
||||
|
||||
If You Use the Software in violation of this Agreement, such Use is not
|
||||
Licensed, and Your Licenses will automatically terminate. If the
|
||||
Licensor provides You with a notice of your violation, and You cease all
|
||||
violations of this License no later than 30 days after You receive that
|
||||
notice, Your Licenses will be reinstated retroactively. However, if You
|
||||
violate this Agreement after such reinstatement, any additional
|
||||
violation of this Agreement will cause your Licenses to terminate
|
||||
automatically and permanently.
|
||||
|
||||
No Liability
|
||||
------------
|
||||
|
||||
As far as the law allows, the Software comes as is, without any
|
||||
warranty or condition, and the Licensor will not be liable to You for
|
||||
any damages arising out of this Agreement or the Use or nature of the
|
||||
Software, under any kind of legal claim.
|
||||
|
||||
Governing Law and Jurisdiction
|
||||
------------------------------
|
||||
|
||||
If You are located in Asia, Pacific, Americas, or other jurisdictions
|
||||
not listed below, the Agreement will be construed and enforced in all
|
||||
respects in accordance with the laws of the State of California, U.S.A.,
|
||||
without reference to its choice of law rules. The courts located in the
|
||||
County of Santa Clara, California, have exclusive jurisdiction for all
|
||||
purposes relating to this Agreement.
|
||||
|
||||
If You are located in Israel, the Agreement will be construed and
|
||||
enforced in all respects in accordance with the laws of the State of
|
||||
Israel without reference to its choice of law rules. The courts located
|
||||
in the Central District of the State of Israel have exclusive
|
||||
jurisdiction for all purposes relating to this Agreement.
|
||||
|
||||
If You are located in Europe, United Kingdom, Middle East or Africa, the
|
||||
Agreement will be construed and enforced in all respects in accordance
|
||||
with the laws of England and Wales without reference to its choice of
|
||||
law rules. The competent courts located in London, England, have
|
||||
exclusive jurisdiction for all purposes relating to this Agreement.
|
||||
|
||||
|
||||
|
||||
2. Server Side Public License (SSPL)
|
||||
====================================
|
||||
|
||||
Server Side Public License
|
||||
VERSION 1, OCTOBER 16, 2018
|
||||
|
||||
Copyright (c) 2018 MongoDB, Inc.
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to Server Side Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work in
|
||||
a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based on
|
||||
the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through a
|
||||
computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices" to the
|
||||
extent that it includes a convenient and prominently visible feature that
|
||||
(1) displays an appropriate copyright notice, and (2) tells the user that
|
||||
there is no warranty for the work (except to the extent that warranties
|
||||
are provided), that licensees may convey the work under this License, and
|
||||
how to view a copy of this License. If the interface presents a list of
|
||||
user commands or options, such as a menu, a prominent item in the list
|
||||
meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work for
|
||||
making modifications to it. "Object code" means any non-source form of a
|
||||
work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that is
|
||||
widely used among developers working in that language. The "System
|
||||
Libraries" of an executable work include anything, other than the work as
|
||||
a whole, that (a) is included in the normal form of packaging a Major
|
||||
Component, but which is not part of that Major Component, and (b) serves
|
||||
only to enable use of the work with that Major Component, or to implement
|
||||
a Standard Interface for which an implementation is available to the
|
||||
public in source code form. A "Major Component", in this context, means a
|
||||
major essential component (kernel, window system, and so on) of the
|
||||
specific operating system (if any) on which the executable work runs, or
|
||||
a compiler used to produce the work, or an object code interpreter used
|
||||
to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all the
|
||||
source code needed to generate, install, and (for an executable work) run
|
||||
the object code and to modify the work, including scripts to control
|
||||
those activities. However, it does not include the work's System
|
||||
Libraries, or general-purpose tools or generally available free programs
|
||||
which are used unmodified in performing those activities but which are
|
||||
not part of the work. For example, Corresponding Source includes
|
||||
interface definition files associated with source files for the work, and
|
||||
the source code for shared libraries and dynamically linked subprograms
|
||||
that the work is specifically designed to require, such as by intimate
|
||||
data communication or control flow between those subprograms and other
|
||||
parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users can
|
||||
regenerate automatically from other parts of the Corresponding Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program, subject to section 13. The
|
||||
output from running a covered work is covered by this License only if the
|
||||
output, given its content, constitutes a covered work. This License
|
||||
acknowledges your rights of fair use or other equivalent, as provided by
|
||||
copyright law. Subject to section 13, you may make, run and propagate
|
||||
covered works that you do not convey, without conditions so long as your
|
||||
license otherwise remains in force. You may convey covered works to
|
||||
others for the sole purpose of having them make modifications exclusively
|
||||
for you, or provide you with facilities for running those works, provided
|
||||
that you comply with the terms of this License in conveying all
|
||||
material for which you do not control copyright. Those thus making or
|
||||
running the covered works for you must do so exclusively on your
|
||||
behalf, under your direction and control, on terms that prohibit them
|
||||
from making any copies of your copyrighted material outside their
|
||||
relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under the
|
||||
conditions stated below. Sublicensing is not allowed; section 10 makes it
|
||||
unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article 11
|
||||
of the WIPO copyright treaty adopted on 20 December 1996, or similar laws
|
||||
prohibiting or restricting circumvention of such measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention is
|
||||
effected by exercising rights under this License with respect to the
|
||||
covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's users,
|
||||
your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice; keep
|
||||
intact all notices stating that this License and any non-permissive terms
|
||||
added in accord with section 7 apply to the code; keep intact all notices
|
||||
of the absence of any warranty; and give all recipients a copy of this
|
||||
License along with the Program. You may charge any price or no price for
|
||||
each copy that you convey, and you may offer support or warranty
|
||||
protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the terms
|
||||
of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified it,
|
||||
and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is released
|
||||
under this License and any conditions added under section 7. This
|
||||
requirement modifies the requirement in section 4 to "keep intact all
|
||||
notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this License to
|
||||
anyone who comes into possession of a copy. This License will therefore
|
||||
apply, along with any applicable section 7 additional terms, to the
|
||||
whole of the work, and all its parts, regardless of how they are
|
||||
packaged. This License gives no permission to license the work in any
|
||||
other way, but it does not invalidate such permission if you have
|
||||
separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your work
|
||||
need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work, and
|
||||
which are not combined with it such as to form a larger program, in or on
|
||||
a volume of a storage or distribution medium, is called an "aggregate" if
|
||||
the compilation and its resulting copyright are not used to limit the
|
||||
access or legal rights of the compilation's users beyond what the
|
||||
individual works permit. Inclusion of a covered work in an aggregate does
|
||||
not cause this License to apply to the other parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms of
|
||||
sections 4 and 5, provided that you also convey the machine-readable
|
||||
Corresponding Source under the terms of this License, in one of these
|
||||
ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium customarily
|
||||
used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a written
|
||||
offer, valid for at least three years and valid for as long as you
|
||||
offer spare parts or customer support for that product model, to give
|
||||
anyone who possesses the object code either (1) a copy of the
|
||||
Corresponding Source for all the software in the product that is
|
||||
covered by this License, on a durable physical medium customarily used
|
||||
for software interchange, for a price no more than your reasonable cost
|
||||
of physically performing this conveying of source, or (2) access to
|
||||
copy the Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This alternative is
|
||||
allowed only occasionally and noncommercially, and only if you received
|
||||
the object code with such an offer, in accord with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated place
|
||||
(gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to copy
|
||||
the object code is a network server, the Corresponding Source may be on
|
||||
a different server (operated by you or a third party) that supports
|
||||
equivalent copying facilities, provided you maintain clear directions
|
||||
next to the object code saying where to find the Corresponding Source.
|
||||
Regardless of what server hosts the Corresponding Source, you remain
|
||||
obligated to ensure that it is available for as long as needed to
|
||||
satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided you
|
||||
inform other peers where the object code and Corresponding Source of
|
||||
the work are being offered to the general public at no charge under
|
||||
subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be included
|
||||
in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as part
|
||||
of a transaction in which the right of possession and use of the User
|
||||
Product is transferred to the recipient in perpetuity or for a fixed term
|
||||
(regardless of how the transaction is characterized), the Corresponding
|
||||
Source conveyed under this section must be accompanied by the
|
||||
Installation Information. But this requirement does not apply if neither
|
||||
you nor any third party retains the ability to install modified object
|
||||
code on the User Product (for example, the work has been installed in
|
||||
ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access
|
||||
to a network may be denied when the modification itself materially
|
||||
and adversely affects the operation of the network or violates the
|
||||
rules and protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, in
|
||||
accord with this section must be in a format that is publicly documented
|
||||
(and with an implementation available to the public in source code form),
|
||||
and must require no special password or key for unpacking, reading or
|
||||
copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall be
|
||||
treated as though they were included in this License, to the extent that
|
||||
they are valid under applicable law. If additional permissions apply only
|
||||
to part of the Program, that part may be used separately under those
|
||||
permissions, but the entire Program remains governed by this License
|
||||
without regard to the additional permissions. When you convey a copy of
|
||||
a covered work, you may at your option remove any additional permissions
|
||||
from that copy, or from any part of it. (Additional permissions may be
|
||||
written to require their own removal in certain cases when you modify the
|
||||
work.) You may place additional permissions on material, added by you to
|
||||
a covered work, for which you have or can give appropriate copyright
|
||||
permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you add
|
||||
to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some trade
|
||||
names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that material
|
||||
by anyone who conveys the material (or modified versions of it) with
|
||||
contractual assumptions of liability to the recipient, for any
|
||||
liability that these contractual assumptions directly impose on those
|
||||
licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further restriction,
|
||||
you may remove that term. If a license document contains a further
|
||||
restriction but permits relicensing or conveying under this License, you
|
||||
may add to a covered work material governed by the terms of that license
|
||||
document, provided that the further restriction does not survive such
|
||||
relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you must
|
||||
place, in the relevant source files, a statement of the additional terms
|
||||
that apply to those files, or a notice indicating where to find the
|
||||
applicable terms. Additional terms, permissive or non-permissive, may be
|
||||
stated in the form of a separately written license, or stated as
|
||||
exceptions; the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or modify
|
||||
it is void, and will automatically terminate your rights under this
|
||||
License (including any patent licenses granted under the third paragraph
|
||||
of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your license
|
||||
from a particular copyright holder is reinstated (a) provisionally,
|
||||
unless and until the copyright holder explicitly and finally terminates
|
||||
your license, and (b) permanently, if the copyright holder fails to
|
||||
notify you of the violation by some reasonable means prior to 60 days
|
||||
after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is reinstated
|
||||
permanently if the copyright holder notifies you of the violation by some
|
||||
reasonable means, this is the first time you have received notice of
|
||||
violation of this License (for any work) from that copyright holder, and
|
||||
you cure the violation prior to 30 days after your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or run a
|
||||
copy of the Program. Ancillary propagation of a covered work occurring
|
||||
solely as a consequence of using peer-to-peer transmission to receive a
|
||||
copy likewise does not require acceptance. However, nothing other than
|
||||
this License grants you permission to propagate or modify any covered
|
||||
work. These actions infringe copyright if you do not accept this License.
|
||||
Therefore, by modifying or propagating a covered work, you indicate your
|
||||
acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically receives
|
||||
a license from the original licensors, to run, modify and propagate that
|
||||
work, subject to this License. You are not responsible for enforcing
|
||||
compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered work
|
||||
results from an entity transaction, each party to that transaction who
|
||||
receives a copy of the work also receives whatever licenses to the work
|
||||
the party's predecessor in interest had or could give under the previous
|
||||
paragraph, plus a right to possession of the Corresponding Source of the
|
||||
work from the predecessor in interest, if the predecessor has it or can
|
||||
get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the rights
|
||||
granted or affirmed under this License. For example, you may not impose a
|
||||
license fee, royalty, or other charge for exercise of rights granted
|
||||
under this License, and you may not initiate litigation (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that any patent claim
|
||||
is infringed by making, using, selling, offering for sale, or importing
|
||||
the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The work
|
||||
thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims owned or
|
||||
controlled by the contributor, whether already acquired or hereafter
|
||||
acquired, that would be infringed by some manner, permitted by this
|
||||
License, of making, using, or selling its contributor version, but do not
|
||||
include claims that would be infringed only as a consequence of further
|
||||
modification of the contributor version. For purposes of this definition,
|
||||
"control" includes the right to grant patent sublicenses in a manner
|
||||
consistent with the requirements of this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to make,
|
||||
use, sell, offer for sale, import and otherwise run, modify and propagate
|
||||
the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a party
|
||||
means to make such an agreement or commitment not to enforce a patent
|
||||
against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, and
|
||||
the Corresponding Source of the work is not available for anyone to copy,
|
||||
free of charge and under the terms of this License, through a publicly
|
||||
available network server or other readily accessible means, then you must
|
||||
either (1) cause the Corresponding Source to be so available, or (2)
|
||||
arrange to deprive yourself of the benefit of the patent license for this
|
||||
particular work, or (3) arrange, in a manner consistent with the
|
||||
requirements of this License, to extend the patent license to downstream
|
||||
recipients. "Knowingly relying" means you have actual knowledge that, but
|
||||
for the patent license, your conveying the covered work in a country, or
|
||||
your recipient's use of the covered work in a country, would infringe
|
||||
one or more identifiable patents in that country that you have reason
|
||||
to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties receiving
|
||||
the covered work authorizing them to use, propagate, modify or convey a
|
||||
specific copy of the covered work, then the patent license you grant is
|
||||
automatically extended to all recipients of the covered work and works
|
||||
based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within the
|
||||
scope of its coverage, prohibits the exercise of, or is conditioned on
|
||||
the non-exercise of one or more of the rights that are specifically
|
||||
granted under this License. You may not convey a covered work if you are
|
||||
a party to an arrangement with a third party that is in the business of
|
||||
distributing software, under which you make payment to the third party
|
||||
based on the extent of your activity of conveying the work, and under
|
||||
which the third party grants, to any of the parties who would receive the
|
||||
covered work from you, a discriminatory patent license (a) in connection
|
||||
with copies of the covered work conveyed by you (or copies made from
|
||||
those copies), or (b) primarily for and in connection with specific
|
||||
products or compilations that contain the covered work, unless you
|
||||
entered into that arrangement, or that patent license was granted, prior
|
||||
to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting any
|
||||
implied license or other defenses to infringement that may otherwise be
|
||||
available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot use,
|
||||
propagate or convey a covered work so as to satisfy simultaneously your
|
||||
obligations under this License and any other pertinent obligations, then
|
||||
as a consequence you may not use, propagate or convey it at all. For
|
||||
example, if you agree to terms that obligate you to collect a royalty for
|
||||
further conveying from those to whom you convey the Program, the only way
|
||||
you could satisfy both those terms and this License would be to refrain
|
||||
entirely from conveying the Program.
|
||||
|
||||
13. Offering the Program as a Service.
|
||||
|
||||
If you make the functionality of the Program or a modified version
|
||||
available to third parties as a service, you must make the Service Source
|
||||
Code available via network download to everyone at no charge, under the
|
||||
terms of this License. Making the functionality of the Program or
|
||||
modified version available to third parties as a service includes,
|
||||
without limitation, enabling third parties to interact with the
|
||||
functionality of the Program or modified version remotely through a
|
||||
computer network, offering a service the value of which entirely or
|
||||
primarily derives from the value of the Program or modified version, or
|
||||
offering a service that accomplishes for users the primary purpose of the
|
||||
Program or modified version.
|
||||
|
||||
"Service Source Code" means the Corresponding Source for the Program or
|
||||
the modified version, and the Corresponding Source for all programs that
|
||||
you use to make the Program or modified version available as a service,
|
||||
including, without limitation, management software, user interfaces,
|
||||
application program interfaces, automation software, monitoring software,
|
||||
backup software, storage software and hosting software, all such that a
|
||||
user could run an instance of the service using the Service Source Code
|
||||
you make available.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
MongoDB, Inc. may publish revised and/or new versions of the Server Side
|
||||
Public License from time to time. Such new versions will be similar in
|
||||
spirit to the present version, but may differ in detail to address new
|
||||
problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies that a certain numbered version of the Server Side Public
|
||||
License "or any later version" applies to it, you have the option of
|
||||
following the terms and conditions either of that numbered version or of
|
||||
any later version published by MongoDB, Inc. If the Program does not
|
||||
specify a version number of the Server Side Public License, you may
|
||||
choose any version ever published by MongoDB, Inc.
|
||||
|
||||
If the Program specifies that a proxy can decide which future versions of
|
||||
the Server Side Public License can be used, that proxy's public statement
|
||||
of acceptance of a version permanently authorizes you to choose that
|
||||
version for the Program.
|
||||
|
||||
Later license versions may give you additional or different permissions.
|
||||
However, no additional obligations are imposed on any author or copyright
|
||||
holder as a result of your choosing to follow a later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING
|
||||
ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF
|
||||
THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO
|
||||
LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU
|
||||
OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided above
|
||||
cannot be given local legal effect according to their terms, reviewing
|
||||
courts shall apply local law that most closely approximates an absolute
|
||||
waiver of all civil liability in connection with the Program, unless a
|
||||
warranty or assumption of liability accompanies a copy of the Program in
|
||||
return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -1,11 +1,16 @@
|
||||
# Top level makefile, the real shit is at src/Makefile
|
||||
# Top level makefile, the real stuff is at ./src/Makefile and in ./modules/Makefile
|
||||
|
||||
SUBDIRS = src
|
||||
ifeq ($(BUILD_WITH_MODULES), yes)
|
||||
SUBDIRS += modules
|
||||
endif
|
||||
|
||||
default: all
|
||||
|
||||
.DEFAULT:
|
||||
cd src && $(MAKE) $@
|
||||
for dir in $(SUBDIRS); do $(MAKE) -C $$dir $@; done
|
||||
|
||||
install:
|
||||
cd src && $(MAKE) $@
|
||||
for dir in $(SUBDIRS); do $(MAKE) -C $$dir $@; done
|
||||
|
||||
.PHONY: install
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
[](https://codecov.io/github/redis/redis)
|
||||
|
||||
This README is just a fast *quick start* document. You can find more detailed documentation at [redis.io](https://redis.io).
|
||||
|
||||
What is Redis?
|
||||
--------------
|
||||
---
|
||||
|
||||
Redis is often referred to as a *data structures* server. What this means is that Redis provides access to mutable data structures via a set of commands, which are sent using a *server-client* model with TCP sockets and a simple protocol. So different processes can query and modify the same data structures in a shared way.
|
||||
|
||||
@@ -15,13 +17,23 @@ 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
|
||||
* Try Redis directly inside your browser. https://try.redis.io
|
||||
* Introduction to Redis data types. https://redis.io/docs/latest/develop/data-types/
|
||||
|
||||
* The full list of Redis commands. https://redis.io/commands
|
||||
* There is much more inside the official Redis documentation. https://redis.io/documentation
|
||||
|
||||
What is Redis Community Edition?
|
||||
---
|
||||
|
||||
Redis OSS was renamed Redis Community Edition (CE) with the v7.4 release.
|
||||
|
||||
Redis Ltd. also offers [Redis Software](https://redis.io/enterprise/), a self-managed software with additional compliance, reliability, and resiliency for enterprise scaling,
|
||||
and [Redis Cloud](https://redis.io/cloud/), a fully managed service integrated with Google Cloud, Azure, and AWS for production-ready apps.
|
||||
|
||||
Read more about the differences between Redis Community Edition and Redis [here](https://redis.io/comparisons/oss-vs-enterprise/).
|
||||
|
||||
Building Redis
|
||||
--------------
|
||||
---
|
||||
|
||||
Redis can be compiled and used on Linux, OSX, OpenBSD, NetBSD, FreeBSD.
|
||||
We support big endian and little endian architectures, and both 32 bit
|
||||
@@ -65,7 +77,7 @@ installed):
|
||||
|
||||
|
||||
Fixing build problems with dependencies or cached build options
|
||||
---------
|
||||
---
|
||||
|
||||
Redis has some dependencies which are included in the `deps` directory.
|
||||
`make` does not automatically rebuild dependencies even if something in
|
||||
@@ -85,7 +97,7 @@ those options are cached indefinitely until you issue a `make distclean`
|
||||
command.
|
||||
|
||||
Fixing problems building 32 bit binaries
|
||||
---------
|
||||
---
|
||||
|
||||
If after building Redis with a 32 bit target you need to rebuild it
|
||||
with a 64 bit target, or the other way around, you need to perform a
|
||||
@@ -99,7 +111,7 @@ the following steps:
|
||||
`make CFLAGS="-m32 -march=native" LDFLAGS="-m32"`
|
||||
|
||||
Allocator
|
||||
---------
|
||||
---
|
||||
|
||||
Selecting a non-default memory allocator when building Redis is done by setting
|
||||
the `MALLOC` environment variable. Redis is compiled and linked against libc
|
||||
@@ -116,7 +128,7 @@ 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
|
||||
@@ -128,7 +140,7 @@ To build with support for the processor's internal instruction clock, use:
|
||||
% make CFLAGS="-DUSE_PROCESSOR_CLOCK"
|
||||
|
||||
Verbose build
|
||||
-------------
|
||||
---
|
||||
|
||||
Redis will build with a user-friendly colorized output by default.
|
||||
If you want to see a more verbose output, use the following:
|
||||
@@ -136,7 +148,7 @@ If you want to see a more verbose output, use the following:
|
||||
% make V=1
|
||||
|
||||
Running Redis
|
||||
-------------
|
||||
---
|
||||
|
||||
To run Redis with the default configuration, just type:
|
||||
|
||||
@@ -158,14 +170,14 @@ as options using the command line. Examples:
|
||||
All the options in redis.conf are also supported as options using the command
|
||||
line, with exactly the same name.
|
||||
|
||||
Running Redis with TLS:
|
||||
------------------
|
||||
Running Redis with TLS
|
||||
---
|
||||
|
||||
Please consult the [TLS.md](TLS.md) file for more information on
|
||||
how to use Redis with TLS.
|
||||
|
||||
Playing with Redis
|
||||
------------------
|
||||
---
|
||||
|
||||
You can use redis-cli to play with Redis. Start a redis-server instance,
|
||||
then in another terminal try the following:
|
||||
@@ -187,7 +199,7 @@ then in another terminal try the following:
|
||||
You can find the list of all the available commands at https://redis.io/commands.
|
||||
|
||||
Installing Redis
|
||||
-----------------
|
||||
---
|
||||
|
||||
In order to install Redis binaries into /usr/local/bin, just use:
|
||||
|
||||
@@ -215,25 +227,35 @@ You'll be able to stop and start Redis using the script named
|
||||
`/etc/init.d/redis_<portnumber>`, for instance `/etc/init.d/redis_6379`.
|
||||
|
||||
Code contributions
|
||||
-----------------
|
||||
---
|
||||
|
||||
Note: By contributing code to the Redis project in any form, including sending
|
||||
a pull request via Github, a code fragment or patch via private email or
|
||||
public discussion groups, you agree to release your code under the terms
|
||||
of the BSD license that you can find in the [COPYING][1] file included in the Redis
|
||||
source distribution.
|
||||
By contributing code to the Redis project in any form, including sending a pull request via GitHub,
|
||||
a code fragment or patch via private email or public discussion groups, you agree to release your
|
||||
code under the terms of the [Redis Software Grant and Contributor License Agreement][1]. Redis software
|
||||
contains contributions to the original Redis core project, which are owned by their contributors and
|
||||
licensed under the 3BSD license. Any copy of that license in this repository applies only to those
|
||||
contributions. Redis releases all Redis Community Edition versions from 7.4.x and thereafter under the
|
||||
RSALv2/SSPL dual-license as described in the [LICENSE.txt][2] file included in the Redis Community Edition source distribution.
|
||||
|
||||
Please see the [CONTRIBUTING.md][2] file in this source distribution for more
|
||||
information. For security bugs and vulnerabilities, please see [SECURITY.md][3].
|
||||
Please see the [CONTRIBUTING.md][1] file in this source distribution for more information. For
|
||||
security bugs and vulnerabilities, please see [SECURITY.md][3].
|
||||
|
||||
[1]: https://github.com/redis/redis/blob/unstable/COPYING
|
||||
[2]: https://github.com/redis/redis/blob/unstable/CONTRIBUTING.md
|
||||
[1]: https://github.com/redis/redis/blob/unstable/CONTRIBUTING.md
|
||||
[2]: https://github.com/redis/redis/blob/unstable/LICENSE.txt
|
||||
[3]: https://github.com/redis/redis/blob/unstable/SECURITY.md
|
||||
|
||||
Redis Trademarks
|
||||
---
|
||||
|
||||
The purpose of a trademark is to identify the goods and services of a person or company without
|
||||
causing confusion. As the registered owner of its name and logo, Redis accepts certain limited uses
|
||||
of its trademarks but it has requirements that must be followed as described in its Trademark
|
||||
Guidelines available at: https://redis.com/legal/trademark-guidelines/.
|
||||
|
||||
Redis internals
|
||||
===
|
||||
|
||||
If you are reading this README you are likely in front of a Github page
|
||||
If you are reading this README you are likely in front of a GitHub page
|
||||
or you just untarred the Redis distribution tar ball. In both the cases
|
||||
you are basically one step away from the source code, so here we explain
|
||||
the Redis source code layout, what is in each file as a general idea, the
|
||||
@@ -249,7 +271,7 @@ Source code layout
|
||||
|
||||
The Redis root directory just contains this README, the Makefile which
|
||||
calls the real Makefile inside the `src` directory and an example
|
||||
configuration for Redis and Sentinel. You can find a few shell
|
||||
configuration for Redis and Redis Sentinel. You can find a few shell
|
||||
scripts that are used in order to execute the Redis, Redis Cluster and
|
||||
Redis Sentinel unit tests, which are implemented inside the `tests`
|
||||
directory.
|
||||
@@ -420,7 +442,7 @@ implementations are the following:
|
||||
* `lookupKeyRead()` and `lookupKeyWrite()` are used in order to get a pointer to the value associated to a given key, or `NULL` if the key does not exist.
|
||||
* `dbAdd()` and its higher level counterpart `setKey()` create a new key in a Redis database.
|
||||
* `dbDelete()` removes a key and its associated value.
|
||||
* `emptyDb()` removes an entire single database or all the databases defined.
|
||||
* `emptyData()` removes an entire single database or all the databases defined.
|
||||
|
||||
The rest of the file implements the generic commands exposed to the client.
|
||||
|
||||
@@ -458,9 +480,9 @@ Script
|
||||
|
||||
The script unit is composed of 3 units:
|
||||
* `script.c` - integration of scripts with Redis (commands execution, set replication/resp, ...)
|
||||
* `script_lua.c` - responsible to execute Lua code, uses script.c to interact with Redis from within the Lua code.
|
||||
* `function_lua.c` - contains the Lua engine implementation, uses script_lua.c to execute the Lua code.
|
||||
* `functions.c` - contains Redis Functions implementation (FUNCTION command), uses functions_lua.c if the function it wants to invoke needs the Lua engine.
|
||||
* `script_lua.c` - responsible to execute Lua code, uses `script.c` to interact with Redis from within the Lua code.
|
||||
* `function_lua.c` - contains the Lua engine implementation, uses `script_lua.c` to execute the Lua code.
|
||||
* `functions.c` - contains Redis Functions implementation (`FUNCTION` command), uses `functions_lua.c` if the function it wants to invoke needs the Lua engine.
|
||||
* `eval.c` - contains the `eval` implementation using `script_lua.c` to invoke the Lua code.
|
||||
|
||||
|
||||
@@ -474,7 +496,7 @@ Other C files
|
||||
* `dict.c` is an implementation of a non-blocking hash table which rehashes incrementally.
|
||||
* `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][4].
|
||||
|
||||
[4]: https://redis.io/topics/cluster-spec
|
||||
[4]: https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/
|
||||
|
||||
Anatomy of a Redis command
|
||||
---
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
Copyright (c) 2006-Present, Redis Ltd. and Contributors
|
||||
All rights reserved.
|
||||
|
||||
Note: Continued Applicability of the BSD-3-Clause License
|
||||
|
||||
Despite the shift to the dual-licensing model with Redis Community Edition version 7.4 (RSALv2 or SSPLv1), portions of
|
||||
Redis Community Edition remain available subject to the BSD-3-Clause License (BSD). See below for the full BSD
|
||||
license:
|
||||
|
||||
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.
|
||||
|
||||
3. Neither the name of the copyright holder 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 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.
|
||||
+7
-11
@@ -2,7 +2,7 @@
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Redis is generally backwards compatible with very few exceptions, so we
|
||||
Redis is generally backward compatible with very few exceptions, so we
|
||||
recommend users to always use the latest version to experience stability,
|
||||
performance and security.
|
||||
|
||||
@@ -11,17 +11,18 @@ unless this is not possible or feasible with a reasonable effort.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 7.0.x | :white_check_mark: |
|
||||
| 7.4.x | :white_check_mark: |
|
||||
| 7.2.x | :white_check_mark: |
|
||||
| < 7.2.x | :x: |
|
||||
| 6.2.x | :white_check_mark: |
|
||||
| 6.0.x | :white_check_mark: |
|
||||
| < 6.0 | :x: |
|
||||
| < 6.2.x | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you believe you’ve discovered a serious vulnerability, please contact the
|
||||
If you believe you've discovered a serious vulnerability, please contact the
|
||||
Redis core team at redis@redis.io. We will evaluate your report and if
|
||||
necessary issue a fix and an advisory. If the issue was previously undisclosed,
|
||||
we’ll also mention your name in the credits.
|
||||
we'll also mention your name in the credits.
|
||||
|
||||
## Responsible Disclosure
|
||||
|
||||
@@ -34,10 +35,5 @@ This process involves providing an early notification about the vulnerability,
|
||||
its impact and mitigations to a short list of vendors under a time-limited
|
||||
embargo on public disclosure.
|
||||
|
||||
Vendors on the list are individuals or organizations that maintain Redis
|
||||
distributions or provide Redis as a service, who have third party users who
|
||||
will benefit from the vendor’s ability to prepare for a new version or deploy a
|
||||
fix early.
|
||||
|
||||
If you believe you should be on the list, please contact us and we will
|
||||
consider your request based on the above criteria.
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
coverage:
|
||||
status:
|
||||
patch:
|
||||
default:
|
||||
informational: true
|
||||
project:
|
||||
default:
|
||||
informational: true
|
||||
|
||||
comment:
|
||||
require_changes: false
|
||||
require_head: false
|
||||
require_base: false
|
||||
layout: "condensed_header, diff, files"
|
||||
hide_project_coverage: false
|
||||
behavior: default
|
||||
|
||||
github_checks:
|
||||
annotations: false
|
||||
Vendored
+14
-1
@@ -3,6 +3,7 @@
|
||||
uname_S:= $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
|
||||
LUA_DEBUG?=no
|
||||
LUA_COVERAGE?=no
|
||||
|
||||
CCCOLOR="\033[34m"
|
||||
LINKCOLOR="\033[34;1m"
|
||||
@@ -41,6 +42,7 @@ distclean:
|
||||
-(cd jemalloc && [ -f Makefile ] && $(MAKE) distclean) > /dev/null || true
|
||||
-(cd hdr_histogram && $(MAKE) clean) > /dev/null || true
|
||||
-(cd fpconv && $(MAKE) clean) > /dev/null || true
|
||||
-(cd fast_float && $(MAKE) clean) > /dev/null || true
|
||||
-(rm -f .make-*)
|
||||
|
||||
.PHONY: distclean
|
||||
@@ -73,6 +75,12 @@ fpconv: .make-prerequisites
|
||||
|
||||
.PHONY: fpconv
|
||||
|
||||
fast_float: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd fast_float && $(MAKE) libfast_float
|
||||
|
||||
.PHONY: fast_float
|
||||
|
||||
ifeq ($(uname_S),SunOS)
|
||||
# Make isinf() available
|
||||
LUA_CFLAGS= -D__C99FEATURES__=1
|
||||
@@ -85,6 +93,11 @@ ifeq ($(LUA_DEBUG),yes)
|
||||
else
|
||||
LUA_CFLAGS+= -O2
|
||||
endif
|
||||
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.
|
||||
@@ -106,7 +119,7 @@ endif
|
||||
|
||||
jemalloc: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd jemalloc && ./configure --disable-cxx --with-version=5.3.0-0-g0 --with-lg-quantum=3 --with-jemalloc-prefix=je_ CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)" $(JEMALLOC_CONFIGURE_OPTS)
|
||||
cd jemalloc && ./configure --disable-cxx --with-version=5.3.0-0-g0 --with-lg-quantum=3 --disable-cache-oblivious --with-jemalloc-prefix=je_ CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)" $(JEMALLOC_CONFIGURE_OPTS)
|
||||
cd jemalloc && $(MAKE) lib/libjemalloc.a
|
||||
|
||||
.PHONY: jemalloc
|
||||
|
||||
Vendored
+4
-5
@@ -61,12 +61,11 @@ cd deps/jemalloc
|
||||
Hiredis
|
||||
---
|
||||
|
||||
Hiredis uses the SDS string library, that must be the same version used inside Redis itself. Hiredis is also very critical for Sentinel. Historically Redis often used forked versions of hiredis in a way or the other. In order to upgrade it is advised to take a lot of care:
|
||||
Hiredis is used by Sentinel, `redis-cli` and `redis-benchmark`. Like Redis, uses the SDS string library, but not necessarily the same version. In order to avoid conflicts, this version has all SDS identifiers prefixed by `hi`.
|
||||
|
||||
1. Check with diff if hiredis API changed and what impact it could have in Redis.
|
||||
2. Make sure that the SDS library inside Hiredis and inside Redis are compatible.
|
||||
3. After the upgrade, run the Redis Sentinel test.
|
||||
4. Check manually that redis-cli and redis-benchmark behave as expected, since we have no tests for CLI utilities currently.
|
||||
1. `git subtree pull --prefix deps/hiredis https://github.com/redis/hiredis.git <version-tag> --squash`<br>
|
||||
This should hopefully merge the local changes into the new version.
|
||||
2. Conflicts will arise (due to our changes) you'll need to resolve them and commit.
|
||||
|
||||
Linenoise
|
||||
---
|
||||
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
# Fallback to gcc/g++ when $CC or $CXX is not in $PATH.
|
||||
CC ?= gcc
|
||||
CXX ?= g++
|
||||
|
||||
CFLAGS=-Wall -O3
|
||||
# This avoids loosing the fastfloat specific compile flags when we override the CFLAGS via the main project
|
||||
FASTFLOAT_CFLAGS=-std=c++11 -DFASTFLOAT_ALLOWS_LEADING_PLUS
|
||||
LDFLAGS=
|
||||
|
||||
libfast_float: fast_float_strtod.o
|
||||
$(AR) -r libfast_float.a fast_float_strtod.o
|
||||
|
||||
32bit: CFLAGS += -m32
|
||||
32bit: LDFLAGS += -m32
|
||||
32bit: libfast_float
|
||||
|
||||
fast_float_strtod.o: fast_float_strtod.cpp
|
||||
$(CXX) $(CFLAGS) $(FASTFLOAT_CFLAGS) -c fast_float_strtod.cpp $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f *.a
|
||||
rm -f *.h.gch
|
||||
rm -rf *.dSYM
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
README for fast_float v6.1.4
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
We're using the fast_float library[1] in our (compiled-in)
|
||||
floating-point fast_float_strtod implementation for faster and more
|
||||
portable parsing of 64 decimal strings.
|
||||
|
||||
The single file fast_float.h is an amalgamation of the entire library,
|
||||
which can be (re)generated with the amalgamate.py script (from the
|
||||
fast_float repository) via the command
|
||||
|
||||
```
|
||||
git clone https://github.com/fastfloat/fast_float
|
||||
cd fast_float
|
||||
git checkout v6.1.4
|
||||
python3 ./script/amalgamate.py --license=MIT \
|
||||
> $REDIS_SRC/deps/fast_float/fast_float.h
|
||||
```
|
||||
|
||||
[1]: https://github.com/fastfloat/fast_float
|
||||
Vendored
+3838
File diff suppressed because it is too large
Load Diff
+32
@@ -0,0 +1,32 @@
|
||||
#include "fast_float.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <cerrno>
|
||||
|
||||
/* Convert NPTR to a double using the fast_float library.
|
||||
*
|
||||
* This function behaves similarly to the standard strtod function, converting
|
||||
* the initial portion of the string pointed to by `nptr` to a `double` value,
|
||||
* using the fast_float library for high performance. If the conversion fails,
|
||||
* errno is set to EINVAL error code.
|
||||
*
|
||||
* @param nptr A pointer to the null-terminated byte string to be interpreted.
|
||||
* @param endptr A pointer to a pointer to character. If `endptr` is not NULL,
|
||||
* it will point to the character after the last character used
|
||||
* in the conversion.
|
||||
* @return The converted value as a double. If no valid conversion could
|
||||
* be performed, returns 0.0.
|
||||
* If ENDPTR is not NULL, a pointer to the character after the last one used
|
||||
* in the number is put in *ENDPTR. */
|
||||
extern "C" double fast_float_strtod(const char *nptr, char **endptr) {
|
||||
double result = 0.0;
|
||||
auto answer = fast_float::from_chars(nptr, nptr + strlen(nptr), result);
|
||||
if (answer.ec != std::errc()) {
|
||||
errno = EINVAL; // Fallback to for other errors
|
||||
}
|
||||
if (endptr != NULL) {
|
||||
*endptr = (char *)answer.ptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
|
||||
#ifndef __FAST_FLOAT_STRTOD_H__
|
||||
#define __FAST_FLOAT_STRTOD_H__
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
double fast_float_strtod(const char *in, char **out);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __FAST_FLOAT_STRTOD_H__ */
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
name-template: '$NEXT_MAJOR_VERSION'
|
||||
tag-template: 'v$NEXT_MAJOR_VERSION'
|
||||
autolabeler:
|
||||
- label: 'maintenance'
|
||||
files:
|
||||
- '*.md'
|
||||
- '.github/*'
|
||||
- label: 'bug'
|
||||
branch:
|
||||
- '/bug-.+'
|
||||
- label: 'maintenance'
|
||||
branch:
|
||||
- '/maintenance-.+'
|
||||
- label: 'feature'
|
||||
branch:
|
||||
- '/feature-.+'
|
||||
categories:
|
||||
- title: 'Breaking Changes'
|
||||
labels:
|
||||
- 'breakingchange'
|
||||
|
||||
- title: '🧪 Experimental Features'
|
||||
labels:
|
||||
- 'experimental'
|
||||
- title: '🚀 New Features'
|
||||
labels:
|
||||
- 'feature'
|
||||
- 'enhancement'
|
||||
- title: '🐛 Bug Fixes'
|
||||
labels:
|
||||
- 'fix'
|
||||
- 'bugfix'
|
||||
- 'bug'
|
||||
- 'BUG'
|
||||
- title: '🧰 Maintenance'
|
||||
label: 'maintenance'
|
||||
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
|
||||
exclude-labels:
|
||||
- 'skip-changelog'
|
||||
template: |
|
||||
## Changes
|
||||
|
||||
$CHANGES
|
||||
|
||||
## Contributors
|
||||
We'd like to thank all the contributors who worked on this release!
|
||||
|
||||
$CONTRIBUTORS
|
||||
|
||||
+23
-51
@@ -6,24 +6,21 @@ jobs:
|
||||
name: Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo add-apt-repository -y ppa:chris-lea/redis-server
|
||||
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
|
||||
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y redis-server valgrind libevent-dev
|
||||
|
||||
|
||||
- name: Build using cmake
|
||||
env:
|
||||
env:
|
||||
EXTRA_CMAKE_OPTS: -DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON -DENABLE_SSL_TESTS:BOOL=ON -DENABLE_ASYNC_TESTS:BOOL=ON
|
||||
CFLAGS: -Werror
|
||||
CXXFLAGS: -Werror
|
||||
run: mkdir build-ubuntu && cd build-ubuntu && cmake ..
|
||||
run: mkdir build && cd build && cmake .. && make
|
||||
|
||||
- name: Build using makefile
|
||||
run: USE_SSL=1 TEST_ASYNC=1 make
|
||||
@@ -45,11 +42,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
container: centos:7
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -58,11 +51,11 @@ jobs:
|
||||
yum -y install gcc gcc-c++ make openssl openssl-devel cmake3 valgrind libevent-devel
|
||||
|
||||
- name: Build using cmake
|
||||
env:
|
||||
env:
|
||||
EXTRA_CMAKE_OPTS: -DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON -DENABLE_SSL_TESTS:BOOL=ON -DENABLE_ASYNC_TESTS:BOOL=ON
|
||||
CFLAGS: -Werror
|
||||
CXXFLAGS: -Werror
|
||||
run: mkdir build-centos7 && cd build-centos7 && cmake3 ..
|
||||
run: mkdir build && cd build && cmake3 .. && make
|
||||
|
||||
- name: Build using Makefile
|
||||
run: USE_SSL=1 TEST_ASYNC=1 make
|
||||
@@ -85,25 +78,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
container: rockylinux:8
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dnf -y upgrade --refresh
|
||||
dnf -y install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
|
||||
dnf -y module install redis:remi-6.0
|
||||
dnf -y group install "Development Tools"
|
||||
dnf -y install openssl-devel cmake valgrind libevent-devel
|
||||
|
||||
- name: Build using cmake
|
||||
env:
|
||||
env:
|
||||
EXTRA_CMAKE_OPTS: -DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON -DENABLE_SSL_TESTS:BOOL=ON -DENABLE_ASYNC_TESTS:BOOL=ON
|
||||
CFLAGS: -Werror
|
||||
CXXFLAGS: -Werror
|
||||
run: mkdir build-centos8 && cd build-centos8 && cmake ..
|
||||
run: mkdir build && cd build && cmake .. && make
|
||||
|
||||
- name: Build using Makefile
|
||||
run: USE_SSL=1 TEST_ASYNC=1 make
|
||||
@@ -122,17 +112,13 @@ jobs:
|
||||
run: $GITHUB_WORKSPACE/test.sh
|
||||
|
||||
freebsd:
|
||||
runs-on: macos-10.15
|
||||
runs-on: macos-12
|
||||
name: FreeBSD
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build in FreeBSD
|
||||
uses: vmactions/freebsd-vm@v0.1.5
|
||||
uses: vmactions/freebsd-vm@v0
|
||||
with:
|
||||
prepare: pkg install -y gmake cmake
|
||||
run: |
|
||||
@@ -143,15 +129,12 @@ jobs:
|
||||
name: macOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew install openssl redis
|
||||
brew install openssl redis@7.0
|
||||
brew link redis@7.0 --force
|
||||
|
||||
- name: Build hiredis
|
||||
run: USE_SSL=1 make
|
||||
@@ -165,11 +148,7 @@ jobs:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -186,20 +165,13 @@ jobs:
|
||||
run: |
|
||||
./build/hiredis-test.exe
|
||||
|
||||
- name: Setup cygwin
|
||||
uses: egor-tensin/setup-cygwin@v3
|
||||
- name: Install Cygwin Action
|
||||
uses: cygwin/cygwin-install-action@v2
|
||||
with:
|
||||
platform: x64
|
||||
packages: make git gcc-core
|
||||
|
||||
- name: Build in cygwin
|
||||
env:
|
||||
HIREDIS_PATH: ${{ github.workspace }}
|
||||
run: |
|
||||
build_hiredis() {
|
||||
cd $(cygpath -u $HIREDIS_PATH)
|
||||
git clean -xfd
|
||||
make
|
||||
}
|
||||
build_hiredis
|
||||
shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}'
|
||||
make clean && make
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
name: Release Drafter
|
||||
|
||||
on:
|
||||
push:
|
||||
# branches to consider in the event; optional, defaults to all
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
update_release_draft:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Drafts your next Release notes as Pull Requests are merged into "master"
|
||||
- uses: release-drafter/release-drafter@v5
|
||||
with:
|
||||
# (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
|
||||
config-name: release-drafter-config.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
full-build:
|
||||
name: Build all, plus default examples, run tests against redis
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# the docker image used by the test.sh
|
||||
REDIS_DOCKER: redis:alpine
|
||||
|
||||
steps:
|
||||
- name: Install prerequisites
|
||||
run: sudo apt-get update && sudo apt-get install -y libev-dev libevent-dev libglib2.0-dev libssl-dev valgrind
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run make
|
||||
run: make all examples
|
||||
- name: Run unittests
|
||||
run: make check
|
||||
- name: Run tests with valgrind
|
||||
env:
|
||||
TEST_PREFIX: valgrind --error-exitcode=100
|
||||
SKIPS_ARG: --skip-throughput
|
||||
run: make check
|
||||
|
||||
build-32-bit:
|
||||
name: Build and test minimal 32 bit linux
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install prerequisites
|
||||
run: sudo apt-get update && sudo apt-get install gcc-multilib
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run make
|
||||
run: make all
|
||||
env:
|
||||
PLATFORM_FLAGS: -m32
|
||||
- name: Run unittests
|
||||
env:
|
||||
REDIS_DOCKER: redis:alpine
|
||||
run: make check
|
||||
|
||||
build-arm:
|
||||
name: Cross-compile and test arm linux with Qemu
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: arm
|
||||
toolset: arm-linux-gnueabi
|
||||
emulator: qemu-arm
|
||||
- name: aarch64
|
||||
toolset: aarch64-linux-gnu
|
||||
emulator: qemu-aarch64
|
||||
|
||||
steps:
|
||||
- name: Install qemu
|
||||
if: matrix.emulator
|
||||
run: sudo apt-get update && sudo apt-get install -y qemu-user
|
||||
- name: Install platform toolset
|
||||
if: matrix.toolset
|
||||
run: sudo apt-get install -y gcc-${{matrix.toolset}}
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run make
|
||||
run: make all
|
||||
env:
|
||||
CC: ${{matrix.toolset}}-gcc
|
||||
AR: ${{matrix.toolset}}-ar
|
||||
- name: Run unittests
|
||||
env:
|
||||
REDIS_DOCKER: redis:alpine
|
||||
TEST_PREFIX: ${{matrix.emulator}} -L /usr/${{matrix.toolset}}/
|
||||
run: make check
|
||||
|
||||
build-windows:
|
||||
name: Build and test on windows 64 bit Intel
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: microsoft/setup-msbuild@v1.0.2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run CMake (shared lib)
|
||||
run: cmake -Wno-dev CMakeLists.txt
|
||||
- name: Build hiredis (shared lib)
|
||||
run: MSBuild hiredis.vcxproj /p:Configuration=Debug
|
||||
- name: Run CMake (static lib)
|
||||
run: cmake -Wno-dev CMakeLists.txt -DBUILD_SHARED_LIBS=OFF
|
||||
- name: Build hiredis (static lib)
|
||||
run: MSBuild hiredis.vcxproj /p:Configuration=Debug
|
||||
- name: Build hiredis-test
|
||||
run: MSBuild hiredis-test.vcxproj /p:Configuration=Debug
|
||||
# use memurai, redis compatible server, since it is easy to install. Can't
|
||||
# install official redis containers on the windows runner
|
||||
- name: Install Memurai redis server
|
||||
run: choco install -y memurai-developer.install
|
||||
- name: Run tests
|
||||
run: Debug\hiredis-test.exe
|
||||
Vendored
+216
@@ -1,3 +1,219 @@
|
||||
## [1.2.0](https://github.com/redis/hiredis/tree/v1.2.0) - (2023-06-04)
|
||||
|
||||
Announcing Hiredis v1.2.0 with with new adapters, and a great many bug fixes.
|
||||
|
||||
## 🚀 New Features
|
||||
|
||||
- Add sdevent adapter @Oipo (#1144)
|
||||
- Allow specifying the keepalive interval @michael-grunder (#1168)
|
||||
- Add RedisModule adapter @tezc (#1182)
|
||||
- Helper for setting TCP_USER_TIMEOUT socket option @zuiderkwast (#1188)
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
- Fix a typo in b6a052f. @yossigo (#1190)
|
||||
- Fix wincrypt symbols conflict @hudayou (#1151)
|
||||
- Don't attempt to set a timeout if we are in an error state. @michael-grunder (#1180)
|
||||
- Accept -nan per the RESP3 spec recommendation. @michael-grunder (#1178)
|
||||
- Fix colliding option values @zuiderkwast (#1172)
|
||||
- Ensure functionality without `_MSC_VER` definition @windyakin (#1194)
|
||||
|
||||
## 🧰 Maintenance
|
||||
|
||||
- Add a test for the TCP_USER_TIMEOUT option. @michael-grunder (#1192)
|
||||
- Add -Werror as a default. @yossigo (#1193)
|
||||
- CI: Update homebrew Redis version. @yossigo (#1191)
|
||||
- Fix typo in makefile. @michael-grunder (#1179)
|
||||
- Write a version file for the CMake package @Neverlord (#1165)
|
||||
- CMakeLists.txt: respect BUILD_SHARED_LIBS @ffontaine (#1147)
|
||||
- Cmake static or shared @autoantwort (#1160)
|
||||
- fix typo @tillkruss (#1153)
|
||||
- Add a test ensuring we don't clobber connection error. @michael-grunder (#1181)
|
||||
- Search for openssl on macOS @michael-grunder (#1169)
|
||||
|
||||
|
||||
## Contributors
|
||||
We'd like to thank all the contributors who worked on this release!
|
||||
|
||||
<a href="https://github.com/neverlord"><img src="https://github.com/neverlord.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/Oipo"><img src="https://github.com/Oipo.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/autoantwort"><img src="https://github.com/autoantwort.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/ffontaine"><img src="https://github.com/ffontaine.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/hudayou"><img src="https://github.com/hudayou.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/michael-grunder"><img src="https://github.com/michael-grunder.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/postgraph"><img src="https://github.com/postgraph.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/tezc"><img src="https://github.com/tezc.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/tillkruss"><img src="https://github.com/tillkruss.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/vityafx"><img src="https://github.com/vityafx.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/windyakin"><img src="https://github.com/windyakin.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/yossigo"><img src="https://github.com/yossigo.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/zuiderkwast"><img src="https://github.com/zuiderkwast.png" width="32" height="32"></a>
|
||||
|
||||
## [1.1.0](https://github.com/redis/hiredis/tree/v1.1.0) - (2022-11-15)
|
||||
|
||||
Announcing Hiredis v1.1.0 GA with better SSL convenience, new async adapters and a great many bug fixes.
|
||||
|
||||
**NOTE**: Hiredis can now return `nan` in addition to `-inf` and `inf` when returning a `REDIS_REPLY_DOUBLE`.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
- Add support for nan in RESP3 double [@filipecosta90](https://github.com/filipecosta90)
|
||||
([\#1133](https://github.com/redis/hiredis/pull/1133))
|
||||
|
||||
## 🧰 Maintenance
|
||||
|
||||
- Add an example that calls redisCommandArgv [@michael-grunder](https://github.com/michael-grunder)
|
||||
([\#1140](https://github.com/redis/hiredis/pull/1140))
|
||||
- fix flag reference [@pata00](https://github.com/pata00) ([\#1136](https://github.com/redis/hiredis/pull/1136))
|
||||
- Make freeing a NULL redisAsyncContext a no op. [@michael-grunder](https://github.com/michael-grunder)
|
||||
([\#1135](https://github.com/redis/hiredis/pull/1135))
|
||||
- CI updates ([@bjosv](https://github.com/redis/bjosv) ([\#1139](https://github.com/redis/hiredis/pull/1139))
|
||||
|
||||
|
||||
## Contributors
|
||||
We'd like to thank all the contributors who worked on this release!
|
||||
|
||||
<a href="https://github.com/bjsov"><img src="https://github.com/bjosv.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/filipecosta90"><img src="https://github.com/filipecosta90.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/michael-grunder"><img src="https://github.com/michael-grunder.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/pata00"><img src="https://github.com/pata00.png" width="32" height="32"></a>
|
||||
|
||||
## [1.1.0-rc1](https://github.com/redis/hiredis/tree/v1.1.0-rc1) - (2022-11-06)
|
||||
|
||||
Announcing Hiredis v1.1.0-rc1, with better SSL convenience, new async adapters, and a great many bug fixes.
|
||||
|
||||
## 🚀 New Features
|
||||
|
||||
- Add possibility to prefer IPv6, IPv4 or unspecified [@zuiderkwast](https://github.com/zuiderkwast)
|
||||
([\#1096](https://github.com/redis/hiredis/pull/1096))
|
||||
- Add adapters/libhv [@ithewei](https://github.com/ithewei) ([\#904](https://github.com/redis/hiredis/pull/904))
|
||||
- Add timeout support to libhv adapter. [@michael-grunder](https://github.com/michael-grunder) ([\#1109](https://github.com/redis/hiredis/pull/1109))
|
||||
- set default SSL verification path [@adobeturchenko](https://github.com/adobeturchenko) ([\#928](https://github.com/redis/hiredis/pull/928))
|
||||
- Introduce .close method for redisContextFuncs [@pizhenwei](https://github.com/pizhenwei) ([\#1094](https://github.com/redis/hiredis/pull/1094))
|
||||
- Make it possible to set SSL verify mode [@stanhu](https://github.com/stanhu) ([\#1085](https://github.com/redis/hiredis/pull/1085))
|
||||
- Polling adapter and example [@kristjanvalur](https://github.com/kristjanvalur) ([\#932](https://github.com/redis/hiredis/pull/932))
|
||||
- Unsubscribe handling in async [@bjosv](https://github.com/bjosv) ([\#1047](https://github.com/redis/hiredis/pull/1047))
|
||||
- Add timeout support for libuv adapter [@MichaelSuen-thePointer](https://github.com/@MichaelSuenthePointer) ([\#1016](https://github.com/redis/hiredis/pull/1016))
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
- Update for MinGW cross compile [@bit0fun](https://github.com/bit0fun) ([\#1127](https://github.com/redis/hiredis/pull/1127))
|
||||
- fixed CPP build error with adapters/libhv.h [@mtdxc](https://github.com/mtdxc) ([\#1125](https://github.com/redis/hiredis/pull/1125))
|
||||
- Fix protocol error
|
||||
[@michael-grunder](https://github.com/michael-grunder),
|
||||
[@mtuleika-appcast](https://github.com/mtuleika-appcast) ([\#1106](https://github.com/redis/hiredis/pull/1106))
|
||||
- Use a windows specific keepalive function. [@michael-grunder](https://github.com/michael-grunder) ([\#1104](https://github.com/redis/hiredis/pull/1104))
|
||||
- Fix CMake config path on Linux. [@xkszltl](https://github.com/xkszltl) ([\#989](https://github.com/redis/hiredis/pull/989))
|
||||
- Fix potential fault at createDoubleObject [@afcidk](https://github.com/afcidk) ([\#964](https://github.com/redis/hiredis/pull/964))
|
||||
- Fix some undefined behavior [@jengab](https://github.com/jengab) ([\#1091](https://github.com/redis/hiredis/pull/1091))
|
||||
- Copy OOM errors to redisAsyncContext when finding subscribe callback [@bjosv](https://github.com/bjosv) ([\#1090](https://github.com/redis/hiredis/pull/1090))
|
||||
- Maintain backward compatibility with our onConnect callback. [@michael-grunder](https://github.com/michael-grunder) ([\#1087](https://github.com/redis/hiredis/pull/1087))
|
||||
- Fix PUSH handler tests for Redis >= 7.0.5 [@michael-grunder](https://github.com/michael-grunder) ([\#1121](https://github.com/redis/hiredis/pull/1121))
|
||||
- fix heap-buffer-overflow [@zhangtaoXT5](https://github.com/zhangtaoXT5) ([\#957](https://github.com/redis/hiredis/pull/957))
|
||||
- Fix heap-buffer-overflow issue in redisvFormatCommad [@bjosv](https://github.com/bjosv) ([\#1097](https://github.com/redis/hiredis/pull/1097))
|
||||
- Polling adapter requires sockcompat.h [@michael-grunder](https://github.com/michael-grunder) ([\#1095](https://github.com/redis/hiredis/pull/1095))
|
||||
- Illumos test fixes, error message difference for bad hostname test. [@devnexen](https://github.com/devnexen) ([\#901](https://github.com/redis/hiredis/pull/901))
|
||||
- Remove semicolon after do-while in \_EL\_CLEANUP [@sundb](https://github.com/sundb) ([\#905](https://github.com/redis/hiredis/pull/905))
|
||||
- Stability: Support calling redisAsyncCommand and redisAsyncDisconnect from the onConnected callback [@kristjanvalur](https://github.com/kristjanvalur)
|
||||
([\#931](https://github.com/redis/hiredis/pull/931))
|
||||
- Fix async connect on Windows [@kristjanvalur](https://github.com/kristjanvalur) ([\#1073](https://github.com/redis/hiredis/pull/1073))
|
||||
- Fix tests so they work for Redis 7.0 [@michael-grunder](https://github.com/michael-grunder) ([\#1072](https://github.com/redis/hiredis/pull/1072))
|
||||
- Fix warnings on Win64 [@orgads](https://github.com/orgads) ([\#1058](https://github.com/redis/hiredis/pull/1058))
|
||||
- Handle push notifications before or after reply. [@yossigo](https://github.com/yossigo) ([\#1062](https://github.com/redis/hiredis/pull/1062))
|
||||
- Update hiredis sds with improvements found in redis [@bjosv](https://github.com/bjosv) ([\#1045](https://github.com/redis/hiredis/pull/1045))
|
||||
- Avoid incorrect call to the previous reply's callback [@bjosv](https://github.com/bjosv) ([\#1040](https://github.com/redis/hiredis/pull/1040))
|
||||
- fix building on AIX and SunOS [\#1031](https://github.com/redis/hiredis/pull/1031) ([@scddev](https://github.com/scddev))
|
||||
- Allow sending commands after sending an unsubscribe [@bjosv](https://github.com/bjosv) ([\#1036](https://github.com/redis/hiredis/pull/1036))
|
||||
- Correction for command timeout during pubsub [@bjosv](https://github.com/bjosv) ([\#1038](https://github.com/redis/hiredis/pull/1038))
|
||||
- Fix adapters/libevent.h compilation for 64-bit Windows [@pbtummillo](https://github.com/pbtummillo) ([\#937](https://github.com/redis/hiredis/pull/937))
|
||||
- Fix integer overflow when format command larger than 4GB [@sundb](https://github.com/sundb) ([\#1030](https://github.com/redis/hiredis/pull/1030))
|
||||
- Handle array response during subscribe in RESP3 [@bjosv](https://github.com/bjosv) ([\#1014](https://github.com/redis/hiredis/pull/1014))
|
||||
- Support PING while subscribing (RESP2) [@bjosv](https://github.com/bjosv) ([\#1027](https://github.com/redis/hiredis/pull/1027))
|
||||
|
||||
## 🧰 Maintenance
|
||||
|
||||
- CI fixes in preparation of release [@michael-grunder](https://github.com/michael-grunder) ([\#1130](https://github.com/redis/hiredis/pull/1130))
|
||||
- Add do while(0) (protection for macros [@afcidk](https://github.com/afcidk) [\#959](https://github.com/redis/hiredis/pull/959))
|
||||
- Fixup of PR734: Coverage of hiredis.c [@bjosv](https://github.com/bjosv) ([\#1124](https://github.com/redis/hiredis/pull/1124))
|
||||
- CMake corrections for building on Windows [@bjosv](https://github.com/bjosv) ([\#1122](https://github.com/redis/hiredis/pull/1122))
|
||||
- Install on windows fixes [@bjosv](https://github.com/bjosv) ([\#1117](https://github.com/redis/hiredis/pull/1117))
|
||||
- Add libhv example to our standard Makefile [@michael-grunder](https://github.com/michael-grunder) ([\#1108](https://github.com/redis/hiredis/pull/1108))
|
||||
- Additional include directory given by pkg-config [@bjosv](https://github.com/bjosv) ([\#1118](https://github.com/redis/hiredis/pull/1118))
|
||||
- Use __attribute__ when building with Clang on Windows [@bjosv](https://github.com/bjosv) ([\#1115](https://github.com/redis/hiredis/pull/1115))
|
||||
- Minor refactor [@michael-grunder](https://github.com/michael-grunder) ([\#1110](https://github.com/redis/hiredis/pull/1110))
|
||||
- Fix pkgconfig result for hiredis_ssl [@bjosv](https://github.com/bjosv) ([\#1107](https://github.com/redis/hiredis/pull/1107))
|
||||
- Update documentation to explain redisConnectWithOptions. [@michael-grunder](https://github.com/michael-grunder) ([\#1099](https://github.com/redis/hiredis/pull/1099))
|
||||
- uvadapter: reduce number of uv_poll_start calls [@noxiouz](https://github.com/noxiouz) ([\#1098](https://github.com/redis/hiredis/pull/1098))
|
||||
- Regression test for off-by-one parsing error [@bugwz](https://github.com/bugwz) ([\#1092](https://github.com/redis/hiredis/pull/1092))
|
||||
- CMake: remove dict.c form hiredis_sources [@Lipraxde](https://github.com/Lipraxde) ([\#1055](https://github.com/redis/hiredis/pull/1055))
|
||||
- Do store command timeout in the context for redisSetTimeout [@catterer](https://github.com/catterer) ([\#593](https://github.com/redis/hiredis/pull/593), [\#1093](https://github.com/redis/hiredis/pull/1093))
|
||||
- Add GitHub Actions CI workflow for hiredis: Arm, Arm64, 386, windows. [@kristjanvalur](https://github.com/kristjanvalur) ([\#943](https://github.com/redis/hiredis/pull/943))
|
||||
- CI: bump macOS runner version [@SukkaW](https://github.com/SukkaW) ([\#1079](https://github.com/redis/hiredis/pull/1079))
|
||||
- Support for generating release notes [@chayim](https://github.com/chayim) ([\#1083](https://github.com/redis/hiredis/pull/1083))
|
||||
- Improve example for SSL initialization in README.md [@stanhu](https://github.com/stanhu) ([\#1084](https://github.com/redis/hiredis/pull/1084))
|
||||
- Fix README typos [@bjosv](https://github.com/bjosv) ([\#1080](https://github.com/redis/hiredis/pull/1080))
|
||||
- fix cmake version [@smmir-cent](https://github.com/@smmircent) ([\#1050](https://github.com/redis/hiredis/pull/1050))
|
||||
- Use the same name for static and shared libraries [@orgads](https://github.com/orgads) ([\#1057](https://github.com/redis/hiredis/pull/1057))
|
||||
- Embed debug information in windows static .lib file [@kristjanvalur](https://github.com/kristjanvalur) ([\#1054](https://github.com/redis/hiredis/pull/1054))
|
||||
- Improved async documentation [@kristjanvalur](https://github.com/kristjanvalur) ([\#1074](https://github.com/redis/hiredis/pull/1074))
|
||||
- Use official repository for redis package. [@yossigo](https://github.com/yossigo) ([\#1061](https://github.com/redis/hiredis/pull/1061))
|
||||
- Whitelist hiredis repo path in cygwin [@michael-grunder](https://github.com/michael-grunder) ([\#1063](https://github.com/redis/hiredis/pull/1063))
|
||||
- CentOS 8 is EOL, switch to RockyLinux [@michael-grunder](https://github.com/michael-grunder) ([\#1046](https://github.com/redis/hiredis/pull/1046))
|
||||
- CMakeLists.txt: allow building without a C++ compiler [@ffontaine](https://github.com/ffontaine) ([\#872](https://github.com/redis/hiredis/pull/872))
|
||||
- Makefile: move SSL options into a block and refine rules [@pizhenwei](https://github.com/pizhenwei) ([\#997](https://github.com/redis/hiredis/pull/997))
|
||||
- Update CMakeLists.txt for more portability [@EricDeng1001](https://github.com/EricDeng1001) ([\#1005](https://github.com/redis/hiredis/pull/1005))
|
||||
- FreeBSD build fixes + CI [@michael-grunder](https://github.com/michael-grunder) ([\#1026](https://github.com/redis/hiredis/pull/1026))
|
||||
- Add asynchronous test for pubsub using RESP3 [@bjosv](https://github.com/bjosv) ([\#1012](https://github.com/redis/hiredis/pull/1012))
|
||||
- Trigger CI failure when Valgrind issues are found [@bjosv](https://github.com/bjosv) ([\#1011](https://github.com/redis/hiredis/pull/1011))
|
||||
- Move to using make directly in Cygwin [@michael-grunder](https://github.com/michael-grunder) ([\#1020](https://github.com/redis/hiredis/pull/1020))
|
||||
- Add asynchronous API tests [@bjosv](https://github.com/bjosv) ([\#1010](https://github.com/redis/hiredis/pull/1010))
|
||||
- Correcting the build target `coverage` for enabled SSL [@bjosv](https://github.com/bjosv) ([\#1009](https://github.com/redis/hiredis/pull/1009))
|
||||
- GH Actions: Run SSL tests during CI [@bjosv](https://github.com/bjosv) ([\#1008](https://github.com/redis/hiredis/pull/1008))
|
||||
- GH: Actions - Add valgrind and CMake [@michael-grunder](https://github.com/michael-grunder) ([\#1004](https://github.com/redis/hiredis/pull/1004))
|
||||
- Add Centos8 tests in GH Actions [@michael-grunder](https://github.com/michael-grunder) ([\#1001](https://github.com/redis/hiredis/pull/1001))
|
||||
- We should run actions on PRs [@michael-grunder](https://github.com/michael-grunder) (([\#1000](https://github.com/redis/hiredis/pull/1000))
|
||||
- Add Cygwin test in GitHub actions [@michael-grunder](https://github.com/michael-grunder) ([\#999](https://github.com/redis/hiredis/pull/999))
|
||||
- Add Windows tests in GitHub actions [@michael-grunder](https://github.com/michael-grunder) ([\#996](https://github.com/redis/hiredis/pull/996))
|
||||
- Switch to GitHub actions [@michael-grunder](https://github.com/michael-grunder) ([\#995](https://github.com/redis/hiredis/pull/995))
|
||||
- Minor refactor of CVE-2021-32765 fix. [@michael-grunder](https://github.com/michael-grunder) ([\#993](https://github.com/redis/hiredis/pull/993))
|
||||
- Remove extra comma from CMake var. [@xkszltl](https://github.com/xkszltl) ([\#988](https://github.com/redis/hiredis/pull/988))
|
||||
- Add REDIS\_OPT\_PREFER\_UNSPEC [@michael-grunder](https://github.com/michael-grunder) ([\#1101](https://github.com/redis/hiredis/pull/1101))
|
||||
|
||||
## Contributors
|
||||
We'd like to thank all the contributors who worked on this release!
|
||||
|
||||
<a href="https://github.com/EricDeng1001"><img src="https://github.com/EricDeng1001.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/Lipraxde"><img src="https://github.com/Lipraxde.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/MichaelSuen-thePointer"><img src="https://github.com/MichaelSuen-thePointer.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/SukkaW"><img src="https://github.com/SukkaW.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/adobeturchenko"><img src="https://github.com/adobeturchenko.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/afcidk"><img src="https://github.com/afcidk.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/bit0fun"><img src="https://github.com/bit0fun.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/bjosv"><img src="https://github.com/bjosv.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/bugwz"><img src="https://github.com/bugwz.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/catterer"><img src="https://github.com/catterer.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/chayim"><img src="https://github.com/chayim.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/devnexen"><img src="https://github.com/devnexen.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/ffontaine"><img src="https://github.com/ffontaine.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/ithewei"><img src="https://github.com/ithewei.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/jengab"><img src="https://github.com/jengab.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/kristjanvalur"><img src="https://github.com/kristjanvalur.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/michael-grunder"><img src="https://github.com/michael-grunder.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/noxiouz"><img src="https://github.com/noxiouz.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/mtdxc"><img src="https://github.com/mtdxc.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/orgads"><img src="https://github.com/orgads.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/pbtummillo"><img src="https://github.com/pbtummillo.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/pizhenwei"><img src="https://github.com/pizhenwei.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/scddev"><img src="https://github.com/scddev.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/smmir-cent"><img src="https://github.com/smmir-cent.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/stanhu"><img src="https://github.com/stanhu.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/sundb"><img src="https://github.com/sundb.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/vturchenko"><img src="https://github.com/vturchenko.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/xkszltl"><img src="https://github.com/xkszltl.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/yossigo"><img src="https://github.com/yossigo.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/zhangtaoXT5"><img src="https://github.com/zhangtaoXT5.png" width="32" height="32"></a>
|
||||
<a href="https://github.com/zuiderkwast"><img src="https://github.com/zuiderkwast.png" width="32" height="32"></a>
|
||||
|
||||
## [1.0.2](https://github.com/redis/hiredis/tree/v1.0.2) - (2021-10-07)
|
||||
|
||||
Announcing Hiredis v1.0.2, which fixes CVE-2021-32765 but returns the SONAME to the correct value of `1.0.0`.
|
||||
|
||||
Vendored
+40
-48
@@ -1,8 +1,10 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0)
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.0.0)
|
||||
|
||||
OPTION(BUILD_SHARED_LIBS "Build shared libraries" ON)
|
||||
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)
|
||||
OPTION(ENABLE_EXAMPLES "Enable building hiredis examples" OFF)
|
||||
OPTION(ENABLE_ASYNC_TESTS "Should we run all asynchronous API tests" OFF)
|
||||
|
||||
MACRO(getVersionBit name)
|
||||
@@ -24,15 +26,11 @@ INCLUDE(GNUInstallDirs)
|
||||
|
||||
# Hiredis requires C99
|
||||
SET(CMAKE_C_STANDARD 99)
|
||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
SET(CMAKE_DEBUG_POSTFIX d)
|
||||
|
||||
SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples")
|
||||
|
||||
SET(hiredis_sources
|
||||
alloc.c
|
||||
async.c
|
||||
dict.c
|
||||
hiredis.c
|
||||
net.c
|
||||
read.c
|
||||
@@ -42,34 +40,30 @@ SET(hiredis_sources
|
||||
SET(hiredis_sources ${hiredis_sources})
|
||||
|
||||
IF(WIN32)
|
||||
ADD_COMPILE_DEFINITIONS(_CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN)
|
||||
ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS -DWIN32_LEAN_AND_MEAN)
|
||||
ENDIF()
|
||||
|
||||
ADD_LIBRARY(hiredis SHARED ${hiredis_sources})
|
||||
ADD_LIBRARY(hiredis_static STATIC ${hiredis_sources})
|
||||
ADD_LIBRARY(hiredis ${hiredis_sources})
|
||||
ADD_LIBRARY(hiredis::hiredis ALIAS hiredis)
|
||||
ADD_LIBRARY(hiredis::hiredis_static ALIAS hiredis_static)
|
||||
set(hiredis_export_name hiredis CACHE STRING "Name of the exported target")
|
||||
set_target_properties(hiredis PROPERTIES EXPORT_NAME ${hiredis_export_name})
|
||||
|
||||
SET_TARGET_PROPERTIES(hiredis
|
||||
PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE
|
||||
VERSION "${HIREDIS_SONAME}")
|
||||
SET_TARGET_PROPERTIES(hiredis_static
|
||||
PROPERTIES COMPILE_PDB_NAME hiredis_static)
|
||||
SET_TARGET_PROPERTIES(hiredis_static
|
||||
PROPERTIES COMPILE_PDB_NAME_DEBUG hiredis_static${CMAKE_DEBUG_POSTFIX})
|
||||
IF(WIN32 OR MINGW)
|
||||
IF(MSVC)
|
||||
SET_TARGET_PROPERTIES(hiredis
|
||||
PROPERTIES COMPILE_FLAGS /Z7)
|
||||
ENDIF()
|
||||
IF(WIN32)
|
||||
TARGET_LINK_LIBRARIES(hiredis PUBLIC ws2_32 crypt32)
|
||||
TARGET_LINK_LIBRARIES(hiredis_static PUBLIC ws2_32 crypt32)
|
||||
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
TARGET_LINK_LIBRARIES(hiredis PUBLIC m)
|
||||
TARGET_LINK_LIBRARIES(hiredis_static PUBLIC m)
|
||||
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "SunOS")
|
||||
TARGET_LINK_LIBRARIES(hiredis PUBLIC socket)
|
||||
TARGET_LINK_LIBRARIES(hiredis_static PUBLIC socket)
|
||||
ENDIF()
|
||||
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC $<INSTALL_INTERFACE:include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis_static PUBLIC $<INSTALL_INTERFACE:include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
|
||||
CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY)
|
||||
|
||||
@@ -99,26 +93,23 @@ set(CPACK_RPM_PACKAGE_AUTOREQPROV ON)
|
||||
|
||||
include(CPack)
|
||||
|
||||
INSTALL(TARGETS hiredis hiredis_static
|
||||
INSTALL(TARGETS hiredis
|
||||
EXPORT hiredis-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND BUILD_SHARED_LIBS)
|
||||
INSTALL(FILES $<TARGET_PDB_FILE:hiredis>
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
CONFIGURATIONS Debug RelWithDebInfo)
|
||||
INSTALL(FILES $<TARGET_FILE_DIR:hiredis_static>/$<TARGET_FILE_BASE_NAME:hiredis_static>.pdb
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
CONFIGURATIONS Debug RelWithDebInfo)
|
||||
endif()
|
||||
|
||||
# For NuGet packages
|
||||
INSTALL(FILES hiredis.targets
|
||||
DESTINATION build/native)
|
||||
|
||||
INSTALL(FILES hiredis.h read.h sds.h async.h alloc.h
|
||||
INSTALL(FILES hiredis.h read.h sds.h async.h alloc.h sockcompat.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
||||
|
||||
INSTALL(DIRECTORY adapters
|
||||
@@ -131,9 +122,15 @@ export(EXPORT hiredis-targets
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis-targets.cmake"
|
||||
NAMESPACE hiredis::)
|
||||
|
||||
SET(CMAKE_CONF_INSTALL_DIR share/hiredis)
|
||||
if(WIN32)
|
||||
SET(CMAKE_CONF_INSTALL_DIR share/hiredis)
|
||||
else()
|
||||
SET(CMAKE_CONF_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/hiredis)
|
||||
endif()
|
||||
SET(INCLUDE_INSTALL_DIR include)
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/hiredis-config-version.cmake"
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
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)
|
||||
@@ -144,6 +141,7 @@ INSTALL(EXPORT hiredis-targets
|
||||
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
|
||||
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/hiredis-config-version.cmake
|
||||
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
|
||||
|
||||
|
||||
@@ -156,12 +154,10 @@ IF(ENABLE_SSL)
|
||||
FIND_PACKAGE(OpenSSL REQUIRED)
|
||||
SET(hiredis_ssl_sources
|
||||
ssl.c)
|
||||
ADD_LIBRARY(hiredis_ssl SHARED
|
||||
${hiredis_ssl_sources})
|
||||
ADD_LIBRARY(hiredis_ssl_static STATIC
|
||||
${hiredis_ssl_sources})
|
||||
ADD_LIBRARY(hiredis_ssl ${hiredis_ssl_sources})
|
||||
ADD_LIBRARY(hiredis::hiredis_ssl ALIAS hiredis_ssl)
|
||||
|
||||
IF (APPLE)
|
||||
IF (APPLE AND BUILD_SHARED_LIBS)
|
||||
SET_PROPERTY(TARGET hiredis_ssl PROPERTY LINK_FLAGS "-Wl,-undefined -Wl,dynamic_lookup")
|
||||
ENDIF()
|
||||
|
||||
@@ -169,34 +165,26 @@ IF(ENABLE_SSL)
|
||||
PROPERTIES
|
||||
WINDOWS_EXPORT_ALL_SYMBOLS TRUE
|
||||
VERSION "${HIREDIS_SONAME}")
|
||||
SET_TARGET_PROPERTIES(hiredis_ssl_static
|
||||
PROPERTIES COMPILE_PDB_NAME hiredis_ssl_static)
|
||||
SET_TARGET_PROPERTIES(hiredis_ssl_static
|
||||
PROPERTIES COMPILE_PDB_NAME_DEBUG hiredis_ssl_static${CMAKE_DEBUG_POSTFIX})
|
||||
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE "${OPENSSL_INCLUDE_DIR}")
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis_ssl_static PRIVATE "${OPENSSL_INCLUDE_DIR}")
|
||||
|
||||
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES})
|
||||
IF (WIN32 OR MINGW)
|
||||
IF(MSVC)
|
||||
SET_TARGET_PROPERTIES(hiredis_ssl
|
||||
PROPERTIES COMPILE_FLAGS /Z7)
|
||||
ENDIF()
|
||||
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE OpenSSL::SSL)
|
||||
IF(WIN32)
|
||||
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE hiredis)
|
||||
TARGET_LINK_LIBRARIES(hiredis_ssl_static PUBLIC hiredis_static)
|
||||
ENDIF()
|
||||
CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY)
|
||||
|
||||
INSTALL(TARGETS hiredis_ssl hiredis_ssl_static
|
||||
INSTALL(TARGETS hiredis_ssl
|
||||
EXPORT hiredis_ssl-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND BUILD_SHARED_LIBS)
|
||||
INSTALL(FILES $<TARGET_PDB_FILE:hiredis_ssl>
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
CONFIGURATIONS Debug RelWithDebInfo)
|
||||
INSTALL(FILES $<TARGET_FILE_DIR:hiredis_ssl_static>/$<TARGET_FILE_BASE_NAME:hiredis_ssl_static>.pdb
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
CONFIGURATIONS Debug RelWithDebInfo)
|
||||
endif()
|
||||
|
||||
INSTALL(FILES hiredis_ssl.h
|
||||
@@ -209,7 +197,11 @@ IF(ENABLE_SSL)
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-targets.cmake"
|
||||
NAMESPACE hiredis::)
|
||||
|
||||
SET(CMAKE_CONF_INSTALL_DIR share/hiredis_ssl)
|
||||
if(WIN32)
|
||||
SET(CMAKE_CONF_INSTALL_DIR share/hiredis_ssl)
|
||||
else()
|
||||
SET(CMAKE_CONF_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/hiredis_ssl)
|
||||
endif()
|
||||
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)
|
||||
@@ -241,5 +233,5 @@ ENDIF()
|
||||
|
||||
# Add examples
|
||||
IF(ENABLE_EXAMPLES)
|
||||
ADD_SUBDIRECTORY(examples)
|
||||
ADD_SUBDIRECTORY(examples)
|
||||
ENDIF(ENABLE_EXAMPLES)
|
||||
|
||||
Vendored
+38
-22
@@ -4,7 +4,7 @@
|
||||
# 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
|
||||
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 hiredis-example-push hiredis-example-poll
|
||||
TESTS=hiredis-test
|
||||
LIBNAME=libhiredis
|
||||
PKGCONFNAME=hiredis.pc
|
||||
@@ -39,9 +39,9 @@ export REDIS_TEST_CONFIG
|
||||
CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
|
||||
CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++')
|
||||
OPTIMIZATION?=-O3
|
||||
WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers
|
||||
WARNINGS=-Wall -Wextra -Werror -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers
|
||||
DEBUG_FLAGS?= -g -ggdb
|
||||
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS)
|
||||
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS) $(PLATFORM_FLAGS)
|
||||
REAL_LDFLAGS=$(LDFLAGS)
|
||||
|
||||
DYLIBSUFFIX=so
|
||||
@@ -50,7 +50,7 @@ DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)
|
||||
DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
|
||||
DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
|
||||
|
||||
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME)
|
||||
DYLIB_MAKE_CMD=$(CC) $(PLATFORM_FLAGS) -shared -Wl,-soname,$(DYLIB_MINOR_NAME)
|
||||
STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
|
||||
STLIB_MAKE_CMD=$(AR) rcs
|
||||
|
||||
@@ -63,7 +63,7 @@ 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)
|
||||
SSL_DYLIB_MAKE_CMD=$(CC) $(PLATFORM_FLAGS) -shared -Wl,-soname,$(SSL_DYLIB_MINOR_NAME)
|
||||
|
||||
USE_SSL?=0
|
||||
ifeq ($(USE_SSL),1)
|
||||
@@ -92,18 +92,25 @@ ifeq ($(TEST_ASYNC),1)
|
||||
endif
|
||||
|
||||
ifeq ($(USE_SSL),1)
|
||||
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
|
||||
ifndef OPENSSL_PREFIX
|
||||
ifeq ($(uname_S),Darwin)
|
||||
SEARCH_PATH1=/opt/homebrew/opt/openssl
|
||||
SEARCH_PATH2=/usr/local/opt/openssl
|
||||
|
||||
ifneq ($(wildcard $(SEARCH_PATH1)),)
|
||||
OPENSSL_PREFIX=$(SEARCH_PATH1)
|
||||
else ifneq ($(wildcard $(SEARCH_PATH2)),)
|
||||
OPENSSL_PREFIX=$(SEARCH_PATH2)
|
||||
endif
|
||||
endif
|
||||
else
|
||||
OPENSSL_PREFIX?=/usr/local/opt/openssl
|
||||
CFLAGS+=-I$(OPENSSL_PREFIX)/include
|
||||
SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto
|
||||
endif
|
||||
|
||||
ifdef OPENSSL_PREFIX
|
||||
CFLAGS+=-I$(OPENSSL_PREFIX)/include
|
||||
SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib
|
||||
endif
|
||||
|
||||
SSL_LDFLAGS+=-lssl -lcrypto
|
||||
endif
|
||||
|
||||
ifeq ($(uname_S),FreeBSD)
|
||||
@@ -180,6 +187,9 @@ hiredis-example-libevent-ssl: examples/example-libevent-ssl.c adapters/libevent.
|
||||
hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -lev $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
hiredis-example-libhv: examples/example-libhv.c adapters/libhv.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -lhv $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
@@ -192,6 +202,9 @@ 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)
|
||||
|
||||
hiredis-example-poll: examples/example-poll.c adapters/poll.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
ifndef AE_DIR
|
||||
hiredis-example-ae:
|
||||
@echo "Please specify AE_DIR (e.g. <redis repository>/src)"
|
||||
@@ -269,20 +282,22 @@ $(PKGCONFNAME): hiredis.h
|
||||
@echo prefix=$(PREFIX) > $@
|
||||
@echo exec_prefix=\$${prefix} >> $@
|
||||
@echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@
|
||||
@echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@
|
||||
@echo includedir=$(PREFIX)/include >> $@
|
||||
@echo pkgincludedir=$(PREFIX)/$(INCLUDE_PATH) >> $@
|
||||
@echo >> $@
|
||||
@echo Name: hiredis >> $@
|
||||
@echo Description: Minimalistic C client library for Redis. >> $@
|
||||
@echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@
|
||||
@echo Libs: -L\$${libdir} -lhiredis >> $@
|
||||
@echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@
|
||||
@echo Cflags: -I\$${pkgincludedir} -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@
|
||||
|
||||
$(SSL_PKGCONFNAME): hiredis_ssl.h
|
||||
@echo "Generating $@ for pkgconfig..."
|
||||
@echo prefix=$(PREFIX) > $@
|
||||
@echo exec_prefix=\$${prefix} >> $@
|
||||
@echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@
|
||||
@echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@
|
||||
@echo includedir=$(PREFIX)/include >> $@
|
||||
@echo pkgincludedir=$(PREFIX)/$(INCLUDE_PATH) >> $@
|
||||
@echo >> $@
|
||||
@echo Name: hiredis_ssl >> $@
|
||||
@echo Description: SSL Support for hiredis. >> $@
|
||||
@@ -293,10 +308,10 @@ $(SSL_PKGCONFNAME): hiredis_ssl.h
|
||||
|
||||
install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) $(SSL_INSTALL)
|
||||
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 alloc.h sockcompat.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)
|
||||
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIB_MAJOR_NAME)
|
||||
$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
|
||||
mkdir -p $(INSTALL_PKGCONF_PATH)
|
||||
$(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
|
||||
@@ -305,7 +320,7 @@ 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)
|
||||
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(SSL_DYLIB_MINOR_NAME) $(SSL_DYLIBNAME) && ln -sf $(SSL_DYLIB_MINOR_NAME) $(SSL_DYLIB_MAJOR_NAME)
|
||||
$(INSTALL) $(SSL_STLIBNAME) $(INSTALL_LIBRARY_PATH)
|
||||
mkdir -p $(INSTALL_PKGCONF_PATH)
|
||||
$(INSTALL) $(SSL_PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
|
||||
@@ -330,7 +345,8 @@ coverage: gcov
|
||||
make check
|
||||
mkdir -p tmp/lcov
|
||||
lcov -d . -c --exclude '/usr*' -o tmp/lcov/hiredis.info
|
||||
genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info
|
||||
lcov -q -l tmp/lcov/hiredis.info
|
||||
genhtml --legend -q -o tmp/lcov/report tmp/lcov/hiredis.info
|
||||
|
||||
noopt:
|
||||
$(MAKE) OPTIMIZATION=""
|
||||
|
||||
Vendored
+168
-18
@@ -23,6 +23,13 @@ Redis version >= 1.2.0.
|
||||
The library comes with multiple APIs. There is the
|
||||
*synchronous API*, the *asynchronous API* and the *reply parsing API*.
|
||||
|
||||
## Upgrading to `1.1.0`
|
||||
|
||||
Almost all users will simply need to recompile their applications against the newer version of hiredis.
|
||||
|
||||
**NOTE**: Hiredis can now return `nan` in addition to `-inf` and `inf` in a `REDIS_REPLY_DOUBLE`.
|
||||
Applications that deal with `RESP3` doubles should make sure to account for this.
|
||||
|
||||
## Upgrading to `1.0.2`
|
||||
|
||||
<span style="color:red">NOTE: v1.0.1 erroneously bumped SONAME, which is why it is skipped here.</span>
|
||||
@@ -82,6 +89,7 @@ an error state. The field `errstr` will contain a string with a description of
|
||||
the error. More information on errors can be found in the **Errors** section.
|
||||
After trying to connect to Redis using `redisConnect` you should
|
||||
check the `err` field to see if establishing the connection was successful:
|
||||
|
||||
```c
|
||||
redisContext *c = redisConnect("127.0.0.1", 6379);
|
||||
if (c == NULL || c->err) {
|
||||
@@ -94,8 +102,74 @@ if (c == NULL || c->err) {
|
||||
}
|
||||
```
|
||||
|
||||
One can also use `redisConnectWithOptions` which takes a `redisOptions` argument
|
||||
that can be configured with endpoint information as well as many different flags
|
||||
to change how the `redisContext` will be configured.
|
||||
|
||||
```c
|
||||
redisOptions opt = {0};
|
||||
|
||||
/* One can set the endpoint with one of our helper macros */
|
||||
if (tcp) {
|
||||
REDIS_OPTIONS_SET_TCP(&opt, "localhost", 6379);
|
||||
} else {
|
||||
REDIS_OPTIONS_SET_UNIX(&opt, "/tmp/redis.sock");
|
||||
}
|
||||
|
||||
/* And privdata can be specified with another helper */
|
||||
REDIS_OPTIONS_SET_PRIVDATA(&opt, myPrivData, myPrivDataDtor);
|
||||
|
||||
/* Finally various options may be set via the `options` member, as described below */
|
||||
opt->options |= REDIS_OPT_PREFER_IPV4;
|
||||
```
|
||||
|
||||
If a connection is lost, `int redisReconnect(redisContext *c)` can be used to restore the connection using the same endpoint and options as the given context.
|
||||
|
||||
### Configurable redisOptions flags
|
||||
|
||||
There are several flags you may set in the `redisOptions` struct to change default behavior. You can specify the flags via the `redisOptions->options` member.
|
||||
|
||||
| Flag | Description |
|
||||
| --- | --- |
|
||||
| REDIS\_OPT\_NONBLOCK | Tells hiredis to make a non-blocking connection. |
|
||||
| REDIS\_OPT\_REUSEADDR | Tells hiredis to set the [SO_REUSEADDR](https://man7.org/linux/man-pages/man7/socket.7.html) socket option |
|
||||
| REDIS\_OPT\_PREFER\_IPV4<br>REDIS\_OPT\_PREFER_IPV6<br>REDIS\_OPT\_PREFER\_IP\_UNSPEC | Informs hiredis to either prefer IPv4 or IPv6 when invoking [getaddrinfo](https://man7.org/linux/man-pages/man3/gai_strerror.3.html). `REDIS_OPT_PREFER_IP_UNSPEC` will cause hiredis to specify `AF_UNSPEC` in the getaddrinfo call, which means both IPv4 and IPv6 addresses will be searched simultaneously.<br>Hiredis prefers IPv4 by default. |
|
||||
| REDIS\_OPT\_NO\_PUSH\_AUTOFREE | Tells hiredis to not install the default RESP3 PUSH handler (which just intercepts and frees the replies). This is useful in situations where you want to process these messages in-band. |
|
||||
| REDIS\_OPT\_NOAUTOFREEREPLIES | **ASYNC**: tells hiredis not to automatically invoke `freeReplyObject` after executing the reply callback. |
|
||||
| REDIS\_OPT\_NOAUTOFREE | **ASYNC**: Tells hiredis not to automatically free the `redisAsyncContext` on connection/communication failure, but only if the user makes an explicit call to `redisAsyncDisconnect` or `redisAsyncFree` |
|
||||
|
||||
*Note: A `redisContext` is not thread-safe.*
|
||||
|
||||
### Other configuration using socket options
|
||||
|
||||
The following socket options are applied directly to the underlying socket.
|
||||
The values are not stored in the `redisContext`, so they are not automatically applied when reconnecting using `redisReconnect()`.
|
||||
These functions return `REDIS_OK` on success.
|
||||
On failure, `REDIS_ERR` is returned and the underlying connection is closed.
|
||||
|
||||
To configure these for an asyncronous context (see *Asynchronous API* below), use `ac->c` to get the redisContext out of an asyncRedisContext.
|
||||
|
||||
```C
|
||||
int redisEnableKeepAlive(redisContext *c);
|
||||
int redisEnableKeepAliveWithInterval(redisContext *c, int interval);
|
||||
```
|
||||
|
||||
Enables TCP keepalive by setting the following socket options (with some variations depending on OS):
|
||||
|
||||
* `SO_KEEPALIVE`;
|
||||
* `TCP_KEEPALIVE` or `TCP_KEEPIDLE`, value configurable using the `interval` parameter, default 15 seconds;
|
||||
* `TCP_KEEPINTVL` set to 1/3 of `interval`;
|
||||
* `TCP_KEEPCNT` set to 3.
|
||||
|
||||
```C
|
||||
int redisSetTcpUserTimeout(redisContext *c, unsigned int timeout);
|
||||
```
|
||||
|
||||
Set the `TCP_USER_TIMEOUT` Linux-specific socket option which is as described in the `tcp` man page:
|
||||
|
||||
> When the value is greater than 0, it specifies the maximum amount of time in milliseconds that trans mitted data may remain unacknowledged before TCP will forcibly close the corresponding connection and return ETIMEDOUT to the application.
|
||||
> If the option value is specified as 0, TCP will use the system default.
|
||||
|
||||
### Sending commands
|
||||
|
||||
There are several ways to issue commands to Redis. The first that will be introduced is
|
||||
@@ -250,7 +324,7 @@ following two execution paths:
|
||||
* Read from the socket until a single reply could be parsed
|
||||
|
||||
The function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply
|
||||
is expected on the socket. To pipeline commands, the only things that needs to be done is
|
||||
is expected on the socket. To pipeline commands, the only thing that needs to be done is
|
||||
filling up the output buffer. For this cause, two commands can be used that are identical
|
||||
to the `redisCommand` family, apart from not returning a reply:
|
||||
```c
|
||||
@@ -320,23 +394,48 @@ Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The
|
||||
should be checked after creation to see if there were errors creating the connection.
|
||||
Because the connection that will be created is non-blocking, the kernel is not able to
|
||||
instantly return if the specified host and port is able to accept a connection.
|
||||
In case of error, it is the caller's responsibility to free the context using `redisAsyncFree()`
|
||||
|
||||
*Note: A `redisAsyncContext` is not thread-safe.*
|
||||
|
||||
An application function creating a connection might look like this:
|
||||
|
||||
```c
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
// handle error
|
||||
void appConnect(myAppData *appData)
|
||||
{
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
// handle error
|
||||
redisAsyncFree(c);
|
||||
c = NULL;
|
||||
} else {
|
||||
appData->context = c;
|
||||
appData->connecting = 1;
|
||||
c->data = appData; /* store application pointer for the callbacks */
|
||||
redisAsyncSetConnectCallback(c, appOnConnect);
|
||||
redisAsyncSetDisconnectCallback(c, appOnDisconnect);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The asynchronous context can hold a disconnect callback function that is called when the
|
||||
connection is disconnected (either because of an error or per user request). This function should
|
||||
|
||||
The asynchronous context _should_ hold a *connect* callback function that is called when the connection
|
||||
attempt completes, either successfully or with an error.
|
||||
It _can_ also hold a *disconnect* callback function that is called when the
|
||||
connection is disconnected (either because of an error or per user request). Both callbacks should
|
||||
have the following prototype:
|
||||
```c
|
||||
void(const redisAsyncContext *c, int status);
|
||||
```
|
||||
|
||||
On a *connect*, the `status` argument is set to `REDIS_OK` if the connection attempt succeeded. In this
|
||||
case, the context is ready to accept commands. If it is called with `REDIS_ERR` then the
|
||||
connection attempt failed. The `err` field in the context can be accessed to find out the cause of the error.
|
||||
After a failed connection attempt, the context object is automatically freed by the library after calling
|
||||
the connect callback. This may be a good point to create a new context and retry the connection.
|
||||
|
||||
On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the
|
||||
user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err`
|
||||
field in the context can be accessed to find out the cause of the error.
|
||||
@@ -344,12 +443,46 @@ field in the context can be accessed to find out the cause of the error.
|
||||
The context object is always freed after the disconnect callback fired. When a reconnect is needed,
|
||||
the disconnect callback is a good point to do so.
|
||||
|
||||
Setting the disconnect callback can only be done once per context. For subsequent calls it will
|
||||
return `REDIS_ERR`. The function to set the disconnect callback has the following prototype:
|
||||
Setting the connect or disconnect callbacks can only be done once per context. For subsequent calls the
|
||||
api will return `REDIS_ERR`. The function to set the callbacks have the following prototype:
|
||||
```c
|
||||
/* Alternatively you can use redisAsyncSetConnectCallbackNC which will be passed a non-const
|
||||
redisAsyncContext* on invocation (e.g. allowing writes to the privdata member). */
|
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
|
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
||||
```
|
||||
`ac->data` may be used to pass user data to this callback, the same can be done for redisConnectCallback.
|
||||
`ac->data` may be used to pass user data to both callbacks. A typical implementation
|
||||
might look something like this:
|
||||
```c
|
||||
void appOnConnect(redisAsyncContext *c, int status)
|
||||
{
|
||||
myAppData *appData = (myAppData*)c->data; /* get my application specific context*/
|
||||
appData->connecting = 0;
|
||||
if (status == REDIS_OK) {
|
||||
appData->connected = 1;
|
||||
} else {
|
||||
appData->connected = 0;
|
||||
appData->err = c->err;
|
||||
appData->context = NULL; /* avoid stale pointer when callback returns */
|
||||
}
|
||||
appAttemptReconnect();
|
||||
}
|
||||
|
||||
void appOnDisconnect(redisAsyncContext *c, int status)
|
||||
{
|
||||
myAppData *appData = (myAppData*)c->data; /* get my application specific context*/
|
||||
appData->connected = 0;
|
||||
appData->err = c->err;
|
||||
appData->context = NULL; /* avoid stale pointer when callback returns */
|
||||
if (status == REDIS_OK) {
|
||||
appNotifyDisconnectCompleted(mydata);
|
||||
} else {
|
||||
appNotifyUnexpectedDisconnect(mydata);
|
||||
appAttemptReconnect();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Sending commands and their callbacks
|
||||
|
||||
In an asynchronous context, commands are automatically pipelined due to the nature of an event loop.
|
||||
@@ -382,6 +515,14 @@ valid for the duration of the callback.
|
||||
|
||||
All pending callbacks are called with a `NULL` reply when the context encountered an error.
|
||||
|
||||
For every command issued, with the exception of **SUBSCRIBE** and **PSUBSCRIBE**, the callback is
|
||||
called exactly once. Even if the context object id disconnected or deleted, every pending callback
|
||||
will be called with a `NULL` reply.
|
||||
|
||||
For **SUBSCRIBE** and **PSUBSCRIBE**, the callbacks may be called repeatedly until an `unsubscribe`
|
||||
message arrives. This will be the last invocation of the callback. In case of error, the callbacks
|
||||
may receive a final `NULL` reply instead.
|
||||
|
||||
### Disconnecting
|
||||
|
||||
An asynchronous connection can be terminated using:
|
||||
@@ -394,6 +535,15 @@ have been written to the socket, their respective replies have been read and the
|
||||
callbacks have been executed. After this, the disconnection callback is executed with the
|
||||
`REDIS_OK` status and the context object is freed.
|
||||
|
||||
The connection can be forcefully disconnected using
|
||||
```c
|
||||
void redisAsyncFree(redisAsyncContext *ac);
|
||||
```
|
||||
In this case, nothing more is written to the socket, all pending callbacks are called with a `NULL`
|
||||
reply and the disconnection callback is called with `REDIS_OK`, after which the context object
|
||||
is freed.
|
||||
|
||||
|
||||
### Hooking it up to event library *X*
|
||||
|
||||
There are a few hooks that need to be set on the context object after it is created.
|
||||
@@ -497,8 +647,8 @@ unaffected so no additional dependencies are introduced.
|
||||
First, you'll need to make sure you include the SSL header file:
|
||||
|
||||
```c
|
||||
#include "hiredis.h"
|
||||
#include "hiredis_ssl.h"
|
||||
#include <hiredis/hiredis.h>
|
||||
#include <hiredis/hiredis_ssl.h>
|
||||
```
|
||||
|
||||
You will also need to link against `libhiredis_ssl`, **in addition** to
|
||||
@@ -529,7 +679,7 @@ redisSSLContext *ssl_context;
|
||||
/* An error variable to indicate what went wrong, if the context fails to
|
||||
* initialize.
|
||||
*/
|
||||
redisSSLContextError ssl_error;
|
||||
redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE;
|
||||
|
||||
/* Initialize global OpenSSL state.
|
||||
*
|
||||
@@ -547,11 +697,11 @@ ssl_context = redisCreateSSLContext(
|
||||
"redis.mydomain.com", /* Server name to request (SNI), optional */
|
||||
&ssl_error);
|
||||
|
||||
if(ssl_context == NULL || ssl_error != 0) {
|
||||
if(ssl_context == NULL || ssl_error != REDIS_SSL_CTX_NONE) {
|
||||
/* Handle error and abort... */
|
||||
/* e.g.
|
||||
printf("SSL error: %s\n",
|
||||
(ssl_error != 0) ?
|
||||
/* e.g.
|
||||
printf("SSL error: %s\n",
|
||||
(ssl_error != REDIS_SSL_CTX_NONE) ?
|
||||
redisSSLContextGetError(ssl_error) : "Unknown error");
|
||||
// Abort
|
||||
*/
|
||||
@@ -633,7 +783,7 @@ If you have a unique use-case where you don't want hiredis to automatically inte
|
||||
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)._
|
||||
_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
|
||||
|
||||
|
||||
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
#ifndef __HIREDIS_LIBHV_H__
|
||||
#define __HIREDIS_LIBHV_H__
|
||||
|
||||
#include <hv/hloop.h>
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
|
||||
typedef struct redisLibhvEvents {
|
||||
hio_t *io;
|
||||
htimer_t *timer;
|
||||
} redisLibhvEvents;
|
||||
|
||||
static void redisLibhvHandleEvents(hio_t* io) {
|
||||
redisAsyncContext* context = (redisAsyncContext*)hevent_userdata(io);
|
||||
int events = hio_events(io);
|
||||
int revents = hio_revents(io);
|
||||
if (context && (events & HV_READ) && (revents & HV_READ)) {
|
||||
redisAsyncHandleRead(context);
|
||||
}
|
||||
if (context && (events & HV_WRITE) && (revents & HV_WRITE)) {
|
||||
redisAsyncHandleWrite(context);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibhvAddRead(void *privdata) {
|
||||
redisLibhvEvents* events = (redisLibhvEvents*)privdata;
|
||||
hio_add(events->io, redisLibhvHandleEvents, HV_READ);
|
||||
}
|
||||
|
||||
static void redisLibhvDelRead(void *privdata) {
|
||||
redisLibhvEvents* events = (redisLibhvEvents*)privdata;
|
||||
hio_del(events->io, HV_READ);
|
||||
}
|
||||
|
||||
static void redisLibhvAddWrite(void *privdata) {
|
||||
redisLibhvEvents* events = (redisLibhvEvents*)privdata;
|
||||
hio_add(events->io, redisLibhvHandleEvents, HV_WRITE);
|
||||
}
|
||||
|
||||
static void redisLibhvDelWrite(void *privdata) {
|
||||
redisLibhvEvents* events = (redisLibhvEvents*)privdata;
|
||||
hio_del(events->io, HV_WRITE);
|
||||
}
|
||||
|
||||
static void redisLibhvCleanup(void *privdata) {
|
||||
redisLibhvEvents* events = (redisLibhvEvents*)privdata;
|
||||
|
||||
if (events->timer)
|
||||
htimer_del(events->timer);
|
||||
|
||||
hio_close(events->io);
|
||||
hevent_set_userdata(events->io, NULL);
|
||||
|
||||
hi_free(events);
|
||||
}
|
||||
|
||||
static void redisLibhvTimeout(htimer_t* timer) {
|
||||
hio_t* io = (hio_t*)hevent_userdata(timer);
|
||||
redisAsyncHandleTimeout((redisAsyncContext*)hevent_userdata(io));
|
||||
}
|
||||
|
||||
static void redisLibhvSetTimeout(void *privdata, struct timeval tv) {
|
||||
redisLibhvEvents* events;
|
||||
uint32_t millis;
|
||||
hloop_t* loop;
|
||||
|
||||
events = (redisLibhvEvents*)privdata;
|
||||
millis = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
|
||||
if (millis == 0) {
|
||||
/* Libhv disallows zero'd timers so treat this as a delete or NO OP */
|
||||
if (events->timer) {
|
||||
htimer_del(events->timer);
|
||||
events->timer = NULL;
|
||||
}
|
||||
} else if (events->timer == NULL) {
|
||||
/* Add new timer */
|
||||
loop = hevent_loop(events->io);
|
||||
events->timer = htimer_add(loop, redisLibhvTimeout, millis, 1);
|
||||
hevent_set_userdata(events->timer, events->io);
|
||||
} else {
|
||||
/* Update existing timer */
|
||||
htimer_reset(events->timer, millis);
|
||||
}
|
||||
}
|
||||
|
||||
static int redisLibhvAttach(redisAsyncContext* ac, hloop_t* loop) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisLibhvEvents *events;
|
||||
hio_t* io = NULL;
|
||||
|
||||
if (ac->ev.data != NULL) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Create container struct to keep track of our io and any timer */
|
||||
events = (redisLibhvEvents*)hi_malloc(sizeof(*events));
|
||||
if (events == NULL) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
io = hio_get(loop, c->fd);
|
||||
if (io == NULL) {
|
||||
hi_free(events);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
hevent_set_userdata(io, ac);
|
||||
|
||||
events->io = io;
|
||||
events->timer = NULL;
|
||||
|
||||
ac->ev.addRead = redisLibhvAddRead;
|
||||
ac->ev.delRead = redisLibhvDelRead;
|
||||
ac->ev.addWrite = redisLibhvAddWrite;
|
||||
ac->ev.delWrite = redisLibhvDelWrite;
|
||||
ac->ev.cleanup = redisLibhvCleanup;
|
||||
ac->ev.scheduleTimer = redisLibhvSetTimeout;
|
||||
ac->ev.data = events;
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
||||
Vendored
+177
@@ -0,0 +1,177 @@
|
||||
#ifndef HIREDIS_LIBSDEVENT_H
|
||||
#define HIREDIS_LIBSDEVENT_H
|
||||
#include <systemd/sd-event.h>
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
|
||||
#define REDIS_LIBSDEVENT_DELETED 0x01
|
||||
#define REDIS_LIBSDEVENT_ENTERED 0x02
|
||||
|
||||
typedef struct redisLibsdeventEvents {
|
||||
redisAsyncContext *context;
|
||||
struct sd_event *event;
|
||||
struct sd_event_source *fdSource;
|
||||
struct sd_event_source *timerSource;
|
||||
int fd;
|
||||
short flags;
|
||||
short state;
|
||||
} redisLibsdeventEvents;
|
||||
|
||||
static void redisLibsdeventDestroy(redisLibsdeventEvents *e) {
|
||||
if (e->fdSource) {
|
||||
e->fdSource = sd_event_source_disable_unref(e->fdSource);
|
||||
}
|
||||
if (e->timerSource) {
|
||||
e->timerSource = sd_event_source_disable_unref(e->timerSource);
|
||||
}
|
||||
sd_event_unref(e->event);
|
||||
hi_free(e);
|
||||
}
|
||||
|
||||
static int redisLibsdeventTimeoutHandler(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
((void)s);
|
||||
((void)usec);
|
||||
redisLibsdeventEvents *e = (redisLibsdeventEvents*)userdata;
|
||||
redisAsyncHandleTimeout(e->context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int redisLibsdeventHandler(sd_event_source *s, int fd, uint32_t event, void *userdata) {
|
||||
((void)s);
|
||||
((void)fd);
|
||||
redisLibsdeventEvents *e = (redisLibsdeventEvents*)userdata;
|
||||
e->state |= REDIS_LIBSDEVENT_ENTERED;
|
||||
|
||||
#define CHECK_DELETED() if (e->state & REDIS_LIBSDEVENT_DELETED) {\
|
||||
redisLibsdeventDestroy(e);\
|
||||
return 0; \
|
||||
}
|
||||
|
||||
if ((event & EPOLLIN) && e->context && (e->state & REDIS_LIBSDEVENT_DELETED) == 0) {
|
||||
redisAsyncHandleRead(e->context);
|
||||
CHECK_DELETED();
|
||||
}
|
||||
|
||||
if ((event & EPOLLOUT) && e->context && (e->state & REDIS_LIBSDEVENT_DELETED) == 0) {
|
||||
redisAsyncHandleWrite(e->context);
|
||||
CHECK_DELETED();
|
||||
}
|
||||
|
||||
e->state &= ~REDIS_LIBSDEVENT_ENTERED;
|
||||
#undef CHECK_DELETED
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void redisLibsdeventAddRead(void *userdata) {
|
||||
redisLibsdeventEvents *e = (redisLibsdeventEvents*)userdata;
|
||||
|
||||
if (e->flags & EPOLLIN) {
|
||||
return;
|
||||
}
|
||||
|
||||
e->flags |= EPOLLIN;
|
||||
|
||||
if (e->flags & EPOLLOUT) {
|
||||
sd_event_source_set_io_events(e->fdSource, e->flags);
|
||||
} else {
|
||||
sd_event_add_io(e->event, &e->fdSource, e->fd, e->flags, redisLibsdeventHandler, e);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibsdeventDelRead(void *userdata) {
|
||||
redisLibsdeventEvents *e = (redisLibsdeventEvents*)userdata;
|
||||
|
||||
e->flags &= ~EPOLLIN;
|
||||
|
||||
if (e->flags) {
|
||||
sd_event_source_set_io_events(e->fdSource, e->flags);
|
||||
} else {
|
||||
e->fdSource = sd_event_source_disable_unref(e->fdSource);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibsdeventAddWrite(void *userdata) {
|
||||
redisLibsdeventEvents *e = (redisLibsdeventEvents*)userdata;
|
||||
|
||||
if (e->flags & EPOLLOUT) {
|
||||
return;
|
||||
}
|
||||
|
||||
e->flags |= EPOLLOUT;
|
||||
|
||||
if (e->flags & EPOLLIN) {
|
||||
sd_event_source_set_io_events(e->fdSource, e->flags);
|
||||
} else {
|
||||
sd_event_add_io(e->event, &e->fdSource, e->fd, e->flags, redisLibsdeventHandler, e);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibsdeventDelWrite(void *userdata) {
|
||||
redisLibsdeventEvents *e = (redisLibsdeventEvents*)userdata;
|
||||
|
||||
e->flags &= ~EPOLLOUT;
|
||||
|
||||
if (e->flags) {
|
||||
sd_event_source_set_io_events(e->fdSource, e->flags);
|
||||
} else {
|
||||
e->fdSource = sd_event_source_disable_unref(e->fdSource);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibsdeventCleanup(void *userdata) {
|
||||
redisLibsdeventEvents *e = (redisLibsdeventEvents*)userdata;
|
||||
|
||||
if (!e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->state & REDIS_LIBSDEVENT_ENTERED) {
|
||||
e->state |= REDIS_LIBSDEVENT_DELETED;
|
||||
} else {
|
||||
redisLibsdeventDestroy(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibsdeventSetTimeout(void *userdata, struct timeval tv) {
|
||||
redisLibsdeventEvents *e = (redisLibsdeventEvents *)userdata;
|
||||
|
||||
uint64_t usec = tv.tv_sec * 1000000 + tv.tv_usec;
|
||||
if (!e->timerSource) {
|
||||
sd_event_add_time_relative(e->event, &e->timerSource, CLOCK_MONOTONIC, usec, 1, redisLibsdeventTimeoutHandler, e);
|
||||
} else {
|
||||
sd_event_source_set_time_relative(e->timerSource, usec);
|
||||
}
|
||||
}
|
||||
|
||||
static int redisLibsdeventAttach(redisAsyncContext *ac, struct sd_event *event) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisLibsdeventEvents *e;
|
||||
|
||||
/* Nothing should be attached when something is already attached */
|
||||
if (ac->ev.data != NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisLibsdeventEvents*)hi_calloc(1, sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Initialize and increase event refcount */
|
||||
e->context = ac;
|
||||
e->event = event;
|
||||
e->fd = c->fd;
|
||||
sd_event_ref(event);
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
ac->ev.addRead = redisLibsdeventAddRead;
|
||||
ac->ev.delRead = redisLibsdeventDelRead;
|
||||
ac->ev.addWrite = redisLibsdeventAddWrite;
|
||||
ac->ev.delWrite = redisLibsdeventDelWrite;
|
||||
ac->ev.cleanup = redisLibsdeventCleanup;
|
||||
ac->ev.scheduleTimer = redisLibsdeventSetTimeout;
|
||||
ac->ev.data = e;
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
||||
Vendored
+8
@@ -30,6 +30,10 @@ static void redisLibuvPoll(uv_poll_t* handle, int status, int events) {
|
||||
static void redisLibuvAddRead(void *privdata) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
||||
|
||||
if (p->events & UV_READABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
p->events |= UV_READABLE;
|
||||
|
||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
||||
@@ -52,6 +56,10 @@ static void redisLibuvDelRead(void *privdata) {
|
||||
static void redisLibuvAddWrite(void *privdata) {
|
||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
||||
|
||||
if (p->events & UV_WRITABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
p->events |= UV_WRITABLE;
|
||||
|
||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
||||
|
||||
Vendored
+197
@@ -0,0 +1,197 @@
|
||||
|
||||
#ifndef HIREDIS_POLL_H
|
||||
#define HIREDIS_POLL_H
|
||||
|
||||
#include "../async.h"
|
||||
#include "../sockcompat.h"
|
||||
#include <string.h> // for memset
|
||||
#include <errno.h>
|
||||
|
||||
/* Values to return from redisPollTick */
|
||||
#define REDIS_POLL_HANDLED_READ 1
|
||||
#define REDIS_POLL_HANDLED_WRITE 2
|
||||
#define REDIS_POLL_HANDLED_TIMEOUT 4
|
||||
|
||||
/* An adapter to allow manual polling of the async context by checking the state
|
||||
* of the underlying file descriptor. Useful in cases where there is no formal
|
||||
* IO event loop but regular ticking can be used, such as in game engines. */
|
||||
|
||||
typedef struct redisPollEvents {
|
||||
redisAsyncContext *context;
|
||||
redisFD fd;
|
||||
char reading, writing;
|
||||
char in_tick;
|
||||
char deleted;
|
||||
double deadline;
|
||||
} redisPollEvents;
|
||||
|
||||
static double redisPollTimevalToDouble(struct timeval *tv) {
|
||||
if (tv == NULL)
|
||||
return 0.0;
|
||||
return tv->tv_sec + tv->tv_usec / 1000000.00;
|
||||
}
|
||||
|
||||
static double redisPollGetNow(void) {
|
||||
#ifndef _MSC_VER
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
return redisPollTimevalToDouble(&tv);
|
||||
#else
|
||||
FILETIME ft;
|
||||
ULARGE_INTEGER li;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
li.HighPart = ft.dwHighDateTime;
|
||||
li.LowPart = ft.dwLowDateTime;
|
||||
return (double)li.QuadPart * 1e-7;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Poll for io, handling any pending callbacks. The timeout argument can be
|
||||
* positive to wait for a maximum given time for IO, zero to poll, or negative
|
||||
* to wait forever */
|
||||
static int redisPollTick(redisAsyncContext *ac, double timeout) {
|
||||
int reading, writing;
|
||||
struct pollfd pfd;
|
||||
int handled;
|
||||
int ns;
|
||||
int itimeout;
|
||||
|
||||
redisPollEvents *e = (redisPollEvents*)ac->ev.data;
|
||||
if (!e)
|
||||
return 0;
|
||||
|
||||
/* local flags, won't get changed during callbacks */
|
||||
reading = e->reading;
|
||||
writing = e->writing;
|
||||
if (!reading && !writing)
|
||||
return 0;
|
||||
|
||||
pfd.fd = e->fd;
|
||||
pfd.events = 0;
|
||||
if (reading)
|
||||
pfd.events = POLLIN;
|
||||
if (writing)
|
||||
pfd.events |= POLLOUT;
|
||||
|
||||
if (timeout >= 0.0) {
|
||||
itimeout = (int)(timeout * 1000.0);
|
||||
} else {
|
||||
itimeout = -1;
|
||||
}
|
||||
|
||||
ns = poll(&pfd, 1, itimeout);
|
||||
if (ns < 0) {
|
||||
/* ignore the EINTR error */
|
||||
if (errno != EINTR)
|
||||
return ns;
|
||||
ns = 0;
|
||||
}
|
||||
|
||||
handled = 0;
|
||||
e->in_tick = 1;
|
||||
if (ns) {
|
||||
if (reading && (pfd.revents & POLLIN)) {
|
||||
redisAsyncHandleRead(ac);
|
||||
handled |= REDIS_POLL_HANDLED_READ;
|
||||
}
|
||||
/* on Windows, connection failure is indicated with the Exception fdset.
|
||||
* handle it the same as writable. */
|
||||
if (writing && (pfd.revents & (POLLOUT | POLLERR))) {
|
||||
/* context Read callback may have caused context to be deleted, e.g.
|
||||
by doing an redisAsyncDisconnect() */
|
||||
if (!e->deleted) {
|
||||
redisAsyncHandleWrite(ac);
|
||||
handled |= REDIS_POLL_HANDLED_WRITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* perform timeouts */
|
||||
if (!e->deleted && e->deadline != 0.0) {
|
||||
double now = redisPollGetNow();
|
||||
if (now >= e->deadline) {
|
||||
/* deadline has passed. disable timeout and perform callback */
|
||||
e->deadline = 0.0;
|
||||
redisAsyncHandleTimeout(ac);
|
||||
handled |= REDIS_POLL_HANDLED_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
/* do a delayed cleanup if required */
|
||||
if (e->deleted)
|
||||
hi_free(e);
|
||||
else
|
||||
e->in_tick = 0;
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
static void redisPollAddRead(void *data) {
|
||||
redisPollEvents *e = (redisPollEvents*)data;
|
||||
e->reading = 1;
|
||||
}
|
||||
|
||||
static void redisPollDelRead(void *data) {
|
||||
redisPollEvents *e = (redisPollEvents*)data;
|
||||
e->reading = 0;
|
||||
}
|
||||
|
||||
static void redisPollAddWrite(void *data) {
|
||||
redisPollEvents *e = (redisPollEvents*)data;
|
||||
e->writing = 1;
|
||||
}
|
||||
|
||||
static void redisPollDelWrite(void *data) {
|
||||
redisPollEvents *e = (redisPollEvents*)data;
|
||||
e->writing = 0;
|
||||
}
|
||||
|
||||
static void redisPollCleanup(void *data) {
|
||||
redisPollEvents *e = (redisPollEvents*)data;
|
||||
|
||||
/* if we are currently processing a tick, postpone deletion */
|
||||
if (e->in_tick)
|
||||
e->deleted = 1;
|
||||
else
|
||||
hi_free(e);
|
||||
}
|
||||
|
||||
static void redisPollScheduleTimer(void *data, struct timeval tv)
|
||||
{
|
||||
redisPollEvents *e = (redisPollEvents*)data;
|
||||
double now = redisPollGetNow();
|
||||
e->deadline = now + redisPollTimevalToDouble(&tv);
|
||||
}
|
||||
|
||||
static int redisPollAttach(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisPollEvents *e;
|
||||
|
||||
/* Nothing should be attached when something is already attached */
|
||||
if (ac->ev.data != NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisPollEvents*)hi_malloc(sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
memset(e, 0, sizeof(*e));
|
||||
|
||||
e->context = ac;
|
||||
e->fd = c->fd;
|
||||
e->reading = e->writing = 0;
|
||||
e->in_tick = e->deleted = 0;
|
||||
e->deadline = 0.0;
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
ac->ev.addRead = redisPollAddRead;
|
||||
ac->ev.delRead = redisPollDelRead;
|
||||
ac->ev.addWrite = redisPollAddWrite;
|
||||
ac->ev.delWrite = redisPollDelWrite;
|
||||
ac->ev.scheduleTimer = redisPollScheduleTimer;
|
||||
ac->ev.cleanup = redisPollCleanup;
|
||||
ac->ev.data = e;
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif /* HIREDIS_POLL_H */
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
#ifndef __HIREDIS_REDISMODULEAPI_H__
|
||||
#define __HIREDIS_REDISMODULEAPI_H__
|
||||
|
||||
#include "redismodule.h"
|
||||
|
||||
#include "../async.h"
|
||||
#include "../hiredis.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef struct redisModuleEvents {
|
||||
redisAsyncContext *context;
|
||||
RedisModuleCtx *module_ctx;
|
||||
int fd;
|
||||
int reading, writing;
|
||||
int timer_active;
|
||||
RedisModuleTimerID timer_id;
|
||||
} redisModuleEvents;
|
||||
|
||||
static inline void redisModuleReadEvent(int fd, void *privdata, int mask) {
|
||||
(void) fd;
|
||||
(void) mask;
|
||||
|
||||
redisModuleEvents *e = (redisModuleEvents*)privdata;
|
||||
redisAsyncHandleRead(e->context);
|
||||
}
|
||||
|
||||
static inline void redisModuleWriteEvent(int fd, void *privdata, int mask) {
|
||||
(void) fd;
|
||||
(void) mask;
|
||||
|
||||
redisModuleEvents *e = (redisModuleEvents*)privdata;
|
||||
redisAsyncHandleWrite(e->context);
|
||||
}
|
||||
|
||||
static inline void redisModuleAddRead(void *privdata) {
|
||||
redisModuleEvents *e = (redisModuleEvents*)privdata;
|
||||
if (!e->reading) {
|
||||
e->reading = 1;
|
||||
RedisModule_EventLoopAdd(e->fd, REDISMODULE_EVENTLOOP_READABLE, redisModuleReadEvent, e);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void redisModuleDelRead(void *privdata) {
|
||||
redisModuleEvents *e = (redisModuleEvents*)privdata;
|
||||
if (e->reading) {
|
||||
e->reading = 0;
|
||||
RedisModule_EventLoopDel(e->fd, REDISMODULE_EVENTLOOP_READABLE);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void redisModuleAddWrite(void *privdata) {
|
||||
redisModuleEvents *e = (redisModuleEvents*)privdata;
|
||||
if (!e->writing) {
|
||||
e->writing = 1;
|
||||
RedisModule_EventLoopAdd(e->fd, REDISMODULE_EVENTLOOP_WRITABLE, redisModuleWriteEvent, e);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void redisModuleDelWrite(void *privdata) {
|
||||
redisModuleEvents *e = (redisModuleEvents*)privdata;
|
||||
if (e->writing) {
|
||||
e->writing = 0;
|
||||
RedisModule_EventLoopDel(e->fd, REDISMODULE_EVENTLOOP_WRITABLE);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void redisModuleStopTimer(void *privdata) {
|
||||
redisModuleEvents *e = (redisModuleEvents*)privdata;
|
||||
if (e->timer_active) {
|
||||
RedisModule_StopTimer(e->module_ctx, e->timer_id, NULL);
|
||||
}
|
||||
e->timer_active = 0;
|
||||
}
|
||||
|
||||
static inline void redisModuleCleanup(void *privdata) {
|
||||
redisModuleEvents *e = (redisModuleEvents*)privdata;
|
||||
redisModuleDelRead(privdata);
|
||||
redisModuleDelWrite(privdata);
|
||||
redisModuleStopTimer(privdata);
|
||||
hi_free(e);
|
||||
}
|
||||
|
||||
static inline void redisModuleTimeout(RedisModuleCtx *ctx, void *privdata) {
|
||||
(void) ctx;
|
||||
|
||||
redisModuleEvents *e = (redisModuleEvents*)privdata;
|
||||
e->timer_active = 0;
|
||||
redisAsyncHandleTimeout(e->context);
|
||||
}
|
||||
|
||||
static inline void redisModuleSetTimeout(void *privdata, struct timeval tv) {
|
||||
redisModuleEvents* e = (redisModuleEvents*)privdata;
|
||||
|
||||
redisModuleStopTimer(privdata);
|
||||
|
||||
mstime_t millis = tv.tv_sec * 1000 + tv.tv_usec / 1000.0;
|
||||
e->timer_id = RedisModule_CreateTimer(e->module_ctx, millis, redisModuleTimeout, e);
|
||||
e->timer_active = 1;
|
||||
}
|
||||
|
||||
/* Check if Redis version is compatible with the adapter. */
|
||||
static inline int redisModuleCompatibilityCheck(void) {
|
||||
if (!RedisModule_EventLoopAdd ||
|
||||
!RedisModule_EventLoopDel ||
|
||||
!RedisModule_CreateTimer ||
|
||||
!RedisModule_StopTimer) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static inline int redisModuleAttach(redisAsyncContext *ac, RedisModuleCtx *module_ctx) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisModuleEvents *e;
|
||||
|
||||
/* Nothing should be attached when something is already attached */
|
||||
if (ac->ev.data != NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisModuleEvents*)hi_malloc(sizeof(*e));
|
||||
if (e == NULL)
|
||||
return REDIS_ERR;
|
||||
|
||||
e->context = ac;
|
||||
e->module_ctx = module_ctx;
|
||||
e->fd = c->fd;
|
||||
e->reading = e->writing = 0;
|
||||
e->timer_active = 0;
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
ac->ev.addRead = redisModuleAddRead;
|
||||
ac->ev.delRead = redisModuleDelRead;
|
||||
ac->ev.addWrite = redisModuleAddWrite;
|
||||
ac->ev.delWrite = redisModuleDelWrite;
|
||||
ac->ev.cleanup = redisModuleCleanup;
|
||||
ac->ev.scheduleTimer = redisModuleSetTimeout;
|
||||
ac->ev.data = e;
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+177
-52
@@ -140,6 +140,7 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
||||
ac->ev.scheduleTimer = NULL;
|
||||
|
||||
ac->onConnect = NULL;
|
||||
ac->onConnectNC = NULL;
|
||||
ac->onDisconnect = NULL;
|
||||
|
||||
ac->replies.head = NULL;
|
||||
@@ -148,6 +149,7 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
||||
ac->sub.replies.tail = NULL;
|
||||
ac->sub.channels = channels;
|
||||
ac->sub.patterns = patterns;
|
||||
ac->sub.pending_unsubs = 0;
|
||||
|
||||
return ac;
|
||||
oom:
|
||||
@@ -225,17 +227,34 @@ redisAsyncContext *redisAsyncConnectUnix(const char *path) {
|
||||
return redisAsyncConnectWithOptions(&options);
|
||||
}
|
||||
|
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
|
||||
if (ac->onConnect == NULL) {
|
||||
ac->onConnect = fn;
|
||||
static int
|
||||
redisAsyncSetConnectCallbackImpl(redisAsyncContext *ac, redisConnectCallback *fn,
|
||||
redisConnectCallbackNC *fn_nc)
|
||||
{
|
||||
/* If either are already set, this is an error */
|
||||
if (ac->onConnect || ac->onConnectNC)
|
||||
return REDIS_ERR;
|
||||
|
||||
/* The common way to detect an established connection is to wait for
|
||||
* the first write event to be fired. This assumes the related event
|
||||
* library functions are already set. */
|
||||
_EL_ADD_WRITE(ac);
|
||||
return REDIS_OK;
|
||||
if (fn) {
|
||||
ac->onConnect = fn;
|
||||
} else if (fn_nc) {
|
||||
ac->onConnectNC = fn_nc;
|
||||
}
|
||||
return REDIS_ERR;
|
||||
|
||||
/* The common way to detect an established connection is to wait for
|
||||
* the first write event to be fired. This assumes the related event
|
||||
* library functions are already set. */
|
||||
_EL_ADD_WRITE(ac);
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
|
||||
return redisAsyncSetConnectCallbackImpl(ac, fn, NULL);
|
||||
}
|
||||
|
||||
int redisAsyncSetConnectCallbackNC(redisAsyncContext *ac, redisConnectCallbackNC *fn) {
|
||||
return redisAsyncSetConnectCallbackImpl(ac, NULL, fn);
|
||||
}
|
||||
|
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {
|
||||
@@ -302,6 +321,43 @@ static void __redisRunPushCallback(redisAsyncContext *ac, redisReply *reply) {
|
||||
}
|
||||
}
|
||||
|
||||
static void __redisRunConnectCallback(redisAsyncContext *ac, int status)
|
||||
{
|
||||
if (ac->onConnect == NULL && ac->onConnectNC == NULL)
|
||||
return;
|
||||
|
||||
if (!(ac->c.flags & REDIS_IN_CALLBACK)) {
|
||||
ac->c.flags |= REDIS_IN_CALLBACK;
|
||||
if (ac->onConnect) {
|
||||
ac->onConnect(ac, status);
|
||||
} else {
|
||||
ac->onConnectNC(ac, status);
|
||||
}
|
||||
ac->c.flags &= ~REDIS_IN_CALLBACK;
|
||||
} else {
|
||||
/* already in callback */
|
||||
if (ac->onConnect) {
|
||||
ac->onConnect(ac, status);
|
||||
} else {
|
||||
ac->onConnectNC(ac, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __redisRunDisconnectCallback(redisAsyncContext *ac, int status)
|
||||
{
|
||||
if (ac->onDisconnect) {
|
||||
if (!(ac->c.flags & REDIS_IN_CALLBACK)) {
|
||||
ac->c.flags |= REDIS_IN_CALLBACK;
|
||||
ac->onDisconnect(ac, status);
|
||||
ac->c.flags &= ~REDIS_IN_CALLBACK;
|
||||
} else {
|
||||
/* already in callback */
|
||||
ac->onDisconnect(ac, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to free the context. */
|
||||
static void __redisAsyncFree(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
@@ -337,12 +393,11 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
|
||||
|
||||
/* Execute disconnect callback. When redisAsyncFree() initiated destroying
|
||||
* this context, the status will always be REDIS_OK. */
|
||||
if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) {
|
||||
if (c->flags & REDIS_FREEING) {
|
||||
ac->onDisconnect(ac,REDIS_OK);
|
||||
} else {
|
||||
ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR);
|
||||
}
|
||||
if (c->flags & REDIS_CONNECTED) {
|
||||
int status = ac->err == 0 ? REDIS_OK : REDIS_ERR;
|
||||
if (c->flags & REDIS_FREEING)
|
||||
status = REDIS_OK;
|
||||
__redisRunDisconnectCallback(ac, status);
|
||||
}
|
||||
|
||||
if (ac->dataCleanup) {
|
||||
@@ -358,7 +413,11 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
|
||||
* free'ing. To do so, a flag is set on the context which is picked up by
|
||||
* redisProcessCallbacks(). Otherwise, the context is immediately free'd. */
|
||||
void redisAsyncFree(redisAsyncContext *ac) {
|
||||
if (ac == NULL)
|
||||
return;
|
||||
|
||||
redisContext *c = &(ac->c);
|
||||
|
||||
c->flags |= REDIS_FREEING;
|
||||
if (!(c->flags & REDIS_IN_CALLBACK))
|
||||
__redisAsyncFree(ac);
|
||||
@@ -411,15 +470,15 @@ void redisAsyncDisconnect(redisAsyncContext *ac) {
|
||||
static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
|
||||
redisContext *c = &(ac->c);
|
||||
dict *callbacks;
|
||||
redisCallback *cb;
|
||||
redisCallback *cb = NULL;
|
||||
dictEntry *de;
|
||||
int pvariant;
|
||||
char *stype;
|
||||
hisds sname;
|
||||
hisds sname = NULL;
|
||||
|
||||
/* Match reply with the expected format of a pushed message.
|
||||
* The type and number of elements (3 to 4) are specified at:
|
||||
* https://redis.io/topics/pubsub#format-of-pushed-messages */
|
||||
* https://redis.io/docs/latest/develop/interact/pubsub/#format-of-pushed-messages */
|
||||
if ((reply->type == REDIS_REPLY_ARRAY && !(c->flags & REDIS_SUPPORTS_PUSH) && reply->elements >= 3) ||
|
||||
reply->type == REDIS_REPLY_PUSH) {
|
||||
assert(reply->element[0]->type == REDIS_REPLY_STRING);
|
||||
@@ -432,42 +491,43 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
|
||||
callbacks = ac->sub.channels;
|
||||
|
||||
/* 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;
|
||||
if (reply->element[1]->type == REDIS_REPLY_STRING) {
|
||||
sname = hi_sdsnewlen(reply->element[1]->str,reply->element[1]->len);
|
||||
if (sname == NULL) goto oom;
|
||||
|
||||
de = dictFind(callbacks,sname);
|
||||
if (de != NULL) {
|
||||
cb = dictGetEntryVal(de);
|
||||
|
||||
/* If this is an subscribe reply decrease pending counter. */
|
||||
if (strcasecmp(stype+pvariant,"subscribe") == 0) {
|
||||
cb->pending_subs -= 1;
|
||||
if ((de = dictFind(callbacks,sname)) != NULL) {
|
||||
cb = dictGetEntryVal(de);
|
||||
memcpy(dstcb,cb,sizeof(*dstcb));
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(dstcb,cb,sizeof(*dstcb));
|
||||
/* If this is an subscribe reply decrease pending counter. */
|
||||
if (strcasecmp(stype+pvariant,"subscribe") == 0) {
|
||||
assert(cb != NULL);
|
||||
cb->pending_subs -= 1;
|
||||
|
||||
/* If this is an unsubscribe message, remove it. */
|
||||
if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
|
||||
if (cb->pending_subs == 0)
|
||||
dictDelete(callbacks,sname);
|
||||
} else if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
|
||||
if (cb == NULL)
|
||||
ac->sub.pending_unsubs -= 1;
|
||||
else if (cb->pending_subs == 0)
|
||||
dictDelete(callbacks,sname);
|
||||
|
||||
/* If this was the last unsubscribe message, revert to
|
||||
* non-subscribe mode. */
|
||||
assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
|
||||
/* If this was the last unsubscribe message, revert to
|
||||
* non-subscribe mode. */
|
||||
assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
|
||||
|
||||
/* Unset subscribed flag only when no pipelined pending subscribe. */
|
||||
if (reply->element[2]->integer == 0
|
||||
&& dictSize(ac->sub.channels) == 0
|
||||
&& dictSize(ac->sub.patterns) == 0) {
|
||||
c->flags &= ~REDIS_SUBSCRIBED;
|
||||
/* Unset subscribed flag only when no pipelined pending subscribe
|
||||
* or pending unsubscribe replies. */
|
||||
if (reply->element[2]->integer == 0
|
||||
&& dictSize(ac->sub.channels) == 0
|
||||
&& dictSize(ac->sub.patterns) == 0
|
||||
&& ac->sub.pending_unsubs == 0) {
|
||||
c->flags &= ~REDIS_SUBSCRIBED;
|
||||
|
||||
/* Move ongoing regular command callbacks. */
|
||||
redisCallback cb;
|
||||
while (__redisShiftCallback(&ac->sub.replies,&cb) == REDIS_OK) {
|
||||
__redisPushCallback(&ac->replies,&cb);
|
||||
}
|
||||
/* Move ongoing regular command callbacks. */
|
||||
redisCallback cb;
|
||||
while (__redisShiftCallback(&ac->sub.replies,&cb) == REDIS_OK) {
|
||||
__redisPushCallback(&ac->replies,&cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -479,6 +539,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
|
||||
return REDIS_OK;
|
||||
oom:
|
||||
__redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory");
|
||||
__redisAsyncCopyError(ac);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
@@ -540,7 +601,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
|
||||
|
||||
/* Even if the context is subscribed, pending regular
|
||||
* callbacks will get a reply before pub/sub messages arrive. */
|
||||
redisCallback cb = {NULL, NULL, 0, NULL};
|
||||
redisCallback cb = {NULL, NULL, 0, 0, NULL};
|
||||
if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
|
||||
/*
|
||||
* A spontaneous reply in a not-subscribed context can be the error
|
||||
@@ -601,7 +662,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
|
||||
}
|
||||
|
||||
static void __redisAsyncHandleConnectFailure(redisAsyncContext *ac) {
|
||||
if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);
|
||||
__redisRunConnectCallback(ac, REDIS_ERR);
|
||||
__redisAsyncDisconnect(ac);
|
||||
}
|
||||
|
||||
@@ -626,8 +687,19 @@ static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (ac->onConnect) ac->onConnect(ac, REDIS_OK);
|
||||
/* flag us as fully connect, but allow the callback
|
||||
* to disconnect. For that reason, permit the function
|
||||
* to delete the context here after callback return.
|
||||
*/
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
__redisRunConnectCallback(ac, REDIS_OK);
|
||||
if ((ac->c.flags & REDIS_DISCONNECTING)) {
|
||||
redisAsyncDisconnect(ac);
|
||||
return REDIS_ERR;
|
||||
} else if ((ac->c.flags & REDIS_FREEING)) {
|
||||
redisAsyncFree(ac);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
} else {
|
||||
return REDIS_OK;
|
||||
@@ -651,6 +723,8 @@ void redisAsyncRead(redisAsyncContext *ac) {
|
||||
*/
|
||||
void redisAsyncHandleRead(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
/* must not be called from a callback */
|
||||
assert(!(c->flags & REDIS_IN_CALLBACK));
|
||||
|
||||
if (!(c->flags & REDIS_CONNECTED)) {
|
||||
/* Abort connect was not successful. */
|
||||
@@ -684,6 +758,8 @@ void redisAsyncWrite(redisAsyncContext *ac) {
|
||||
|
||||
void redisAsyncHandleWrite(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
/* must not be called from a callback */
|
||||
assert(!(c->flags & REDIS_IN_CALLBACK));
|
||||
|
||||
if (!(c->flags & REDIS_CONNECTED)) {
|
||||
/* Abort connect was not successful. */
|
||||
@@ -700,6 +776,8 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
|
||||
void redisAsyncHandleTimeout(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisCallback cb;
|
||||
/* must not be called from a callback */
|
||||
assert(!(c->flags & REDIS_IN_CALLBACK));
|
||||
|
||||
if ((c->flags & REDIS_CONNECTED)) {
|
||||
if (ac->replies.head == NULL && ac->sub.replies.head == NULL) {
|
||||
@@ -719,8 +797,8 @@ void redisAsyncHandleTimeout(redisAsyncContext *ac) {
|
||||
__redisAsyncCopyError(ac);
|
||||
}
|
||||
|
||||
if (!(c->flags & REDIS_CONNECTED) && ac->onConnect) {
|
||||
ac->onConnect(ac, REDIS_ERR);
|
||||
if (!(c->flags & REDIS_CONNECTED)) {
|
||||
__redisRunConnectCallback(ac, REDIS_ERR);
|
||||
}
|
||||
|
||||
while (__redisShiftCallback(&ac->replies, &cb) == REDIS_OK) {
|
||||
@@ -757,6 +835,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
||||
redisContext *c = &(ac->c);
|
||||
redisCallback cb;
|
||||
struct dict *cbdict;
|
||||
dictIterator it;
|
||||
dictEntry *de;
|
||||
redisCallback *existcb;
|
||||
int pvariant, hasnext;
|
||||
@@ -773,6 +852,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
||||
cb.fn = fn;
|
||||
cb.privdata = privdata;
|
||||
cb.pending_subs = 1;
|
||||
cb.unsubscribe_sent = 0;
|
||||
|
||||
/* Find out which command will be appended. */
|
||||
p = nextArgument(cmd,&cstr,&clen);
|
||||
@@ -812,6 +892,51 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
||||
* subscribed to one or more channels or patterns. */
|
||||
if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR;
|
||||
|
||||
if (pvariant)
|
||||
cbdict = ac->sub.patterns;
|
||||
else
|
||||
cbdict = ac->sub.channels;
|
||||
|
||||
if (hasnext) {
|
||||
/* Send an unsubscribe with specific channels/patterns.
|
||||
* Bookkeeping the number of expected replies */
|
||||
while ((p = nextArgument(p,&astr,&alen)) != NULL) {
|
||||
sname = hi_sdsnewlen(astr,alen);
|
||||
if (sname == NULL)
|
||||
goto oom;
|
||||
|
||||
de = dictFind(cbdict,sname);
|
||||
if (de != NULL) {
|
||||
existcb = dictGetEntryVal(de);
|
||||
if (existcb->unsubscribe_sent == 0)
|
||||
existcb->unsubscribe_sent = 1;
|
||||
else
|
||||
/* Already sent, reply to be ignored */
|
||||
ac->sub.pending_unsubs += 1;
|
||||
} else {
|
||||
/* Not subscribed to, reply to be ignored */
|
||||
ac->sub.pending_unsubs += 1;
|
||||
}
|
||||
hi_sdsfree(sname);
|
||||
}
|
||||
} else {
|
||||
/* Send an unsubscribe without specific channels/patterns.
|
||||
* Bookkeeping the number of expected replies */
|
||||
int no_subs = 1;
|
||||
dictInitIterator(&it,cbdict);
|
||||
while ((de = dictNext(&it)) != NULL) {
|
||||
existcb = dictGetEntryVal(de);
|
||||
if (existcb->unsubscribe_sent == 0) {
|
||||
existcb->unsubscribe_sent = 1;
|
||||
no_subs = 0;
|
||||
}
|
||||
}
|
||||
/* Unsubscribing to all channels/patterns, where none is
|
||||
* subscribed to, results in a single reply to be ignored. */
|
||||
if (no_subs == 1)
|
||||
ac->sub.pending_unsubs += 1;
|
||||
}
|
||||
|
||||
/* (P)UNSUBSCRIBE does not have its own response: every channel or
|
||||
* pattern that is unsubscribed will receive a message. This means we
|
||||
* should not append a callback function for this command. */
|
||||
|
||||
Vendored
+5
@@ -46,6 +46,7 @@ typedef struct redisCallback {
|
||||
struct redisCallback *next; /* simple singly linked list */
|
||||
redisCallbackFn *fn;
|
||||
int pending_subs;
|
||||
int unsubscribe_sent;
|
||||
void *privdata;
|
||||
} redisCallback;
|
||||
|
||||
@@ -57,6 +58,7 @@ typedef struct redisCallbackList {
|
||||
/* Connection callback prototypes */
|
||||
typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
|
||||
typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
|
||||
typedef void (redisConnectCallbackNC)(struct redisAsyncContext *, int status);
|
||||
typedef void(redisTimerCallback)(void *timer, void *privdata);
|
||||
|
||||
/* Context for an async connection to Redis */
|
||||
@@ -92,6 +94,7 @@ typedef struct redisAsyncContext {
|
||||
|
||||
/* Called when the first write event was received. */
|
||||
redisConnectCallback *onConnect;
|
||||
redisConnectCallbackNC *onConnectNC;
|
||||
|
||||
/* Regular command callbacks */
|
||||
redisCallbackList replies;
|
||||
@@ -105,6 +108,7 @@ typedef struct redisAsyncContext {
|
||||
redisCallbackList replies;
|
||||
struct dict *channels;
|
||||
struct dict *patterns;
|
||||
int pending_unsubs;
|
||||
} sub;
|
||||
|
||||
/* Any configured RESP3 PUSH handler */
|
||||
@@ -119,6 +123,7 @@ redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
|
||||
const char *source_addr);
|
||||
redisAsyncContext *redisAsyncConnectUnix(const char *path);
|
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
|
||||
int redisAsyncSetConnectCallbackNC(redisAsyncContext *ac, redisConnectCallbackNC *fn);
|
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
||||
|
||||
redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn);
|
||||
|
||||
Vendored
+12
@@ -25,12 +25,24 @@ if (LIBEVENT)
|
||||
TARGET_LINK_LIBRARIES(example-libevent hiredis event)
|
||||
ENDIF()
|
||||
|
||||
FIND_PATH(LIBHV hv/hv.h)
|
||||
IF (LIBHV)
|
||||
ADD_EXECUTABLE(example-libhv example-libhv.c)
|
||||
TARGET_LINK_LIBRARIES(example-libhv hiredis hv)
|
||||
ENDIF()
|
||||
|
||||
FIND_PATH(LIBUV uv.h)
|
||||
IF (LIBUV)
|
||||
ADD_EXECUTABLE(example-libuv example-libuv.c)
|
||||
TARGET_LINK_LIBRARIES(example-libuv hiredis uv)
|
||||
ENDIF()
|
||||
|
||||
FIND_PATH(LIBSDEVENT systemd/sd-event.h)
|
||||
IF (LIBSDEVENT)
|
||||
ADD_EXECUTABLE(example-libsdevent example-libsdevent.c)
|
||||
TARGET_LINK_LIBRARIES(example-libsdevent hiredis systemd)
|
||||
ENDIF()
|
||||
|
||||
IF (APPLE)
|
||||
FIND_LIBRARY(CF CoreFoundation)
|
||||
ADD_EXECUTABLE(example-macosx example-macosx.c)
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ int main (int argc, char **argv) {
|
||||
const char *caCert = argc > 5 ? argv[6] : NULL;
|
||||
|
||||
redisSSLContext *ssl;
|
||||
redisSSLContextError ssl_error;
|
||||
redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE;
|
||||
|
||||
redisInitOpenSSL();
|
||||
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/libhv.h>
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) return;
|
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* Disconnect after receiving the reply to GET */
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void debugCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
(void)privdata;
|
||||
redisReply *reply = r;
|
||||
|
||||
if (reply == NULL) {
|
||||
printf("`DEBUG SLEEP` error: %s\n", c->errstr ? c->errstr : "unknown error");
|
||||
return;
|
||||
}
|
||||
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Disconnected...\n");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
hloop_t* loop = hloop_new(HLOOP_FLAG_QUIT_WHEN_NO_ACTIVE_EVENTS);
|
||||
redisLibhvAttach(c, loop);
|
||||
redisAsyncSetTimeout(c, (struct timeval){.tv_sec = 0, .tv_usec = 500000});
|
||||
redisAsyncSetConnectCallback(c,connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
redisAsyncCommand(c, debugCallback, NULL, "DEBUG SLEEP %d", 1);
|
||||
hloop_run(loop);
|
||||
hloop_free(&loop);
|
||||
return 0;
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/libsdevent.h>
|
||||
|
||||
void debugCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
(void)privdata;
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) {
|
||||
/* The DEBUG SLEEP command will almost always fail, because we have set a 1 second timeout */
|
||||
printf("`DEBUG SLEEP` error: %s\n", c->errstr ? c->errstr : "unknown error");
|
||||
return;
|
||||
}
|
||||
/* Disconnect after receiving the reply of DEBUG SLEEP (which will not)*/
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) {
|
||||
printf("`GET key` error: %s\n", c->errstr ? c->errstr : "unknown error");
|
||||
return;
|
||||
}
|
||||
printf("`GET key` result: argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* start another request that demonstrate timeout */
|
||||
redisAsyncCommand(c, debugCallback, NULL, "DEBUG SLEEP %f", 1.5);
|
||||
}
|
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("connect error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("disconnect because of error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Disconnected...\n");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
struct sd_event *event;
|
||||
sd_event_default(&event);
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
redisAsyncFree(c);
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisLibsdeventAttach(c,event);
|
||||
redisAsyncSetConnectCallback(c,connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
||||
redisAsyncSetTimeout(c, (struct timeval){ .tv_sec = 1, .tv_usec = 0});
|
||||
|
||||
/*
|
||||
In this demo, we first `set key`, then `get key` to demonstrate the basic usage of libsdevent adapter.
|
||||
Then in `getCallback`, we start a `debug sleep` command to create 1.5 second long request.
|
||||
Because we have set a 1 second timeout to the connection, the command will always fail with a
|
||||
timeout error, which is shown in the `debugCallback`.
|
||||
*/
|
||||
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
|
||||
/* sd-event does not quit when there are no handlers registered. Manually exit after 1.5 seconds */
|
||||
sd_event_source *s;
|
||||
sd_event_add_time_relative(event, &s, CLOCK_MONOTONIC, 1500000, 1, NULL, NULL);
|
||||
|
||||
sd_event_loop(event);
|
||||
sd_event_source_disable_unref(s);
|
||||
sd_event_unref(event);
|
||||
return 0;
|
||||
}
|
||||
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <async.h>
|
||||
#include <adapters/poll.h>
|
||||
|
||||
/* Put in the global scope, so that loop can be explicitly stopped */
|
||||
static int exit_loop = 0;
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) return;
|
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* Disconnect after receiving the reply to GET */
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
exit_loop = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
exit_loop = 1;
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Disconnected...\n");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
redisPollAttach(c);
|
||||
redisAsyncSetConnectCallback(c,connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
while (!exit_loop)
|
||||
{
|
||||
redisPollTick(c, 0.1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <async.h>
|
||||
#include <adapters/redismoduleapi.h>
|
||||
|
||||
void debugCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
(void)privdata; //unused
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) {
|
||||
/* The DEBUG SLEEP command will almost always fail, because we have set a 1 second timeout */
|
||||
printf("`DEBUG SLEEP` error: %s\n", c->errstr ? c->errstr : "unknown error");
|
||||
return;
|
||||
}
|
||||
/* Disconnect after receiving the reply of DEBUG SLEEP (which will not)*/
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) {
|
||||
if (c->errstr) {
|
||||
printf("errstr: %s\n", c->errstr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* start another request that demonstrate timeout */
|
||||
redisAsyncCommand(c, debugCallback, NULL, "DEBUG SLEEP %f", 1.5);
|
||||
}
|
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Disconnected...\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* This example requires Redis 7.0 or above.
|
||||
*
|
||||
* 1- Compile this file as a shared library. Directory of "redismodule.h" must
|
||||
* be in the include path.
|
||||
* gcc -fPIC -shared -I../../redis/src/ -I.. example-redismoduleapi.c -o example-redismoduleapi.so
|
||||
*
|
||||
* 2- Load module:
|
||||
* redis-server --loadmodule ./example-redismoduleapi.so value
|
||||
*/
|
||||
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
||||
|
||||
int ret = RedisModule_Init(ctx, "example-redismoduleapi", 1, REDISMODULE_APIVER_1);
|
||||
if (ret != REDISMODULE_OK) {
|
||||
printf("error module init \n");
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
|
||||
if (redisModuleCompatibilityCheck() != REDIS_OK) {
|
||||
printf("Redis 7.0 or above is required! \n");
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t len;
|
||||
const char *val = RedisModule_StringPtrLen(argv[argc-1], &len);
|
||||
|
||||
RedisModuleCtx *module_ctx = RedisModule_GetDetachedThreadSafeContext(ctx);
|
||||
redisModuleAttach(c, module_ctx);
|
||||
redisAsyncSetConnectCallback(c,connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
||||
redisAsyncSetTimeout(c, (struct timeval){ .tv_sec = 1, .tv_usec = 0});
|
||||
|
||||
/*
|
||||
In this demo, we first `set key`, then `get key` to demonstrate the basic usage of the adapter.
|
||||
Then in `getCallback`, we start a `debug sleep` command to create 1.5 second long request.
|
||||
Because we have set a 1 second timeout to the connection, the command will always fail with a
|
||||
timeout error, which is shown in the `debugCallback`.
|
||||
*/
|
||||
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", val, len);
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
return 0;
|
||||
}
|
||||
Vendored
+3
-4
@@ -12,7 +12,7 @@
|
||||
int main(int argc, char **argv) {
|
||||
unsigned int j;
|
||||
redisSSLContext *ssl;
|
||||
redisSSLContextError ssl_error;
|
||||
redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE;
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
if (argc < 4) {
|
||||
@@ -27,9 +27,8 @@ int main(int argc, char **argv) {
|
||||
|
||||
redisInitOpenSSL();
|
||||
ssl = redisCreateSSLContext(ca, NULL, cert, key, NULL, &ssl_error);
|
||||
if (!ssl) {
|
||||
printf("SSL Context error: %s\n",
|
||||
redisSSLContextGetError(ssl_error));
|
||||
if (!ssl || ssl_error != REDIS_SSL_CTX_NONE) {
|
||||
printf("SSL Context error: %s\n", redisSSLContextGetError(ssl_error));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
Vendored
+51
@@ -7,6 +7,54 @@
|
||||
#include <winsock2.h> /* For struct timeval */
|
||||
#endif
|
||||
|
||||
static void example_argv_command(redisContext *c, size_t n) {
|
||||
char **argv, tmp[42];
|
||||
size_t *argvlen;
|
||||
redisReply *reply;
|
||||
|
||||
/* We're allocating two additional elements for command and key */
|
||||
argv = malloc(sizeof(*argv) * (2 + n));
|
||||
argvlen = malloc(sizeof(*argvlen) * (2 + n));
|
||||
|
||||
/* First the command */
|
||||
argv[0] = (char*)"RPUSH";
|
||||
argvlen[0] = sizeof("RPUSH") - 1;
|
||||
|
||||
/* Now our key */
|
||||
argv[1] = (char*)"argvlist";
|
||||
argvlen[1] = sizeof("argvlist") - 1;
|
||||
|
||||
/* Now add the entries we wish to add to the list */
|
||||
for (size_t i = 2; i < (n + 2); i++) {
|
||||
argvlen[i] = snprintf(tmp, sizeof(tmp), "argv-element-%zu", i - 2);
|
||||
argv[i] = strdup(tmp);
|
||||
}
|
||||
|
||||
/* Execute the command using redisCommandArgv. We're sending the arguments with
|
||||
* two explicit arrays. One for each argument's string, and the other for its
|
||||
* length. */
|
||||
reply = redisCommandArgv(c, n + 2, (const char **)argv, (const size_t*)argvlen);
|
||||
|
||||
if (reply == NULL || c->err) {
|
||||
fprintf(stderr, "Error: Couldn't execute redisCommandArgv\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (reply->type == REDIS_REPLY_INTEGER) {
|
||||
printf("%s reply: %lld\n", argv[0], reply->integer);
|
||||
}
|
||||
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Clean up */
|
||||
for (size_t i = 2; i < (n + 2); i++) {
|
||||
free(argv[i]);
|
||||
}
|
||||
|
||||
free(argv);
|
||||
free(argvlen);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned int j, isunix = 0;
|
||||
redisContext *c;
|
||||
@@ -87,6 +135,9 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* See function for an example of redisCommandArgv */
|
||||
example_argv_command(c, 10);
|
||||
|
||||
/* Disconnects and frees the context */
|
||||
redisFree(c);
|
||||
|
||||
|
||||
+2
-3
@@ -48,10 +48,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
memcpy(new_str, data, size);
|
||||
new_str[size] = '\0';
|
||||
|
||||
redisFormatCommand(&cmd, new_str);
|
||||
|
||||
if (cmd != NULL)
|
||||
if (redisFormatCommand(&cmd, new_str) != -1)
|
||||
hi_free(cmd);
|
||||
|
||||
free(new_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -2,11 +2,11 @@
|
||||
|
||||
set_and_check(hiredis_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
|
||||
|
||||
IF (NOT TARGET hiredis::hiredis)
|
||||
IF (NOT TARGET hiredis::@hiredis_export_name@)
|
||||
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis-targets.cmake)
|
||||
ENDIF()
|
||||
|
||||
SET(hiredis_LIBRARIES hiredis::hiredis)
|
||||
SET(hiredis_LIBRARIES hiredis::@hiredis_export_name@)
|
||||
SET(hiredis_INCLUDE_DIRS ${hiredis_INCLUDEDIR})
|
||||
|
||||
check_required_components(hiredis)
|
||||
|
||||
Vendored
+42
-11
@@ -48,6 +48,7 @@ extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeva
|
||||
extern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout);
|
||||
|
||||
static redisContextFuncs redisContextDefaultFuncs = {
|
||||
.close = redisNetClose,
|
||||
.free_privctx = NULL,
|
||||
.async_read = redisAsyncRead,
|
||||
.async_write = redisAsyncWrite,
|
||||
@@ -221,6 +222,9 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
|
||||
static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) {
|
||||
redisReply *r, *parent;
|
||||
|
||||
if (len == SIZE_MAX) // Prevents hi_malloc(0) if len equals to SIZE_MAX
|
||||
return NULL;
|
||||
|
||||
r = createReplyObject(REDIS_REPLY_DOUBLE);
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
@@ -388,17 +392,22 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
while (*_p != '\0' && strchr(flags,*_p) != NULL) _p++;
|
||||
|
||||
/* Field width */
|
||||
while (*_p != '\0' && isdigit(*_p)) _p++;
|
||||
while (*_p != '\0' && isdigit((int) *_p)) _p++;
|
||||
|
||||
/* Precision */
|
||||
if (*_p == '.') {
|
||||
_p++;
|
||||
while (*_p != '\0' && isdigit(*_p)) _p++;
|
||||
while (*_p != '\0' && isdigit((int) *_p)) _p++;
|
||||
}
|
||||
|
||||
/* Copy va_list before consuming with va_arg */
|
||||
va_copy(_cpy,ap);
|
||||
|
||||
/* Make sure we have more characters otherwise strchr() accepts
|
||||
* '\0' as an integer specifier. This is checked after above
|
||||
* va_copy() to avoid UB in fmt_invalid's call to va_end(). */
|
||||
if (*_p == '\0') goto fmt_invalid;
|
||||
|
||||
/* Integer conversion (without modifiers) */
|
||||
if (strchr(intfmts,*_p) != NULL) {
|
||||
va_arg(ap,int);
|
||||
@@ -477,6 +486,8 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
|
||||
touched = 1;
|
||||
c++;
|
||||
if (*c == '\0')
|
||||
break;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
@@ -719,7 +730,10 @@ static redisContext *redisContextInit(void) {
|
||||
void redisFree(redisContext *c) {
|
||||
if (c == NULL)
|
||||
return;
|
||||
redisNetClose(c);
|
||||
|
||||
if (c->funcs && c->funcs->close) {
|
||||
c->funcs->close(c);
|
||||
}
|
||||
|
||||
hi_sdsfree(c->obuf);
|
||||
redisReaderFree(c->reader);
|
||||
@@ -733,7 +747,7 @@ void redisFree(redisContext *c) {
|
||||
if (c->privdata && c->free_privdata)
|
||||
c->free_privdata(c->privdata);
|
||||
|
||||
if (c->funcs->free_privctx)
|
||||
if (c->funcs && c->funcs->free_privctx)
|
||||
c->funcs->free_privctx(c->privctx);
|
||||
|
||||
memset(c, 0xff, sizeof(*c));
|
||||
@@ -756,7 +770,9 @@ int redisReconnect(redisContext *c) {
|
||||
c->privctx = NULL;
|
||||
}
|
||||
|
||||
redisNetClose(c);
|
||||
if (c->funcs && c->funcs->close) {
|
||||
c->funcs->close(c);
|
||||
}
|
||||
|
||||
hi_sdsfree(c->obuf);
|
||||
redisReaderFree(c->reader);
|
||||
@@ -806,6 +822,12 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
|
||||
if (options->options & REDIS_OPT_NOAUTOFREEREPLIES) {
|
||||
c->flags |= REDIS_NO_AUTO_FREE_REPLIES;
|
||||
}
|
||||
if (options->options & REDIS_OPT_PREFER_IPV4) {
|
||||
c->flags |= REDIS_PREFER_IPV4;
|
||||
}
|
||||
if (options->options & REDIS_OPT_PREFER_IPV6) {
|
||||
c->flags |= REDIS_PREFER_IPV6;
|
||||
}
|
||||
|
||||
/* Set any user supplied RESP3 PUSH handler or use freeReplyObject
|
||||
* as a default unless specifically flagged that we don't want one. */
|
||||
@@ -838,7 +860,9 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
|
||||
if (c->err == 0 && c->fd != REDIS_INVALID_FD &&
|
||||
options->command_timeout != NULL && (c->flags & REDIS_BLOCK))
|
||||
{
|
||||
redisContextSetTimeout(c, *options->command_timeout);
|
||||
}
|
||||
|
||||
@@ -920,11 +944,18 @@ int redisSetTimeout(redisContext *c, const struct timeval tv) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int redisEnableKeepAliveWithInterval(redisContext *c, int interval) {
|
||||
return redisKeepAlive(c, interval);
|
||||
}
|
||||
|
||||
/* Enable connection KeepAlive. */
|
||||
int redisEnableKeepAlive(redisContext *c) {
|
||||
if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
return REDIS_OK;
|
||||
return redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL);
|
||||
}
|
||||
|
||||
/* Set the socket option TCP_USER_TIMEOUT. */
|
||||
int redisSetTcpUserTimeout(redisContext *c, unsigned int timeout) {
|
||||
return redisContextSetTcpUserTimeout(c, timeout);
|
||||
}
|
||||
|
||||
/* Set a user provided RESP3 PUSH handler and return any old one set. */
|
||||
@@ -964,8 +995,8 @@ int redisBufferRead(redisContext *c) {
|
||||
* successfully written to the socket. When the buffer is empty after the
|
||||
* write operation, "done" is set to 1 (if given).
|
||||
*
|
||||
* Returns REDIS_ERR if an error occurred trying to write and sets
|
||||
* c->errstr to hold the appropriate error string.
|
||||
* Returns REDIS_ERR if an unrecoverable error occurred in the underlying
|
||||
* c->funcs->write function.
|
||||
*/
|
||||
int redisBufferWrite(redisContext *c, int *done) {
|
||||
|
||||
|
||||
Vendored
+41
-27
@@ -46,9 +46,9 @@ typedef long long ssize_t;
|
||||
#include "alloc.h" /* for allocation wrappers */
|
||||
|
||||
#define HIREDIS_MAJOR 1
|
||||
#define HIREDIS_MINOR 0
|
||||
#define HIREDIS_PATCH 3
|
||||
#define HIREDIS_SONAME 1.0.3-dev
|
||||
#define HIREDIS_MINOR 2
|
||||
#define HIREDIS_PATCH 0
|
||||
#define HIREDIS_SONAME 1.1.0
|
||||
|
||||
/* Connection type can be blocking or non-blocking and is set in the
|
||||
* least significant bit of the flags field in redisContext. */
|
||||
@@ -92,6 +92,11 @@ typedef long long ssize_t;
|
||||
/* Flag that indicates the user does not want replies to be automatically freed */
|
||||
#define REDIS_NO_AUTO_FREE_REPLIES 0x400
|
||||
|
||||
/* Flags to prefer IPv6 or IPv4 when doing DNS lookup. (If both are set,
|
||||
* AF_UNSPEC is used.) */
|
||||
#define REDIS_PREFER_IPV4 0x800
|
||||
#define REDIS_PREFER_IPV6 0x1000
|
||||
|
||||
#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
|
||||
|
||||
/* number of times we retry to connect in the case of EADDRNOTAVAIL and
|
||||
@@ -149,20 +154,17 @@ struct redisSsl;
|
||||
|
||||
#define REDIS_OPT_NONBLOCK 0x01
|
||||
#define REDIS_OPT_REUSEADDR 0x02
|
||||
|
||||
/**
|
||||
* Don't automatically free the async object on a connection failure,
|
||||
* or other implicit conditions. Only free on an explicit call to disconnect() or free()
|
||||
*/
|
||||
#define REDIS_OPT_NOAUTOFREE 0x04
|
||||
|
||||
/* Don't automatically intercept and free RESP3 PUSH replies. */
|
||||
#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08
|
||||
|
||||
/**
|
||||
* Don't automatically free replies
|
||||
*/
|
||||
#define REDIS_OPT_NOAUTOFREEREPLIES 0x10
|
||||
#define REDIS_OPT_NOAUTOFREE 0x04 /* Don't automatically free the async
|
||||
* object on a connection failure, or
|
||||
* other implicit conditions. Only free
|
||||
* on an explicit call to disconnect()
|
||||
* or free() */
|
||||
#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08 /* Don't automatically intercept and
|
||||
* free RESP3 PUSH replies. */
|
||||
#define REDIS_OPT_NOAUTOFREEREPLIES 0x10 /* Don't automatically free replies. */
|
||||
#define REDIS_OPT_PREFER_IPV4 0x20 /* Prefer IPv4 in DNS lookups. */
|
||||
#define REDIS_OPT_PREFER_IPV6 0x40 /* Prefer IPv6 in DNS lookups. */
|
||||
#define REDIS_OPT_PREFER_IP_UNSPEC (REDIS_OPT_PREFER_IPV4 | REDIS_OPT_PREFER_IPV6)
|
||||
|
||||
/* In Unix systems a file descriptor is a regular signed int, with -1
|
||||
* representing an invalid descriptor. In Windows it is a SOCKET
|
||||
@@ -220,27 +222,37 @@ typedef struct {
|
||||
/**
|
||||
* Helper macros to initialize options to their specified fields.
|
||||
*/
|
||||
#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \
|
||||
(opts)->type = REDIS_CONN_TCP; \
|
||||
(opts)->endpoint.tcp.ip = ip_; \
|
||||
(opts)->endpoint.tcp.port = port_;
|
||||
#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) do { \
|
||||
(opts)->type = REDIS_CONN_TCP; \
|
||||
(opts)->endpoint.tcp.ip = ip_; \
|
||||
(opts)->endpoint.tcp.port = port_; \
|
||||
} while(0)
|
||||
|
||||
#define REDIS_OPTIONS_SET_UNIX(opts, path) \
|
||||
(opts)->type = REDIS_CONN_UNIX; \
|
||||
(opts)->endpoint.unix_socket = path;
|
||||
#define REDIS_OPTIONS_SET_UNIX(opts, path) do { \
|
||||
(opts)->type = REDIS_CONN_UNIX; \
|
||||
(opts)->endpoint.unix_socket = path; \
|
||||
} while(0)
|
||||
|
||||
#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \
|
||||
(opts)->privdata = data; \
|
||||
(opts)->free_privdata = dtor; \
|
||||
#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) do { \
|
||||
(opts)->privdata = data; \
|
||||
(opts)->free_privdata = dtor; \
|
||||
} while(0)
|
||||
|
||||
typedef struct redisContextFuncs {
|
||||
void (*close)(struct redisContext *);
|
||||
void (*free_privctx)(void *);
|
||||
void (*async_read)(struct redisAsyncContext *);
|
||||
void (*async_write)(struct redisAsyncContext *);
|
||||
|
||||
/* Read/Write data to the underlying communication stream, returning the
|
||||
* number of bytes read/written. In the event of an unrecoverable error
|
||||
* these functions shall return a value < 0. In the event of a
|
||||
* recoverable error, they should return 0. */
|
||||
ssize_t (*read)(struct redisContext *, char *, size_t);
|
||||
ssize_t (*write)(struct redisContext *);
|
||||
} redisContextFuncs;
|
||||
|
||||
|
||||
/* Context for a connection to Redis */
|
||||
typedef struct redisContext {
|
||||
const redisContextFuncs *funcs; /* Function table */
|
||||
@@ -310,6 +322,8 @@ int redisReconnect(redisContext *c);
|
||||
redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn);
|
||||
int redisSetTimeout(redisContext *c, const struct timeval tv);
|
||||
int redisEnableKeepAlive(redisContext *c);
|
||||
int redisEnableKeepAliveWithInterval(redisContext *c, int interval);
|
||||
int redisSetTcpUserTimeout(redisContext *c, unsigned int timeout);
|
||||
void redisFree(redisContext *c);
|
||||
redisFD redisFreeKeepFd(redisContext *c);
|
||||
int redisBufferRead(redisContext *c);
|
||||
|
||||
Vendored
+1
-1
@@ -9,4 +9,4 @@ Name: hiredis
|
||||
Description: Minimalistic C client library for Redis.
|
||||
Version: @PROJECT_VERSION@
|
||||
Libs: -L${libdir} -lhiredis
|
||||
Cflags: -I${pkgincludedir} -D_FILE_OFFSET_BITS=64
|
||||
Cflags: -I${pkgincludedir} -I${includedir} -D_FILE_OFFSET_BITS=64
|
||||
|
||||
+3
@@ -2,6 +2,9 @@
|
||||
|
||||
set_and_check(hiredis_ssl_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(OpenSSL)
|
||||
|
||||
IF (NOT TARGET hiredis::hiredis_ssl)
|
||||
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis_ssl-targets.cmake)
|
||||
ENDIF()
|
||||
|
||||
Vendored
+34
@@ -56,11 +56,33 @@ typedef enum {
|
||||
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_CLIENT_DEFAULT_CERT_FAILED, /* Failed to set client default certificate directory */
|
||||
REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED, /* Failed to load private key */
|
||||
REDIS_SSL_CTX_OS_CERTSTORE_OPEN_FAILED, /* Failed to open system certificate store */
|
||||
REDIS_SSL_CTX_OS_CERT_ADD_FAILED /* Failed to add CA certificates obtained from system to the SSL context */
|
||||
} redisSSLContextError;
|
||||
|
||||
/* Constants that mirror OpenSSL's verify modes. By default,
|
||||
* REDIS_SSL_VERIFY_PEER is used with redisCreateSSLContext().
|
||||
* Some Redis clients disable peer verification if there are no
|
||||
* certificates specified.
|
||||
*/
|
||||
#define REDIS_SSL_VERIFY_NONE 0x00
|
||||
#define REDIS_SSL_VERIFY_PEER 0x01
|
||||
#define REDIS_SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02
|
||||
#define REDIS_SSL_VERIFY_CLIENT_ONCE 0x04
|
||||
#define REDIS_SSL_VERIFY_POST_HANDSHAKE 0x08
|
||||
|
||||
/* Options to create an OpenSSL context. */
|
||||
typedef struct {
|
||||
const char *cacert_filename;
|
||||
const char *capath;
|
||||
const char *cert_filename;
|
||||
const char *private_key_filename;
|
||||
const char *server_name;
|
||||
int verify_mode;
|
||||
} redisSSLOptions;
|
||||
|
||||
/**
|
||||
* Return the error message corresponding with the specified error code.
|
||||
*/
|
||||
@@ -101,6 +123,18 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
|
||||
const char *cert_filename, const char *private_key_filename,
|
||||
const char *server_name, redisSSLContextError *error);
|
||||
|
||||
/**
|
||||
* Helper function to initialize an OpenSSL context that can be used
|
||||
* to initiate SSL connections. This is a more extensible version of redisCreateSSLContext().
|
||||
*
|
||||
* options contains a structure of SSL options to use.
|
||||
*
|
||||
* If error is non-null, it will be populated in case the context creation fails
|
||||
* (returning a NULL).
|
||||
*/
|
||||
redisSSLContext *redisCreateSSLContextWithOptions(redisSSLOptions *options,
|
||||
redisSSLContextError *error);
|
||||
|
||||
/**
|
||||
* Free a previously created OpenSSL context.
|
||||
*/
|
||||
|
||||
Vendored
+2
-1
@@ -1,6 +1,7 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
install_libdir=@CMAKE_INSTALL_LIBDIR@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/lib
|
||||
libdir=${exec_prefix}/${install_libdir}
|
||||
includedir=${prefix}/include
|
||||
pkgincludedir=${includedir}/hiredis
|
||||
|
||||
|
||||
Vendored
+78
-18
@@ -50,6 +50,8 @@
|
||||
/* Defined in hiredis.c */
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout);
|
||||
|
||||
void redisNetClose(redisContext *c) {
|
||||
if (c && c->fd != REDIS_INVALID_FD) {
|
||||
close(c->fd);
|
||||
@@ -68,7 +70,7 @@ ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
__redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout");
|
||||
return -1;
|
||||
} else {
|
||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
||||
__redisSetError(c, REDIS_ERR_IO, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
} else if (nread == 0) {
|
||||
@@ -80,15 +82,19 @@ 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);
|
||||
ssize_t nwritten;
|
||||
|
||||
nwritten = send(c->fd, c->obuf, hi_sdslen(c->obuf), 0);
|
||||
if (nwritten < 0) {
|
||||
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
||||
/* Try again later */
|
||||
/* Try again */
|
||||
return 0;
|
||||
} else {
|
||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
||||
__redisSetError(c, REDIS_ERR_IO, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
@@ -166,6 +172,7 @@ int redisKeepAlive(redisContext *c, int interval) {
|
||||
int val = 1;
|
||||
redisFD fd = c->fd;
|
||||
|
||||
#ifndef _WIN32
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
@@ -199,7 +206,15 @@ int redisKeepAlive(redisContext *c, int interval) {
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
int res;
|
||||
|
||||
res = win32_redisKeepAlive(fd, interval * 1000);
|
||||
if (res != 0) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, strerror(res));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#endif
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
@@ -213,6 +228,23 @@ int redisSetTcpNoDelay(redisContext *c) {
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisContextSetTcpUserTimeout(redisContext *c, unsigned int timeout) {
|
||||
int res;
|
||||
#ifdef TCP_USER_TIMEOUT
|
||||
res = setsockopt(c->fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout, sizeof(timeout));
|
||||
#else
|
||||
res = -1;
|
||||
errno = ENOTSUP;
|
||||
(void)timeout;
|
||||
#endif
|
||||
if (res == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_USER_TIMEOUT)");
|
||||
redisNetClose(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
|
||||
|
||||
static int redisContextTimeoutMsec(redisContext *c, long *result)
|
||||
@@ -223,6 +255,7 @@ static int redisContextTimeoutMsec(redisContext *c, long *result)
|
||||
/* Only use timeout when not NULL. */
|
||||
if (timeout != NULL) {
|
||||
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
|
||||
__redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
|
||||
*result = msec;
|
||||
return REDIS_ERR;
|
||||
}
|
||||
@@ -277,12 +310,28 @@ int redisCheckConnectDone(redisContext *c, int *completed) {
|
||||
*completed = 1;
|
||||
return REDIS_OK;
|
||||
}
|
||||
switch (errno) {
|
||||
int error = errno;
|
||||
if (error == EINPROGRESS) {
|
||||
/* must check error to see if connect failed. Get the socket error */
|
||||
int fail, so_error;
|
||||
socklen_t optlen = sizeof(so_error);
|
||||
fail = getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &so_error, &optlen);
|
||||
if (fail == 0) {
|
||||
if (so_error == 0) {
|
||||
/* Socket is connected! */
|
||||
*completed = 1;
|
||||
return REDIS_OK;
|
||||
}
|
||||
/* connection error; */
|
||||
errno = so_error;
|
||||
error = so_error;
|
||||
}
|
||||
}
|
||||
switch (error) {
|
||||
case EISCONN:
|
||||
*completed = 1;
|
||||
return REDIS_OK;
|
||||
case EALREADY:
|
||||
case EINPROGRESS:
|
||||
case EWOULDBLOCK:
|
||||
*completed = 0;
|
||||
return REDIS_OK;
|
||||
@@ -317,6 +366,10 @@ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
|
||||
const void *to_ptr = &tv;
|
||||
size_t to_sz = sizeof(tv);
|
||||
|
||||
if (redisContextUpdateCommandTimeout(c, &tv) != REDIS_OK) {
|
||||
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
|
||||
return REDIS_ERR;
|
||||
@@ -400,7 +453,6 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
||||
}
|
||||
|
||||
if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
|
||||
__redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -417,17 +469,25 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
/* Try with IPv6 if no IPv4 address was found. We do it in this order since
|
||||
* in a Redis client you can't afford to test if you have IPv6 connectivity
|
||||
* as this would add latency to every connect. Otherwise a more sensible
|
||||
* route could be: Use IPv6 if both addresses are available and there is IPv6
|
||||
* connectivity. */
|
||||
if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {
|
||||
hints.ai_family = AF_INET6;
|
||||
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
/* DNS lookup. To use dual stack, set both flags to prefer both IPv4 and
|
||||
* IPv6. By default, for historical reasons, we try IPv4 first and then we
|
||||
* try IPv6 only if no IPv4 address was found. */
|
||||
if (c->flags & REDIS_PREFER_IPV6 && c->flags & REDIS_PREFER_IPV4)
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
else if (c->flags & REDIS_PREFER_IPV6)
|
||||
hints.ai_family = AF_INET6;
|
||||
else
|
||||
hints.ai_family = AF_INET;
|
||||
|
||||
rv = getaddrinfo(c->tcp.host, _port, &hints, &servinfo);
|
||||
if (rv != 0 && hints.ai_family != AF_UNSPEC) {
|
||||
/* Try again with the other IP version. */
|
||||
hints.ai_family = (hints.ai_family == AF_INET) ? AF_INET6 : AF_INET;
|
||||
rv = getaddrinfo(c->tcp.host, _port, &hints, &servinfo);
|
||||
}
|
||||
if (rv != 0) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, gai_strerror(rv));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
for (p = servinfo; p != NULL; p = p->ai_next) {
|
||||
addrretry:
|
||||
|
||||
Vendored
+1
@@ -52,5 +52,6 @@ int redisKeepAlive(redisContext *c, int interval);
|
||||
int redisCheckConnectDone(redisContext *c, int *completed);
|
||||
|
||||
int redisSetTcpNoDelay(redisContext *c);
|
||||
int redisContextSetTcpUserTimeout(redisContext *c, unsigned int timeout);
|
||||
|
||||
#endif
|
||||
|
||||
Vendored
+8
-5
@@ -303,11 +303,14 @@ static int processLineItem(redisReader *r) {
|
||||
d = INFINITY; /* Positive infinite. */
|
||||
} else if (len == 4 && strcasecmp(buf,"-inf") == 0) {
|
||||
d = -INFINITY; /* Negative infinite. */
|
||||
} else if ((len == 3 && strcasecmp(buf,"nan") == 0) ||
|
||||
(len == 4 && strcasecmp(buf, "-nan") == 0)) {
|
||||
d = NAN; /* nan. */
|
||||
} else {
|
||||
d = strtod((char*)buf,&eptr);
|
||||
/* RESP3 only allows "inf", "-inf", and finite values, while
|
||||
* strtod() allows other variations on infinity, NaN,
|
||||
* etc. We explicity handle our two allowed infinite cases
|
||||
* strtod() allows other variations on infinity,
|
||||
* etc. We explicity handle our two allowed infinite cases and NaN
|
||||
* above, so strtod() should only result in finite values. */
|
||||
if (buf[0] == '\0' || eptr != &buf[len] || !isfinite(d)) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
@@ -374,7 +377,7 @@ static int processLineItem(redisReader *r) {
|
||||
if (r->fn && r->fn->createString)
|
||||
obj = r->fn->createString(cur,p,len);
|
||||
else
|
||||
obj = (void*)(size_t)(cur->type);
|
||||
obj = (void*)(uintptr_t)(cur->type);
|
||||
}
|
||||
|
||||
if (obj == NULL) {
|
||||
@@ -439,7 +442,7 @@ static int processBulkItem(redisReader *r) {
|
||||
if (r->fn && r->fn->createString)
|
||||
obj = r->fn->createString(cur,s+2,len);
|
||||
else
|
||||
obj = (void*)(long)cur->type;
|
||||
obj = (void*)(uintptr_t)cur->type;
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
@@ -536,7 +539,7 @@ static int processAggregateItem(redisReader *r) {
|
||||
if (r->fn && r->fn->createArray)
|
||||
obj = r->fn->createArray(cur,elements);
|
||||
else
|
||||
obj = (void*)(long)cur->type;
|
||||
obj = (void*)(uintptr_t)cur->type;
|
||||
|
||||
if (obj == NULL) {
|
||||
__redisReaderSetErrorOOM(r);
|
||||
|
||||
Vendored
+16
-13
@@ -90,6 +90,7 @@ hisds hi_sdsnewlen(const void *init, size_t initlen) {
|
||||
int hdrlen = hi_sdsHdrSize(type);
|
||||
unsigned char *fp; /* flags pointer. */
|
||||
|
||||
if (hdrlen+initlen+1 <= initlen) return NULL; /* Catch size_t overflow */
|
||||
sh = hi_s_malloc(hdrlen+initlen+1);
|
||||
if (sh == NULL) return NULL;
|
||||
if (!init)
|
||||
@@ -174,7 +175,7 @@ void hi_sdsfree(hisds s) {
|
||||
* the output will be "6" as the string was modified but the logical length
|
||||
* remains 6 bytes. */
|
||||
void hi_sdsupdatelen(hisds s) {
|
||||
int reallen = strlen(s);
|
||||
size_t reallen = strlen(s);
|
||||
hi_sdssetlen(s, reallen);
|
||||
}
|
||||
|
||||
@@ -196,7 +197,7 @@ void hi_sdsclear(hisds s) {
|
||||
hisds hi_sdsMakeRoomFor(hisds s, size_t addlen) {
|
||||
void *sh, *newsh;
|
||||
size_t avail = hi_sdsavail(s);
|
||||
size_t len, newlen;
|
||||
size_t len, newlen, reqlen;
|
||||
char type, oldtype = s[-1] & HI_SDS_TYPE_MASK;
|
||||
int hdrlen;
|
||||
|
||||
@@ -205,7 +206,8 @@ hisds hi_sdsMakeRoomFor(hisds s, size_t addlen) {
|
||||
|
||||
len = hi_sdslen(s);
|
||||
sh = (char*)s-hi_sdsHdrSize(oldtype);
|
||||
newlen = (len+addlen);
|
||||
reqlen = newlen = (len+addlen);
|
||||
if (newlen <= len) return NULL; /* Catch size_t overflow */
|
||||
if (newlen < HI_SDS_MAX_PREALLOC)
|
||||
newlen *= 2;
|
||||
else
|
||||
@@ -219,6 +221,7 @@ hisds hi_sdsMakeRoomFor(hisds s, size_t addlen) {
|
||||
if (type == HI_SDS_TYPE_5) type = HI_SDS_TYPE_8;
|
||||
|
||||
hdrlen = hi_sdsHdrSize(type);
|
||||
if (hdrlen+newlen+1 <= reqlen) return NULL; /* Catch size_t overflow */
|
||||
if (oldtype==type) {
|
||||
newsh = hi_s_realloc(sh, hdrlen+newlen+1);
|
||||
if (newsh == NULL) return NULL;
|
||||
@@ -580,7 +583,7 @@ hisds hi_sdscatprintf(hisds s, const char *fmt, ...) {
|
||||
*/
|
||||
hisds hi_sdscatfmt(hisds s, char const *fmt, ...) {
|
||||
const char *f = fmt;
|
||||
int i;
|
||||
long i;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap,fmt);
|
||||
@@ -753,16 +756,16 @@ int hi_sdsrange(hisds s, ssize_t start, ssize_t end) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Apply tolower() to every character of the hisds string 's'. */
|
||||
/* Apply tolower() to every character of the sds string 's'. */
|
||||
void hi_sdstolower(hisds s) {
|
||||
int len = hi_sdslen(s), j;
|
||||
size_t len = hi_sdslen(s), j;
|
||||
|
||||
for (j = 0; j < len; j++) s[j] = tolower(s[j]);
|
||||
}
|
||||
|
||||
/* Apply toupper() to every character of the hisds string 's'. */
|
||||
/* Apply toupper() to every character of the sds string 's'. */
|
||||
void hi_sdstoupper(hisds s) {
|
||||
int len = hi_sdslen(s), j;
|
||||
size_t len = hi_sdslen(s), j;
|
||||
|
||||
for (j = 0; j < len; j++) s[j] = toupper(s[j]);
|
||||
}
|
||||
@@ -945,7 +948,7 @@ hisds *hi_sdssplitargs(const char *line, int *argc) {
|
||||
*argc = 0;
|
||||
while(1) {
|
||||
/* skip blanks */
|
||||
while(*p && isspace(*p)) p++;
|
||||
while(*p && isspace((int) *p)) p++;
|
||||
if (*p) {
|
||||
/* get a token */
|
||||
int inq=0; /* set to 1 if we are in "quotes" */
|
||||
@@ -956,8 +959,8 @@ hisds *hi_sdssplitargs(const char *line, int *argc) {
|
||||
while(!done) {
|
||||
if (inq) {
|
||||
if (*p == '\\' && *(p+1) == 'x' &&
|
||||
isxdigit(*(p+2)) &&
|
||||
isxdigit(*(p+3)))
|
||||
isxdigit((int) *(p+2)) &&
|
||||
isxdigit((int) *(p+3)))
|
||||
{
|
||||
unsigned char byte;
|
||||
|
||||
@@ -981,7 +984,7 @@ hisds *hi_sdssplitargs(const char *line, int *argc) {
|
||||
} else if (*p == '"') {
|
||||
/* closing quote must be followed by a space or
|
||||
* nothing at all. */
|
||||
if (*(p+1) && !isspace(*(p+1))) goto err;
|
||||
if (*(p+1) && !isspace((int) *(p+1))) goto err;
|
||||
done=1;
|
||||
} else if (!*p) {
|
||||
/* unterminated quotes */
|
||||
@@ -996,7 +999,7 @@ hisds *hi_sdssplitargs(const char *line, int *argc) {
|
||||
} else if (*p == '\'') {
|
||||
/* closing quote must be followed by a space or
|
||||
* nothing at all. */
|
||||
if (*(p+1) && !isspace(*(p+1))) goto err;
|
||||
if (*(p+1) && !isspace((int) *(p+1))) goto err;
|
||||
done=1;
|
||||
} else if (!*p) {
|
||||
/* unterminated quotes */
|
||||
|
||||
Vendored
+3
-1
@@ -35,9 +35,11 @@
|
||||
|
||||
#define HI_SDS_MAX_PREALLOC (1024*1024)
|
||||
#ifdef _MSC_VER
|
||||
#define __attribute__(x)
|
||||
typedef long long ssize_t;
|
||||
#define SSIZE_MAX (LLONG_MAX >> 1)
|
||||
#ifndef __clang__
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
Vendored
+34
-2
@@ -180,10 +180,17 @@ int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
||||
|
||||
/* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as
|
||||
* EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX
|
||||
* logic consistent. */
|
||||
if (errno == EWOULDBLOCK) {
|
||||
* logic consistent.
|
||||
* Additionally, WSAALREADY is can be reported as WSAEINVAL to and this is
|
||||
* translated to EIO. Convert appropriately
|
||||
*/
|
||||
int err = errno;
|
||||
if (err == EWOULDBLOCK) {
|
||||
errno = EINPROGRESS;
|
||||
}
|
||||
else if (err == EIO) {
|
||||
errno = EALREADY;
|
||||
}
|
||||
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
@@ -205,6 +212,14 @@ int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, sockle
|
||||
} else {
|
||||
ret = getsockopt(sockfd, level, optname, (char*)optval, optlen);
|
||||
}
|
||||
if (ret != SOCKET_ERROR && level == SOL_SOCKET && optname == SO_ERROR) {
|
||||
/* translate SO_ERROR codes, if non-zero */
|
||||
int err = *(int*)optval;
|
||||
if (err != 0) {
|
||||
err = _wsaErrorToErrno(err);
|
||||
*(int*)optval = err;
|
||||
}
|
||||
}
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
@@ -245,4 +260,21 @@ int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_redisKeepAlive(SOCKET sockfd, int interval_ms) {
|
||||
struct tcp_keepalive cfg;
|
||||
DWORD bytes_in;
|
||||
int res;
|
||||
|
||||
cfg.onoff = 1;
|
||||
cfg.keepaliveinterval = interval_ms;
|
||||
cfg.keepalivetime = interval_ms;
|
||||
|
||||
res = WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, &cfg,
|
||||
sizeof(struct tcp_keepalive), NULL, 0,
|
||||
&bytes_in, NULL, NULL);
|
||||
|
||||
return res == 0 ? 0 : _wsaErrorToErrno(res);
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
Vendored
+3
@@ -50,6 +50,7 @@
|
||||
#include <ws2tcpip.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
#include <mstcpip.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
typedef long long ssize_t;
|
||||
@@ -71,6 +72,8 @@ ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags);
|
||||
typedef ULONG nfds_t;
|
||||
int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout);
|
||||
|
||||
int win32_redisKeepAlive(SOCKET sockfd, int interval_ms);
|
||||
|
||||
#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION
|
||||
#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res)
|
||||
#undef gai_strerror
|
||||
|
||||
Vendored
+52
-4
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "hiredis.h"
|
||||
#include "async.h"
|
||||
#include "net.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
@@ -39,6 +40,14 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#undef X509_NAME
|
||||
#undef X509_EXTENSIONS
|
||||
#undef PKCS7_ISSUER_AND_SERIAL
|
||||
#undef PKCS7_SIGNER_INFO
|
||||
#undef OCSP_REQUEST
|
||||
#undef OCSP_RESPONSE
|
||||
#endif
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
@@ -50,6 +59,8 @@
|
||||
#include "async_private.h"
|
||||
#include "hiredis_ssl.h"
|
||||
|
||||
#define OPENSSL_1_1_0 0x10100000L
|
||||
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
struct redisSSLContext {
|
||||
@@ -91,7 +102,7 @@ redisContextFuncs redisContextSSLFuncs;
|
||||
* Note that this is only required for OpenSSL < 1.1.0.
|
||||
*/
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
#if OPENSSL_VERSION_NUMBER < OPENSSL_1_1_0
|
||||
#define HIREDIS_USE_CRYPTO_LOCKS
|
||||
#endif
|
||||
|
||||
@@ -219,6 +230,25 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
|
||||
const char *cert_filename, const char *private_key_filename,
|
||||
const char *server_name, redisSSLContextError *error)
|
||||
{
|
||||
redisSSLOptions options = {
|
||||
.cacert_filename = cacert_filename,
|
||||
.capath = capath,
|
||||
.cert_filename = cert_filename,
|
||||
.private_key_filename = private_key_filename,
|
||||
.server_name = server_name,
|
||||
.verify_mode = REDIS_SSL_VERIFY_PEER,
|
||||
};
|
||||
|
||||
return redisCreateSSLContextWithOptions(&options, error);
|
||||
}
|
||||
|
||||
redisSSLContext *redisCreateSSLContextWithOptions(redisSSLOptions *options, redisSSLContextError *error) {
|
||||
const char *cacert_filename = options->cacert_filename;
|
||||
const char *capath = options->capath;
|
||||
const char *cert_filename = options->cert_filename;
|
||||
const char *private_key_filename = options->private_key_filename;
|
||||
const char *server_name = options->server_name;
|
||||
|
||||
#ifdef _WIN32
|
||||
HCERTSTORE win_store = NULL;
|
||||
PCCERT_CONTEXT win_ctx = NULL;
|
||||
@@ -228,14 +258,26 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
|
||||
if (ctx == NULL)
|
||||
goto error;
|
||||
|
||||
ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
const SSL_METHOD *ssl_method;
|
||||
#if OPENSSL_VERSION_NUMBER >= OPENSSL_1_1_0
|
||||
ssl_method = TLS_client_method();
|
||||
#else
|
||||
ssl_method = SSLv23_client_method();
|
||||
#endif
|
||||
|
||||
ctx->ssl_ctx = SSL_CTX_new(ssl_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 OPENSSL_VERSION_NUMBER >= OPENSSL_1_1_0
|
||||
SSL_CTX_set_min_proto_version(ctx->ssl_ctx, TLS1_2_VERSION);
|
||||
#else
|
||||
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
|
||||
#endif
|
||||
|
||||
SSL_CTX_set_verify(ctx->ssl_ctx, options->verify_mode, NULL);
|
||||
|
||||
if ((cert_filename != NULL && private_key_filename == NULL) ||
|
||||
(private_key_filename != NULL && cert_filename == NULL)) {
|
||||
@@ -273,6 +315,11 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
|
||||
if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED;
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
if (!SSL_CTX_set_default_verify_paths(ctx->ssl_ctx)) {
|
||||
if (error) *error = REDIS_SSL_CTX_CLIENT_DEFAULT_CERT_FAILED;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (cert_filename) {
|
||||
@@ -560,6 +607,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
|
||||
}
|
||||
|
||||
redisContextFuncs redisContextSSLFuncs = {
|
||||
.close = redisNetClose,
|
||||
.free_privctx = redisSSLFree,
|
||||
.async_read = redisSSLAsyncRead,
|
||||
.async_write = redisSSLAsyncWrite,
|
||||
|
||||
Vendored
+412
-18
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "hiredis.h"
|
||||
#include "async.h"
|
||||
#include "adapters/poll.h"
|
||||
#ifdef HIREDIS_TEST_SSL
|
||||
#include "hiredis_ssl.h"
|
||||
#endif
|
||||
@@ -34,11 +35,11 @@ enum connection_type {
|
||||
|
||||
struct config {
|
||||
enum connection_type type;
|
||||
struct timeval connect_timeout;
|
||||
|
||||
struct {
|
||||
const char *host;
|
||||
int port;
|
||||
struct timeval timeout;
|
||||
} tcp;
|
||||
|
||||
struct {
|
||||
@@ -75,6 +76,15 @@ static int tests = 0, fails = 0, skips = 0;
|
||||
#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 void millisleep(int ms)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
Sleep(ms);
|
||||
#else
|
||||
usleep(ms*1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
static long long usec(void) {
|
||||
#ifndef _MSC_VER
|
||||
struct timeval tv;
|
||||
@@ -329,10 +339,14 @@ static void test_format_commands(void) {
|
||||
FLOAT_WIDTH_TEST(float);
|
||||
FLOAT_WIDTH_TEST(double);
|
||||
|
||||
test("Format command with invalid printf format: ");
|
||||
test("Format command with unhandled printf format (specifier 'p' not supported): ");
|
||||
len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",(size_t)3);
|
||||
test_cond(len == -1);
|
||||
|
||||
test("Format command with invalid printf format (specifier missing): ");
|
||||
len = redisFormatCommand(&cmd,"%-");
|
||||
test_cond(len == -1);
|
||||
|
||||
const char *argv[3];
|
||||
argv[0] = "SET";
|
||||
argv[1] = "foo\0xxx";
|
||||
@@ -391,6 +405,25 @@ static void test_append_formatted_commands(struct config config) {
|
||||
disconnect(c, 0);
|
||||
}
|
||||
|
||||
static void test_tcp_options(struct config cfg) {
|
||||
redisContext *c;
|
||||
|
||||
c = do_connect(cfg);
|
||||
|
||||
test("We can enable TCP_KEEPALIVE: ");
|
||||
test_cond(redisEnableKeepAlive(c) == REDIS_OK);
|
||||
|
||||
#ifdef TCP_USER_TIMEOUT
|
||||
test("We can set TCP_USER_TIMEOUT: ");
|
||||
test_cond(redisSetTcpUserTimeout(c, 100) == REDIS_OK);
|
||||
#else
|
||||
test("Setting TCP_USER_TIMEOUT errors when unsupported: ");
|
||||
test_cond(redisSetTcpUserTimeout(c, 100) == REDIS_ERR && c->err == REDIS_ERR_IO);
|
||||
#endif
|
||||
|
||||
redisFree(c);
|
||||
}
|
||||
|
||||
static void test_reply_reader(void) {
|
||||
redisReader *reader;
|
||||
void *reply, *root;
|
||||
@@ -568,6 +601,19 @@ static void test_reply_reader(void) {
|
||||
test_cond(ret == REDIS_ERR && reply == NULL);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Don't reset state after protocol error(not segfault): ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader,(char*)"*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$", 25);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
assert(ret == REDIS_OK);
|
||||
redisReaderFeed(reader,(char*)"3\r\nval\r\n", 8);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_OK &&
|
||||
((redisReply*)reply)->type == REDIS_REPLY_ARRAY &&
|
||||
((redisReply*)reply)->elements == 3);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
/* Regression test for issue #45 on GitHub. */
|
||||
test("Don't do empty allocation for empty multi bulk: ");
|
||||
reader = redisReaderCreate();
|
||||
@@ -637,12 +683,23 @@ static void test_reply_reader(void) {
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Set error when RESP3 double is NaN: ");
|
||||
test("Correctly parses RESP3 double NaN: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, ",nan\r\n",6);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_ERR &&
|
||||
strcasecmp(reader->errstr,"Bad double value") == 0);
|
||||
test_cond(ret == REDIS_OK &&
|
||||
((redisReply*)reply)->type == REDIS_REPLY_DOUBLE &&
|
||||
isnan(((redisReply*)reply)->dval));
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Correctly parses RESP3 double -Nan: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, ",-nan\r\n", 7);
|
||||
ret = redisReaderGetReply(reader, &reply);
|
||||
test_cond(ret == REDIS_OK &&
|
||||
((redisReply*)reply)->type == REDIS_REPLY_DOUBLE &&
|
||||
isnan(((redisReply*)reply)->dval));
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
@@ -745,6 +802,20 @@ static void test_reply_reader(void) {
|
||||
!strcmp(((redisReply*)reply)->str,"3492890328409238509324850943850943825024385"));
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
test("Can parse RESP3 doubles in an array: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, "*1\r\n,3.14159265358979323846\r\n",31);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_OK &&
|
||||
((redisReply*)reply)->type == REDIS_REPLY_ARRAY &&
|
||||
((redisReply*)reply)->elements == 1 &&
|
||||
((redisReply*)reply)->element[0]->type == REDIS_REPLY_DOUBLE &&
|
||||
fabs(((redisReply*)reply)->element[0]->dval - 3.14159265358979323846) < 0.00000001 &&
|
||||
((redisReply*)reply)->element[0]->len == 22 &&
|
||||
strcmp(((redisReply*)reply)->element[0]->str, "3.14159265358979323846") == 0);
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
}
|
||||
|
||||
static void test_free_null(void) {
|
||||
@@ -819,9 +890,9 @@ static void test_allocator_injection(void) {
|
||||
|
||||
#define HIREDIS_BAD_DOMAIN "idontexist-noreally.com"
|
||||
static void test_blocking_connection_errors(void) {
|
||||
redisContext *c;
|
||||
struct addrinfo hints = {.ai_family = AF_INET};
|
||||
struct addrinfo *ai_tmp = NULL;
|
||||
redisContext *c;
|
||||
|
||||
int rv = getaddrinfo(HIREDIS_BAD_DOMAIN, "6379", &hints, &ai_tmp);
|
||||
if (rv != 0) {
|
||||
@@ -835,6 +906,7 @@ static void test_blocking_connection_errors(void) {
|
||||
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, "node name or service name 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 ||
|
||||
@@ -847,12 +919,26 @@ static void test_blocking_connection_errors(void) {
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
redisOptions opt = {0};
|
||||
struct timeval tv;
|
||||
|
||||
test("Returns error when the port is not open: ");
|
||||
c = redisConnect((char*)"localhost", 1);
|
||||
test_cond(c->err == REDIS_ERR_IO &&
|
||||
strcmp(c->errstr,"Connection refused") == 0);
|
||||
redisFree(c);
|
||||
|
||||
|
||||
/* Verify we don't regress from the fix in PR #1180 */
|
||||
test("We don't clobber connection exception with setsockopt error: ");
|
||||
tv = (struct timeval){.tv_sec = 0, .tv_usec = 500000};
|
||||
opt.command_timeout = opt.connect_timeout = &tv;
|
||||
REDIS_OPTIONS_SET_TCP(&opt, "localhost", 10337);
|
||||
c = redisConnectWithOptions(&opt);
|
||||
test_cond(c->err == REDIS_ERR_IO &&
|
||||
strcmp(c->errstr, "Connection refused") == 0);
|
||||
redisFree(c);
|
||||
|
||||
test("Returns error when the unix_sock socket path doesn't accept connections: ");
|
||||
c = redisConnectUnix((char*)"/tmp/idontexist.sock");
|
||||
test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */
|
||||
@@ -914,11 +1000,19 @@ static void test_resp3_push_handler(redisContext *c) {
|
||||
old = redisSetPushCallback(c, push_handler);
|
||||
test("We can set a custom RESP3 PUSH handler: ");
|
||||
reply = redisCommand(c, "SET key:0 val:0");
|
||||
/* We need another command because depending on the version of Redis, the
|
||||
* notification may be delivered after the command's reply. */
|
||||
assert(reply != NULL);
|
||||
freeReplyObject(reply);
|
||||
reply = redisCommand(c, "PING");
|
||||
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");
|
||||
assert(reply != NULL);
|
||||
freeReplyObject(reply);
|
||||
reply = redisCommand(c, "PING");
|
||||
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && pc.nil == 1);
|
||||
freeReplyObject(reply);
|
||||
|
||||
@@ -929,6 +1023,12 @@ static void test_resp3_push_handler(redisContext *c) {
|
||||
assert((reply = redisCommand(c, "GET key:0")) != NULL);
|
||||
freeReplyObject(reply);
|
||||
assert((reply = redisCommand(c, "SET key:0 invalid")) != NULL);
|
||||
/* Depending on Redis version, we may receive either push notification or
|
||||
* status reply. Both cases are valid. */
|
||||
if (reply->type == REDIS_REPLY_STATUS) {
|
||||
freeReplyObject(reply);
|
||||
reply = redisCommand(c, "PING");
|
||||
}
|
||||
test_cond(reply->type == REDIS_REPLY_PUSH);
|
||||
freeReplyObject(reply);
|
||||
|
||||
@@ -1089,6 +1189,13 @@ static void test_blocking_connection(struct config config) {
|
||||
strcasecmp(reply->element[1]->str,"pong") == 0);
|
||||
freeReplyObject(reply);
|
||||
|
||||
test("Send command by passing argc/argv: ");
|
||||
const char *argv[3] = {"SET", "foo", "bar"};
|
||||
size_t argvlen[3] = {3, 3, 3};
|
||||
reply = redisCommandArgv(c,3,argv,argvlen);
|
||||
test_cond(reply->type == REDIS_REPLY_STATUS);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Make sure passing NULL to redisGetReply is safe */
|
||||
test("Can pass NULL to redisGetReply: ");
|
||||
assert(redisAppendCommand(c, "PING") == REDIS_OK);
|
||||
@@ -1143,7 +1250,13 @@ static void test_blocking_connection_timeouts(struct config config) {
|
||||
test("Does not return a reply when the command times out: ");
|
||||
if (detect_debug_sleep(c)) {
|
||||
redisAppendFormattedCommand(c, sleep_cmd, strlen(sleep_cmd));
|
||||
|
||||
// flush connection buffer without waiting for the reply
|
||||
s = c->funcs->write(c);
|
||||
assert(s == (ssize_t)hi_sdslen(c->obuf));
|
||||
hi_sdsfree(c->obuf);
|
||||
c->obuf = hi_sdsempty();
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10000;
|
||||
redisSetTimeout(c, tv);
|
||||
@@ -1156,6 +1269,9 @@ static void test_blocking_connection_timeouts(struct config config) {
|
||||
strcmp(c->errstr, "recv timeout") == 0);
|
||||
#endif
|
||||
freeReplyObject(reply);
|
||||
|
||||
// wait for the DEBUG SLEEP to complete so that Redis server is unblocked for the following tests
|
||||
millisleep(3000);
|
||||
} else {
|
||||
test_skipped();
|
||||
}
|
||||
@@ -1226,22 +1342,34 @@ static void test_blocking_io_errors(struct config config) {
|
||||
static void test_invalid_timeout_errors(struct config config) {
|
||||
redisContext *c;
|
||||
|
||||
test("Set error when an invalid timeout usec value is given to redisConnectWithTimeout: ");
|
||||
test("Set error when an invalid timeout usec value is used during connect: ");
|
||||
|
||||
config.tcp.timeout.tv_sec = 0;
|
||||
config.tcp.timeout.tv_usec = 10000001;
|
||||
config.connect_timeout.tv_sec = 0;
|
||||
config.connect_timeout.tv_usec = 10000001;
|
||||
|
||||
c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
|
||||
if (config.type == CONN_TCP || config.type == CONN_SSL) {
|
||||
c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.connect_timeout);
|
||||
} else if(config.type == CONN_UNIX) {
|
||||
c = redisConnectUnixWithTimeout(config.unix_sock.path, config.connect_timeout);
|
||||
} else {
|
||||
assert(NULL);
|
||||
}
|
||||
|
||||
test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0);
|
||||
redisFree(c);
|
||||
|
||||
test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: ");
|
||||
test("Set error when an invalid timeout sec value is used during connect: ");
|
||||
|
||||
config.tcp.timeout.tv_sec = (((LONG_MAX) - 999) / 1000) + 1;
|
||||
config.tcp.timeout.tv_usec = 0;
|
||||
config.connect_timeout.tv_sec = (((LONG_MAX) - 999) / 1000) + 1;
|
||||
config.connect_timeout.tv_usec = 0;
|
||||
|
||||
c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
|
||||
if (config.type == CONN_TCP || config.type == CONN_SSL) {
|
||||
c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.connect_timeout);
|
||||
} else if(config.type == CONN_UNIX) {
|
||||
c = redisConnectUnixWithTimeout(config.unix_sock.path, config.connect_timeout);
|
||||
} else {
|
||||
assert(NULL);
|
||||
}
|
||||
|
||||
test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0);
|
||||
redisFree(c);
|
||||
@@ -1448,6 +1576,9 @@ static void test_throughput(struct config config) {
|
||||
// }
|
||||
|
||||
#ifdef HIREDIS_TEST_ASYNC
|
||||
|
||||
#pragma GCC diagnostic ignored "-Woverlength-strings" /* required on gcc 4.8.x due to assert statements */
|
||||
|
||||
struct event_base *base;
|
||||
|
||||
typedef struct TestState {
|
||||
@@ -1729,10 +1860,14 @@ void subscribe_channel_a_cb(redisAsyncContext *ac, void *r, void *privdata) {
|
||||
strcmp(reply->element[2]->str,"Hello!") == 0);
|
||||
state->checkpoint++;
|
||||
|
||||
/* Unsubscribe to channels, including a channel X which we don't subscribe to */
|
||||
/* Unsubscribe to channels, including channel X & Z which we don't subscribe to */
|
||||
redisAsyncCommand(ac,unexpected_cb,
|
||||
(void*)"unsubscribe should not call unexpected_cb()",
|
||||
"unsubscribe B X A");
|
||||
"unsubscribe B X A A Z");
|
||||
/* Unsubscribe to patterns, none which we subscribe to */
|
||||
redisAsyncCommand(ac,unexpected_cb,
|
||||
(void*)"punsubscribe should not call unexpected_cb()",
|
||||
"punsubscribe");
|
||||
/* Send a regular command after unsubscribing, then disconnect */
|
||||
state->disconnect = 1;
|
||||
redisAsyncCommand(ac,integer_cb,state,"LPUSH mylist foo");
|
||||
@@ -1749,6 +1884,7 @@ void subscribe_channel_a_cb(redisAsyncContext *ac, void *r, void *privdata) {
|
||||
void subscribe_channel_b_cb(redisAsyncContext *ac, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
TestState *state = privdata;
|
||||
(void)ac;
|
||||
|
||||
assert(reply != NULL && reply->type == REDIS_REPLY_ARRAY &&
|
||||
reply->elements == 3);
|
||||
@@ -1767,8 +1903,10 @@ void subscribe_channel_b_cb(redisAsyncContext *ac, void *r, void *privdata) {
|
||||
|
||||
/* Test handling of multiple channels
|
||||
* - subscribe to channel A and B
|
||||
* - a published message on A triggers an unsubscribe of channel B, X and A
|
||||
* where channel X is not subscribed to.
|
||||
* - a published message on A triggers an unsubscribe of channel B, X, A and Z
|
||||
* where channel X and Z are not subscribed to.
|
||||
* - the published message also triggers an unsubscribe to patterns. Since no
|
||||
* pattern is subscribed to the responded pattern element type is NIL.
|
||||
* - a command sent after unsubscribe triggers a disconnect */
|
||||
static void test_pubsub_multiple_channels(struct config config) {
|
||||
test("Subscribe to multiple channels: ");
|
||||
@@ -1881,6 +2019,250 @@ static void test_monitor(struct config config) {
|
||||
}
|
||||
#endif /* HIREDIS_TEST_ASYNC */
|
||||
|
||||
/* tests for async api using polling adapter, requires no extra libraries*/
|
||||
|
||||
/* enum for the test cases, the callbacks have different logic based on them */
|
||||
typedef enum astest_no
|
||||
{
|
||||
ASTEST_CONNECT=0,
|
||||
ASTEST_CONN_TIMEOUT,
|
||||
ASTEST_PINGPONG,
|
||||
ASTEST_PINGPONG_TIMEOUT,
|
||||
ASTEST_ISSUE_931,
|
||||
ASTEST_ISSUE_931_PING
|
||||
}astest_no;
|
||||
|
||||
/* a static context for the async tests */
|
||||
struct _astest {
|
||||
redisAsyncContext *ac;
|
||||
astest_no testno;
|
||||
int counter;
|
||||
int connects;
|
||||
int connect_status;
|
||||
int disconnects;
|
||||
int pongs;
|
||||
int disconnect_status;
|
||||
int connected;
|
||||
int err;
|
||||
char errstr[256];
|
||||
};
|
||||
static struct _astest astest;
|
||||
|
||||
/* async callbacks */
|
||||
static void asCleanup(void* data)
|
||||
{
|
||||
struct _astest *t = (struct _astest *)data;
|
||||
t->ac = NULL;
|
||||
}
|
||||
|
||||
static void commandCallback(struct redisAsyncContext *ac, void* _reply, void* _privdata);
|
||||
|
||||
static void connectCallback(redisAsyncContext *c, int status) {
|
||||
struct _astest *t = (struct _astest *)c->data;
|
||||
assert(t == &astest);
|
||||
assert(t->connects == 0);
|
||||
t->err = c->err;
|
||||
strcpy(t->errstr, c->errstr);
|
||||
t->connects++;
|
||||
t->connect_status = status;
|
||||
t->connected = status == REDIS_OK ? 1 : -1;
|
||||
|
||||
if (t->testno == ASTEST_ISSUE_931) {
|
||||
/* disconnect again */
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
else if (t->testno == ASTEST_ISSUE_931_PING)
|
||||
{
|
||||
redisAsyncCommand(c, commandCallback, NULL, "PING");
|
||||
}
|
||||
}
|
||||
static void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
assert(c->data == (void*)&astest);
|
||||
assert(astest.disconnects == 0);
|
||||
astest.err = c->err;
|
||||
strcpy(astest.errstr, c->errstr);
|
||||
astest.disconnects++;
|
||||
astest.disconnect_status = status;
|
||||
astest.connected = 0;
|
||||
}
|
||||
|
||||
static void commandCallback(struct redisAsyncContext *ac, void* _reply, void* _privdata)
|
||||
{
|
||||
redisReply *reply = (redisReply*)_reply;
|
||||
struct _astest *t = (struct _astest *)ac->data;
|
||||
assert(t == &astest);
|
||||
(void)_privdata;
|
||||
t->err = ac->err;
|
||||
strcpy(t->errstr, ac->errstr);
|
||||
t->counter++;
|
||||
if (t->testno == ASTEST_PINGPONG ||t->testno == ASTEST_ISSUE_931_PING)
|
||||
{
|
||||
assert(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
|
||||
t->pongs++;
|
||||
redisAsyncFree(ac);
|
||||
}
|
||||
if (t->testno == ASTEST_PINGPONG_TIMEOUT)
|
||||
{
|
||||
/* two ping pongs */
|
||||
assert(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
|
||||
t->pongs++;
|
||||
if (t->counter == 1) {
|
||||
int status = redisAsyncCommand(ac, commandCallback, NULL, "PING");
|
||||
assert(status == REDIS_OK);
|
||||
} else {
|
||||
redisAsyncFree(ac);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static redisAsyncContext *do_aconnect(struct config config, astest_no testno)
|
||||
{
|
||||
redisOptions options = {0};
|
||||
memset(&astest, 0, sizeof(astest));
|
||||
|
||||
astest.testno = testno;
|
||||
astest.connect_status = astest.disconnect_status = -2;
|
||||
|
||||
if (config.type == CONN_TCP) {
|
||||
options.type = REDIS_CONN_TCP;
|
||||
options.connect_timeout = &config.connect_timeout;
|
||||
REDIS_OPTIONS_SET_TCP(&options, config.tcp.host, config.tcp.port);
|
||||
} else if (config.type == CONN_SSL) {
|
||||
options.type = REDIS_CONN_TCP;
|
||||
options.connect_timeout = &config.connect_timeout;
|
||||
REDIS_OPTIONS_SET_TCP(&options, config.ssl.host, config.ssl.port);
|
||||
} else if (config.type == CONN_UNIX) {
|
||||
options.type = REDIS_CONN_UNIX;
|
||||
options.endpoint.unix_socket = config.unix_sock.path;
|
||||
} else if (config.type == CONN_FD) {
|
||||
options.type = REDIS_CONN_USERFD;
|
||||
/* Create a dummy connection just to get an fd to inherit */
|
||||
redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path);
|
||||
if (dummy_ctx) {
|
||||
redisFD fd = disconnect(dummy_ctx, 1);
|
||||
printf("Connecting to inherited fd %d\n", (int)fd);
|
||||
options.endpoint.fd = fd;
|
||||
}
|
||||
}
|
||||
redisAsyncContext *c = redisAsyncConnectWithOptions(&options);
|
||||
assert(c);
|
||||
astest.ac = c;
|
||||
c->data = &astest;
|
||||
c->dataCleanup = asCleanup;
|
||||
redisPollAttach(c);
|
||||
redisAsyncSetConnectCallbackNC(c, connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c, disconnectCallback);
|
||||
return c;
|
||||
}
|
||||
|
||||
static void as_printerr(void) {
|
||||
printf("Async err %d : %s\n", astest.err, astest.errstr);
|
||||
}
|
||||
|
||||
#define ASASSERT(e) do { \
|
||||
if (!(e)) \
|
||||
as_printerr(); \
|
||||
assert(e); \
|
||||
} while (0);
|
||||
|
||||
static void test_async_polling(struct config config) {
|
||||
int status;
|
||||
redisAsyncContext *c;
|
||||
struct config defaultconfig = config;
|
||||
|
||||
test("Async connect: ");
|
||||
c = do_aconnect(config, ASTEST_CONNECT);
|
||||
assert(c);
|
||||
while(astest.connected == 0)
|
||||
redisPollTick(c, 0.1);
|
||||
assert(astest.connects == 1);
|
||||
ASASSERT(astest.connect_status == REDIS_OK);
|
||||
assert(astest.disconnects == 0);
|
||||
test_cond(astest.connected == 1);
|
||||
|
||||
test("Async free after connect: ");
|
||||
assert(astest.ac != NULL);
|
||||
redisAsyncFree(c);
|
||||
assert(astest.disconnects == 1);
|
||||
assert(astest.ac == NULL);
|
||||
test_cond(astest.disconnect_status == REDIS_OK);
|
||||
|
||||
if (config.type == CONN_TCP || config.type == CONN_SSL) {
|
||||
/* timeout can only be simulated with network */
|
||||
test("Async connect timeout: ");
|
||||
config.tcp.host = "192.168.254.254"; /* blackhole ip */
|
||||
config.connect_timeout.tv_usec = 100000;
|
||||
c = do_aconnect(config, ASTEST_CONN_TIMEOUT);
|
||||
assert(c);
|
||||
assert(c->err == 0);
|
||||
while(astest.connected == 0)
|
||||
redisPollTick(c, 0.1);
|
||||
assert(astest.connected == -1);
|
||||
/*
|
||||
* freeing should not be done, clearing should have happened.
|
||||
*redisAsyncFree(c);
|
||||
*/
|
||||
assert(astest.ac == NULL);
|
||||
test_cond(astest.connect_status == REDIS_ERR);
|
||||
config = defaultconfig;
|
||||
}
|
||||
|
||||
/* Test a ping/pong after connection */
|
||||
test("Async PING/PONG: ");
|
||||
c = do_aconnect(config, ASTEST_PINGPONG);
|
||||
while(astest.connected == 0)
|
||||
redisPollTick(c, 0.1);
|
||||
status = redisAsyncCommand(c, commandCallback, NULL, "PING");
|
||||
assert(status == REDIS_OK);
|
||||
while(astest.ac)
|
||||
redisPollTick(c, 0.1);
|
||||
test_cond(astest.pongs == 1);
|
||||
|
||||
/* Test a ping/pong after connection that didn't time out.
|
||||
* see https://github.com/redis/hiredis/issues/945
|
||||
*/
|
||||
if (config.type == CONN_TCP || config.type == CONN_SSL) {
|
||||
test("Async PING/PONG after connect timeout: ");
|
||||
config.connect_timeout.tv_usec = 10000; /* 10ms */
|
||||
c = do_aconnect(config, ASTEST_PINGPONG_TIMEOUT);
|
||||
while(astest.connected == 0)
|
||||
redisPollTick(c, 0.1);
|
||||
/* sleep 0.1 s, allowing old timeout to arrive */
|
||||
millisleep(10);
|
||||
status = redisAsyncCommand(c, commandCallback, NULL, "PING");
|
||||
assert(status == REDIS_OK);
|
||||
while(astest.ac)
|
||||
redisPollTick(c, 0.1);
|
||||
test_cond(astest.pongs == 2);
|
||||
config = defaultconfig;
|
||||
}
|
||||
|
||||
/* Test disconnect from an on_connect callback
|
||||
* see https://github.com/redis/hiredis/issues/931
|
||||
*/
|
||||
test("Disconnect from onConnected callback (Issue #931): ");
|
||||
c = do_aconnect(config, ASTEST_ISSUE_931);
|
||||
while(astest.disconnects == 0)
|
||||
redisPollTick(c, 0.1);
|
||||
assert(astest.connected == 0);
|
||||
assert(astest.connects == 1);
|
||||
test_cond(astest.disconnects == 1);
|
||||
|
||||
/* Test ping/pong from an on_connect callback
|
||||
* see https://github.com/redis/hiredis/issues/931
|
||||
*/
|
||||
test("Ping/Pong from onConnected callback (Issue #931): ");
|
||||
c = do_aconnect(config, ASTEST_ISSUE_931_PING);
|
||||
/* connect callback issues ping, reponse callback destroys context */
|
||||
while(astest.ac)
|
||||
redisPollTick(c, 0.1);
|
||||
assert(astest.connected == 0);
|
||||
assert(astest.connects == 1);
|
||||
assert(astest.disconnects == 1);
|
||||
test_cond(astest.pongs == 1);
|
||||
}
|
||||
/* End of Async polling_adapter driven tests */
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct config cfg = {
|
||||
.tcp = {
|
||||
@@ -1963,6 +2345,7 @@ int main(int argc, char **argv) {
|
||||
test_blocking_io_errors(cfg);
|
||||
test_invalid_timeout_errors(cfg);
|
||||
test_append_formatted_commands(cfg);
|
||||
test_tcp_options(cfg);
|
||||
if (throughput) test_throughput(cfg);
|
||||
|
||||
printf("\nTesting against Unix socket connection (%s): ", cfg.unix_sock.path);
|
||||
@@ -1972,6 +2355,7 @@ int main(int argc, char **argv) {
|
||||
test_blocking_connection(cfg);
|
||||
test_blocking_connection_timeouts(cfg);
|
||||
test_blocking_io_errors(cfg);
|
||||
test_invalid_timeout_errors(cfg);
|
||||
if (throughput) test_throughput(cfg);
|
||||
} else {
|
||||
test_skipped();
|
||||
@@ -2000,6 +2384,7 @@ int main(int argc, char **argv) {
|
||||
#endif
|
||||
|
||||
#ifdef HIREDIS_TEST_ASYNC
|
||||
cfg.type = CONN_TCP;
|
||||
printf("\nTesting asynchronous API against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port);
|
||||
cfg.type = CONN_TCP;
|
||||
|
||||
@@ -2017,6 +2402,15 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
#endif /* HIREDIS_TEST_ASYNC */
|
||||
|
||||
cfg.type = CONN_TCP;
|
||||
printf("\nTesting asynchronous API using polling_adapter TCP (%s:%d):\n", cfg.tcp.host, cfg.tcp.port);
|
||||
test_async_polling(cfg);
|
||||
if (test_unix_socket) {
|
||||
cfg.type = CONN_UNIX;
|
||||
printf("\nTesting asynchronous API using polling_adapter UNIX (%s):\n", cfg.unix_sock.path);
|
||||
test_async_polling(cfg);
|
||||
}
|
||||
|
||||
if (test_inherit_fd) {
|
||||
printf("\nTesting against inherited fd (%s): ", cfg.unix_sock.path);
|
||||
if (test_unix_socket) {
|
||||
|
||||
Vendored
+43
-9
@@ -4,9 +4,17 @@ 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}
|
||||
SKIPS_AS_FAILS=${SKIPS_AS_FAILS:-0}
|
||||
ENABLE_DEBUG_CMD=
|
||||
SSL_TEST_ARGS=
|
||||
SKIPS_ARG=
|
||||
SKIPS_ARG=${SKIPS_ARG:-}
|
||||
REDIS_DOCKER=${REDIS_DOCKER:-}
|
||||
|
||||
# We need to enable the DEBUG command for redis-server >= 7.0.0
|
||||
REDIS_MAJOR_VERSION="$(redis-server --version|awk -F'[^0-9]+' '{ print $2 }')"
|
||||
if [ "$REDIS_MAJOR_VERSION" -gt "6" ]; then
|
||||
ENABLE_DEBUG_CMD="enable-debug-command local"
|
||||
fi
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
PID_FILE=${tmpdir}/hiredis-test-redis.pid
|
||||
@@ -43,20 +51,34 @@ if [ "$TEST_SSL" = "1" ]; then
|
||||
fi
|
||||
|
||||
cleanup() {
|
||||
set +e
|
||||
kill $(cat ${PID_FILE})
|
||||
if [ -n "${REDIS_DOCKER}" ] ; then
|
||||
docker kill redis-test-server
|
||||
else
|
||||
set +e
|
||||
kill $(cat ${PID_FILE})
|
||||
fi
|
||||
rm -rf ${tmpdir}
|
||||
}
|
||||
trap cleanup INT TERM EXIT
|
||||
|
||||
# base config
|
||||
cat > ${tmpdir}/redis.conf <<EOF
|
||||
daemonize yes
|
||||
pidfile ${PID_FILE}
|
||||
port ${REDIS_PORT}
|
||||
bind 127.0.0.1
|
||||
unixsocket ${SOCK_FILE}
|
||||
unixsocketperm 777
|
||||
EOF
|
||||
|
||||
# if not running in docker add these:
|
||||
if [ ! -n "${REDIS_DOCKER}" ]; then
|
||||
cat >> ${tmpdir}/redis.conf <<EOF
|
||||
daemonize yes
|
||||
${ENABLE_DEBUG_CMD}
|
||||
bind 127.0.0.1
|
||||
EOF
|
||||
fi
|
||||
|
||||
# if doing ssl, add these
|
||||
if [ "$TEST_SSL" = "1" ]; then
|
||||
cat >> ${tmpdir}/redis.conf <<EOF
|
||||
tls-port ${REDIS_SSL_PORT}
|
||||
@@ -66,13 +88,25 @@ tls-key-file ${SSL_KEY}
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo ${tmpdir}
|
||||
cat ${tmpdir}/redis.conf
|
||||
${REDIS_SERVER} ${tmpdir}/redis.conf
|
||||
|
||||
if [ -n "${REDIS_DOCKER}" ] ; then
|
||||
chmod a+wx ${tmpdir}
|
||||
chmod a+r ${tmpdir}/*
|
||||
docker run -d --rm --name redis-test-server \
|
||||
-p ${REDIS_PORT}:${REDIS_PORT} \
|
||||
-p ${REDIS_SSL_PORT}:${REDIS_SSL_PORT} \
|
||||
-v ${tmpdir}:${tmpdir} \
|
||||
${REDIS_DOCKER} \
|
||||
redis-server ${tmpdir}/redis.conf
|
||||
else
|
||||
${REDIS_SERVER} ${tmpdir}/redis.conf
|
||||
fi
|
||||
# Wait until we detect the unix socket
|
||||
echo waiting for server
|
||||
while [ ! -S "${SOCK_FILE}" ]; do sleep 1; done
|
||||
|
||||
# Treat skips as failures if directed
|
||||
[ "$SKIPS_AS_FAILS" = 1 ] && SKIPS_ARG="--skips-as-fails"
|
||||
[ "$SKIPS_AS_FAILS" = 1 ] && SKIPS_ARG="${SKIPS_ARG} --skips-as-fails"
|
||||
|
||||
${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS} ${SKIPS_ARG}
|
||||
|
||||
Vendored
+1
-1
@@ -108,7 +108,7 @@ to search and re-edit already inserted lines of text.
|
||||
|
||||
The followings are the history API calls:
|
||||
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
int linenoiseHistoryAdd(const char *line, int is_sensitive);
|
||||
int linenoiseHistorySetMaxLen(int len);
|
||||
int linenoiseHistorySave(const char *filename);
|
||||
int linenoiseHistoryLoad(const char *filename);
|
||||
|
||||
Vendored
+238
-11
@@ -117,6 +117,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include "linenoise.h"
|
||||
|
||||
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
|
||||
@@ -134,6 +135,18 @@ static int atexit_registered = 0; /* Register atexit just 1 time. */
|
||||
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
|
||||
static int history_len = 0;
|
||||
static char **history = NULL;
|
||||
static int *history_sensitive = NULL; /* An array records whether each line in
|
||||
* history is sensitive. */
|
||||
|
||||
static int reverse_search_mode_enabled = 0;
|
||||
static int reverse_search_direction = 0; /* 1 means forward, -1 means backward. */
|
||||
static int cycle_to_next_search = 0; /* indicates whether to continue the search with CTRL+S or CTRL+R. */
|
||||
static char search_result[LINENOISE_MAX_LINE];
|
||||
static char search_result_friendly[LINENOISE_MAX_LINE];
|
||||
static int search_result_history_index = 0;
|
||||
static int search_result_start_offset = 0;
|
||||
static int ignore_once_hint = 0; /* Flag to ignore hint once, preventing it from interfering
|
||||
* with search results right after exiting search mode. */
|
||||
|
||||
/* The linenoiseState structure represents the state during line editing.
|
||||
* We pass this state to functions implementing specific editing
|
||||
@@ -143,6 +156,7 @@ struct linenoiseState {
|
||||
int ofd; /* Terminal stdout file descriptor. */
|
||||
char *buf; /* Edited line buffer. */
|
||||
size_t buflen; /* Edited line buffer size. */
|
||||
const char *origin_prompt; /* Original prompt, used to restore when exiting search mode. */
|
||||
const char *prompt; /* Prompt to display. */
|
||||
size_t plen; /* Prompt length. */
|
||||
size_t pos; /* Current cursor position. */
|
||||
@@ -153,6 +167,13 @@ struct linenoiseState {
|
||||
int history_index; /* The history index we are currently editing. */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int len; /* Length of the result string. */
|
||||
char *result; /* Search result string. */
|
||||
int search_term_index; /* Position of the search term in the history record. */
|
||||
int search_term_len; /* Length of the search term. */
|
||||
} linenoiseHistorySearchResult;
|
||||
|
||||
enum KEY_ACTION{
|
||||
KEY_NULL = 0, /* NULL */
|
||||
CTRL_A = 1, /* Ctrl+a */
|
||||
@@ -161,6 +182,7 @@ enum KEY_ACTION{
|
||||
CTRL_D = 4, /* Ctrl-d */
|
||||
CTRL_E = 5, /* Ctrl-e */
|
||||
CTRL_F = 6, /* Ctrl-f */
|
||||
CTRL_G = 7, /* Ctrl-g */
|
||||
CTRL_H = 8, /* Ctrl-h */
|
||||
TAB = 9, /* Tab */
|
||||
NL = 10, /* Enter typed before raw mode was enabled */
|
||||
@@ -169,6 +191,8 @@ enum KEY_ACTION{
|
||||
ENTER = 13, /* Enter */
|
||||
CTRL_N = 14, /* Ctrl-n */
|
||||
CTRL_P = 16, /* Ctrl-p */
|
||||
CTRL_R = 18, /* Ctrl-r */
|
||||
CTRL_S = 19, /* Ctrl-s */
|
||||
CTRL_T = 20, /* Ctrl-t */
|
||||
CTRL_U = 21, /* Ctrl+u */
|
||||
CTRL_W = 23, /* Ctrl+w */
|
||||
@@ -177,8 +201,14 @@ enum KEY_ACTION{
|
||||
};
|
||||
|
||||
static void linenoiseAtExit(void);
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
int linenoiseHistoryAdd(const char *line, int is_sensitive);
|
||||
static void refreshLine(struct linenoiseState *l);
|
||||
static void refreshSearchResult(struct linenoiseState *ls);
|
||||
|
||||
static inline void resetSearchResult(void) {
|
||||
memset(search_result, 0, sizeof(search_result));
|
||||
memset(search_result_friendly, 0, sizeof(search_result_friendly));
|
||||
}
|
||||
|
||||
/* Debugging macro. */
|
||||
#if 0
|
||||
@@ -219,6 +249,41 @@ void linenoiseSetMultiLine(int ml) {
|
||||
mlmode = ml;
|
||||
}
|
||||
|
||||
#define REVERSE_SEARCH_PROMPT(direction) ((direction) == -1 ? "(reverse-i-search): " : "(i-search): ")
|
||||
|
||||
/* Enables the reverse search mode and refreshes the prompt. */
|
||||
static void enableReverseSearchMode(struct linenoiseState *l) {
|
||||
assert(reverse_search_mode_enabled != 1);
|
||||
reverse_search_mode_enabled = 1;
|
||||
l->origin_prompt = l->prompt;
|
||||
l->prompt = REVERSE_SEARCH_PROMPT(reverse_search_direction);
|
||||
refreshLine(l);
|
||||
}
|
||||
|
||||
/* This function disables the reverse search mode and returns the terminal to its original state.
|
||||
* If the 'discard' parameter is true, it discards the user's input search keyword and search result.
|
||||
* Otherwise, it copies the search result into 'buf', If there is no search result, it copies the
|
||||
* input search keyword instead. */
|
||||
static void disableReverseSearchMode(struct linenoiseState *l, char *buf, size_t buflen, int discard) {
|
||||
if (discard) {
|
||||
buf[0] = '\0';
|
||||
l->pos = l->len = 0;
|
||||
} else {
|
||||
ignore_once_hint = 1;
|
||||
if (strlen(search_result)) {
|
||||
strncpy(buf, search_result, buflen);
|
||||
buf[buflen-1] = '\0';
|
||||
l->pos = l->len = strlen(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the state to non-search state. */
|
||||
reverse_search_mode_enabled = 0;
|
||||
l->prompt = l->origin_prompt;
|
||||
resetSearchResult();
|
||||
refreshLine(l);
|
||||
}
|
||||
|
||||
/* Return true if the terminal name is in the list of terminals we know are
|
||||
* not able to understand basic escape sequences. */
|
||||
static int isUnsupportedTerm(void) {
|
||||
@@ -231,8 +296,12 @@ static int isUnsupportedTerm(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Raw mode: 1960 magic shit. */
|
||||
/* Raw mode: 1960's magic. */
|
||||
static int enableRawMode(int fd) {
|
||||
if (getenv("FAKETTY_WITH_PROMPT") != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct termios raw;
|
||||
|
||||
if (!isatty(STDIN_FILENO)) goto fatal;
|
||||
@@ -301,6 +370,9 @@ static int getCursorPosition(int ifd, int ofd) {
|
||||
/* Try to get the number of columns in the current terminal, or assume 80
|
||||
* if it fails. */
|
||||
static int getColumns(int ifd, int ofd) {
|
||||
if (getenv("FAKETTY_WITH_PROMPT") != NULL) {
|
||||
goto failed;
|
||||
}
|
||||
struct winsize ws;
|
||||
|
||||
if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
|
||||
@@ -492,6 +564,13 @@ static void abFree(struct abuf *ab) {
|
||||
* to the right of the prompt. */
|
||||
void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
|
||||
char seq[64];
|
||||
|
||||
/* Show hits when not in reverse search mode and not instructed to ignore once. */
|
||||
if (reverse_search_mode_enabled || ignore_once_hint) {
|
||||
ignore_once_hint = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hintsCallback && plen+l->len < l->cols) {
|
||||
int color = -1, bold = 0;
|
||||
char *hint = hintsCallback(l->buf,&color,&bold);
|
||||
@@ -604,7 +683,12 @@ static void refreshMultiLine(struct linenoiseState *l) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < l->len; i++) abAppend(&ab,"*",1);
|
||||
} else {
|
||||
abAppend(&ab,l->buf,l->len);
|
||||
refreshSearchResult(l);
|
||||
if (strlen(search_result) > 0) {
|
||||
abAppend(&ab, search_result_friendly, strlen(search_result_friendly));
|
||||
} else {
|
||||
abAppend(&ab,l->buf,l->len);
|
||||
}
|
||||
}
|
||||
|
||||
/* Show hits if any. */
|
||||
@@ -637,6 +721,9 @@ static void refreshMultiLine(struct linenoiseState *l) {
|
||||
|
||||
/* Set column. */
|
||||
col = (plen+(int)l->pos) % (int)l->cols;
|
||||
if (strlen(search_result) > 0) {
|
||||
col += search_result_start_offset;
|
||||
}
|
||||
lndebug("set col %d", 1+col);
|
||||
if (col)
|
||||
snprintf(seq,64,"\r\x1b[%dC", col);
|
||||
@@ -818,7 +905,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||
|
||||
/* The latest history entry is always our current buffer, that
|
||||
* initially is just an empty string. */
|
||||
linenoiseHistoryAdd("");
|
||||
linenoiseHistoryAdd("", 0);
|
||||
|
||||
if (write(l.ofd,prompt,l.plen) == -1) return -1;
|
||||
while(1) {
|
||||
@@ -832,7 +919,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||
/* Only autocomplete when the callback is set. It returns < 0 when
|
||||
* there was an error reading from fd. Otherwise it will return the
|
||||
* character that should be handled next. */
|
||||
if (c == 9 && completionCallback != NULL) {
|
||||
if (c == TAB && completionCallback != NULL && !reverse_search_mode_enabled) {
|
||||
c = completeLine(&l);
|
||||
/* Return on errors */
|
||||
if (c < 0) return l.len;
|
||||
@@ -843,6 +930,9 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||
switch(c) {
|
||||
case NL: /* enter, typed before raw mode was enabled */
|
||||
break;
|
||||
case TAB:
|
||||
if (reverse_search_mode_enabled) disableReverseSearchMode(&l, buf, buflen, 0);
|
||||
break;
|
||||
case ENTER: /* enter */
|
||||
history_len--;
|
||||
free(history[history_len]);
|
||||
@@ -855,8 +945,14 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||
refreshLine(&l);
|
||||
hintsCallback = hc;
|
||||
}
|
||||
|
||||
if (reverse_search_mode_enabled) disableReverseSearchMode(&l, buf, buflen, 0);
|
||||
return (int)l.len;
|
||||
case CTRL_C: /* ctrl-c */
|
||||
if (reverse_search_mode_enabled) {
|
||||
disableReverseSearchMode(&l, buf, buflen, 1);
|
||||
break;
|
||||
}
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
case BACKSPACE: /* backspace */
|
||||
@@ -891,6 +987,23 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||
case CTRL_P: /* ctrl-p */
|
||||
linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);
|
||||
break;
|
||||
case CTRL_R:
|
||||
case CTRL_S:
|
||||
reverse_search_direction = c == CTRL_R ? -1 : 1;
|
||||
if (reverse_search_mode_enabled) {
|
||||
/* cycle search results */
|
||||
cycle_to_next_search = 1;
|
||||
l.prompt = REVERSE_SEARCH_PROMPT(reverse_search_direction);
|
||||
refreshLine(&l);
|
||||
break;
|
||||
}
|
||||
buf[0] = '\0';
|
||||
l.pos = l.len = 0;
|
||||
enableReverseSearchMode(&l);
|
||||
break;
|
||||
case CTRL_G:
|
||||
if (reverse_search_mode_enabled) disableReverseSearchMode(&l, buf, buflen, 1);
|
||||
break;
|
||||
case CTRL_N: /* ctrl-n */
|
||||
linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);
|
||||
break;
|
||||
@@ -901,6 +1014,11 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||
if (read(l.ifd,seq,1) == -1) break;
|
||||
if (read(l.ifd,seq+1,1) == -1) break;
|
||||
|
||||
if (reverse_search_mode_enabled) {
|
||||
disableReverseSearchMode(&l, buf, buflen, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
/* ESC [ sequences. */
|
||||
if (seq[0] == '[') {
|
||||
if (seq[1] >= '0' && seq[1] <= '9') {
|
||||
@@ -1067,14 +1185,14 @@ static char *linenoiseNoTTY(void) {
|
||||
* editing function or uses dummy fgets() so that you will be able to type
|
||||
* something even in the most desperate of the conditions. */
|
||||
char *linenoise(const char *prompt) {
|
||||
char buf[LINENOISE_MAX_LINE];
|
||||
char buf[LINENOISE_MAX_LINE] = {0};
|
||||
int count;
|
||||
|
||||
if (!isatty(STDIN_FILENO)) {
|
||||
if (getenv("FAKETTY_WITH_PROMPT") == NULL && !isatty(STDIN_FILENO)) {
|
||||
/* Not a tty: read from file / pipe. In this mode we don't want any
|
||||
* limit to the line size, so we call a function to handle that. */
|
||||
return linenoiseNoTTY();
|
||||
} else if (isUnsupportedTerm()) {
|
||||
} else if (getenv("FAKETTY_WITH_PROMPT") == NULL && isUnsupportedTerm()) {
|
||||
size_t len;
|
||||
|
||||
printf("%s",prompt);
|
||||
@@ -1112,6 +1230,7 @@ static void freeHistory(void) {
|
||||
for (j = 0; j < history_len; j++)
|
||||
free(history[j]);
|
||||
free(history);
|
||||
free(history_sensitive);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1128,7 +1247,7 @@ static void linenoiseAtExit(void) {
|
||||
* histories, but will work well for a few hundred of entries.
|
||||
*
|
||||
* Using a circular buffer is smarter, but a bit more complex to handle. */
|
||||
int linenoiseHistoryAdd(const char *line) {
|
||||
int linenoiseHistoryAdd(const char *line, int is_sensitive) {
|
||||
char *linecopy;
|
||||
|
||||
if (history_max_len == 0) return 0;
|
||||
@@ -1137,7 +1256,14 @@ int linenoiseHistoryAdd(const char *line) {
|
||||
if (history == NULL) {
|
||||
history = malloc(sizeof(char*)*history_max_len);
|
||||
if (history == NULL) return 0;
|
||||
history_sensitive = malloc(sizeof(int)*history_max_len);
|
||||
if (history_sensitive == NULL) {
|
||||
free(history);
|
||||
history = NULL;
|
||||
return 0;
|
||||
}
|
||||
memset(history,0,(sizeof(char*)*history_max_len));
|
||||
memset(history_sensitive,0,(sizeof(int)*history_max_len));
|
||||
}
|
||||
|
||||
/* Don't add duplicated lines. */
|
||||
@@ -1150,9 +1276,11 @@ int linenoiseHistoryAdd(const char *line) {
|
||||
if (history_len == history_max_len) {
|
||||
free(history[0]);
|
||||
memmove(history,history+1,sizeof(char*)*(history_max_len-1));
|
||||
memmove(history_sensitive,history_sensitive+1,sizeof(int)*(history_max_len-1));
|
||||
history_len--;
|
||||
}
|
||||
history[history_len] = linecopy;
|
||||
history_sensitive[history_len] = is_sensitive;
|
||||
history_len++;
|
||||
return 1;
|
||||
}
|
||||
@@ -1163,6 +1291,7 @@ int linenoiseHistoryAdd(const char *line) {
|
||||
* than the amount of items already inside the history. */
|
||||
int linenoiseHistorySetMaxLen(int len) {
|
||||
char **new;
|
||||
int *new_sensitive;
|
||||
|
||||
if (len < 1) return 0;
|
||||
if (history) {
|
||||
@@ -1170,6 +1299,11 @@ int linenoiseHistorySetMaxLen(int len) {
|
||||
|
||||
new = malloc(sizeof(char*)*len);
|
||||
if (new == NULL) return 0;
|
||||
new_sensitive = malloc(sizeof(int)*len);
|
||||
if (new_sensitive == NULL) {
|
||||
free(new);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we can't copy everything, free the elements we'll not use. */
|
||||
if (len < tocopy) {
|
||||
@@ -1179,9 +1313,13 @@ int linenoiseHistorySetMaxLen(int len) {
|
||||
tocopy = len;
|
||||
}
|
||||
memset(new,0,sizeof(char*)*len);
|
||||
memset(new_sensitive,0,sizeof(int)*len);
|
||||
memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
|
||||
memcpy(new_sensitive,history_sensitive+(history_len-tocopy), sizeof(int)*tocopy);
|
||||
free(history);
|
||||
free(history_sensitive);
|
||||
history = new;
|
||||
history_sensitive = new_sensitive;
|
||||
}
|
||||
history_max_len = len;
|
||||
if (history_len > history_max_len)
|
||||
@@ -1201,7 +1339,7 @@ int linenoiseHistorySave(const char *filename) {
|
||||
if (fp == NULL) return -1;
|
||||
fchmod(fileno(fp),S_IRUSR|S_IWUSR);
|
||||
for (j = 0; j < history_len; j++)
|
||||
fprintf(fp,"%s\n",history[j]);
|
||||
if (!history_sensitive[j]) fprintf(fp,"%s\n",history[j]);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
@@ -1223,8 +1361,97 @@ int linenoiseHistoryLoad(const char *filename) {
|
||||
p = strchr(buf,'\r');
|
||||
if (!p) p = strchr(buf,'\n');
|
||||
if (p) *p = '\0';
|
||||
linenoiseHistoryAdd(buf);
|
||||
linenoiseHistoryAdd(buf, 0);
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function updates the search index based on the direction of the search.
|
||||
* Returns 0 if the beginning or end of the history is reached, otherwise, returns 1. */
|
||||
static int setNextSearchIndex(int *i) {
|
||||
if (reverse_search_direction == 1) {
|
||||
if (*i == history_len-1) return 0;
|
||||
*i = *i + 1;
|
||||
} else {
|
||||
if (*i <= 0) return 0;
|
||||
*i = *i - 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
linenoiseHistorySearchResult searchInHistory(char *search_term) {
|
||||
linenoiseHistorySearchResult result = {0};
|
||||
|
||||
if (!history_len || !strlen(search_term)) return result;
|
||||
|
||||
int i = cycle_to_next_search ? search_result_history_index :
|
||||
(reverse_search_direction == -1 ? history_len-1 : 0);
|
||||
|
||||
while (1) {
|
||||
char *found = strstr(history[i], search_term);
|
||||
|
||||
/* check if we found the same string at another index when cycling, this would be annoying to cycle through
|
||||
* as it might appear that cycling isn't working */
|
||||
int strings_are_the_same = cycle_to_next_search && strcmp(history[i], history[search_result_history_index]) == 0;
|
||||
|
||||
if (found && !strings_are_the_same) {
|
||||
int haystack_index = found - history[i];
|
||||
result.result = history[i];
|
||||
result.len = strlen(history[i]);
|
||||
result.search_term_index = haystack_index;
|
||||
result.search_term_len = strlen(search_term);
|
||||
search_result_history_index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Exit if reached the end. */
|
||||
if (!setNextSearchIndex(&i)) break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void refreshSearchResult(struct linenoiseState *ls) {
|
||||
if (!reverse_search_mode_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
linenoiseHistorySearchResult sr = searchInHistory(ls->buf);
|
||||
int found = sr.result && sr.len;
|
||||
|
||||
/* If the search term has not changed and we are cycling to the next search result
|
||||
* (using CTRL+R or CTRL+S), there is no need to reset the old search result. */
|
||||
if (!cycle_to_next_search || found)
|
||||
resetSearchResult();
|
||||
cycle_to_next_search = 0;
|
||||
|
||||
if (found) {
|
||||
char *bold = "\x1B[1m";
|
||||
char *normal = "\x1B[0m";
|
||||
|
||||
int size_needed = sr.search_term_index + sr.search_term_len + sr.len -
|
||||
(sr.search_term_index+sr.search_term_len) + sizeof(normal) + sizeof(bold) + sizeof(normal);
|
||||
if (size_needed > sizeof(search_result_friendly) - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate memory for the prefix, match, and suffix strings, one extra byte for `\0`. */
|
||||
char *prefix = calloc(sizeof(char), sr.search_term_index + 1);
|
||||
char *match = calloc(sizeof(char), sr.search_term_len + 1);
|
||||
char *suffix = calloc(sizeof(char), sr.len - (sr.search_term_index+sr.search_term_len) + 1);
|
||||
|
||||
memcpy(prefix, sr.result, sr.search_term_index);
|
||||
memcpy(match, sr.result + sr.search_term_index, sr.search_term_len);
|
||||
memcpy(suffix, sr.result + sr.search_term_index + sr.search_term_len,
|
||||
sr.len - (sr.search_term_index+sr.search_term_len));
|
||||
sprintf(search_result, "%s%s%s", prefix, match, suffix);
|
||||
sprintf(search_result_friendly, "%s%s%s%s%s%s", normal, prefix, bold, match, normal, suffix);
|
||||
|
||||
free(prefix);
|
||||
free(match);
|
||||
free(suffix);
|
||||
|
||||
search_result_start_offset = sr.search_term_index;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -58,7 +58,7 @@ void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
||||
|
||||
char *linenoise(const char *prompt);
|
||||
void linenoiseFree(void *ptr);
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
int linenoiseHistoryAdd(const char *line, int is_sensitive);
|
||||
int linenoiseHistorySetMaxLen(int len);
|
||||
int linenoiseHistorySave(const char *filename);
|
||||
int linenoiseHistoryLoad(const char *filename);
|
||||
|
||||
Vendored
+8
-1
@@ -234,10 +234,17 @@ static const luaL_Reg syslib[] = {
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
#define UNUSED(V) ((void) V)
|
||||
|
||||
/* Only a subset is loaded currently, for sandboxing concerns. */
|
||||
static const luaL_Reg sandbox_syslib[] = {
|
||||
{"clock", os_clock},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_os (lua_State *L) {
|
||||
luaL_register(L, LUA_OSLIBNAME, syslib);
|
||||
UNUSED(syslib);
|
||||
luaL_register(L, LUA_OSLIBNAME, sandbox_syslib);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
@@ -132,6 +132,7 @@ static int bit_tohex(lua_State *L)
|
||||
const char *hexdigits = "0123456789abcdef";
|
||||
char buf[8];
|
||||
int i;
|
||||
if (n == INT32_MIN) n = INT32_MIN+1;
|
||||
if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }
|
||||
if (n > 8) n = 8;
|
||||
for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
|
||||
|
||||
Vendored
+7
-5
@@ -39,6 +39,7 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
@@ -141,13 +142,13 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
json_token_type_t type;
|
||||
int index;
|
||||
size_t index;
|
||||
union {
|
||||
const char *string;
|
||||
double number;
|
||||
int boolean;
|
||||
} value;
|
||||
int string_len;
|
||||
size_t string_len;
|
||||
} json_token_t;
|
||||
|
||||
static const char *char2escape[256] = {
|
||||
@@ -463,9 +464,8 @@ static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *js
|
||||
static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
|
||||
{
|
||||
const char *escstr;
|
||||
int i;
|
||||
const char *str;
|
||||
size_t len;
|
||||
size_t i, len;
|
||||
|
||||
str = lua_tolstring(l, lindex, &len);
|
||||
|
||||
@@ -473,6 +473,8 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
|
||||
* This buffer is reused constantly for small strings
|
||||
* If there are any excess pages, they won't be hit anyway.
|
||||
* This gains ~5% speedup. */
|
||||
if (len > SIZE_MAX / 6 - 3)
|
||||
abort(); /* Overflow check */
|
||||
strbuf_ensure_empty_length(json, len * 6 + 2);
|
||||
|
||||
strbuf_append_char_unsafe(json, '\"');
|
||||
@@ -706,7 +708,7 @@ static int json_encode(lua_State *l)
|
||||
strbuf_t local_encode_buf;
|
||||
strbuf_t *encode_buf;
|
||||
char *json;
|
||||
int len;
|
||||
size_t len;
|
||||
|
||||
luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument");
|
||||
|
||||
|
||||
Vendored
+17
-14
@@ -117,7 +117,9 @@ mp_buf *mp_buf_new(lua_State *L) {
|
||||
|
||||
void mp_buf_append(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) {
|
||||
if (buf->free < len) {
|
||||
size_t newsize = (buf->len+len)*2;
|
||||
size_t newsize = buf->len+len;
|
||||
if (newsize < buf->len || newsize >= SIZE_MAX/2) abort();
|
||||
newsize *= 2;
|
||||
|
||||
buf->b = (unsigned char*)mp_realloc(L, buf->b, buf->len + buf->free, newsize);
|
||||
buf->free = newsize - buf->len;
|
||||
@@ -173,7 +175,7 @@ void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {
|
||||
|
||||
void mp_encode_bytes(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) {
|
||||
unsigned char hdr[5];
|
||||
int hdrlen;
|
||||
size_t hdrlen;
|
||||
|
||||
if (len < 32) {
|
||||
hdr[0] = 0xa0 | (len&0xff); /* fix raw */
|
||||
@@ -220,7 +222,7 @@ void mp_encode_double(lua_State *L, mp_buf *buf, double d) {
|
||||
|
||||
void mp_encode_int(lua_State *L, mp_buf *buf, int64_t n) {
|
||||
unsigned char b[9];
|
||||
int enclen;
|
||||
size_t enclen;
|
||||
|
||||
if (n >= 0) {
|
||||
if (n <= 127) {
|
||||
@@ -290,9 +292,9 @@ void mp_encode_int(lua_State *L, mp_buf *buf, int64_t n) {
|
||||
mp_buf_append(L,buf,b,enclen);
|
||||
}
|
||||
|
||||
void mp_encode_array(lua_State *L, mp_buf *buf, int64_t n) {
|
||||
void mp_encode_array(lua_State *L, mp_buf *buf, uint64_t n) {
|
||||
unsigned char b[5];
|
||||
int enclen;
|
||||
size_t enclen;
|
||||
|
||||
if (n <= 15) {
|
||||
b[0] = 0x90 | (n & 0xf); /* fix array */
|
||||
@@ -313,7 +315,7 @@ void mp_encode_array(lua_State *L, mp_buf *buf, int64_t n) {
|
||||
mp_buf_append(L,buf,b,enclen);
|
||||
}
|
||||
|
||||
void mp_encode_map(lua_State *L, mp_buf *buf, int64_t n) {
|
||||
void mp_encode_map(lua_State *L, mp_buf *buf, uint64_t n) {
|
||||
unsigned char b[5];
|
||||
int enclen;
|
||||
|
||||
@@ -791,7 +793,7 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
|
||||
}
|
||||
}
|
||||
|
||||
int mp_unpack_full(lua_State *L, int limit, int offset) {
|
||||
int mp_unpack_full(lua_State *L, lua_Integer limit, lua_Integer offset) {
|
||||
size_t len;
|
||||
const char *s;
|
||||
mp_cur c;
|
||||
@@ -803,10 +805,10 @@ int mp_unpack_full(lua_State *L, int limit, int offset) {
|
||||
if (offset < 0 || limit < 0) /* requesting negative off or lim is invalid */
|
||||
return luaL_error(L,
|
||||
"Invalid request to unpack with offset of %d and limit of %d.",
|
||||
offset, len);
|
||||
(int) offset, (int) len);
|
||||
else if (offset > len)
|
||||
return luaL_error(L,
|
||||
"Start offset %d greater than input length %d.", offset, len);
|
||||
"Start offset %d greater than input length %d.", (int) offset, (int) len);
|
||||
|
||||
if (decode_all) limit = INT_MAX;
|
||||
|
||||
@@ -828,12 +830,13 @@ int mp_unpack_full(lua_State *L, int limit, int offset) {
|
||||
/* c->left is the remaining size of the input buffer.
|
||||
* subtract the entire buffer size from the unprocessed size
|
||||
* to get our next start offset */
|
||||
int offset = len - c.left;
|
||||
size_t new_offset = len - c.left;
|
||||
if (new_offset > LONG_MAX) abort();
|
||||
|
||||
luaL_checkstack(L, 1, "in function mp_unpack_full");
|
||||
|
||||
/* Return offset -1 when we have have processed the entire buffer. */
|
||||
lua_pushinteger(L, c.left == 0 ? -1 : offset);
|
||||
lua_pushinteger(L, c.left == 0 ? -1 : (lua_Integer) new_offset);
|
||||
/* Results are returned with the arg elements still
|
||||
* in place. Lua takes care of only returning
|
||||
* elements above the args for us.
|
||||
@@ -852,15 +855,15 @@ int mp_unpack(lua_State *L) {
|
||||
}
|
||||
|
||||
int mp_unpack_one(lua_State *L) {
|
||||
int offset = luaL_optinteger(L, 2, 0);
|
||||
lua_Integer offset = luaL_optinteger(L, 2, 0);
|
||||
/* Variable pop because offset may not exist */
|
||||
lua_pop(L, lua_gettop(L)-1);
|
||||
return mp_unpack_full(L, 1, offset);
|
||||
}
|
||||
|
||||
int mp_unpack_limit(lua_State *L) {
|
||||
int limit = luaL_checkinteger(L, 2);
|
||||
int offset = luaL_optinteger(L, 3, 0);
|
||||
lua_Integer limit = luaL_checkinteger(L, 2);
|
||||
lua_Integer offset = luaL_optinteger(L, 3, 0);
|
||||
/* Variable pop because offset may not exist */
|
||||
lua_pop(L, lua_gettop(L)-1);
|
||||
|
||||
|
||||
Vendored
+27
-81
@@ -26,6 +26,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "strbuf.h"
|
||||
|
||||
@@ -38,22 +39,22 @@ static void die(const char *fmt, ...)
|
||||
va_end(arg);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
exit(-1);
|
||||
abort();
|
||||
}
|
||||
|
||||
void strbuf_init(strbuf_t *s, int len)
|
||||
void strbuf_init(strbuf_t *s, size_t len)
|
||||
{
|
||||
int size;
|
||||
size_t size;
|
||||
|
||||
if (len <= 0)
|
||||
if (!len)
|
||||
size = STRBUF_DEFAULT_SIZE;
|
||||
else
|
||||
size = len + 1; /* \0 terminator */
|
||||
|
||||
size = len + 1;
|
||||
if (size < len)
|
||||
die("Overflow, len: %zu", len);
|
||||
s->buf = NULL;
|
||||
s->size = size;
|
||||
s->length = 0;
|
||||
s->increment = STRBUF_DEFAULT_INCREMENT;
|
||||
s->dynamic = 0;
|
||||
s->reallocs = 0;
|
||||
s->debug = 0;
|
||||
@@ -65,7 +66,7 @@ void strbuf_init(strbuf_t *s, int len)
|
||||
strbuf_ensure_null(s);
|
||||
}
|
||||
|
||||
strbuf_t *strbuf_new(int len)
|
||||
strbuf_t *strbuf_new(size_t len)
|
||||
{
|
||||
strbuf_t *s;
|
||||
|
||||
@@ -81,20 +82,10 @@ strbuf_t *strbuf_new(int len)
|
||||
return s;
|
||||
}
|
||||
|
||||
void strbuf_set_increment(strbuf_t *s, int increment)
|
||||
{
|
||||
/* Increment > 0: Linear buffer growth rate
|
||||
* Increment < -1: Exponential buffer growth rate */
|
||||
if (increment == 0 || increment == -1)
|
||||
die("BUG: Invalid string increment");
|
||||
|
||||
s->increment = increment;
|
||||
}
|
||||
|
||||
static inline void debug_stats(strbuf_t *s)
|
||||
{
|
||||
if (s->debug) {
|
||||
fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
|
||||
fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %zd, size: %zd\n",
|
||||
(long)s, s->reallocs, s->length, s->size);
|
||||
}
|
||||
}
|
||||
@@ -113,7 +104,7 @@ void strbuf_free(strbuf_t *s)
|
||||
free(s);
|
||||
}
|
||||
|
||||
char *strbuf_free_to_string(strbuf_t *s, int *len)
|
||||
char *strbuf_free_to_string(strbuf_t *s, size_t *len)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
@@ -131,57 +122,61 @@ char *strbuf_free_to_string(strbuf_t *s, int *len)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int calculate_new_size(strbuf_t *s, int len)
|
||||
static size_t calculate_new_size(strbuf_t *s, size_t len)
|
||||
{
|
||||
int reqsize, newsize;
|
||||
size_t reqsize, newsize;
|
||||
|
||||
if (len <= 0)
|
||||
die("BUG: Invalid strbuf length requested");
|
||||
|
||||
/* Ensure there is room for optional NULL termination */
|
||||
reqsize = len + 1;
|
||||
if (reqsize < len)
|
||||
die("Overflow, len: %zu", len);
|
||||
|
||||
/* If the user has requested to shrink the buffer, do it exactly */
|
||||
if (s->size > reqsize)
|
||||
return reqsize;
|
||||
|
||||
newsize = s->size;
|
||||
if (s->increment < 0) {
|
||||
if (reqsize >= SIZE_MAX / 2) {
|
||||
newsize = reqsize;
|
||||
} else {
|
||||
/* Exponential sizing */
|
||||
while (newsize < reqsize)
|
||||
newsize *= -s->increment;
|
||||
} else {
|
||||
/* Linear sizing */
|
||||
newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
|
||||
newsize *= 2;
|
||||
}
|
||||
|
||||
if (newsize < reqsize)
|
||||
die("BUG: strbuf length would overflow, len: %zu", len);
|
||||
|
||||
return newsize;
|
||||
}
|
||||
|
||||
|
||||
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
|
||||
* optional termination). */
|
||||
void strbuf_resize(strbuf_t *s, int len)
|
||||
void strbuf_resize(strbuf_t *s, size_t len)
|
||||
{
|
||||
int newsize;
|
||||
size_t newsize;
|
||||
|
||||
newsize = calculate_new_size(s, len);
|
||||
|
||||
if (s->debug > 1) {
|
||||
fprintf(stderr, "strbuf(%lx) resize: %d => %d\n",
|
||||
fprintf(stderr, "strbuf(%lx) resize: %zd => %zd\n",
|
||||
(long)s, s->size, newsize);
|
||||
}
|
||||
|
||||
s->size = newsize;
|
||||
s->buf = realloc(s->buf, s->size);
|
||||
if (!s->buf)
|
||||
die("Out of memory");
|
||||
die("Out of memory, len: %zu", len);
|
||||
s->reallocs++;
|
||||
}
|
||||
|
||||
void strbuf_append_string(strbuf_t *s, const char *str)
|
||||
{
|
||||
int space, i;
|
||||
size_t i, space;
|
||||
|
||||
space = strbuf_empty_length(s);
|
||||
|
||||
@@ -197,55 +192,6 @@ void strbuf_append_string(strbuf_t *s, const char *str)
|
||||
}
|
||||
}
|
||||
|
||||
/* strbuf_append_fmt() should only be used when an upper bound
|
||||
* is known for the output string. */
|
||||
void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int fmt_len;
|
||||
|
||||
strbuf_ensure_empty_length(s, len);
|
||||
|
||||
va_start(arg, fmt);
|
||||
fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (fmt_len < 0)
|
||||
die("BUG: Unable to convert number"); /* This should never happen.. */
|
||||
|
||||
s->length += fmt_len;
|
||||
}
|
||||
|
||||
/* strbuf_append_fmt_retry() can be used when the there is no known
|
||||
* upper bound for the output string. */
|
||||
void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int fmt_len, try;
|
||||
int empty_len;
|
||||
|
||||
/* If the first attempt to append fails, resize the buffer appropriately
|
||||
* and try again */
|
||||
for (try = 0; ; try++) {
|
||||
va_start(arg, fmt);
|
||||
/* Append the new formatted string */
|
||||
/* fmt_len is the length of the string required, excluding the
|
||||
* trailing NULL */
|
||||
empty_len = strbuf_empty_length(s);
|
||||
/* Add 1 since there is also space to store the terminating NULL. */
|
||||
fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (fmt_len <= empty_len)
|
||||
break; /* SUCCESS */
|
||||
if (try > 0)
|
||||
die("BUG: length of formatted string changed");
|
||||
|
||||
strbuf_resize(s, s->length + fmt_len);
|
||||
}
|
||||
|
||||
s->length += fmt_len;
|
||||
}
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
||||
|
||||
Vendored
+19
-27
@@ -27,15 +27,13 @@
|
||||
|
||||
/* Size: Total bytes allocated to *buf
|
||||
* Length: String length, excluding optional NULL terminator.
|
||||
* Increment: Allocation increments when resizing the string buffer.
|
||||
* Dynamic: True if created via strbuf_new()
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
char *buf;
|
||||
int size;
|
||||
int length;
|
||||
int increment;
|
||||
size_t size;
|
||||
size_t length;
|
||||
int dynamic;
|
||||
int reallocs;
|
||||
int debug;
|
||||
@@ -44,32 +42,26 @@ typedef struct {
|
||||
#ifndef STRBUF_DEFAULT_SIZE
|
||||
#define STRBUF_DEFAULT_SIZE 1023
|
||||
#endif
|
||||
#ifndef STRBUF_DEFAULT_INCREMENT
|
||||
#define STRBUF_DEFAULT_INCREMENT -2
|
||||
#endif
|
||||
|
||||
/* Initialise */
|
||||
extern strbuf_t *strbuf_new(int len);
|
||||
extern void strbuf_init(strbuf_t *s, int len);
|
||||
extern void strbuf_set_increment(strbuf_t *s, int increment);
|
||||
extern strbuf_t *strbuf_new(size_t len);
|
||||
extern void strbuf_init(strbuf_t *s, size_t len);
|
||||
|
||||
/* Release */
|
||||
extern void strbuf_free(strbuf_t *s);
|
||||
extern char *strbuf_free_to_string(strbuf_t *s, int *len);
|
||||
extern char *strbuf_free_to_string(strbuf_t *s, size_t *len);
|
||||
|
||||
/* Management */
|
||||
extern void strbuf_resize(strbuf_t *s, int len);
|
||||
static int strbuf_empty_length(strbuf_t *s);
|
||||
static int strbuf_length(strbuf_t *s);
|
||||
static char *strbuf_string(strbuf_t *s, int *len);
|
||||
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
|
||||
extern void strbuf_resize(strbuf_t *s, size_t len);
|
||||
static size_t strbuf_empty_length(strbuf_t *s);
|
||||
static size_t strbuf_length(strbuf_t *s);
|
||||
static char *strbuf_string(strbuf_t *s, size_t *len);
|
||||
static void strbuf_ensure_empty_length(strbuf_t *s, size_t len);
|
||||
static char *strbuf_empty_ptr(strbuf_t *s);
|
||||
static void strbuf_extend_length(strbuf_t *s, int len);
|
||||
static void strbuf_extend_length(strbuf_t *s, size_t len);
|
||||
|
||||
/* Update */
|
||||
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
|
||||
extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);
|
||||
static void strbuf_append_mem(strbuf_t *s, const char *c, int len);
|
||||
static void strbuf_append_mem(strbuf_t *s, const char *c, size_t len);
|
||||
extern void strbuf_append_string(strbuf_t *s, const char *str);
|
||||
static void strbuf_append_char(strbuf_t *s, const char c);
|
||||
static void strbuf_ensure_null(strbuf_t *s);
|
||||
@@ -87,12 +79,12 @@ static inline int strbuf_allocated(strbuf_t *s)
|
||||
|
||||
/* Return bytes remaining in the string buffer
|
||||
* Ensure there is space for a NULL terminator. */
|
||||
static inline int strbuf_empty_length(strbuf_t *s)
|
||||
static inline size_t strbuf_empty_length(strbuf_t *s)
|
||||
{
|
||||
return s->size - s->length - 1;
|
||||
}
|
||||
|
||||
static inline void strbuf_ensure_empty_length(strbuf_t *s, int len)
|
||||
static inline void strbuf_ensure_empty_length(strbuf_t *s, size_t len)
|
||||
{
|
||||
if (len > strbuf_empty_length(s))
|
||||
strbuf_resize(s, s->length + len);
|
||||
@@ -103,12 +95,12 @@ static inline char *strbuf_empty_ptr(strbuf_t *s)
|
||||
return s->buf + s->length;
|
||||
}
|
||||
|
||||
static inline void strbuf_extend_length(strbuf_t *s, int len)
|
||||
static inline void strbuf_extend_length(strbuf_t *s, size_t len)
|
||||
{
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline int strbuf_length(strbuf_t *s)
|
||||
static inline size_t strbuf_length(strbuf_t *s)
|
||||
{
|
||||
return s->length;
|
||||
}
|
||||
@@ -124,14 +116,14 @@ static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
|
||||
s->buf[s->length++] = c;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len)
|
||||
static inline void strbuf_append_mem(strbuf_t *s, const char *c, size_t len)
|
||||
{
|
||||
strbuf_ensure_empty_length(s, len);
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len)
|
||||
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, size_t len)
|
||||
{
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
@@ -142,7 +134,7 @@ static inline void strbuf_ensure_null(strbuf_t *s)
|
||||
s->buf[s->length] = 0;
|
||||
}
|
||||
|
||||
static inline char *strbuf_string(strbuf_t *s, int *len)
|
||||
static inline char *strbuf_string(strbuf_t *s, size_t *len)
|
||||
{
|
||||
if (len)
|
||||
*len = s->length;
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
|
||||
SUBDIRS = redisjson redistimeseries redisbloom redisearch
|
||||
|
||||
define submake
|
||||
for dir in $(SUBDIRS); do $(MAKE) -C $$dir $(1); done
|
||||
endef
|
||||
|
||||
all: prepare_source
|
||||
$(call submake,$@)
|
||||
|
||||
get_source:
|
||||
$(call submake,$@)
|
||||
|
||||
prepare_source: get_source handle-werrors setup_environment
|
||||
|
||||
clean:
|
||||
$(call submake,$@)
|
||||
|
||||
distclean: clean_environment
|
||||
$(call submake,$@)
|
||||
|
||||
pristine:
|
||||
$(call submake,$@)
|
||||
|
||||
install:
|
||||
$(call submake,$@)
|
||||
|
||||
setup_environment: install-rust handle-werrors
|
||||
|
||||
clean_environment: uninstall-rust
|
||||
|
||||
# Keep all of the Rust stuff in one place
|
||||
install-rust:
|
||||
ifeq ($(INSTALL_RUST_TOOLCHAIN),yes)
|
||||
@RUST_VERSION=1.80.1; \
|
||||
ARCH="$$(uname -m)"; \
|
||||
if ldd --version 2>&1 | grep -q musl; then LIBC_TYPE="musl"; else LIBC_TYPE="gnu"; fi; \
|
||||
echo "Detected architecture: $${ARCH} and libc: $${LIBC_TYPE}"; \
|
||||
case "$${ARCH}" in \
|
||||
'x86_64') \
|
||||
if [ "$${LIBC_TYPE}" = "musl" ]; then \
|
||||
RUST_INSTALLER="rust-$${RUST_VERSION}-x86_64-unknown-linux-musl"; \
|
||||
RUST_SHA256="37bbec6a7b9f55fef79c451260766d281a7a5b9d2e65c348bbc241127cf34c8d"; \
|
||||
else \
|
||||
RUST_INSTALLER="rust-$${RUST_VERSION}-x86_64-unknown-linux-gnu"; \
|
||||
RUST_SHA256="85e936d5d36970afb80756fa122edcc99bd72a88155f6bdd514f5d27e778e00a"; \
|
||||
fi ;; \
|
||||
'aarch64') \
|
||||
if [ "$${LIBC_TYPE}" = "musl" ]; then \
|
||||
RUST_INSTALLER="rust-$${RUST_VERSION}-aarch64-unknown-linux-musl"; \
|
||||
RUST_SHA256="dd668c2d82f77c5458deb023932600fae633fff8d7f876330e01bc47e9976d17"; \
|
||||
else \
|
||||
RUST_INSTALLER="rust-$${RUST_VERSION}-aarch64-unknown-linux-gnu"; \
|
||||
RUST_SHA256="2e89bad7857711a1c11d017ea28fbfeec54076317763901194f8f5decbac1850"; \
|
||||
fi ;; \
|
||||
*) echo >&2 "Unsupported architecture: '$${ARCH}'"; exit 1 ;; \
|
||||
esac; \
|
||||
echo "Downloading and installing Rust standalone installer: $${RUST_INSTALLER}"; \
|
||||
wget --quiet -O $${RUST_INSTALLER}.tar.xz https://static.rust-lang.org/dist/$${RUST_INSTALLER}.tar.xz; \
|
||||
echo "$${RUST_SHA256} $${RUST_INSTALLER}.tar.xz" | sha256sum -c --quiet || { echo "Rust standalone installer checksum failed!"; exit 1; }; \
|
||||
tar -xf $${RUST_INSTALLER}.tar.xz; \
|
||||
(cd $${RUST_INSTALLER} && ./install.sh); \
|
||||
rm -rf $${RUST_INSTALLER}
|
||||
endif
|
||||
|
||||
uninstall-rust:
|
||||
ifeq ($(INSTALL_RUST_TOOLCHAIN),yes)
|
||||
@if [ -x "/usr/local/lib/rustlib/uninstall.sh" ]; then \
|
||||
echo "Uninstalling Rust using uninstall.sh script"; \
|
||||
rm -rf ~/.cargo; \
|
||||
/usr/local/lib/rustlib/uninstall.sh; \
|
||||
else \
|
||||
echo "WARNING: Rust toolchain not found or uninstall script is missing."; \
|
||||
fi
|
||||
endif
|
||||
|
||||
handle-werrors: get_source
|
||||
ifeq ($(DISABLE_WERRORS),yes)
|
||||
@echo "Disabling -Werror for all modules"
|
||||
@for dir in $(SUBDIRS); do \
|
||||
echo "Processing $$dir"; \
|
||||
find $$dir/src -type f \
|
||||
\( -name "Makefile" \
|
||||
-o -name "*.mk" \
|
||||
-o -name "CMakeLists.txt" \) \
|
||||
-exec sed -i 's/-Werror//g' {} +; \
|
||||
done
|
||||
endif
|
||||
|
||||
.PHONY: all clean distclean install $(SUBDIRS) setup_environment clean_environment install-rust uninstall-rust handle-werrors
|
||||
@@ -0,0 +1,49 @@
|
||||
PREFIX ?= /usr/local
|
||||
INSTALL_DIR ?= $(DESTDIR)$(PREFIX)/lib/redis/modules
|
||||
INSTALL ?= install
|
||||
|
||||
# This logic *partially* follows the current module build system. It is a bit awkward and
|
||||
# should be changed if/when the modules' build process is refactored.
|
||||
|
||||
ARCH_MAP_x86_64 := x64
|
||||
ARCH_MAP_i386 := x86
|
||||
ARCH_MAP_i686 := x86
|
||||
ARCH_MAP_aarch64 := arm64v8
|
||||
ARCH_MAP_arm64 := arm64v8
|
||||
|
||||
OS := $(shell uname -s | tr '[:upper:]' '[:lower:]')
|
||||
ARCH := $(ARCH_MAP_$(shell uname -m))
|
||||
ifeq ($(ARCH),)
|
||||
$(error Unrecognized CPU architecture $(shell uname -m))
|
||||
endif
|
||||
|
||||
FULL_VARIANT := $(OS)-$(ARCH)-release
|
||||
|
||||
# Common rules for all modules, based on per-module configuration
|
||||
|
||||
all: $(TARGET_MODULE)
|
||||
|
||||
$(TARGET_MODULE): get_source
|
||||
$(MAKE) -C $(SRC_DIR)
|
||||
|
||||
get_source: $(SRC_DIR)/.prepared
|
||||
|
||||
$(SRC_DIR)/.prepared:
|
||||
mkdir -p $(SRC_DIR)
|
||||
git clone --recursive --depth 1 --branch $(MODULE_VERSION) $(MODULE_REPO) $(SRC_DIR)
|
||||
touch $@
|
||||
|
||||
clean:
|
||||
-$(MAKE) -C $(SRC_DIR) clean
|
||||
|
||||
distclean:
|
||||
-$(MAKE) -C $(SRC_DIR) distclean
|
||||
|
||||
pristine:
|
||||
-rm -rf $(SRC_DIR)
|
||||
|
||||
install: $(TARGET_MODULE)
|
||||
mkdir -p $(INSTALL_DIR)
|
||||
$(INSTALL) -m 0755 -D $(TARGET_MODULE) $(INSTALL_DIR)
|
||||
|
||||
.PHONY: all clean distclean pristine install
|
||||
@@ -0,0 +1,6 @@
|
||||
SRC_DIR = src
|
||||
MODULE_VERSION = v7.99.4
|
||||
MODULE_REPO = https://github.com/redisbloom/redisbloom
|
||||
TARGET_MODULE = $(SRC_DIR)/bin/$(FULL_VARIANT)/redisbloom.so
|
||||
|
||||
include ../common.mk
|
||||
@@ -0,0 +1,7 @@
|
||||
SRC_DIR = src
|
||||
MODULE_VERSION = v7.99.5
|
||||
MODULE_REPO = https://github.com/redisearch/redisearch
|
||||
TARGET_MODULE = $(SRC_DIR)/bin/$(FULL_VARIANT)/search-community/redisearch.so
|
||||
|
||||
include ../common.mk
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
SRC_DIR = src
|
||||
MODULE_VERSION = v7.99.4
|
||||
MODULE_REPO = https://github.com/redisjson/redisjson
|
||||
TARGET_MODULE = $(SRC_DIR)/bin/$(FULL_VARIANT)/rejson.so
|
||||
|
||||
include ../common.mk
|
||||
|
||||
$(SRC_DIR)/.cargo_fetched:
|
||||
cd $(SRC_DIR) && cargo fetch
|
||||
|
||||
get_source: $(SRC_DIR)/.cargo_fetched
|
||||
@@ -0,0 +1,6 @@
|
||||
SRC_DIR = src
|
||||
MODULE_VERSION = v7.99.4
|
||||
MODULE_REPO = https://github.com/redistimeseries/redistimeseries
|
||||
TARGET_MODULE = $(SRC_DIR)/bin/$(FULL_VARIANT)/redistimeseries.so
|
||||
|
||||
include ../common.mk
|
||||
+77
-33
@@ -51,6 +51,7 @@
|
||||
#
|
||||
# loadmodule /path/to/my_module.so
|
||||
# loadmodule /path/to/other_module.so
|
||||
# loadmodule /path/to/args_module.so [arg [arg ...]]
|
||||
|
||||
################################## NETWORK #####################################
|
||||
|
||||
@@ -346,6 +347,7 @@ pidfile /var/run/redis_6379.pid
|
||||
# verbose (many rarely useful info, but not a mess like the debug level)
|
||||
# notice (moderately verbose, what you want in production probably)
|
||||
# warning (only very important / critical messages are logged)
|
||||
# nothing (nothing is logged)
|
||||
loglevel notice
|
||||
|
||||
# Specify the log file name. Also the empty string can be used to force
|
||||
@@ -387,6 +389,11 @@ databases 16
|
||||
# ASCII art logo in startup logs by setting the following option to yes.
|
||||
always-show-logo no
|
||||
|
||||
# To avoid logging personal identifiable information (PII) into server log file,
|
||||
# uncomment the following:
|
||||
#
|
||||
# hide-user-data-from-log yes
|
||||
|
||||
# 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.
|
||||
@@ -661,7 +668,7 @@ repl-diskless-sync-max-replicas 0
|
||||
repl-diskless-load disabled
|
||||
|
||||
# Master send PINGs to its replicas in a predefined interval. It's possible to
|
||||
# change this interval with the repl_ping_replica_period option. The default
|
||||
# change this interval with the repl-ping-replica-period option. The default
|
||||
# value is 10 seconds.
|
||||
#
|
||||
# repl-ping-replica-period 10
|
||||
@@ -720,6 +727,24 @@ repl-disable-tcp-nodelay no
|
||||
#
|
||||
# repl-backlog-ttl 3600
|
||||
|
||||
# During a fullsync, the master may decide to send both the RDB file and the
|
||||
# replication stream to the replica in parallel. This approach shifts the
|
||||
# responsibility of buffering the replication stream to the replica during the
|
||||
# fullsync process. The replica accumulates the replication stream data until
|
||||
# the RDB file is fully loaded. Once the RDB delivery is completed and
|
||||
# successfully loaded, the replica begins processing and applying the
|
||||
# accumulated replication data to the db. The configuration below controls how
|
||||
# much replication data the replica can accumulate during a fullsync.
|
||||
#
|
||||
# When the replica reaches this limit, it will stop accumulating further data.
|
||||
# At this point, additional data accumulation may occur on the master side
|
||||
# depending on the 'client-output-buffer-limit <replica>' config of master.
|
||||
#
|
||||
# A value of 0 means replica inherits hard limit of
|
||||
# 'client-output-buffer-limit <replica>' config to limit accumulation size.
|
||||
#
|
||||
# replica-full-sync-buffer-limit 0
|
||||
|
||||
# The replica priority is an integer number published by Redis in the INFO
|
||||
# output. It is used by Redis Sentinel in order to select a replica to promote
|
||||
# into a master if the master is no longer working correctly.
|
||||
@@ -831,7 +856,7 @@ replica-priority 100
|
||||
# this is used in order to send invalidation messages to clients. Please
|
||||
# check this page to understand more about the feature:
|
||||
#
|
||||
# https://redis.io/topics/client-side-caching
|
||||
# https://redis.io/docs/latest/develop/use/client-side-caching/
|
||||
#
|
||||
# When tracking is enabled for a client, all the read only queries are assumed
|
||||
# to be cached: this will force Redis to store information in the invalidation
|
||||
@@ -1009,7 +1034,7 @@ replica-priority 100
|
||||
# * stream - Data type: streams related.
|
||||
#
|
||||
# For more information about ACL configuration please refer to
|
||||
# the Redis web site at https://redis.io/topics/acl
|
||||
# the Redis web site at https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/
|
||||
|
||||
# ACL LOG
|
||||
#
|
||||
@@ -1161,7 +1186,8 @@ acllog-max-len 128
|
||||
# configuration directive.
|
||||
#
|
||||
# The default of 5 produces good enough results. 10 Approximates very closely
|
||||
# true LRU but costs more CPU. 3 is faster but not very accurate.
|
||||
# true LRU but costs more CPU. 3 is faster but not very accurate. The maximum
|
||||
# value that can be set is 64.
|
||||
#
|
||||
# maxmemory-samples 5
|
||||
|
||||
@@ -1283,38 +1309,27 @@ lazyfree-lazy-user-flush no
|
||||
# in different I/O threads. Since especially writing is so slow, normally
|
||||
# Redis users use pipelining in order to speed up the Redis performances per
|
||||
# core, and spawn multiple instances in order to scale more. Using I/O
|
||||
# threads it is possible to easily speedup two times Redis without resorting
|
||||
# threads it is possible to easily speedup several times Redis without resorting
|
||||
# to pipelining nor sharding of the instance.
|
||||
#
|
||||
# By default threading is disabled, we suggest enabling it only in machines
|
||||
# that have at least 4 or more cores, leaving at least one spare core.
|
||||
# Using more than 8 threads is unlikely to help much. We also recommend using
|
||||
# threaded I/O only if you actually have performance problems, with Redis
|
||||
# instances being able to use a quite big percentage of CPU time, otherwise
|
||||
# there is no point in using this feature.
|
||||
# We also recommend using threaded I/O only if you actually have performance
|
||||
# problems, with Redis instances being able to use a quite big percentage of
|
||||
# CPU time, otherwise there is no point in using this feature.
|
||||
#
|
||||
# So for instance if you have a four cores boxes, try to use 2 or 3 I/O
|
||||
# threads, if you have a 8 cores, try to use 6 threads. In order to
|
||||
# So for instance if you have a four cores boxes, try to use 3 I/O
|
||||
# threads, if you have a 8 cores, try to use 7 threads. In order to
|
||||
# enable I/O threads use the following configuration directive:
|
||||
#
|
||||
# io-threads 4
|
||||
#
|
||||
# Setting io-threads to 1 will just use the main thread as usual.
|
||||
# When I/O threads are enabled, we only use threads for writes, that is
|
||||
# to thread the write(2) syscall and transfer the client buffers to the
|
||||
# socket. However it is also possible to enable threading of reads and
|
||||
# protocol parsing using the following configuration directive, by setting
|
||||
# it to yes:
|
||||
# When I/O threads are enabled, we not only use threads for writes, that
|
||||
# is to thread the write(2) syscall and transfer the client buffers to the
|
||||
# socket, but also use threads for reads and protocol parsing.
|
||||
#
|
||||
# io-threads-do-reads no
|
||||
#
|
||||
# Usually threading reads doesn't help much.
|
||||
#
|
||||
# NOTE 1: This configuration directive cannot be changed at runtime via
|
||||
# CONFIG SET. Also, this feature currently does not work when SSL is
|
||||
# enabled.
|
||||
#
|
||||
# NOTE 2: If you want to test the Redis speedup using redis-benchmark, make
|
||||
# NOTE: If you want to test the Redis speedup using redis-benchmark, make
|
||||
# sure you also run the benchmark itself in threaded mode, using the
|
||||
# --threads option to match the number of Redis threads, otherwise you'll not
|
||||
# be able to notice the improvements.
|
||||
@@ -1354,7 +1369,7 @@ 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
|
||||
# "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.
|
||||
@@ -1381,7 +1396,11 @@ 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.
|
||||
# Note that changing this value in a config file of an existing database and
|
||||
# restarting the server can lead to data loss. A conversion needs to be done
|
||||
# by setting it via CONFIG command on a live server first.
|
||||
#
|
||||
# Please check https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/ for more information.
|
||||
|
||||
appendonly no
|
||||
|
||||
@@ -1742,6 +1761,11 @@ aof-timestamp-enabled no
|
||||
#
|
||||
# cluster-announce-hostname ""
|
||||
|
||||
# Clusters can configure an optional nodename to be used in addition to the node ID for
|
||||
# debugging and admin information. This name is broadcasted between nodes, so will be used
|
||||
# in addition to the node ID when reporting cross node events such as node failures.
|
||||
# cluster-announce-human-nodename ""
|
||||
|
||||
# Clusters can advertise how clients should connect to them using either their IP address,
|
||||
# a user defined hostname, or by declaring they have no endpoint. Which endpoint is
|
||||
# shown as the preferred endpoint is set by using the cluster-preferred-endpoint-type
|
||||
@@ -1863,7 +1887,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 https://redis.io/docs/latest/develop/use/keyspace-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
|
||||
@@ -2064,7 +2088,7 @@ client-output-buffer-limit pubsub 32mb 8mb 60
|
||||
# amount by default in order to avoid that a protocol desynchronization (for
|
||||
# instance due to a bug in the client) will lead to unbound memory usage in
|
||||
# the query buffer. However you can configure it here if you have very special
|
||||
# needs, such us huge multi/exec requests or alike.
|
||||
# needs, such as a command with huge argument, or huge multi/exec requests or alike.
|
||||
#
|
||||
# client-query-buffer-limit 1gb
|
||||
|
||||
@@ -2189,6 +2213,26 @@ rdb-save-incremental-fsync yes
|
||||
# lfu-log-factor 10
|
||||
# lfu-decay-time 1
|
||||
|
||||
|
||||
# The maximum number of new client connections accepted per event-loop cycle. This configuration
|
||||
# is set independently for TLS connections.
|
||||
#
|
||||
# By default, up to 10 new connection will be accepted per event-loop cycle for normal connections
|
||||
# and up to 1 new connection per event-loop cycle for TLS connections.
|
||||
#
|
||||
# Adjusting this to a larger number can slightly improve efficiency for new connections
|
||||
# at the risk of causing timeouts for regular commands on established connections. It is
|
||||
# not advised to change this without ensuring that all clients have limited connection
|
||||
# pools and exponential backoff in the case of command/connection timeouts.
|
||||
#
|
||||
# If your application is establishing a large number of new connections per second you should
|
||||
# also consider tuning the value of tcp-backlog, which allows the kernel to buffer more
|
||||
# pending connections before dropping or rejecting connections.
|
||||
#
|
||||
# max-new-connections-per-cycle 10
|
||||
# max-new-tls-connections-per-cycle 1
|
||||
|
||||
|
||||
########################### ACTIVE DEFRAGMENTATION #######################
|
||||
#
|
||||
# What is active defragmentation?
|
||||
@@ -2270,16 +2314,16 @@ jemalloc-bg-thread yes
|
||||
# the taskset command:
|
||||
#
|
||||
# Set redis server/io threads to cpu affinity 0,2,4,6:
|
||||
# server_cpulist 0-7:2
|
||||
# server-cpulist 0-7:2
|
||||
#
|
||||
# Set bio threads to cpu affinity 1,3:
|
||||
# bio_cpulist 1,3
|
||||
# bio-cpulist 1,3
|
||||
#
|
||||
# Set aof rewrite child process to cpu affinity 8,9,10,11:
|
||||
# aof_rewrite_cpulist 8-11
|
||||
# aof-rewrite-cpulist 8-11
|
||||
#
|
||||
# Set bgsave child process to cpu affinity 1,10,11
|
||||
# bgsave_cpulist 1,10-11
|
||||
# bgsave-cpulist 1,10-11
|
||||
|
||||
# In some cases redis will emit warnings and even refuse to start if it detects
|
||||
# that the system is in bad state, it is possible to suppress these warnings
|
||||
|
||||
@@ -55,4 +55,6 @@ $TCLSH tests/test_helper.tcl \
|
||||
--single unit/moduleapi/async_rm_call \
|
||||
--single unit/moduleapi/moduleauth \
|
||||
--single unit/moduleapi/rdbloadsave \
|
||||
--single unit/moduleapi/crash \
|
||||
--single unit/moduleapi/internalsecret \
|
||||
"${@}"
|
||||
|
||||
+4
-3
@@ -25,6 +25,7 @@ pidfile /var/run/redis-sentinel.pid
|
||||
# verbose (many rarely useful info, but not a mess like the debug level)
|
||||
# notice (moderately verbose, what you want in production probably)
|
||||
# warning (only very important / critical messages are logged)
|
||||
# nothing (nothing is logged)
|
||||
loglevel notice
|
||||
|
||||
# Specify the log file name. Also the empty string can be used to force
|
||||
@@ -132,7 +133,7 @@ sentinel monitor mymaster 127.0.0.1 6379 2
|
||||
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
|
||||
# Sentinel mode, please refer to the Redis website https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/
|
||||
# for more details.
|
||||
|
||||
# Sentinel's ACL users are defined in the following format:
|
||||
@@ -144,7 +145,7 @@ sentinel down-after-milliseconds mymaster 30000
|
||||
# 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
|
||||
# website at https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/ and redis server configuration
|
||||
# template redis.conf.
|
||||
|
||||
# ACL LOG
|
||||
@@ -173,7 +174,7 @@ acllog-max-len 128
|
||||
# so Sentinel will try to authenticate with the same password to all the
|
||||
# other Sentinels. So you need to configure all your Sentinels in a given
|
||||
# group with the same "requirepass" password. Check the following documentation
|
||||
# for more info: https://redis.io/topics/sentinel
|
||||
# for more info: https://redis.io/docs/latest/operate/oss_and_stack/management/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
|
||||
|
||||
+34
-19
@@ -1,6 +1,9 @@
|
||||
# Redis Makefile
|
||||
# Copyright (C) 2009 Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
# This file is released under the BSD license, see the COPYING file
|
||||
# Copyright (c) 2011-Present, Redis Ltd.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Licensed under your choice of the Redis Source Available License 2.0
|
||||
# (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
#
|
||||
# The Makefile composes the final FINAL_CFLAGS and FINAL_LDFLAGS using
|
||||
# what is needed for Redis plus the standard CFLAGS and LDFLAGS passed.
|
||||
@@ -16,16 +19,22 @@ 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')
|
||||
CLANG := $(findstring clang,$(shell sh -c '$(CC) --version | head -1'))
|
||||
|
||||
# Optimization flags. To override, the OPTIMIZATION variable can be passed, but
|
||||
# some automatic defaults are added to it. To specify optimization flags
|
||||
# explicitly without any defaults added, pass the OPT variable instead.
|
||||
OPTIMIZATION?=-O3
|
||||
ifeq ($(OPTIMIZATION),-O3)
|
||||
ifeq (clang,$(CLANG))
|
||||
REDIS_CFLAGS+=-flto
|
||||
OPTIMIZATION+=-flto
|
||||
else
|
||||
REDIS_CFLAGS+=-flto=auto
|
||||
OPTIMIZATION+=-flto=auto
|
||||
endif
|
||||
REDIS_LDFLAGS+=-flto
|
||||
endif
|
||||
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram fpconv
|
||||
ifneq ($(OPTIMIZATION),-O0)
|
||||
OPTIMIZATION+=-fno-omit-frame-pointer
|
||||
endif
|
||||
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram fpconv fast_float
|
||||
NODEPS:=clean distclean
|
||||
|
||||
# Default settings
|
||||
@@ -47,10 +56,10 @@ OPT=$(OPTIMIZATION)
|
||||
# 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; \
|
||||
$(CC) -std=gnu11 -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
|
||||
STD+=-std=gnu11
|
||||
else
|
||||
STD+=-std=c99
|
||||
endif
|
||||
@@ -117,8 +126,8 @@ endif
|
||||
-include .make-settings
|
||||
|
||||
FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS)
|
||||
FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG)
|
||||
FINAL_LIBS=-lm
|
||||
FINAL_LDFLAGS=$(LDFLAGS) $(OPT) $(REDIS_LDFLAGS) $(DEBUG)
|
||||
FINAL_LIBS=-lm -lstdc++
|
||||
DEBUG=-g -ggdb
|
||||
|
||||
# Linux ARM32 needs -latomic at linking time
|
||||
@@ -226,7 +235,7 @@ ifdef OPENSSL_PREFIX
|
||||
endif
|
||||
|
||||
# Include paths to dependencies
|
||||
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram -I../deps/fpconv
|
||||
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram -I../deps/fpconv -I../deps/fast_float
|
||||
|
||||
# Determine systemd support and/or build preference (defaulting to auto-detection)
|
||||
BUILD_WITH_SYSTEMD=no
|
||||
@@ -345,11 +354,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 eval.o bio.o rio.o rand.o memtest.o syscheck.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 tracking.o socket.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o resp_parser.o call_reply.o script_lua.o script.o functions.o function_lua.o commands.o strl.o connection.o unix.o logreqres.o
|
||||
REDIS_SERVER_OBJ=threads_mngr.o adlist.o quicklist.o ae.o anet.o dict.o ebuckets.o eventnotifier.o iothread.o mstr.o kvstore.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 cluster_legacy.o crc16.o endianconv.o slowlog.o eval.o bio.o rio.o rand.o memtest.o syscheck.o crcspeed.o crccombine.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 tracking.o socket.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o resp_parser.o call_reply.o script_lua.o script.o functions.o function_lua.o commands.o strl.o connection.o unix.o logreqres.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 redisassert.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o strl.o cli_commands.o
|
||||
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o redisassert.o crcspeed.o crccombine.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o strl.o cli_commands.o
|
||||
REDIS_BENCHMARK_NAME=redis-benchmark$(PROG_SUFFIX)
|
||||
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o redisassert.o release.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o strl.o
|
||||
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o redisassert.o release.o crcspeed.o crccombine.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o strl.o
|
||||
REDIS_CHECK_RDB_NAME=redis-check-rdb$(PROG_SUFFIX)
|
||||
REDIS_CHECK_AOF_NAME=redis-check-aof$(PROG_SUFFIX)
|
||||
ALL_SOURCES=$(sort $(patsubst %.o,%.c,$(REDIS_SERVER_OBJ) $(REDIS_CLI_OBJ) $(REDIS_BENCHMARK_OBJ)))
|
||||
@@ -400,7 +409,7 @@ endif
|
||||
|
||||
# redis-server
|
||||
$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a ../deps/hdr_histogram/libhdrhistogram.a ../deps/fpconv/libfpconv.a $(FINAL_LIBS)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a ../deps/hdr_histogram/libhdrhistogram.a ../deps/fpconv/libfpconv.a ../deps/fast_float/libfast_float.a $(FINAL_LIBS)
|
||||
|
||||
# redis-sentinel
|
||||
$(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME)
|
||||
@@ -420,7 +429,7 @@ $(TLS_MODULE_NAME): $(REDIS_SERVER_NAME)
|
||||
|
||||
# redis-cli
|
||||
$(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) $(TLS_CLIENT_LIBS)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o ../deps/hdr_histogram/libhdrhistogram.a $(FINAL_LIBS) $(TLS_CLIENT_LIBS)
|
||||
|
||||
# redis-benchmark
|
||||
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
|
||||
@@ -435,11 +444,16 @@ DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ
|
||||
%.o: %.c .make-prerequisites
|
||||
$(REDIS_CC) -MMD -o $@ -c $<
|
||||
|
||||
# The file commands.def is checked in and doesn't normally need to be rebuilt. It
|
||||
# is built only if python is available and its prereqs are modified.
|
||||
# The following files are checked in and don't normally need to be rebuilt. They
|
||||
# are built only if python is available and their prereqs are modified.
|
||||
ifneq (,$(PYTHON))
|
||||
$(COMMANDS_DEF_FILENAME).def: commands/*.json ../utils/generate-command-code.py
|
||||
$(QUIET_GEN)$(PYTHON) ../utils/generate-command-code.py $(GEN_COMMANDS_FLAGS)
|
||||
|
||||
fmtargs.h: ../utils/generate-fmtargs.py
|
||||
$(QUITE_GEN)sed '/Everything below this line/,$$d' $@ > $@.tmp
|
||||
$(QUITE_GEN)$(PYTHON) ../utils/generate-fmtargs.py >> $@.tmp
|
||||
$(QUITE_GEN)mv $@.tmp $@
|
||||
endif
|
||||
|
||||
commands.c: $(COMMANDS_DEF_FILENAME).def
|
||||
@@ -473,8 +487,9 @@ test-cluster: $(REDIS_SERVER_NAME) $(REDIS_CLI_NAME)
|
||||
check: test
|
||||
|
||||
lcov:
|
||||
@lcov --version
|
||||
$(MAKE) gcov
|
||||
@(set -e; cd ..; ./runtest --clients 1)
|
||||
@(set -e; cd ..; ./runtest)
|
||||
@geninfo -o redis.info .
|
||||
@genhtml --legend -o lcov-html redis.info
|
||||
|
||||
|
||||
@@ -1,33 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2018-Present, Redis Ltd.
|
||||
* 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.
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
#include "cluster.h"
|
||||
#include "sha256.h"
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
@@ -59,10 +39,12 @@ static rax *commandId = NULL; /* Command name to id mapping */
|
||||
|
||||
static unsigned long nextid = 0; /* Next command id that has not been assigned */
|
||||
|
||||
#define ACL_MAX_CATEGORIES 64 /* Maximum number of command categories */
|
||||
|
||||
struct ACLCategoryItem {
|
||||
const char *name;
|
||||
char *name;
|
||||
uint64_t flag;
|
||||
} ACLCommandCategories[] = { /* See redis.conf for details on each category. */
|
||||
} ACLDefaultCommandCategories[] = { /* See redis.conf for details on each category. */
|
||||
{"keyspace", ACL_CATEGORY_KEYSPACE},
|
||||
{"read", ACL_CATEGORY_READ},
|
||||
{"write", ACL_CATEGORY_WRITE},
|
||||
@@ -87,6 +69,54 @@ struct ACLCategoryItem {
|
||||
{NULL,0} /* Terminator. */
|
||||
};
|
||||
|
||||
static struct ACLCategoryItem *ACLCommandCategories = NULL;
|
||||
static size_t nextCommandCategory = 0; /* Index of the next command category to be added */
|
||||
|
||||
/* Implements the ability to add to the list of ACL categories at runtime. Since each ACL category
|
||||
* also requires a bit in the acl_categories flag, there is a limit to the number that can be added.
|
||||
* The new ACL categories occupy the remaining bits of acl_categories flag, other than the bits
|
||||
* occupied by the default ACL command categories.
|
||||
*
|
||||
* The optional `flag` argument allows the assignment of the `acl_categories` flag bit to the ACL category.
|
||||
* When adding a new category, except for the default ACL command categories, this arguments should be `0`
|
||||
* to allow the function to assign the next available `acl_categories` flag bit to the new ACL category.
|
||||
*
|
||||
* returns 1 -> Added, 0 -> Failed (out of space)
|
||||
*
|
||||
* This function is present here to gain access to the ACLCommandCategories array and add a new ACL category.
|
||||
*/
|
||||
int ACLAddCommandCategory(const char *name, uint64_t flag) {
|
||||
if (nextCommandCategory >= ACL_MAX_CATEGORIES) return 0;
|
||||
ACLCommandCategories[nextCommandCategory].name = zstrdup(name);
|
||||
ACLCommandCategories[nextCommandCategory].flag = flag != 0 ? flag : (1ULL<<nextCommandCategory);
|
||||
nextCommandCategory++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Initializes ACLCommandCategories with default ACL categories and allocates space for
|
||||
* new ACL categories.
|
||||
*/
|
||||
void ACLInitCommandCategories(void) {
|
||||
ACLCommandCategories = zcalloc(sizeof(struct ACLCategoryItem) * (ACL_MAX_CATEGORIES + 1));
|
||||
for (int j = 0; ACLDefaultCommandCategories[j].flag; j++) {
|
||||
serverAssert(ACLAddCommandCategory(ACLDefaultCommandCategories[j].name, ACLDefaultCommandCategories[j].flag));
|
||||
}
|
||||
}
|
||||
|
||||
/* This function removes the specified number of categories from the trailing end of
|
||||
* the `ACLCommandCategories` array.
|
||||
* The purpose of this is to remove the categories added by modules that fail
|
||||
* during the onload function.
|
||||
*/
|
||||
void ACLCleanupCategoriesOnFailure(size_t num_acl_categories_added) {
|
||||
for (size_t j = nextCommandCategory - num_acl_categories_added; j < nextCommandCategory; j++) {
|
||||
zfree(ACLCommandCategories[j].name);
|
||||
ACLCommandCategories[j].name = NULL;
|
||||
ACLCommandCategories[j].flag = 0;
|
||||
}
|
||||
nextCommandCategory -= num_acl_categories_added;
|
||||
}
|
||||
|
||||
struct ACLUserFlag {
|
||||
const char *name;
|
||||
uint64_t flag;
|
||||
@@ -248,7 +278,7 @@ int ACLListMatchSds(void *a, void *b) {
|
||||
|
||||
/* Method to free list elements from ACL users password/patterns lists. */
|
||||
void ACLListFreeSds(void *item) {
|
||||
sdsfree(item);
|
||||
sdsfreegeneric(item);
|
||||
}
|
||||
|
||||
/* Method to duplicate list elements from ACL users password/patterns lists. */
|
||||
@@ -259,7 +289,7 @@ void *ACLListDupSds(void *item) {
|
||||
/* Structure used for handling key patterns with different key
|
||||
* based permissions. */
|
||||
typedef struct {
|
||||
int flags; /* The CMD_KEYS_* flags for this key pattern */
|
||||
int flags; /* The ACL key permission types for this key pattern */
|
||||
sds pattern; /* The pattern to match keys against */
|
||||
} keyPattern;
|
||||
|
||||
@@ -387,7 +417,7 @@ aclSelector *ACLUserGetRootSelector(user *u) {
|
||||
*
|
||||
* If the user with such name already exists NULL is returned. */
|
||||
user *ACLCreateUser(const char *name, size_t namelen) {
|
||||
if (raxFind(Users,(unsigned char*)name,namelen) != raxNotFound) return NULL;
|
||||
if (raxFind(Users,(unsigned char*)name,namelen,NULL)) return NULL;
|
||||
user *u = zmalloc(sizeof(*u));
|
||||
u->name = sdsnewlen(name,namelen);
|
||||
u->flags = USER_FLAG_DISABLED;
|
||||
@@ -440,6 +470,11 @@ void ACLFreeUser(user *u) {
|
||||
zfree(u);
|
||||
}
|
||||
|
||||
/* Generic version of ACLFreeUser. */
|
||||
void ACLFreeUserGeneric(void *u) {
|
||||
ACLFreeUser((user *)u);
|
||||
}
|
||||
|
||||
/* When a user is deleted we need to cycle the active
|
||||
* connections in order to kill all the pending ones that
|
||||
* are authenticated with such user. */
|
||||
@@ -456,15 +491,7 @@ void ACLFreeUserAndKillClients(user *u) {
|
||||
* this may result in some security hole: it's much
|
||||
* more defensive to set the default user and put
|
||||
* it in non authenticated mode. */
|
||||
c->user = DefaultUser;
|
||||
c->authenticated = 0;
|
||||
/* We will write replies to this client later, so we can't
|
||||
* close it directly even if async. */
|
||||
if (c == server.current_client) {
|
||||
c->flags |= CLIENT_CLOSE_AFTER_COMMAND;
|
||||
} else {
|
||||
freeClientAsync(c);
|
||||
}
|
||||
deauthenticateAndCloseClient(c);
|
||||
}
|
||||
}
|
||||
ACLFreeUser(u);
|
||||
@@ -489,12 +516,6 @@ void ACLCopyUser(user *dst, user *src) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Free all the users registered in the radix tree 'users' and free the
|
||||
* radix tree itself. */
|
||||
void ACLFreeUsersSet(rax *users) {
|
||||
raxFreeWithCallback(users,(void(*)(void*))ACLFreeUserAndKillClients);
|
||||
}
|
||||
|
||||
/* Given a command ID, this function set by reference 'word' and 'bit'
|
||||
* so that user->allowed_commands[word] will address the right word
|
||||
* where the corresponding bit for the provided ID is stored, and
|
||||
@@ -563,7 +584,7 @@ void ACLSelectorRemoveCommandRule(aclSelector *selector, sds new_rule) {
|
||||
* as well if the command is removed. */
|
||||
char *rule_end = strchr(existing_rule, ' ');
|
||||
if (!rule_end) {
|
||||
/* This is the last rule, so it it to the end of the string. */
|
||||
/* This is the last rule, so move it to the end of the string. */
|
||||
rule_end = existing_rule + strlen(existing_rule);
|
||||
|
||||
/* This approach can leave a trailing space if the last rule is removed,
|
||||
@@ -580,6 +601,8 @@ void ACLSelectorRemoveCommandRule(aclSelector *selector, sds new_rule) {
|
||||
/* Copy the remaining rules starting at the next rule to replace the rule to be
|
||||
* deleted, including the terminating NULL character. */
|
||||
memmove(copy_position, copy_end, strlen(copy_end) + 1);
|
||||
existing_rule = copy_position;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
existing_rule = copy_end;
|
||||
@@ -911,7 +934,7 @@ void ACLResetFirstArgs(aclSelector *selector) {
|
||||
selector->allowed_firstargs = NULL;
|
||||
}
|
||||
|
||||
/* Add a first-arh to the list of subcommands for the user 'u' and
|
||||
/* Add a first-arg to the list of subcommands for the user 'u' and
|
||||
* the command id specified. */
|
||||
void ACLAddAllowedFirstArg(aclSelector *selector, unsigned long id, const char *sub) {
|
||||
/* If this is the first first-arg to be configured for
|
||||
@@ -1044,6 +1067,7 @@ int ACLSetSelector(aclSelector *selector, const char* op, size_t oplen) {
|
||||
int flags = 0;
|
||||
size_t offset = 1;
|
||||
if (op[0] == '%') {
|
||||
int perm_ok = 1;
|
||||
for (; offset < oplen; offset++) {
|
||||
if (toupper(op[offset]) == 'R' && !(flags & ACL_READ_PERMISSION)) {
|
||||
flags |= ACL_READ_PERMISSION;
|
||||
@@ -1053,10 +1077,14 @@ int ACLSetSelector(aclSelector *selector, const char* op, size_t oplen) {
|
||||
offset++;
|
||||
break;
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return C_ERR;
|
||||
perm_ok = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!flags || !perm_ok) {
|
||||
errno = EINVAL;
|
||||
return C_ERR;
|
||||
}
|
||||
} else {
|
||||
flags = ACL_ALL_PERMISSION;
|
||||
}
|
||||
@@ -1396,6 +1424,7 @@ user *ACLCreateDefaultUser(void) {
|
||||
void ACLInit(void) {
|
||||
Users = raxNew();
|
||||
UsersToLoad = listCreate();
|
||||
ACLInitCommandCategories();
|
||||
listSetMatchMethod(UsersToLoad, ACLListMatchLoadedUser);
|
||||
ACLLog = listCreate();
|
||||
DefaultUser = ACLCreateDefaultUser();
|
||||
@@ -1405,7 +1434,7 @@ void ACLInit(void) {
|
||||
* otherwise C_ERR is returned and errno is set to:
|
||||
*
|
||||
* EINVAL: if the username-password do not match.
|
||||
* ENONENT: if the specified user does not exist at all.
|
||||
* ENOENT: if the specified user does not exist at all.
|
||||
*/
|
||||
int ACLCheckUserCredentials(robj *username, robj *password) {
|
||||
user *u = ACLGetUserByName(username->ptr,sdslen(username->ptr));
|
||||
@@ -1500,8 +1529,8 @@ unsigned long ACLGetCommandID(sds cmdname) {
|
||||
sds lowername = sdsdup(cmdname);
|
||||
sdstolower(lowername);
|
||||
if (commandId == NULL) commandId = raxNew();
|
||||
void *id = raxFind(commandId,(unsigned char*)lowername,sdslen(lowername));
|
||||
if (id != raxNotFound) {
|
||||
void *id;
|
||||
if (raxFind(commandId,(unsigned char*)lowername,sdslen(lowername),&id)) {
|
||||
sdsfree(lowername);
|
||||
return (unsigned long)id;
|
||||
}
|
||||
@@ -1532,8 +1561,8 @@ void ACLClearCommandID(void) {
|
||||
|
||||
/* 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);
|
||||
if (myuser == raxNotFound) return NULL;
|
||||
void *myuser = NULL;
|
||||
raxFind(Users,(unsigned char*)name,namelen,&myuser);
|
||||
return myuser;
|
||||
}
|
||||
|
||||
@@ -1559,14 +1588,22 @@ static int ACLSelectorCheckKey(aclSelector *selector, const char *key, int keyle
|
||||
if (keyspec_flags & CMD_KEY_DELETE) key_flags |= ACL_WRITE_PERMISSION;
|
||||
if (keyspec_flags & CMD_KEY_UPDATE) key_flags |= ACL_WRITE_PERMISSION;
|
||||
|
||||
/* Is given key represent a prefix of a set of keys */
|
||||
int prefix = keyspec_flags & CMD_KEY_PREFIX;
|
||||
|
||||
/* Test this key against every pattern. */
|
||||
while((ln = listNext(&li))) {
|
||||
keyPattern *pattern = listNodeValue(ln);
|
||||
if ((pattern->flags & key_flags) != key_flags)
|
||||
continue;
|
||||
size_t plen = sdslen(pattern->pattern);
|
||||
if (stringmatchlen(pattern->pattern,plen,key,keylen,0))
|
||||
return ACL_OK;
|
||||
if (prefix) {
|
||||
if (prefixmatch(pattern->pattern,plen,key,keylen,0))
|
||||
return ACL_OK;
|
||||
} else {
|
||||
if (stringmatchlen(pattern->pattern, plen, key, keylen, 0))
|
||||
return ACL_OK;
|
||||
}
|
||||
}
|
||||
return ACL_DENIED_KEY;
|
||||
}
|
||||
@@ -1856,23 +1893,20 @@ int ACLCheckAllPerm(client *c, int *idxptr) {
|
||||
return ACLCheckAllUserCommandPerm(c->user, c->cmd, c->argv, c->argc, idxptr);
|
||||
}
|
||||
|
||||
/* 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 *new, user *original) {
|
||||
/* If 'new' can access all channels 'original' could then return NULL;
|
||||
Otherwise return a list of channels that the new user can access */
|
||||
list *getUpcomingChannelList(user *new, user *original) {
|
||||
listIter li, lpi;
|
||||
listNode *ln, *lpn;
|
||||
robj *o;
|
||||
int kill = 0;
|
||||
|
||||
/* First optimization is we check if any selector has all channel
|
||||
* permissions. */
|
||||
|
||||
/* Optimization: we check if any selector has all channel permissions. */
|
||||
listRewind(new->selectors,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
aclSelector *s = (aclSelector *) listNodeValue(ln);
|
||||
if (s->flags & SELECTOR_FLAG_ALLCHANNELS) return;
|
||||
if (s->flags & SELECTOR_FLAG_ALLCHANNELS) return NULL;
|
||||
}
|
||||
|
||||
/* Second optimization is to check if the new list of channels
|
||||
/* Next, check if the new list of channels
|
||||
* is a strict superset of the original. This is done by
|
||||
* created an "upcoming" list of all channels that are in
|
||||
* the new user and checking each of the existing channels
|
||||
@@ -1910,57 +1944,87 @@ void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
|
||||
if (match) {
|
||||
/* All channels were matched, no need to kill clients. */
|
||||
listRelease(upcoming);
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
return upcoming;
|
||||
}
|
||||
|
||||
/* Check if the client should be killed because it is subscribed to channels that were
|
||||
* permitted in the past, are not in the `upcoming` channel list. */
|
||||
int ACLShouldKillPubsubClient(client *c, list *upcoming) {
|
||||
robj *o;
|
||||
int kill = 0;
|
||||
|
||||
if (getClientType(c) == CLIENT_TYPE_PUBSUB) {
|
||||
/* Check for pattern violations. */
|
||||
dictIterator *di = dictGetIterator(c->pubsub_patterns);
|
||||
dictEntry *de;
|
||||
while (!kill && ((de = dictNext(di)) != NULL)) {
|
||||
o = dictGetKey(de);
|
||||
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 1);
|
||||
kill = (res == ACL_DENIED_CHANNEL);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
|
||||
/* Check for channel violations. */
|
||||
if (!kill) {
|
||||
/* Check for global channels violation. */
|
||||
di = dictGetIterator(c->pubsub_channels);
|
||||
|
||||
while (!kill && ((de = dictNext(di)) != NULL)) {
|
||||
o = dictGetKey(de);
|
||||
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
|
||||
kill = (res == ACL_DENIED_CHANNEL);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
if (!kill) {
|
||||
/* Check for shard channels violation. */
|
||||
di = dictGetIterator(c->pubsubshard_channels);
|
||||
while (!kill && ((de = dictNext(di)) != NULL)) {
|
||||
o = dictGetKey(de);
|
||||
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
|
||||
kill = (res == ACL_DENIED_CHANNEL);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
|
||||
if (kill) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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 *new, user *original) {
|
||||
/* Do nothing if there are no subscribers. */
|
||||
if (pubsubTotalSubscriptions() == 0)
|
||||
return;
|
||||
|
||||
list *channels = getUpcomingChannelList(new, original);
|
||||
/* If the new user's pubsub permissions are a strict superset of the original, return early. */
|
||||
if (!channels)
|
||||
return;
|
||||
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
|
||||
/* Permissions have changed, so we need to iterate through all
|
||||
* the clients and disconnect those that are no longer valid.
|
||||
* 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 == original && getClientType(c) == CLIENT_TYPE_PUBSUB) {
|
||||
/* Check for pattern violations. */
|
||||
listRewind(c->pubsub_patterns,&lpi);
|
||||
while (!kill && ((lpn = listNext(&lpi)) != NULL)) {
|
||||
|
||||
o = lpn->value;
|
||||
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 1);
|
||||
kill = (res == ACL_DENIED_CHANNEL);
|
||||
}
|
||||
/* Check for channel violations. */
|
||||
if (!kill) {
|
||||
/* Check for global channels violation. */
|
||||
dictIterator *di = dictGetIterator(c->pubsub_channels);
|
||||
|
||||
dictEntry *de;
|
||||
while (!kill && ((de = dictNext(di)) != NULL)) {
|
||||
o = dictGetKey(de);
|
||||
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
|
||||
kill = (res == ACL_DENIED_CHANNEL);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
|
||||
/* Check for shard channels violation. */
|
||||
di = dictGetIterator(c->pubsubshard_channels);
|
||||
while (!kill && ((de = dictNext(di)) != NULL)) {
|
||||
o = dictGetKey(de);
|
||||
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
|
||||
kill = (res == ACL_DENIED_CHANNEL);
|
||||
}
|
||||
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
|
||||
/* Kill it. */
|
||||
if (kill) {
|
||||
freeClient(c);
|
||||
}
|
||||
}
|
||||
if (c->user != original)
|
||||
continue;
|
||||
if (ACLShouldKillPubsubClient(c, channels))
|
||||
deauthenticateAndCloseClient(c);
|
||||
}
|
||||
listRelease(upcoming);
|
||||
|
||||
listRelease(channels);
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
@@ -1989,7 +2053,8 @@ sds *ACLMergeSelectorArguments(sds *argv, int argc, int *merged_argc, int *inval
|
||||
for (int j = 0; j < argc; j++) {
|
||||
char *op = argv[j];
|
||||
|
||||
if (op[0] == '(' && op[sdslen(op) - 1] != ')') {
|
||||
if (open_bracket_start == -1 &&
|
||||
(op[0] == '(' && op[sdslen(op) - 1] != ')')) {
|
||||
selector = sdsdup(argv[j]);
|
||||
open_bracket_start = j;
|
||||
continue;
|
||||
@@ -2112,10 +2177,6 @@ int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
/* Try to apply the user rules in a fake user to see if they
|
||||
* are actually valid. */
|
||||
user *fakeuser = ACLCreateUnlinkedUser();
|
||||
|
||||
/* Merged selectors before trying to process */
|
||||
int merged_argc;
|
||||
sds *acl_args = ACLMergeSelectorArguments(argv + 2, argc - 2, &merged_argc, argc_err);
|
||||
@@ -2124,6 +2185,10 @@ int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
/* Try to apply the user rules in a fake user to see if they
|
||||
* are actually valid. */
|
||||
user *fakeuser = ACLCreateUnlinkedUser();
|
||||
|
||||
for (int j = 0; j < merged_argc; j++) {
|
||||
if (ACLSetUser(fakeuser,acl_args[j],sdslen(acl_args[j])) == C_ERR) {
|
||||
if (errno != ENOENT) {
|
||||
@@ -2366,11 +2431,46 @@ sds ACLLoadFromFile(const char *filename) {
|
||||
ACLFreeUser(new_default);
|
||||
raxInsert(Users,(unsigned char*)"default",7,DefaultUser,NULL);
|
||||
raxRemove(old_users,(unsigned char*)"default",7,NULL);
|
||||
ACLFreeUsersSet(old_users);
|
||||
|
||||
/* If there are some subscribers, we need to check if we need to drop some clients. */
|
||||
rax *user_channels = NULL;
|
||||
if (pubsubTotalSubscriptions() > 0) {
|
||||
user_channels = raxNew();
|
||||
}
|
||||
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
|
||||
listRewind(server.clients,&li);
|
||||
while ((ln = listNext(&li)) != NULL) {
|
||||
client *c = listNodeValue(ln);
|
||||
/* a MASTER client can do everything (and user = NULL) so we can skip it */
|
||||
if (c->flags & CLIENT_MASTER)
|
||||
continue;
|
||||
user *original = c->user;
|
||||
list *channels = NULL;
|
||||
user *new = ACLGetUserByName(c->user->name, sdslen(c->user->name));
|
||||
if (new && user_channels) {
|
||||
if (!raxFind(user_channels, (unsigned char*)(new->name), sdslen(new->name), (void**)&channels)) {
|
||||
channels = getUpcomingChannelList(new, original);
|
||||
raxInsert(user_channels, (unsigned char*)(new->name), sdslen(new->name), channels, NULL);
|
||||
}
|
||||
}
|
||||
/* When the new channel list is NULL, it means the new user's channel list is a superset of the old user's list. */
|
||||
if (!new || (channels && ACLShouldKillPubsubClient(c, channels))) {
|
||||
deauthenticateAndCloseClient(c);
|
||||
continue;
|
||||
}
|
||||
c->user = new;
|
||||
}
|
||||
|
||||
if (user_channels)
|
||||
raxFreeWithCallback(user_channels, listReleaseGeneric);
|
||||
raxFreeWithCallback(old_users, ACLFreeUserGeneric);
|
||||
sdsfree(errors);
|
||||
return NULL;
|
||||
} else {
|
||||
ACLFreeUsersSet(Users);
|
||||
raxFreeWithCallback(Users, ACLFreeUserGeneric);
|
||||
Users = old_users;
|
||||
errors = sdscat(errors,"WARNING: ACL errors detected, no change to the previously active ACL rules was performed");
|
||||
return errors;
|
||||
@@ -2548,6 +2648,15 @@ void ACLUpdateInfoMetrics(int reason){
|
||||
}
|
||||
}
|
||||
|
||||
static void trimACLLogEntriesToMaxLen(void) {
|
||||
while(listLength(ACLLog) > server.acllog_max_len) {
|
||||
listNode *ln = listLast(ACLLog);
|
||||
ACLLogEntry *le = listNodeValue(ln);
|
||||
ACLFreeLogEntry(le);
|
||||
listDelNode(ACLLog,ln);
|
||||
}
|
||||
}
|
||||
|
||||
/* Adds a new entry in the ACL log, making sure to delete the old entry
|
||||
* if we reach the maximum length allowed for the log. This function attempts
|
||||
* to find similar entries in the current log in order to bump the counter of
|
||||
@@ -2567,6 +2676,11 @@ void addACLLogEntry(client *c, int reason, int context, int argpos, sds username
|
||||
/* Update ACL info metrics */
|
||||
ACLUpdateInfoMetrics(reason);
|
||||
|
||||
if (server.acllog_max_len == 0) {
|
||||
trimACLLogEntriesToMaxLen();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create a new entry. */
|
||||
struct ACLLogEntry *le = zmalloc(sizeof(*le));
|
||||
le->count = 1;
|
||||
@@ -2629,12 +2743,7 @@ void addACLLogEntry(client *c, int reason, int context, int argpos, sds username
|
||||
* to its maximum size. */
|
||||
ACLLogEntryCount++; /* Incrementing the entry_id count to make each record in the log unique. */
|
||||
listAddNodeHead(ACLLog, le);
|
||||
while(listLength(ACLLog) > server.acllog_max_len) {
|
||||
listNode *ln = listLast(ACLLog);
|
||||
ACLLogEntry *le = listNodeValue(ln);
|
||||
ACLFreeLogEntry(le);
|
||||
listDelNode(ACLLog,ln);
|
||||
}
|
||||
trimACLLogEntriesToMaxLen();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2672,7 +2781,6 @@ void aclCatWithFlags(client *c, dict *commands, uint64_t cflag, int *arraylen) {
|
||||
|
||||
while ((de = dictNext(di)) != NULL) {
|
||||
struct redisCommand *cmd = dictGetVal(de);
|
||||
if (cmd->flags & CMD_MODULE) continue;
|
||||
if (cmd->acl_categories & cflag) {
|
||||
addReplyBulkCBuffer(c, cmd->fullname, sdslen(cmd->fullname));
|
||||
(*arraylen)++;
|
||||
@@ -2758,8 +2866,7 @@ void aclCommand(client *c) {
|
||||
sds username = c->argv[2]->ptr;
|
||||
/* Check username validity. */
|
||||
if (ACLStringHasSpaces(username,sdslen(username))) {
|
||||
addReplyErrorFormat(c,
|
||||
"Usernames can't contain spaces or null characters");
|
||||
addReplyError(c, "Usernames can't contain spaces or null characters");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2777,6 +2884,10 @@ void aclCommand(client *c) {
|
||||
}
|
||||
return;
|
||||
} else if (!strcasecmp(sub,"deluser") && c->argc >= 3) {
|
||||
/* Initially redact all the arguments to not leak any information
|
||||
* about the users. */
|
||||
for (int j = 2; j < c->argc; j++) redactClientCommandArgument(c, j);
|
||||
|
||||
int deleted = 0;
|
||||
for (int j = 2; j < c->argc; j++) {
|
||||
sds username = c->argv[j]->ptr;
|
||||
@@ -2799,6 +2910,9 @@ void aclCommand(client *c) {
|
||||
}
|
||||
addReplyLongLong(c,deleted);
|
||||
} else if (!strcasecmp(sub,"getuser") && c->argc == 3) {
|
||||
/* Redact the username to not leak any information about the user. */
|
||||
redactClientCommandArgument(c, 2);
|
||||
|
||||
user *u = ACLGetUserByName(c->argv[2]->ptr,sdslen(c->argv[2]->ptr));
|
||||
if (u == NULL) {
|
||||
addReplyNull(c);
|
||||
@@ -3081,6 +3195,38 @@ void addReplyCommandCategories(client *c, struct redisCommand *cmd) {
|
||||
setDeferredSetLen(c, flaglen, flagcount);
|
||||
}
|
||||
|
||||
/* When successful, initiates an internal connection, that is able to execute
|
||||
* internal commands (see CMD_INTERNAL). */
|
||||
static void internalAuth(client *c) {
|
||||
if (server.cluster == NULL) {
|
||||
addReplyError(c, "Cannot authenticate as an internal connection on non-cluster instances");
|
||||
return;
|
||||
}
|
||||
|
||||
sds password = c->argv[2]->ptr;
|
||||
|
||||
/* Get internal secret. */
|
||||
size_t len = -1;
|
||||
const char *internal_secret = clusterGetSecret(&len);
|
||||
if (sdslen(password) != len) {
|
||||
addReplyError(c, "-WRONGPASS invalid internal password");
|
||||
return;
|
||||
}
|
||||
if (!time_independent_strcmp((char *)internal_secret, (char *)password, len)) {
|
||||
c->flags |= CLIENT_INTERNAL;
|
||||
/* No further authentication is needed. */
|
||||
c->authenticated = 1;
|
||||
/* Set the user to the unrestricted user, if it is not already set (default). */
|
||||
if (c->user != NULL) {
|
||||
c->user = NULL;
|
||||
moduleNotifyUserChanged(c);
|
||||
}
|
||||
addReply(c, shared.ok);
|
||||
} else {
|
||||
addReplyError(c, "-WRONGPASS invalid internal password");
|
||||
}
|
||||
}
|
||||
|
||||
/* AUTH <password>
|
||||
* AUTH <username> <password> (Redis >= 6.0 form)
|
||||
*
|
||||
@@ -3114,6 +3260,14 @@ void authCommand(client *c) {
|
||||
username = c->argv[1];
|
||||
password = c->argv[2];
|
||||
redactClientCommandArgument(c, 2);
|
||||
|
||||
/* Handle internal authentication commands.
|
||||
* Note: No user-defined ACL user can have this username (no spaces
|
||||
* allowed), thus no conflicts with ACL possible. */
|
||||
if (!strcmp(username->ptr, "internal connection")) {
|
||||
internalAuth(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
robj *err = NULL;
|
||||
|
||||
+10
-24
@@ -1,31 +1,10 @@
|
||||
/* adlist.c - A generic doubly linked list implementation
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2006-Present, Redis Ltd.
|
||||
* 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.
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*/
|
||||
|
||||
|
||||
@@ -76,10 +55,17 @@ void listEmpty(list *list)
|
||||
* This function can't fail. */
|
||||
void listRelease(list *list)
|
||||
{
|
||||
if (!list)
|
||||
return;
|
||||
listEmpty(list);
|
||||
zfree(list);
|
||||
}
|
||||
|
||||
/* Generic version of listRelease. */
|
||||
void listReleaseGeneric(void *list) {
|
||||
listRelease((struct list*)list);
|
||||
}
|
||||
|
||||
/* Add a new node to the list, to head, containing the specified 'value'
|
||||
* pointer as value.
|
||||
*
|
||||
|
||||
+4
-24
@@ -1,31 +1,10 @@
|
||||
/* adlist.h - A generic doubly linked list implementation
|
||||
*
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2006-Present, Redis Ltd.
|
||||
* 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.
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*/
|
||||
|
||||
#ifndef __ADLIST_H__
|
||||
@@ -72,6 +51,7 @@ typedef struct list {
|
||||
/* Prototypes */
|
||||
list *listCreate(void);
|
||||
void listRelease(list *list);
|
||||
void listReleaseGeneric(void *list);
|
||||
void listEmpty(list *list);
|
||||
list *listAddNodeHead(list *list, void *value);
|
||||
list *listAddNodeTail(list *list, void *value);
|
||||
|
||||
@@ -2,32 +2,11 @@
|
||||
* for the Jim's event-loop (Jim is a Tcl interpreter) but later translated
|
||||
* it in form of a library for easy reuse.
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2006-Present, Redis Ltd.
|
||||
* 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.
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*/
|
||||
|
||||
#include "ae.h"
|
||||
@@ -63,7 +42,7 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#define INITIAL_EVENT 1024
|
||||
aeEventLoop *aeCreateEventLoop(int setsize) {
|
||||
aeEventLoop *eventLoop;
|
||||
int i;
|
||||
@@ -71,8 +50,9 @@ aeEventLoop *aeCreateEventLoop(int setsize) {
|
||||
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);
|
||||
eventLoop->nevents = setsize < INITIAL_EVENT ? setsize : INITIAL_EVENT;
|
||||
eventLoop->events = zmalloc(sizeof(aeFileEvent)*eventLoop->nevents);
|
||||
eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*eventLoop->nevents);
|
||||
if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
|
||||
eventLoop->setsize = setsize;
|
||||
eventLoop->timeEventHead = NULL;
|
||||
@@ -82,10 +62,11 @@ aeEventLoop *aeCreateEventLoop(int setsize) {
|
||||
eventLoop->beforesleep = NULL;
|
||||
eventLoop->aftersleep = NULL;
|
||||
eventLoop->flags = 0;
|
||||
memset(eventLoop->privdata, 0, sizeof(eventLoop->privdata));
|
||||
if (aeApiCreate(eventLoop) == -1) goto err;
|
||||
/* Events with mask == AE_NONE are not set. So let's initialize the
|
||||
* vector with it. */
|
||||
for (i = 0; i < setsize; i++)
|
||||
for (i = 0; i < eventLoop->nevents; i++)
|
||||
eventLoop->events[i].mask = AE_NONE;
|
||||
return eventLoop;
|
||||
|
||||
@@ -123,20 +104,19 @@ void aeSetDontWait(aeEventLoop *eventLoop, int noWait) {
|
||||
*
|
||||
* Otherwise AE_OK is returned and the operation is successful. */
|
||||
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {
|
||||
int i;
|
||||
|
||||
if (setsize == eventLoop->setsize) return AE_OK;
|
||||
if (eventLoop->maxfd >= setsize) return AE_ERR;
|
||||
if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR;
|
||||
|
||||
eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);
|
||||
eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);
|
||||
eventLoop->setsize = setsize;
|
||||
|
||||
/* Make sure that if we created new slots, they are initialized with
|
||||
* an AE_NONE mask. */
|
||||
for (i = eventLoop->maxfd+1; i < setsize; i++)
|
||||
eventLoop->events[i].mask = AE_NONE;
|
||||
/* If the current allocated space is larger than the requested size,
|
||||
* we need to shrink it to the requested size. */
|
||||
if (setsize < eventLoop->nevents) {
|
||||
eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);
|
||||
eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);
|
||||
eventLoop->nevents = setsize;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
@@ -149,6 +129,8 @@ void aeDeleteEventLoop(aeEventLoop *eventLoop) {
|
||||
aeTimeEvent *next_te, *te = eventLoop->timeEventHead;
|
||||
while (te) {
|
||||
next_te = te->next;
|
||||
if (te->finalizerProc)
|
||||
te->finalizerProc(eventLoop, te->clientData);
|
||||
zfree(te);
|
||||
te = next_te;
|
||||
}
|
||||
@@ -166,6 +148,22 @@ int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
|
||||
errno = ERANGE;
|
||||
return AE_ERR;
|
||||
}
|
||||
|
||||
/* Resize the events and fired arrays if the file
|
||||
* descriptor exceeds the current number of events. */
|
||||
if (unlikely(fd >= eventLoop->nevents)) {
|
||||
int newnevents = eventLoop->nevents;
|
||||
newnevents = (newnevents * 2 > fd + 1) ? newnevents * 2 : fd + 1;
|
||||
newnevents = (newnevents > eventLoop->setsize) ? eventLoop->setsize : newnevents;
|
||||
eventLoop->events = zrealloc(eventLoop->events, sizeof(aeFileEvent) * newnevents);
|
||||
eventLoop->fired = zrealloc(eventLoop->fired, sizeof(aeFiredEvent) * newnevents);
|
||||
|
||||
/* Initialize new slots with an AE_NONE mask */
|
||||
for (int i = eventLoop->nevents; i < newnevents; i++)
|
||||
eventLoop->events[i].mask = AE_NONE;
|
||||
eventLoop->nevents = newnevents;
|
||||
}
|
||||
|
||||
aeFileEvent *fe = &eventLoop->events[fd];
|
||||
|
||||
if (aeApiAddEvent(eventLoop, fd, mask) == -1)
|
||||
@@ -333,7 +331,7 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
processed++;
|
||||
now = getMonotonicUs();
|
||||
if (retval != AE_NOMORE) {
|
||||
te->when = now + retval * 1000;
|
||||
te->when = now + (monotime)retval * 1000;
|
||||
} else {
|
||||
te->id = AE_DELETED_EVENT_ID;
|
||||
}
|
||||
@@ -343,8 +341,8 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
return processed;
|
||||
}
|
||||
|
||||
/* Process every pending time event, then every pending file event
|
||||
* (that may be registered by time event callbacks just processed).
|
||||
/* Process every pending file event, then every pending time event
|
||||
* (that may be registered by file event callbacks just processed).
|
||||
* Without special flags the function sleeps until some file event
|
||||
* fires, or when the next time event occurs (if any).
|
||||
*
|
||||
|
||||
@@ -2,32 +2,11 @@
|
||||
* for the Jim's event-loop (Jim is a Tcl interpreter) but later translated
|
||||
* it in form of a library for easy reuse.
|
||||
*
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2006-Present, Redis Ltd.
|
||||
* 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.
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*/
|
||||
|
||||
#ifndef __AE_H__
|
||||
@@ -100,6 +79,7 @@ typedef struct aeEventLoop {
|
||||
int maxfd; /* highest file descriptor currently registered */
|
||||
int setsize; /* max number of file descriptors tracked */
|
||||
long long timeEventNextId;
|
||||
int nevents; /* Size of Registered events */
|
||||
aeFileEvent *events; /* Registered events */
|
||||
aeFiredEvent *fired; /* Fired events */
|
||||
aeTimeEvent *timeEventHead;
|
||||
@@ -108,6 +88,7 @@ typedef struct aeEventLoop {
|
||||
aeBeforeSleepProc *beforesleep;
|
||||
aeBeforeSleepProc *aftersleep;
|
||||
int flags;
|
||||
void *privdata[2];
|
||||
} aeEventLoop;
|
||||
|
||||
/* Prototypes */
|
||||
|
||||
+3
-24
@@ -1,31 +1,10 @@
|
||||
/* Linux epoll(2) based ae.c module
|
||||
*
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* 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.
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+3
-24
@@ -1,31 +1,10 @@
|
||||
/* Select()-based ae.c module.
|
||||
*
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* 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.
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+146
-63
@@ -1,31 +1,10 @@
|
||||
/* anet.c -- Basic TCP socket stuff made a bit less boring
|
||||
*
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2006-Present, Redis Ltd.
|
||||
* 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.
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
@@ -82,7 +61,7 @@ int anetSetBlock(char *err, int fd, int non_block) {
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
/* Check if this flag has been set or unset, if so,
|
||||
/* 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;
|
||||
@@ -107,8 +86,8 @@ 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
|
||||
/* 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;
|
||||
@@ -130,57 +109,145 @@ int anetCloexec(int fd) {
|
||||
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. */
|
||||
/* Enable TCP keep-alive mechanism to detect dead peers,
|
||||
* TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT will be set accordingly. */
|
||||
int anetKeepAlive(char *err, int fd, int interval)
|
||||
{
|
||||
int val = 1;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1)
|
||||
int enabled = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled)))
|
||||
{
|
||||
anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
/* Default settings are more or less garbage, with the keepalive time
|
||||
* set to 7200 by default on Linux. Modify settings to make the feature
|
||||
* actually useful. */
|
||||
int idle;
|
||||
int intvl;
|
||||
int cnt;
|
||||
|
||||
/* Send first probe after interval. */
|
||||
val = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
|
||||
/* There are platforms that are expected to support the full mechanism of TCP keep-alive,
|
||||
* we want the compiler to emit warnings of unused variables if the preprocessor directives
|
||||
* somehow fail, and other than those platforms, just omit these warnings if they happen.
|
||||
*/
|
||||
#if !(defined(_AIX) || defined(__APPLE__) || defined(__DragonFly__) || \
|
||||
defined(__FreeBSD__) || defined(__illumos__) || defined(__linux__) || \
|
||||
defined(__NetBSD__) || defined(__sun))
|
||||
UNUSED(interval);
|
||||
UNUSED(idle);
|
||||
UNUSED(intvl);
|
||||
UNUSED(cnt);
|
||||
#endif
|
||||
|
||||
#ifdef __sun
|
||||
/* The implementation of TCP keep-alive on Solaris/SmartOS is a bit unusual
|
||||
* compared to other Unix-like systems.
|
||||
* Thus, we need to specialize it on Solaris.
|
||||
*
|
||||
* There are two keep-alive mechanisms on Solaris:
|
||||
* - By default, the first keep-alive probe is sent out after a TCP connection is idle for two hours.
|
||||
* If the peer does not respond to the probe within eight minutes, the TCP connection is aborted.
|
||||
* You can alter the interval for sending out the first probe using the socket option TCP_KEEPALIVE_THRESHOLD
|
||||
* in milliseconds or TCP_KEEPIDLE in seconds.
|
||||
* The system default is controlled by the TCP ndd parameter tcp_keepalive_interval. The minimum value is ten seconds.
|
||||
* The maximum is ten days, while the default is two hours. If you receive no response to the probe,
|
||||
* you can use the TCP_KEEPALIVE_ABORT_THRESHOLD socket option to change the time threshold for aborting a TCP connection.
|
||||
* The option value is an unsigned integer in milliseconds. The value zero indicates that TCP should never time out and
|
||||
* abort the connection when probing. The system default is controlled by the TCP ndd parameter tcp_keepalive_abort_interval.
|
||||
* The default is eight minutes.
|
||||
*
|
||||
* - The second implementation is activated if socket option TCP_KEEPINTVL and/or TCP_KEEPCNT are set.
|
||||
* The time between each consequent probes is set by TCP_KEEPINTVL in seconds.
|
||||
* The minimum value is ten seconds. The maximum is ten days, while the default is two hours.
|
||||
* The TCP connection will be aborted after certain amount of probes, which is set by TCP_KEEPCNT, without receiving response.
|
||||
*/
|
||||
|
||||
idle = interval;
|
||||
if (idle < 10) idle = 10; // kernel expects at least 10 seconds
|
||||
if (idle > 10*24*60*60) idle = 10*24*60*60; // kernel expects at most 10 days
|
||||
|
||||
/* `TCP_KEEPIDLE`, `TCP_KEEPINTVL`, and `TCP_KEEPCNT` were not available on Solaris
|
||||
* until version 11.4, but let's take a chance here. */
|
||||
#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT)
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
/* Send next probes after the specified interval. Note that we set the
|
||||
* delay as interval / 3, as we send three probes before detecting
|
||||
* an error (see the next setsockopt call). */
|
||||
val = interval/3;
|
||||
if (val == 0) val = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
|
||||
intvl = idle/3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
/* Consider the socket in error state after three we send three ACK
|
||||
* probes without getting a reply. */
|
||||
val = 3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
|
||||
cnt = 3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
/* Set idle time with interval */
|
||||
val = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
|
||||
#else
|
||||
/* Fall back to the first implementation of tcp-alive mechanism for older Solaris,
|
||||
* simulate the tcp-alive mechanism on other platforms via `TCP_KEEPALIVE_THRESHOLD` + `TCP_KEEPALIVE_ABORT_THRESHOLD`.
|
||||
*/
|
||||
idle *= 1000; // kernel expects milliseconds
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, &idle, sizeof(idle))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
/* Note that the consequent probes will not be sent at equal intervals on Solaris,
|
||||
* but will be sent using the exponential backoff algorithm. */
|
||||
intvl = idle/3;
|
||||
cnt = 3;
|
||||
int time_to_abort = intvl * cnt;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, &time_to_abort, sizeof(time_to_abort))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ANET_OK;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef TCP_KEEPIDLE
|
||||
/* Default settings are more or less garbage, with the keepalive time
|
||||
* set to 7200 by default on Linux and other Unix-like systems.
|
||||
* Modify settings to make the feature actually useful. */
|
||||
|
||||
/* Send first probe after interval. */
|
||||
idle = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#elif defined(TCP_KEEPALIVE)
|
||||
/* Darwin/macOS uses TCP_KEEPALIVE in place of TCP_KEEPIDLE. */
|
||||
idle = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &idle, sizeof(idle))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPALIVE: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#else
|
||||
((void) interval); /* Avoid unused var warning for non Linux systems. */
|
||||
#endif
|
||||
|
||||
#ifdef TCP_KEEPINTVL
|
||||
/* Send next probes after the specified interval. Note that we set the
|
||||
* delay as interval / 3, as we send three probes before detecting
|
||||
* an error (see the next setsockopt call). */
|
||||
intvl = interval/3;
|
||||
if (intvl == 0) intvl = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TCP_KEEPCNT
|
||||
/* Consider the socket in error state after three we send three ACK
|
||||
* probes without getting a reply. */
|
||||
cnt = 3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ANET_OK;
|
||||
@@ -239,7 +306,11 @@ int anetRecvTimeout(char *err, int fd, long long ms) {
|
||||
*
|
||||
* 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. */
|
||||
* into a validating / normalizing function.
|
||||
*
|
||||
* If the flag ANET_PREFER_IPV4 is set, IPv4 is preferred over IPv6.
|
||||
* If the flag ANET_PREFER_IPV6 is set, IPv6 is preferred over IPv4.
|
||||
* */
|
||||
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
|
||||
int flags)
|
||||
{
|
||||
@@ -249,9 +320,20 @@ int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
|
||||
memset(&hints,0,sizeof(hints));
|
||||
if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
if (flags & ANET_PREFER_IPV4 && !(flags & ANET_PREFER_IPV6)) {
|
||||
hints.ai_family = AF_INET;
|
||||
} else if (flags & ANET_PREFER_IPV6 && !(flags & ANET_PREFER_IPV4)) {
|
||||
hints.ai_family = AF_INET6;
|
||||
}
|
||||
hints.ai_socktype = SOCK_STREAM; /* specify socktype to avoid dups */
|
||||
|
||||
if ((rv = getaddrinfo(host, NULL, &hints, &info)) != 0) {
|
||||
rv = getaddrinfo(host, NULL, &hints, &info);
|
||||
if (rv != 0 && hints.ai_family != AF_UNSPEC) {
|
||||
/* Try the other IP version. */
|
||||
hints.ai_family = (hints.ai_family == AF_INET) ? AF_INET6 : AF_INET;
|
||||
rv = getaddrinfo(host, NULL, &hints, &info);
|
||||
}
|
||||
if (rv != 0) {
|
||||
anetSetError(err, "%s", gai_strerror(rv));
|
||||
return ANET_ERR;
|
||||
}
|
||||
@@ -417,13 +499,16 @@ 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) {
|
||||
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog, mode_t perm) {
|
||||
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);
|
||||
@@ -467,7 +552,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) == ANET_ERR) s = ANET_ERR;
|
||||
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog,0) == ANET_ERR) s = ANET_ERR;
|
||||
goto end;
|
||||
}
|
||||
if (p == NULL) {
|
||||
@@ -508,10 +593,8 @@ int anetUnixServer(char *err, char *path, mode_t perm, int backlog)
|
||||
memset(&sa,0,sizeof(sa));
|
||||
sa.sun_family = AF_LOCAL;
|
||||
redis_strlcpy(sa.sun_path,path,sizeof(sa.sun_path));
|
||||
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog) == ANET_ERR)
|
||||
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog,perm) == ANET_ERR)
|
||||
return ANET_ERR;
|
||||
if (perm)
|
||||
chmod(sa.sun_path, perm);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+5
-24
@@ -1,31 +1,10 @@
|
||||
/* anet.c -- Basic TCP socket stuff made a bit less boring
|
||||
*
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2006-Present, Redis Ltd.
|
||||
* 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.
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*/
|
||||
|
||||
#ifndef ANET_H
|
||||
@@ -40,6 +19,8 @@
|
||||
/* Flags used with certain functions. */
|
||||
#define ANET_NONE 0
|
||||
#define ANET_IP_ONLY (1<<0)
|
||||
#define ANET_PREFER_IPV4 (1<<1)
|
||||
#define ANET_PREFER_IPV6 (1<<2)
|
||||
|
||||
#if defined(__sun) || defined(_AIX)
|
||||
#define AF_LOCAL AF_UNIX
|
||||
|
||||
@@ -1,30 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* 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.
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
@@ -51,6 +30,13 @@ aofManifest *aofLoadManifestFromFile(sds am_filepath);
|
||||
void aofManifestFreeAndUpdate(aofManifest *am);
|
||||
void aof_background_fsync_and_close(int fd);
|
||||
|
||||
/* When we call 'startAppendOnly', we will create a temp INCR AOF, and rename
|
||||
* it to the real INCR AOF name when the AOFRW is done, so if want to know the
|
||||
* accurate start offset of the INCR AOF, we need to record it when we create
|
||||
* the temp INCR AOF. This variable is used to record the start offset, and
|
||||
* set the start offset of the real INCR AOF when the AOFRW is done. */
|
||||
static long long tempIncAofStartReplOffset = 0;
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* AOF Manifest file implementation.
|
||||
*
|
||||
@@ -94,10 +80,15 @@ void aof_background_fsync_and_close(int fd);
|
||||
#define AOF_MANIFEST_KEY_FILE_NAME "file"
|
||||
#define AOF_MANIFEST_KEY_FILE_SEQ "seq"
|
||||
#define AOF_MANIFEST_KEY_FILE_TYPE "type"
|
||||
#define AOF_MANIFEST_KEY_FILE_STARTOFFSET "startoffset"
|
||||
#define AOF_MANIFEST_KEY_FILE_ENDOFFSET "endoffset"
|
||||
|
||||
/* Create an empty aofInfo. */
|
||||
aofInfo *aofInfoCreate(void) {
|
||||
return zcalloc(sizeof(aofInfo));
|
||||
aofInfo *ai = zcalloc(sizeof(aofInfo));
|
||||
ai->start_offset = -1;
|
||||
ai->end_offset = -1;
|
||||
return ai;
|
||||
}
|
||||
|
||||
/* Free the aofInfo structure (pointed to by ai) and its embedded file_name. */
|
||||
@@ -114,20 +105,33 @@ aofInfo *aofInfoDup(aofInfo *orig) {
|
||||
ai->file_name = sdsdup(orig->file_name);
|
||||
ai->file_seq = orig->file_seq;
|
||||
ai->file_type = orig->file_type;
|
||||
ai->start_offset = orig->start_offset;
|
||||
ai->end_offset = orig->end_offset;
|
||||
return ai;
|
||||
}
|
||||
|
||||
/* Format aofInfo as a string and it will be a line in the manifest. */
|
||||
/* Format aofInfo as a string and it will be a line in the manifest.
|
||||
*
|
||||
* When update this format, make sure to update redis-check-aof as well. */
|
||||
sds aofInfoFormat(sds buf, aofInfo *ai) {
|
||||
sds filename_repr = NULL;
|
||||
|
||||
if (sdsneedsrepr(ai->file_name))
|
||||
filename_repr = sdscatrepr(sdsempty(), ai->file_name, sdslen(ai->file_name));
|
||||
|
||||
sds ret = sdscatprintf(buf, "%s %s %s %lld %s %c\n",
|
||||
sds ret = sdscatprintf(buf, "%s %s %s %lld %s %c",
|
||||
AOF_MANIFEST_KEY_FILE_NAME, filename_repr ? filename_repr : ai->file_name,
|
||||
AOF_MANIFEST_KEY_FILE_SEQ, ai->file_seq,
|
||||
AOF_MANIFEST_KEY_FILE_TYPE, ai->file_type);
|
||||
|
||||
if (ai->start_offset != -1) {
|
||||
ret = sdscatprintf(ret, " %s %lld", AOF_MANIFEST_KEY_FILE_STARTOFFSET, ai->start_offset);
|
||||
if (ai->end_offset != -1) {
|
||||
ret = sdscatprintf(ret, " %s %lld", AOF_MANIFEST_KEY_FILE_ENDOFFSET, ai->end_offset);
|
||||
}
|
||||
}
|
||||
|
||||
ret = sdscatlen(ret, "\n", 1);
|
||||
sdsfree(filename_repr);
|
||||
|
||||
return ret;
|
||||
@@ -323,6 +327,10 @@ aofManifest *aofLoadManifestFromFile(sds am_filepath) {
|
||||
ai->file_seq = atoll(argv[i+1]);
|
||||
} else if (!strcasecmp(argv[i], AOF_MANIFEST_KEY_FILE_TYPE)) {
|
||||
ai->file_type = (argv[i+1])[0];
|
||||
} else if (!strcasecmp(argv[i], AOF_MANIFEST_KEY_FILE_STARTOFFSET)) {
|
||||
ai->start_offset = atoll(argv[i+1]);
|
||||
} else if (!strcasecmp(argv[i], AOF_MANIFEST_KEY_FILE_ENDOFFSET)) {
|
||||
ai->end_offset = atoll(argv[i+1]);
|
||||
}
|
||||
/* else if (!strcasecmp(argv[i], AOF_MANIFEST_KEY_OTHER)) {} */
|
||||
}
|
||||
@@ -452,12 +460,13 @@ sds getNewBaseFileNameAndMarkPreAsHistory(aofManifest *am) {
|
||||
* for example:
|
||||
* appendonly.aof.1.incr.aof
|
||||
*/
|
||||
sds getNewIncrAofName(aofManifest *am) {
|
||||
sds getNewIncrAofName(aofManifest *am, long long start_reploff) {
|
||||
aofInfo *ai = aofInfoCreate();
|
||||
ai->file_type = AOF_FILE_TYPE_INCR;
|
||||
ai->file_name = sdscatprintf(sdsempty(), "%s.%lld%s%s", server.aof_filename,
|
||||
++am->curr_incr_file_seq, INCR_FILE_SUFFIX, AOF_FORMAT_SUFFIX);
|
||||
ai->file_seq = am->curr_incr_file_seq;
|
||||
ai->start_offset = start_reploff;
|
||||
listAddNodeTail(am->incr_aof_list, ai);
|
||||
am->dirty = 1;
|
||||
return ai->file_name;
|
||||
@@ -475,7 +484,7 @@ sds getLastIncrAofName(aofManifest *am) {
|
||||
|
||||
/* If 'incr_aof_list' is empty, just create a new one. */
|
||||
if (!listLength(am->incr_aof_list)) {
|
||||
return getNewIncrAofName(am);
|
||||
return getNewIncrAofName(am, server.master_repl_offset);
|
||||
}
|
||||
|
||||
/* Or return the last one. */
|
||||
@@ -800,10 +809,11 @@ int openNewIncrAofForAppend(void) {
|
||||
if (server.aof_state == AOF_WAIT_REWRITE) {
|
||||
/* Use a temporary INCR AOF file to accumulate data during AOF_WAIT_REWRITE. */
|
||||
new_aof_name = getTempIncrAofName();
|
||||
tempIncAofStartReplOffset = server.master_repl_offset;
|
||||
} else {
|
||||
/* Dup a temp aof_manifest to modify. */
|
||||
temp_am = aofManifestDup(server.aof_manifest);
|
||||
new_aof_name = sdsdup(getNewIncrAofName(temp_am));
|
||||
new_aof_name = sdsdup(getNewIncrAofName(temp_am, server.master_repl_offset));
|
||||
}
|
||||
sds new_aof_filepath = makePath(server.aof_dirname, new_aof_name);
|
||||
newfd = open(new_aof_filepath, O_WRONLY|O_TRUNC|O_CREAT, 0644);
|
||||
@@ -833,7 +843,7 @@ int openNewIncrAofForAppend(void) {
|
||||
* is already synced at this point so fsync doesn't matter. */
|
||||
if (server.aof_fd != -1) {
|
||||
aof_background_fsync_and_close(server.aof_fd);
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
server.aof_last_fsync = server.mstime;
|
||||
}
|
||||
server.aof_fd = newfd;
|
||||
|
||||
@@ -852,6 +862,50 @@ cleanup:
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
/* When we close gracefully the AOF file, we have the chance to persist the
|
||||
* end replication offset of current INCR AOF. */
|
||||
void updateCurIncrAofEndOffset(void) {
|
||||
if (server.aof_state != AOF_ON) return;
|
||||
serverAssert(server.aof_manifest != NULL);
|
||||
|
||||
if (listLength(server.aof_manifest->incr_aof_list) == 0) return;
|
||||
aofInfo *ai = listNodeValue(listLast(server.aof_manifest->incr_aof_list));
|
||||
ai->end_offset = server.master_repl_offset;
|
||||
server.aof_manifest->dirty = 1;
|
||||
/* It doesn't matter if the persistence fails since this information is not
|
||||
* critical, we can get an approximate value by start offset plus file size. */
|
||||
persistAofManifest(server.aof_manifest);
|
||||
}
|
||||
|
||||
/* After loading AOF data, we need to update the `server.master_repl_offset`
|
||||
* based on the information of the last INCR AOF, to avoid the rollback of
|
||||
* the start offset of new INCR AOF. */
|
||||
void updateReplOffsetAndResetEndOffset(void) {
|
||||
if (server.aof_state != AOF_ON) return;
|
||||
serverAssert(server.aof_manifest != NULL);
|
||||
|
||||
/* If the INCR file has an end offset, we directly use it, and clear it
|
||||
* to avoid the next time we load the manifest file, we will use the same
|
||||
* offset, but the real offset may have advanced. */
|
||||
if (listLength(server.aof_manifest->incr_aof_list) == 0) return;
|
||||
aofInfo *ai = listNodeValue(listLast(server.aof_manifest->incr_aof_list));
|
||||
if (ai->end_offset != -1) {
|
||||
server.master_repl_offset = ai->end_offset;
|
||||
ai->end_offset = -1;
|
||||
server.aof_manifest->dirty = 1;
|
||||
/* We must update the end offset of INCR file correctly, otherwise we
|
||||
* may keep wrong information in the manifest file, since we continue
|
||||
* to append data to the same INCR file. */
|
||||
if (persistAofManifest(server.aof_manifest) != AOF_OK)
|
||||
exit(1);
|
||||
} else {
|
||||
/* If the INCR file doesn't have an end offset, we need to calculate
|
||||
* the replication offset by the start offset plus the file size. */
|
||||
server.master_repl_offset = (ai->start_offset == -1 ? 0 : ai->start_offset) +
|
||||
getAppendOnlyFileSize(ai->file_name, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Whether to limit the execution of Background AOF rewrite.
|
||||
*
|
||||
* At present, if AOFRW fails, redis will automatically retry. If it continues
|
||||
@@ -954,9 +1008,10 @@ void stopAppendOnly(void) {
|
||||
if (redis_fsync(server.aof_fd) == -1) {
|
||||
serverLog(LL_WARNING,"Fail to fsync the AOF file: %s",strerror(errno));
|
||||
} else {
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
server.aof_last_fsync = server.mstime;
|
||||
}
|
||||
close(server.aof_fd);
|
||||
updateCurIncrAofEndOffset();
|
||||
|
||||
server.aof_fd = -1;
|
||||
server.aof_selected_db = -1;
|
||||
@@ -976,18 +1031,6 @@ void stopAppendOnly(void) {
|
||||
int startAppendOnly(void) {
|
||||
serverAssert(server.aof_state == AOF_OFF);
|
||||
|
||||
/* Wait for all bio jobs related to AOF to drain. This prevents a race
|
||||
* between updates to `fsynced_reploff_pending` of the worker thread, belonging
|
||||
* to the previous AOF, and the new one. This concern is specific for a full
|
||||
* sync scenario where we don't wanna risk the ACKed replication offset
|
||||
* jumping backwards or forward when switching to a different master. */
|
||||
bioDrainWorker(BIO_AOF_FSYNC);
|
||||
|
||||
/* Set the initial repl_offset, which will be applied to fsynced_reploff
|
||||
* when AOFRW finishes (after possibly being updated by a bio thread) */
|
||||
atomicSet(server.fsynced_reploff_pending, server.master_repl_offset);
|
||||
server.fsynced_reploff = 0;
|
||||
|
||||
server.aof_state = AOF_WAIT_REWRITE;
|
||||
if (hasActiveChildProcess() && server.child_type != CHILD_TYPE_AOF) {
|
||||
server.aof_rewrite_scheduled = 1;
|
||||
@@ -1010,7 +1053,7 @@ int startAppendOnly(void) {
|
||||
return C_ERR;
|
||||
}
|
||||
}
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
server.aof_last_fsync = server.mstime;
|
||||
/* 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);
|
||||
@@ -1028,6 +1071,29 @@ int startAppendOnly(void) {
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
void startAppendOnlyWithRetry(void) {
|
||||
unsigned int tries, max_tries = 10;
|
||||
for (tries = 0; tries < max_tries; ++tries) {
|
||||
if (startAppendOnly() == C_OK)
|
||||
break;
|
||||
serverLog(LL_WARNING, "Failed to enable AOF! Trying it again in one second.");
|
||||
sleep(1);
|
||||
}
|
||||
if (tries == max_tries) {
|
||||
serverLog(LL_WARNING, "FATAL: AOF can't be turned on. Exiting now.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called after "appendonly" config is changed. */
|
||||
void applyAppendOnlyConfig(void) {
|
||||
if (!server.aof_enabled && server.aof_state != AOF_OFF) {
|
||||
stopAppendOnly();
|
||||
} else if (server.aof_enabled && server.aof_state == AOF_OFF) {
|
||||
startAppendOnlyWithRetry();
|
||||
}
|
||||
}
|
||||
|
||||
/* This is a wrapper to the write syscall in order to retry on short writes
|
||||
* or if the syscall gets interrupted. It could look strange that we retry
|
||||
* on short writes given that we are writing to a block device: normally if
|
||||
@@ -1079,28 +1145,34 @@ void flushAppendOnlyFile(int force) {
|
||||
mstime_t latency;
|
||||
|
||||
if (sdslen(server.aof_buf) == 0) {
|
||||
/* Check if we need to do fsync even the aof buffer is empty,
|
||||
* because previously in AOF_FSYNC_EVERYSEC mode, fsync is
|
||||
* called only when aof buffer is not empty, so if users
|
||||
* stop write commands before fsync called in one second,
|
||||
* the data in page cache cannot be flushed in time. */
|
||||
if (server.aof_fsync == AOF_FSYNC_EVERYSEC &&
|
||||
server.aof_last_incr_fsync_offset != server.aof_last_incr_size &&
|
||||
server.unixtime > server.aof_last_fsync &&
|
||||
!(sync_in_progress = aofFsyncInProgress())) {
|
||||
goto try_fsync;
|
||||
|
||||
/* Check if we need to do fsync even the aof buffer is empty,
|
||||
* the reason is described in the previous AOF_FSYNC_EVERYSEC block,
|
||||
* and AOF_FSYNC_ALWAYS is also checked here to handle a case where
|
||||
* aof_fsync is changed from everysec to always. */
|
||||
} else if (server.aof_fsync == AOF_FSYNC_ALWAYS &&
|
||||
server.aof_last_incr_fsync_offset != server.aof_last_incr_size)
|
||||
{
|
||||
goto try_fsync;
|
||||
if (server.aof_last_incr_fsync_offset == server.aof_last_incr_size) {
|
||||
/* All data is fsync'd already: Update fsynced_reploff_pending just in case.
|
||||
* This is needed to avoid a WAITAOF hang in case a module used RM_Call
|
||||
* with the NO_AOF flag, in which case master_repl_offset will increase but
|
||||
* fsynced_reploff_pending won't be updated (because there's no reason, from
|
||||
* the AOF POV, to call fsync) and then WAITAOF may wait on the higher offset
|
||||
* (which contains data that was only propagated to replicas, and not to AOF) */
|
||||
if (!aofFsyncInProgress())
|
||||
atomicSet(server.fsynced_reploff_pending, server.master_repl_offset);
|
||||
} else {
|
||||
return;
|
||||
/* Check if we need to do fsync even the aof buffer is empty,
|
||||
* because previously in AOF_FSYNC_EVERYSEC mode, fsync is
|
||||
* called only when aof buffer is not empty, so if users
|
||||
* stop write commands before fsync called in one second,
|
||||
* the data in page cache cannot be flushed in time. */
|
||||
if (server.aof_fsync == AOF_FSYNC_EVERYSEC &&
|
||||
server.mstime - server.aof_last_fsync >= 1000 &&
|
||||
!(sync_in_progress = aofFsyncInProgress()))
|
||||
goto try_fsync;
|
||||
|
||||
/* Check if we need to do fsync even the aof buffer is empty,
|
||||
* the reason is described in the previous AOF_FSYNC_EVERYSEC block,
|
||||
* and AOF_FSYNC_ALWAYS is also checked here to handle a case where
|
||||
* aof_fsync is changed from everysec to always. */
|
||||
if (server.aof_fsync == AOF_FSYNC_ALWAYS)
|
||||
goto try_fsync;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (server.aof_fsync == AOF_FSYNC_EVERYSEC)
|
||||
@@ -1114,9 +1186,9 @@ void flushAppendOnlyFile(int force) {
|
||||
if (server.aof_flush_postponed_start == 0) {
|
||||
/* No previous write postponing, remember that we are
|
||||
* postponing the flush and return. */
|
||||
server.aof_flush_postponed_start = server.unixtime;
|
||||
server.aof_flush_postponed_start = server.mstime;
|
||||
return;
|
||||
} else if (server.unixtime - server.aof_flush_postponed_start < 2) {
|
||||
} else if (server.mstime - server.aof_flush_postponed_start < 2000) {
|
||||
/* We were already waiting for fsync to finish, but for less
|
||||
* than two seconds this is still ok. Postpone again. */
|
||||
return;
|
||||
@@ -1265,15 +1337,15 @@ try_fsync:
|
||||
latencyEndMonitor(latency);
|
||||
latencyAddSampleIfNeeded("aof-fsync-always",latency);
|
||||
server.aof_last_incr_fsync_offset = server.aof_last_incr_size;
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
server.aof_last_fsync = server.mstime;
|
||||
atomicSet(server.fsynced_reploff_pending, server.master_repl_offset);
|
||||
} else if (server.aof_fsync == AOF_FSYNC_EVERYSEC &&
|
||||
server.unixtime > server.aof_last_fsync) {
|
||||
server.mstime - server.aof_last_fsync >= 1000) {
|
||||
if (!sync_in_progress) {
|
||||
aof_background_fsync(server.aof_fd);
|
||||
server.aof_last_incr_fsync_offset = server.aof_last_incr_size;
|
||||
}
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
server.aof_last_fsync = server.mstime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1859,6 +1931,7 @@ int rewriteSetObject(rio *r, robj *key, robj *o) {
|
||||
!rioWriteBulkString(r,"SADD",4) ||
|
||||
!rioWriteBulkObject(r,key))
|
||||
{
|
||||
setTypeReleaseIterator(si);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1962,19 +2035,21 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
|
||||
*
|
||||
* The function returns 0 on error, non-zero on success. */
|
||||
static int rioWriteHashIteratorCursor(rio *r, hashTypeIterator *hi, int what) {
|
||||
if (hi->encoding == OBJ_ENCODING_LISTPACK) {
|
||||
if ((hi->encoding == OBJ_ENCODING_LISTPACK) || (hi->encoding == OBJ_ENCODING_LISTPACK_EX)) {
|
||||
unsigned char *vstr = NULL;
|
||||
unsigned int vlen = UINT_MAX;
|
||||
long long vll = LLONG_MAX;
|
||||
|
||||
hashTypeCurrentFromListpack(hi, what, &vstr, &vlen, &vll);
|
||||
hashTypeCurrentFromListpack(hi, what, &vstr, &vlen, &vll, NULL);
|
||||
if (vstr)
|
||||
return rioWriteBulkString(r, (char*)vstr, vlen);
|
||||
else
|
||||
return rioWriteBulkLongLong(r, vll);
|
||||
} else if (hi->encoding == OBJ_ENCODING_HT) {
|
||||
sds value = hashTypeCurrentFromHashTable(hi, what);
|
||||
return rioWriteBulkString(r, value, sdslen(value));
|
||||
char *str;
|
||||
size_t len;
|
||||
hashTypeCurrentFromHashTable(hi, what, &str, &len, NULL);
|
||||
return rioWriteBulkString(r, str, len);
|
||||
}
|
||||
|
||||
serverPanic("Unknown hash encoding");
|
||||
@@ -1984,37 +2059,60 @@ static int rioWriteHashIteratorCursor(rio *r, hashTypeIterator *hi, int what) {
|
||||
/* Emit the commands needed to rebuild a hash object.
|
||||
* The function returns 0 on error, 1 on success. */
|
||||
int rewriteHashObject(rio *r, robj *key, robj *o) {
|
||||
int res = 0; /*fail*/
|
||||
|
||||
hashTypeIterator *hi;
|
||||
long long count = 0, items = hashTypeLength(o);
|
||||
long long count = 0, items = hashTypeLength(o, 0);
|
||||
|
||||
int isHFE = hashTypeGetMinExpire(o, 0) != EB_EXPIRE_TIME_INVALID;
|
||||
hi = hashTypeInitIterator(o);
|
||||
while (hashTypeNext(hi) != C_ERR) {
|
||||
if (count == 0) {
|
||||
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 (!isHFE) {
|
||||
while (hashTypeNext(hi, 0) != C_ERR) {
|
||||
if (count == 0) {
|
||||
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))
|
||||
goto reHashEnd;
|
||||
}
|
||||
|
||||
if (!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY) ||
|
||||
!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_VALUE))
|
||||
goto reHashEnd;
|
||||
|
||||
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
|
||||
items--;
|
||||
}
|
||||
} else {
|
||||
while (hashTypeNext(hi, 0) != C_ERR) {
|
||||
|
||||
char hmsetCmd[] = "*4\r\n$5\r\nHMSET\r\n";
|
||||
if ( (!rioWrite(r, hmsetCmd, sizeof(hmsetCmd) - 1)) ||
|
||||
(!rioWriteBulkObject(r, key)) ||
|
||||
(!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY)) ||
|
||||
(!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_VALUE)) )
|
||||
goto reHashEnd;
|
||||
|
||||
if (hi->expire_time != EB_EXPIRE_TIME_INVALID) {
|
||||
char cmd[] = "*6\r\n$10\r\nHPEXPIREAT\r\n";
|
||||
if ( (!rioWrite(r, cmd, sizeof(cmd) - 1)) ||
|
||||
(!rioWriteBulkObject(r, key)) ||
|
||||
(!rioWriteBulkLongLong(r, hi->expire_time)) ||
|
||||
(!rioWriteBulkString(r, "FIELDS", 6)) ||
|
||||
(!rioWriteBulkString(r, "1", 1)) ||
|
||||
(!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY)) )
|
||||
goto reHashEnd;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY) ||
|
||||
!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_VALUE))
|
||||
{
|
||||
hashTypeReleaseIterator(hi);
|
||||
return 0;
|
||||
}
|
||||
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
|
||||
items--;
|
||||
}
|
||||
|
||||
hashTypeReleaseIterator(hi);
|
||||
res = 1; /* success */
|
||||
|
||||
return 1;
|
||||
reHashEnd:
|
||||
hashTypeReleaseIterator(hi);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Helper for rewriteStreamObject() that generates a bulk string into the
|
||||
@@ -2245,11 +2343,11 @@ werr:
|
||||
}
|
||||
|
||||
int rewriteAppendOnlyFileRio(rio *aof) {
|
||||
dictIterator *di = NULL;
|
||||
dictEntry *de;
|
||||
int j;
|
||||
long key_count = 0;
|
||||
long long updated_time = 0;
|
||||
kvstoreIterator *kvs_it = NULL;
|
||||
|
||||
/* Record timestamp at the beginning of rewriting AOF. */
|
||||
if (server.aof_timestamp_enabled) {
|
||||
@@ -2262,17 +2360,16 @@ int rewriteAppendOnlyFileRio(rio *aof) {
|
||||
|
||||
for (j = 0; j < server.dbnum; j++) {
|
||||
char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";
|
||||
redisDb *db = server.db+j;
|
||||
dict *d = db->dict;
|
||||
if (dictSize(d) == 0) continue;
|
||||
di = dictGetSafeIterator(d);
|
||||
redisDb *db = server.db + j;
|
||||
if (kvstoreSize(db->keys) == 0) continue;
|
||||
|
||||
/* SELECT the new DB */
|
||||
if (rioWrite(aof,selectcmd,sizeof(selectcmd)-1) == 0) goto werr;
|
||||
if (rioWriteBulkLongLong(aof,j) == 0) goto werr;
|
||||
|
||||
kvs_it = kvstoreIteratorInit(db->keys);
|
||||
/* Iterate this DB writing every entry */
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
while((de = kvstoreIteratorNext(kvs_it)) != NULL) {
|
||||
sds keystr;
|
||||
robj key, *o;
|
||||
long long expiretime;
|
||||
@@ -2337,13 +2434,12 @@ int rewriteAppendOnlyFileRio(rio *aof) {
|
||||
if (server.rdb_key_save_delay)
|
||||
debugDelay(server.rdb_key_save_delay);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
di = NULL;
|
||||
kvstoreIteratorRelease(kvs_it);
|
||||
}
|
||||
return C_OK;
|
||||
|
||||
werr:
|
||||
if (di) dictReleaseIterator(di);
|
||||
if (kvs_it) kvstoreIteratorRelease(kvs_it);
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
@@ -2454,7 +2550,23 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
server.aof_lastbgrewrite_status = C_ERR;
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
if (server.aof_state == AOF_WAIT_REWRITE) {
|
||||
/* Wait for all bio jobs related to AOF to drain. This prevents a race
|
||||
* between updates to `fsynced_reploff_pending` of the worker thread, belonging
|
||||
* to the previous AOF, and the new one. This concern is specific for a full
|
||||
* sync scenario where we don't wanna risk the ACKed replication offset
|
||||
* jumping backwards or forward when switching to a different master. */
|
||||
bioDrainWorker(BIO_AOF_FSYNC);
|
||||
|
||||
/* Set the initial repl_offset, which will be applied to fsynced_reploff
|
||||
* when AOFRW finishes (after possibly being updated by a bio thread) */
|
||||
atomicSet(server.fsynced_reploff_pending, server.master_repl_offset);
|
||||
server.fsynced_reploff = 0;
|
||||
}
|
||||
|
||||
server.stat_aof_rewrites++;
|
||||
|
||||
if ((childpid = redisFork(CHILD_TYPE_AOF)) == 0) {
|
||||
char tmpfile[256];
|
||||
|
||||
@@ -2626,7 +2738,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
|
||||
sds temp_incr_aof_name = getTempIncrAofName();
|
||||
sds temp_incr_filepath = makePath(server.aof_dirname, temp_incr_aof_name);
|
||||
/* Get next new incr aof name. */
|
||||
sds new_incr_filename = getNewIncrAofName(temp_am);
|
||||
sds new_incr_filename = getNewIncrAofName(temp_am, tempIncAofStartReplOffset);
|
||||
new_incr_filepath = makePath(server.aof_dirname, new_incr_filename);
|
||||
latencyStartMonitor(latency);
|
||||
if (rename(temp_incr_filepath, new_incr_filepath) == -1) {
|
||||
|
||||
+5
-26
@@ -1,37 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* 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.
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*/
|
||||
|
||||
const char *ascii_logo =
|
||||
" _._ \n"
|
||||
" _.-``__ ''-._ \n"
|
||||
" _.-`` `. `_. ''-._ Redis %s (%s/%d) %s bit\n"
|
||||
" .-`` .-```. ```\\/ _.,_ ''-._ \n"
|
||||
" _.-`` `. `_. ''-._ Redis Community Edition \n"
|
||||
" .-`` .-```. ```\\/ _.,_ ''-._ %s (%s/%d) %s bit\n"
|
||||
" ( ' , .-` | `, ) Running in %s mode\n"
|
||||
" |`-._`-...-` __...-.``-._|'` _.-'| Port: %d\n"
|
||||
" | `-._ `._ / _.-' | PID: %ld\n"
|
||||
|
||||
+42
-26
@@ -1,16 +1,41 @@
|
||||
/* This file implements atomic counters using c11 _Atomic, __atomic or __sync
|
||||
* macros if available, otherwise we will throw an error when compile.
|
||||
*
|
||||
* The exported interface is composed of three macros:
|
||||
* The exported interface is composed of the following macros:
|
||||
*
|
||||
* atomicIncr(var,count) -- Increment the atomic counter
|
||||
* atomicGetIncr(var,oldvalue_var,count) -- Get and increment the atomic counter
|
||||
* atomicIncrGet(var,newvalue_var,count) -- Increment and get the atomic counter new value
|
||||
* atomicDecr(var,count) -- Decrement the atomic counter
|
||||
* atomicGet(var,dstvar) -- Fetch the atomic counter value
|
||||
* atomicSet(var,value) -- Set the atomic counter value
|
||||
* atomicGetWithSync(var,value) -- 'atomicGet' with inter-thread synchronization
|
||||
* atomicSetWithSync(var,value) -- 'atomicSet' with inter-thread synchronization
|
||||
*
|
||||
*
|
||||
* Atomic operations on flags.
|
||||
* Flag type can be int, long, long long or their unsigned counterparts.
|
||||
* The value of the flag can be 1 or 0.
|
||||
*
|
||||
* atomicFlagGetSet(var,oldvalue_var) -- Get and set the atomic counter value
|
||||
*
|
||||
* NOTE1: __atomic* and _Atomic implementations can be actually elaborated to support any value by changing the
|
||||
* hardcoded new value passed to __atomic_exchange* from 1 to @param count
|
||||
* i.e oldvalue_var = atomic_exchange_explicit(&var, count).
|
||||
* However, in order to be compatible with the __sync functions family, we can use only 0 and 1.
|
||||
* The only exchange alternative suggested by __sync is __sync_lock_test_and_set,
|
||||
* But as described by the gnu manual for __sync_lock_test_and_set():
|
||||
* https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
||||
* "A target may support reduced functionality here by which the only valid value to store is the immediate constant 1. The exact value
|
||||
* actually stored in *ptr is implementation defined."
|
||||
* Hence, we can't rely on it for a any value other than 1.
|
||||
* We eventually chose to implement this method with __sync_val_compare_and_swap since it satisfies functionality needed for atomicFlagGetSet
|
||||
* (if the flag was 0 -> set to 1, if it's already 1 -> do nothing, but the final result is that the flag is set),
|
||||
* and also it has a full barrier (__sync_lock_test_and_set has acquire barrier).
|
||||
*
|
||||
* NOTE2: Unlike other atomic type, which aren't guaranteed to be lock free, c11 atomic_flag does.
|
||||
* To check whether a type is lock free, atomic_is_lock_free() can be used.
|
||||
* It can be considered to limit the flag type to atomic_flag to improve performance.
|
||||
*
|
||||
* Never use return value from the macros, instead use the AtomicGetIncr()
|
||||
* if you need to get the current value and increment it atomically, like
|
||||
* in the following example:
|
||||
@@ -21,32 +46,11 @@
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2015-Present, Redis Ltd.
|
||||
* 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.
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
@@ -93,6 +97,8 @@
|
||||
#define atomicGetIncr(var,oldvalue_var,count) do { \
|
||||
oldvalue_var = atomic_fetch_add_explicit(&var,(count),memory_order_relaxed); \
|
||||
} while(0)
|
||||
#define atomicIncrGet(var, newvalue_var, count) \
|
||||
newvalue_var = atomicIncr(var,count) + count
|
||||
#define atomicDecr(var,count) atomic_fetch_sub_explicit(&var,(count),memory_order_relaxed)
|
||||
#define atomicGet(var,dstvar) do { \
|
||||
dstvar = atomic_load_explicit(&var,memory_order_relaxed); \
|
||||
@@ -103,6 +109,8 @@
|
||||
} while(0)
|
||||
#define atomicSetWithSync(var,value) \
|
||||
atomic_store_explicit(&var,value,memory_order_seq_cst)
|
||||
#define atomicFlagGetSet(var,oldvalue_var) \
|
||||
oldvalue_var = atomic_exchange_explicit(&var,1,memory_order_relaxed)
|
||||
#define REDIS_ATOMIC_API "c11-builtin"
|
||||
|
||||
#elif !defined(__ATOMIC_VAR_FORCE_SYNC_MACROS) && \
|
||||
@@ -111,6 +119,8 @@
|
||||
/* Implementation using __atomic macros. */
|
||||
|
||||
#define atomicIncr(var,count) __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED)
|
||||
#define atomicIncrGet(var, newvalue_var, count) \
|
||||
newvalue_var = __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED)
|
||||
#define atomicGetIncr(var,oldvalue_var,count) do { \
|
||||
oldvalue_var = __atomic_fetch_add(&var,(count),__ATOMIC_RELAXED); \
|
||||
} while(0)
|
||||
@@ -124,12 +134,16 @@
|
||||
} while(0)
|
||||
#define atomicSetWithSync(var,value) \
|
||||
__atomic_store_n(&var,value,__ATOMIC_SEQ_CST)
|
||||
#define atomicFlagGetSet(var,oldvalue_var) \
|
||||
oldvalue_var = __atomic_exchange_n(&var,1,__ATOMIC_RELAXED)
|
||||
#define REDIS_ATOMIC_API "atomic-builtin"
|
||||
|
||||
#elif defined(HAVE_ATOMIC)
|
||||
/* Implementation using __sync macros. */
|
||||
|
||||
#define atomicIncr(var,count) __sync_add_and_fetch(&var,(count))
|
||||
#define atomicIncrGet(var, newvalue_var, count) \
|
||||
newvalue_var = __sync_add_and_fetch(&var,(count))
|
||||
#define atomicGetIncr(var,oldvalue_var,count) do { \
|
||||
oldvalue_var = __sync_fetch_and_add(&var,(count)); \
|
||||
} while(0)
|
||||
@@ -149,6 +163,8 @@
|
||||
ANNOTATE_HAPPENS_BEFORE(&var); \
|
||||
while(!__sync_bool_compare_and_swap(&var,var,value,__sync_synchronize)); \
|
||||
} while(0)
|
||||
#define atomicFlagGetSet(var,oldvalue_var) \
|
||||
oldvalue_var = __sync_val_compare_and_swap(&var,0,1)
|
||||
#define REDIS_ATOMIC_API "sync-builtin"
|
||||
|
||||
#else
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user