Compare commits
824 Commits
Author | SHA1 | Date | |
---|---|---|---|
332ffb0603 | |||
90df3d1805 | |||
bda1bb6ab4 | |||
d4e1f71e3c | |||
adf6aa1d6c | |||
cb1f9f5a44 | |||
83ae105edb | |||
7642ccc99e | |||
cb1ec7dc96 | |||
09b9483ddf | |||
27a8ae309f | |||
3df7c942d7 | |||
6a4d69afc5 | |||
0a11132b07 | |||
cb9f0cb968 | |||
b1f30bb40f | |||
4ef04b8339 | |||
e581f29b42 | |||
a42f115f79 | |||
5ce1a596cf | |||
21db7b6c5b | |||
e15a2900e7 | |||
140a21c8b3 | |||
6d0c568aaa | |||
c96abcef1c | |||
4a9b0b9dfd | |||
8837d5e784 | |||
242c945400 | |||
feab4dee0f | |||
8175829b4b | |||
4c66a7561e | |||
111475e65c | |||
45061b0b14 | |||
1bb847a51c | |||
ce9feeafdf | |||
415f0b3e6d | |||
94431756ff | |||
5f6c54bb90 | |||
f994a41845 | |||
f3760318b7 | |||
c2362e6875 | |||
d91b24723d | |||
2d28b7d4ff | |||
86d3fc8621 | |||
422c7ff855 | |||
1c7ccbae12 | |||
ed2a81f115 | |||
7ed8ac208a | |||
aa12098cb5 | |||
5d4bb90703 | |||
fad9f89846 | |||
527308a049 | |||
68b318ab97 | |||
65bae2736d | |||
a923caec0b | |||
4d7332c4ee | |||
4f3f1f3e4c | |||
2a954b5b5b | |||
b96b3b099f | |||
bb31612ebe | |||
d2c5ab1cc4 | |||
a01584ad9d | |||
9651f3823d | |||
0544cdedeb | |||
df0239ae68 | |||
c8bf2a0d82 | |||
4d96804b22 | |||
ce9db42c23 | |||
a844dbc587 | |||
28952cb0b0 | |||
fe29579755 | |||
b816f1fbda | |||
1320e4ddaf | |||
f4a7277d61 | |||
34d7b18c85 | |||
d72d516a92 | |||
f6c482c65d | |||
dec8a2b9ab | |||
a5bc39be55 | |||
c62ad5f466 | |||
effa115ed2 | |||
b9504fcd44 | |||
299bcd4b92 | |||
f56098dd4c | |||
e0187b2d8e | |||
31682f5f2d | |||
221087ffff | |||
c7fc4f0f8e | |||
2eece1d11a | |||
5ba05212ec | |||
9e457871b4 | |||
b5481262fb | |||
98e98f083e | |||
d60777b9cc | |||
8b5e42fe84 | |||
d89d152ad7 | |||
47a3736b24 | |||
1cd7cf340a | |||
3a25a05d9c | |||
d2acfd5d1f | |||
9dfcd8ea69 | |||
ee65f95fe3 | |||
b9cc6d7e23 | |||
1e97e5e536 | |||
440a1058b3 | |||
ff489515be | |||
536a502b60 | |||
db0ff4ecd1 | |||
e4aed56d72 | |||
1427a97dd9 | |||
d7318f97e6 | |||
d1a4cab134 | |||
8e7d310439 | |||
9347664622 | |||
4e240e4992 | |||
626e190d91 | |||
1933da7044 | |||
494f408320 | |||
299afc1425 | |||
0679b6399f | |||
862f8ea2d4 | |||
cae016564e | |||
e223f8fac2 | |||
c86fa8ad3b | |||
ba4cf75e0f | |||
9f98628709 | |||
0b2d816320 | |||
8b1c4ff081 | |||
fde0b717d3 | |||
228cdca250 | |||
18386ae66f | |||
5c5ea6dec1 | |||
2e1657167f | |||
c91a0d2a35 | |||
d465511812 | |||
6b98694caa | |||
d82e05f72d | |||
48622a24db | |||
94a274ced4 | |||
a7977cf1a5 | |||
3fffd2ed0b | |||
a0d5f37402 | |||
1b11ef006a | |||
155bbfa984 | |||
89aae8b344 | |||
28a0ba4768 | |||
708969c126 | |||
cc492bf1a3 | |||
901c21e499 | |||
16c6f08e2d | |||
6e9f27d63c | |||
5b282ce3b4 | |||
c7b2446164 | |||
0e43c618a5 | |||
af6100b90f | |||
a4cd248368 | |||
6e52723c8c | |||
a3ad92b9d9 | |||
b07827790f | |||
85e00a195c | |||
081dd2c4bb | |||
bbfa76a2c9 | |||
16a060131a | |||
6dabc68d29 | |||
ac0593bfb3 | |||
ac3e9eab25 | |||
6de426a574 | |||
c3a619f114 | |||
128545cc2b | |||
8152898c4e | |||
852bf452b6 | |||
b38e80f846 | |||
a383ac10d9 | |||
2c32f5c593 | |||
41cbee2cd2 | |||
1f8944852a | |||
95dd03b298 | |||
a0cfaff528 | |||
d6542383ed | |||
afed387bcf | |||
313949f087 | |||
e5c8e2e7f4 | |||
845bbc5208 | |||
7a26ff4de8 | |||
a1f3c40a2d | |||
1fdbc2bc22 | |||
0f4def2338 | |||
7939f7ad50 | |||
8bfc3c5ea6 | |||
bf46f3fe8f | |||
55141aa875 | |||
4e4792d6dc | |||
2aca019d84 | |||
864249b62d | |||
d1fb34694c | |||
d1fc31b894 | |||
f6bf2531bb | |||
681390f22f | |||
0ee675e554 | |||
7e1a60e61d | |||
4cd11fdbc7 | |||
05c7a26e3a | |||
6762978ddf | |||
658650cf24 | |||
8a662b35e1 | |||
3cd7c0194f | |||
05f29639e5 | |||
5c18ffdae2 | |||
d3cee46e93 | |||
94c589f696 | |||
490fbf4cb5 | |||
d46ce7eb63 | |||
169e225ccd | |||
ceb0c602c9 | |||
95722e3bbb | |||
1cde26771a | |||
cd3c9d879c | |||
398c4fbf99 | |||
e7b4a985b4 | |||
6f76fea188 | |||
f6b8e7f234 | |||
041fa3e340 | |||
408ee41990 | |||
ed4be773a2 | |||
fcdb57f31d | |||
47b3368f78 | |||
20ce498d23 | |||
354c9b412e | |||
1c08cedd8a | |||
e2121d809e | |||
9c1065bc1b | |||
27136bbce8 | |||
c3238b7e02 | |||
b11640b477 | |||
2453f5b717 | |||
65c5c5f894 | |||
64d3c99f99 | |||
6c0890594b | |||
fb271953f7 | |||
7080c5679f | |||
427db5bd59 | |||
c09eac49c9 | |||
ae6f87eb42 | |||
82b3c5e2f3 | |||
e41ba279e9 | |||
c259c918ac | |||
2f7438f0d5 | |||
37a7b362d8 | |||
c0f098a578 | |||
21404c23dd | |||
eeefc9cf4b | |||
24b2475b11 | |||
2defe6f597 | |||
f9dc460325 | |||
30b83b5ff0 | |||
003afc8b56 | |||
95a97a197a | |||
eccd8b3c0e | |||
a43046c921 | |||
b360fb9ca0 | |||
0bf185e143 | |||
da3d134be0 | |||
b4c4746d43 | |||
fdd1ad9b17 | |||
1be8d9d46f | |||
51799353a6 | |||
3ad13a592d | |||
55934918ff | |||
ab93e4f168 | |||
e1ad0b0889 | |||
cbe3e7617c | |||
94c8966e86 | |||
d973a062c2 | |||
ba41dbc69a | |||
96380dd462 | |||
61bd765784 | |||
4a0f06193b | |||
826917ef17 | |||
4e1dbbbecf | |||
b5b0de2083 | |||
a95fcbbdbf | |||
7a73ec7c06 | |||
0c1f2252a1 | |||
98a397696c | |||
225b7d7db7 | |||
f968d4c333 | |||
59e15be524 | |||
ccabe93ae8 | |||
56def6def4 | |||
3b1a1efed2 | |||
185869e628 | |||
108f6238e3 | |||
d3e5a63fa2 | |||
0847e60d2a | |||
ac60ec4320 | |||
93debc00dc | |||
ff75846d2d | |||
53c767140d | |||
839a747ce8 | |||
f78a3f88ff | |||
142987259c | |||
c8d41f987f | |||
d139dd88e8 | |||
7898a5f4eb | |||
53efbf3ca9 | |||
b6e5ff2f3d | |||
ae1386a1d7 | |||
2f730303c2 | |||
e98ec9ec75 | |||
ffecef901a | |||
5c13cbb08f | |||
64cfbe9514 | |||
5cc21c87fb | |||
505e0c79d9 | |||
ca9d59c1c1 | |||
8319dbb56a | |||
b1b6697c37 | |||
280716394d | |||
5795f72eab | |||
7e16c8959b | |||
dfc188a24d | |||
d18bae0c95 | |||
747746cba1 | |||
5b73edec8c | |||
3750781bce | |||
e646e53dac | |||
6b8aa43ec0 | |||
e8a20c7e8a | |||
0423ed01a6 | |||
4a600c2611 | |||
e5faa23d4f | |||
b2f9c219b1 | |||
77d65760f0 | |||
98d0460af0 | |||
de8d93341c | |||
0b6a773087 | |||
dc1aa676c4 | |||
5ae9cc1cd7 | |||
f84bc53c8d | |||
b26e9d0338 | |||
dbd62b8622 | |||
20c80352bf | |||
9d70fb2b86 | |||
71aad26d44 | |||
3a1918eb2f | |||
a1709c07d9 | |||
b8c1176c79 | |||
03b5a57474 | |||
f58cf4826a | |||
d050cc5e13 | |||
6ef88b6303 | |||
3e15840b14 | |||
0d147cbd94 | |||
be93f7480a | |||
2e5f24f1f8 | |||
23cf4bd59b | |||
051c34bc5d | |||
9623ceb4d5 | |||
e1a7395880 | |||
51aeeca024 | |||
076e8bf6a3 | |||
73c5b9b847 | |||
f4f3eed78d | |||
89909c15bc | |||
78b3d7ff2d | |||
012193c74e | |||
539abe5151 | |||
7e5aba140e | |||
6b933391e5 | |||
1e2a5ee21d | |||
225a5ef808 | |||
396d0d9bdc | |||
88c8fb74bb | |||
fd902c7a58 | |||
06cbebe22e | |||
ba4a9e1586 | |||
285386e47f | |||
c65fec7271 | |||
879a4b38aa | |||
13c87e38ed | |||
718154cfb4 | |||
26de9b9714 | |||
3365facf9f | |||
8a4826164b | |||
d6eabae4f0 | |||
6bd81bb92e | |||
126a91dfec | |||
51fa147b99 | |||
6160877167 | |||
717f049579 | |||
f71331056c | |||
c131ff2662 | |||
616447e01d | |||
8c1d66bcf3 | |||
bea580b906 | |||
c513e02b24 | |||
90d71deb0f | |||
fc0776303a | |||
bb0c6f9a8a | |||
dae21e7681 | |||
d28437ecc0 | |||
7fec24af67 | |||
0de3637569 | |||
f94c7cef59 | |||
7734d63b1b | |||
f894504761 | |||
7aa9b4ee64 | |||
a12250dc16 | |||
d8fbe1a63e | |||
91e1760719 | |||
02c8a62e7d | |||
0c9bcfb8ac | |||
bd4c5037c7 | |||
ccaaedf38f | |||
751f294164 | |||
e2acbe8499 | |||
aba880a6b3 | |||
616e8da0a5 | |||
a220774a3b | |||
4fc8224264 | |||
66dae5840c | |||
89b9e6e531 | |||
fc4623381a | |||
1023e925f6 | |||
5d65365751 | |||
ee64a6ec7e | |||
9bc09105d7 | |||
4b96632a69 | |||
22a806ca6f | |||
d8dc977fc1 | |||
6d900b8ffb | |||
5a2c070898 | |||
58f17ddb09 | |||
47c9786bab | |||
e5edbeacb4 | |||
964fb20df9 | |||
309ed80446 | |||
01f2eb6615 | |||
3a55c7aaba | |||
243f41bf28 | |||
934b28f5b6 | |||
edfc03bed2 | |||
59ce71b64d | |||
d473a7dc22 | |||
efb063d600 | |||
12fe55905c | |||
aed831c075 | |||
5ad15fad8b | |||
a7d328896c | |||
1274a151d0 | |||
d93d639345 | |||
e0d9a16985 | |||
7f1070dde6 | |||
7f1e70329c | |||
186c8fbb62 | |||
c935be6a49 | |||
4ee502fa1a | |||
084805b248 | |||
eaebd258c0 | |||
2843a20814 | |||
08a56726a8 | |||
7cad8654e5 | |||
841cfe37dd | |||
849a41293f | |||
5b17edb3f9 | |||
9f2047dad6 | |||
c01c53c5c7 | |||
27bf1684cb | |||
be2aee3354 | |||
5f42a07d0d | |||
8a94c945b7 | |||
7740028291 | |||
7369e9c233 | |||
8c66825a78 | |||
a56f8272a9 | |||
e846ca392f | |||
f9ec89cf7a | |||
b935bc526a | |||
97617ced4a | |||
d575c7c2a9 | |||
99a3bbc4f9 | |||
80226cb7d3 | |||
6189e0cd50 | |||
2254421ead | |||
4934f5846b | |||
c8c1aabf20 | |||
93b901a286 | |||
518a445074 | |||
080a6db657 | |||
d49b1bd78a | |||
bd96050d84 | |||
2a90ad9e53 | |||
16758293ff | |||
e965322a98 | |||
fcf5c41b43 | |||
88adbf0ca2 | |||
3df142db7a | |||
f90fc4bfa2 | |||
dc1f9fb243 | |||
7c30c8aa07 | |||
b1d13105e7 | |||
287537b34a | |||
fe04014e1c | |||
094b5834b7 | |||
1eb98cc74f | |||
232aa069d2 | |||
a69408fa25 | |||
75011d0b4e | |||
28ae70ed20 | |||
2727d39fa4 | |||
467f99b3bb | |||
d53c9c5ecf | |||
48c19b4f3c | |||
16e5b08a0f | |||
636a69e9e1 | |||
45d40dc159 | |||
88abb79a96 | |||
1e1d5c3f7d | |||
95e9faff95 | |||
979ff60a6b | |||
ea2e8f0787 | |||
3893c12054 | |||
8ad47a315b | |||
3b576c3047 | |||
4d83dab4f3 | |||
28a7ec4f35 | |||
b5aae88a0b | |||
bee97237d9 | |||
8c8e950455 | |||
ad8b344298 | |||
6d310d417a | |||
9dca893ce7 | |||
e3444e666b | |||
e37e69311b | |||
6918f863b1 | |||
9ee05bf591 | |||
e15700235e | |||
1c7c07e128 | |||
4fbc4e3be9 | |||
e251c81d43 | |||
f30a5074ab | |||
f6ae490723 | |||
7f96c43d6f | |||
52260a63fb | |||
82b528ee30 | |||
a443bbdf80 | |||
26ee46b246 | |||
cf37816602 | |||
b8087f6c48 | |||
28cbe8fbeb | |||
28210ee31d | |||
0caf875399 | |||
323684efff | |||
678ee0615d | |||
6c889def37 | |||
93586deb6f | |||
b6b9ffd15c | |||
1189b5f693 | |||
1c2b6d59da | |||
eacc28fedf | |||
5c85ee1214 | |||
7df7453365 | |||
3d8297247e | |||
662f66e501 | |||
9131cb4790 | |||
3b616676c6 | |||
cce759b8dd | |||
98b6c9b89e | |||
5370201df8 | |||
419c395966 | |||
2962ce9a0f | |||
c96398aa0c | |||
e68ce1d680 | |||
d34f10b4ea | |||
7e2c1f274b | |||
9fe3811c45 | |||
b74bccee0a | |||
a2d170f415 | |||
03f762db86 | |||
aea859af52 | |||
f61fd7b7f1 | |||
5682129b1d | |||
c3431f19bf | |||
65b11cb968 | |||
f4f60d38b8 | |||
4337345103 | |||
52f460f66d | |||
d486c72e02 | |||
bdfc55b951 | |||
b0f6026c23 | |||
b2aca491b6 | |||
401fc39879 | |||
d4b0839328 | |||
37c64841ff | |||
8f8dd076ff | |||
51fcf65424 | |||
56b19fa2b0 | |||
fe38dab405 | |||
6cb2aaab65 | |||
c604c4591f | |||
f0f54434cb | |||
f9de85c257 | |||
44f817646e | |||
91786779f8 | |||
27d0aed2c9 | |||
7767c96a9e | |||
3388bb50e1 | |||
1554c587b3 | |||
ce70e73a34 | |||
804ec9246f | |||
f029321664 | |||
d41b7f64e4 | |||
0366d1afb9 | |||
4ef3073ca4 | |||
68a53aa884 | |||
2358b3ff26 | |||
00ed1d2817 | |||
5b6fdb6526 | |||
fe700d1e7b | |||
eac611ab1e | |||
0635313566 | |||
663299c91e | |||
523a6e989a | |||
13f4aee5ee | |||
bbc0e2106c | |||
eb6ee52aaa | |||
80e330d4c3 | |||
a3adc49d8c | |||
9c6eeed0f8 | |||
705e9a93f8 | |||
572a217050 | |||
d58798e36c | |||
e98634a277 | |||
f1f08eced0 | |||
6c2da9f0e4 | |||
e158635f57 | |||
5a241e77da | |||
68e397ab34 | |||
4c78b94cd8 | |||
3a9cc9b079 | |||
6ff8c6e7f3 | |||
6f90425154 | |||
49ec9b1d9e | |||
a0e35ad644 | |||
b91dc77d6f | |||
b462b5a5c8 | |||
b5e7fb20b6 | |||
9d245add9c | |||
ded03ed743 | |||
6cc260c04e | |||
5b4354a6f3 | |||
34de36fe01 | |||
08c2cdbf1d | |||
dedbeb3eab | |||
d88bf16500 | |||
ad9c8318a7 | |||
f2778c0729 | |||
03ca73658c | |||
1da5f6c30c | |||
bee8a99a34 | |||
f5efd41dc1 | |||
c202f8cf1d | |||
0c226c7629 | |||
a9e772f330 | |||
86cc76388e | |||
0f4e9af172 | |||
3a1352c8ec | |||
2c86fd947f | |||
927484a71f | |||
88adb8f5ef | |||
98e7f5cb22 | |||
67e5957064 | |||
8c3473a91a | |||
1c775cb759 | |||
c4054d4984 | |||
76e2f9f3ac | |||
41a40b8b9f | |||
6b8694bfbf | |||
2d696b6806 | |||
709a5ec89a | |||
932bb732e0 | |||
dd4e29542c | |||
726e66ca66 | |||
474fd70ec7 | |||
5074dbc3c6 | |||
eb54e7f1ae | |||
8983dd97a1 | |||
7eaea28970 | |||
907605c59c | |||
58921592c8 | |||
b9c49c38ed | |||
76c97027c8 | |||
5db3620f49 | |||
a2e6fa0b16 | |||
1d359f0fc4 | |||
885d245afb | |||
75d01c0c11 | |||
ea219e3ddd | |||
e9cd54b2f4 | |||
a8b6573f96 | |||
4e158c8ba7 | |||
121c84a2d1 | |||
bfb76de9ab | |||
2434605c06 | |||
a0f15bfb56 | |||
7fabd14a63 | |||
6b4d2aeb07 | |||
5ab61b46b5 | |||
39c9691d70 | |||
d00960b33f | |||
4a5cfac2ea | |||
7e1d21239f | |||
1a8b870a9e | |||
3e150db493 | |||
1cf8b7e952 | |||
ea75432f12 | |||
85370dfd21 | |||
8a0afef825 | |||
76f196206b | |||
bf3e8195f7 | |||
bce3071a12 | |||
911f684e4d | |||
49bc156a56 | |||
791c3701d7 | |||
372af54e46 | |||
20c8953e5b | |||
2f9ad54bae | |||
57c22cddb8 | |||
a5026849a3 | |||
60dc765178 | |||
5e1c150024 | |||
0fd06bb592 | |||
9641c8cf49 | |||
4ca31be4a2 | |||
667f18cbfe | |||
4df5050760 | |||
f50881a72f | |||
b80696af00 | |||
3cb2f6dcf7 | |||
b1e2c4ce72 | |||
89ff9dd5ac | |||
8b95b173cd | |||
80a877ddab | |||
e892b21872 | |||
c344913ae8 | |||
3eef8fba27 | |||
d34e47f716 | |||
9bfba28d01 | |||
cc43357fb4 | |||
bce5ef2dca | |||
3021ac4b95 | |||
385e80d185 | |||
bd1c8873d0 | |||
6ac3bdb94a | |||
e964a0a1f0 | |||
97255fbd62 | |||
d5041f64be | |||
64f649d1f9 | |||
097c7e5397 | |||
c6adc00eac | |||
b1710bfa31 | |||
0e52c42970 | |||
2b136b2981 | |||
b95e5e36dc | |||
1bc5bc7f1c | |||
946d7015a2 | |||
4adf5720f0 | |||
973a3f03c3 | |||
06177afd6a | |||
e5937638d4 | |||
7c4f14f941 | |||
ead54d6c37 | |||
b749f3c724 | |||
d279aecb87 | |||
67de0fc8da | |||
8ed2399517 | |||
cf340011e2 | |||
ec2ad37860 | |||
3443e82812 | |||
63138507d6 | |||
b2eb07db14 | |||
379a845166 | |||
266cf93584 | |||
0ee71e9a09 | |||
ea07afcc0b | |||
43cd6d34ca | |||
3b67032adb | |||
2d46a0605b | |||
ba54664748 | |||
a79f4c10a1 | |||
bd04b28b9e | |||
cbadecab33 | |||
8c079787f0 | |||
62528e6a0b | |||
49bf8bd830 | |||
c64bc20bb5 | |||
54da891f79 | |||
e847716076 | |||
3a68bbd1b2 | |||
9cb1d03411 | |||
4fed156b90 | |||
de8bcd36e8 | |||
135cfe3238 | |||
6dbfe28427 | |||
2b203c4616 | |||
f12e655cf8 | |||
cf0045a483 | |||
9c81eeace0 | |||
5b333f91f6 | |||
912d926260 | |||
a8dfd640a7 | |||
390044b716 | |||
8ac36e6ee5 | |||
208c693088 | |||
eae2f7d113 | |||
45f92115f9 | |||
42f3adc7a2 | |||
71b40c6d6c | |||
af12089e7a | |||
33677c4b2b | |||
376c8c2e00 | |||
8232e9e8ce | |||
436bf8deb5 | |||
c858a1c9e5 |
17
.github/workflows/ccpp.yml
vendored
17
.github/workflows/ccpp.yml
vendored
@ -1,7 +1,8 @@
|
||||
name: C/C++ CI
|
||||
name: unittest
|
||||
|
||||
on: [push]
|
||||
|
||||
# fake comment to trigger an action 1
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
@ -16,16 +17,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: install redis
|
||||
run: brew install redis
|
||||
|
||||
- name: start redis server
|
||||
run: brew services start redis
|
||||
|
||||
- name: make test
|
||||
run: make test
|
||||
|
||||
# We don't need to have redis running anymore, as we have our fake limited one
|
||||
# - name: install redis
|
||||
# run: brew install redis
|
||||
#
|
||||
# - name: start redis server
|
||||
# run: brew services start redis
|
||||
|
||||
# # Windows does not work yet, I'm stuck at getting CMake to run + finding vcpkg
|
||||
# win:
|
||||
# runs-on: windows-2016
|
||||
|
19
CMake/FindSpdLog.cmake
Normal file
19
CMake/FindSpdLog.cmake
Normal file
@ -0,0 +1,19 @@
|
||||
# Find package structure taken from libcurl
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_path(SPDLOG_INCLUDE_DIRS spdlog/spdlog.h)
|
||||
find_library(JSONCPP_LIBRARY spdlog)
|
||||
|
||||
find_package_handle_standard_args(SPDLOG
|
||||
FOUND_VAR
|
||||
SPDLOG_FOUND
|
||||
REQUIRED_VARS
|
||||
SPDLOG_LIBRARY
|
||||
SPDLOG_INCLUDE_DIRS
|
||||
FAIL_MESSAGE
|
||||
"Could NOT find spdlog"
|
||||
)
|
||||
|
||||
set(SPDLOG_INCLUDE_DIRS ${SPDLOG_INCLUDE_DIRS})
|
||||
set(SPDLOG_LIBRARIES ${SPDLOG_LIBRARY})
|
@ -129,9 +129,6 @@ if (USE_TLS)
|
||||
elseif (APPLE AND NOT USE_OPEN_SSL)
|
||||
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketAppleSSL.h)
|
||||
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketAppleSSL.cpp)
|
||||
elseif (WIN32 AND NOT USE_OPEN_SSL)
|
||||
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketSChannel.h)
|
||||
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketSChannel.cpp)
|
||||
else()
|
||||
set(USE_OPEN_SSL ON)
|
||||
list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketOpenSSL.h)
|
||||
@ -204,8 +201,8 @@ if (ZLIB_FOUND)
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
|
||||
else()
|
||||
add_subdirectory(third_party/zlib)
|
||||
include_directories(third_party/zlib ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib)
|
||||
add_subdirectory(third_party/zlib)
|
||||
target_link_libraries(ixwebsocket zlibstatic)
|
||||
endif()
|
||||
|
||||
@ -233,6 +230,7 @@ if (USE_WS OR USE_TEST)
|
||||
add_subdirectory(ixcobra)
|
||||
add_subdirectory(ixsnake)
|
||||
add_subdirectory(ixsentry)
|
||||
add_subdirectory(ixbots)
|
||||
|
||||
add_subdirectory(third_party/spdlog spdlog)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
## Hello world
|
||||
|
||||

|
||||

|
||||
|
||||
IXWebSocket is a C++ library for WebSocket client and server development. It has minimal dependencies (no boost), is very simple to use and support everything you'll likely need for websocket dev (SSL, deflate compression, compiles on most platforms, etc...). HTTP client and server code is also available, but it hasn't received as much testing.
|
||||
|
||||
@ -38,7 +38,7 @@ Interested? Go read the [docs](https://machinezone.github.io/IXWebSocket/)! If t
|
||||
|
||||
IXWebSocket is actively being developed, check out the [changelog](https://machinezone.github.io/IXWebSocket/CHANGELOG/) to know what's cooking. If you are looking for a real time messaging service (the chat-like 'server' your websocket code will talk to) with many features such as history, backed by Redis, look at [cobra](https://github.com/machinezone/cobra).
|
||||
|
||||
IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version. See the current [test results](https://bsergean.github.io/IXWebSocket/autobahn/index.html). Some tests are still failing in the server code.
|
||||
IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version. See the current [test results](https://bsergean.github.io/autobahn/reports/clients/index.html). Some tests are still failing in the server code.
|
||||
|
||||
## Users
|
||||
|
||||
|
@ -21,6 +21,7 @@ FROM alpine:3.11 as runtime
|
||||
|
||||
RUN apk add --no-cache libstdc++
|
||||
RUN apk add --no-cache strace
|
||||
RUN apk add --no-cache gdb
|
||||
|
||||
RUN addgroup -S app && adduser -S -G app app
|
||||
COPY --chown=app:app --from=build /usr/local/bin/ws /usr/local/bin/ws
|
||||
@ -36,4 +37,3 @@ WORKDIR /home/app
|
||||
|
||||
ENTRYPOINT ["ws"]
|
||||
EXPOSE 8008
|
||||
C
|
||||
|
@ -1,9 +1,109 @@
|
||||
# Changelog
|
||||
All changes to this project will be documented in this file.
|
||||
|
||||
## [7.9.3] - 2020-01-08
|
||||
## [8.2.3] - 2020-03-13
|
||||
|
||||
(Windows) OpenSSL can be used for SSL communication
|
||||
(cobra client) pass the message position to the subscription data callback
|
||||
|
||||
## [8.2.2] - 2020-03-12
|
||||
|
||||
(openssl tls backend) Fix a hand in OpenSSL when using TLS v1.3 ... by disabling TLS v1.3
|
||||
|
||||
## [8.2.1] - 2020-03-11
|
||||
|
||||
(cobra) IXCobraConfig struct has tlsOptions and per message deflate options
|
||||
|
||||
## [8.2.0] - 2020-03-11
|
||||
|
||||
(cobra) add IXCobraConfig struct to pass cobra config around
|
||||
|
||||
## [8.1.9] - 2020-03-09
|
||||
|
||||
(ws cobra_subscribe) add a --fluentd option to wrap a message in an enveloppe so that fluentd can recognize it
|
||||
|
||||
## [8.1.8] - 2020-03-02
|
||||
|
||||
(websocket server) fix regression with disabling zlib extension on the server side. If a client does not support this extension the server will handle it fine. We still need to figure out how to disable the option.
|
||||
|
||||
## [8.1.7] - 2020-02-26
|
||||
|
||||
(websocket) traffic tracker received bytes is message size while it should be wire size
|
||||
|
||||
## [8.1.6] - 2020-02-26
|
||||
|
||||
(ws_connect) display sent/received bytes statistics on exit
|
||||
|
||||
## [8.1.5] - 2020-02-23
|
||||
|
||||
(server) give thread name to some usual worker threads / unittest is broken !!
|
||||
|
||||
## [8.1.4] - 2020-02-22
|
||||
|
||||
(websocket server) fix regression from 8.1.2, where per-deflate message compression was always disabled
|
||||
|
||||
## [8.1.3] - 2020-02-21
|
||||
|
||||
(client + server) Fix #155 / http header parser should treat the space(s) after the : delimiter as optional. Fixing this bug made us discover that websocket sub-protocols are not properly serialiazed, but start with a ,
|
||||
|
||||
## [8.1.2] - 2020-02-18
|
||||
|
||||
(WebSocketServer) add option to disable deflate compression, exposed with the -x option to ws echo_server
|
||||
|
||||
## [8.1.1] - 2020-02-18
|
||||
|
||||
(ws cobra to statsd and sentry sender) exit if no messages are received for one minute, which is a sign that something goes wrong on the server side. That should be changed to be configurable in the future
|
||||
|
||||
## [8.1.0] - 2020-02-13
|
||||
|
||||
(http client + sentry minidump upload) Multipart stream closing boundary is invalid + mark some options as mandatory in the command line tools
|
||||
|
||||
## [8.0.7] - 2020-02-12
|
||||
|
||||
(build) remove the unused subtree which was causing some way of installing to break
|
||||
|
||||
## [8.0.6] - 2020-01-31
|
||||
|
||||
(snake) add an option to disable answering pongs as response to pings, to test cobra client behavior with hanged connections
|
||||
|
||||
## [8.0.5] - 2020-01-31
|
||||
|
||||
(IXCobraConnection) set a ping timeout of 90 seconds. If no pong messages are received as responses to ping for a while, give up and close the connection
|
||||
|
||||
## [8.0.4] - 2020-01-31
|
||||
|
||||
(cobra to sentry) remove noisy logging
|
||||
|
||||
## [8.0.3] - 2020-01-30
|
||||
|
||||
(ixcobra) check if we are authenticated in publishNext before trying to publish a message
|
||||
|
||||
## [8.0.2] - 2020-01-28
|
||||
|
||||
Extract severity level when emitting messages to sentry
|
||||
|
||||
## [8.0.1] - 2020-01-28
|
||||
|
||||
Fix bug #151 - If a socket connection is interrupted, calling stop() on the IXWebSocket object blocks until the next retry
|
||||
|
||||
## [8.0.0] - 2020-01-26
|
||||
|
||||
(SocketServer) add ability to bind on an ipv6 address
|
||||
|
||||
## [7.9.6] - 2020-01-22
|
||||
|
||||
(ws) add a dnslookup sub-command, to get the ip address of a remote host
|
||||
|
||||
## [7.9.5] - 2020-01-14
|
||||
|
||||
(windows) fix #144, get rid of stubbed/un-implemented windows schannel ssl backend
|
||||
|
||||
## [7.9.4] - 2020-01-12
|
||||
|
||||
(openssl + mbedssl) fix #140, can send large files with ws send over ssl / still broken with apple ssl
|
||||
|
||||
## [7.9.3] - 2020-01-10
|
||||
|
||||
(apple ssl) model write method after the OpenSSL one for consistency
|
||||
|
||||
## [7.9.2] - 2020-01-06
|
||||
|
||||
|
@ -23,6 +23,16 @@ Options for building:
|
||||
|
||||
If you are on Windows, look at the [appveyor](https://github.com/machinezone/IXWebSocket/blob/master/appveyor.yml) file that has instructions for building dependencies.
|
||||
|
||||
It is also possible to externally include the project, so that everything is fetched over the wire when you build like so:
|
||||
|
||||
```
|
||||
ExternalProject_Add(
|
||||
IXWebSocket
|
||||
GIT_REPOSITORY https://github.com/machinezone/IXWebSocket.git
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
### vcpkg
|
||||
|
||||
It is possible to get IXWebSocket through Microsoft [vcpkg](https://github.com/microsoft/vcpkg).
|
||||
@ -33,11 +43,16 @@ vcpkg install ixwebsocket
|
||||
|
||||
### Conan
|
||||
|
||||
Support for building with conan was contributed by Olivia Zoe (thanks!). The package name to reference is `IXWebSocket/5.0.0@LunarWatcher/stable`, and a list of the uploaded versions is available on [Bintray](https://bintray.com/oliviazoe0/conan-packages/IXWebSocket%3ALunarWatcher). The package is in the process to be published to the official conan package repo, but in the meantime, it can be accessed by adding a new remote
|
||||
[  ](https://bintray.com/conan/conan-center/ixwebsocket%3A_/_latestVersion)
|
||||
|
||||
```
|
||||
conan remote add remote_name_here https://api.bintray.com/conan/oliviazoe0/conan-packages
|
||||
```
|
||||
Conan is currently supported through a recipe in [Conan Center](https://github.com/conan-io/conan-center-index/tree/master/recipes/ixwebsocket) ([Bintray entry](https://bintray.com/conan/conan-center/ixwebsocket%3A_)).
|
||||
|
||||
Package reference
|
||||
|
||||
* Conan 1.21.0 and up: `ixwebsocket/7.9.2`
|
||||
* Earlier versions: `ixwebsocket/7.9.2@_/_`
|
||||
|
||||
Note that the version listed here might not be the latest one. See Bintray or the recipe itself for the latest version. If you're migrating from the previous, custom Bintray remote, note that the package reference _has_ to be lower-case.
|
||||
|
||||
### Docker
|
||||
|
||||
|
@ -6,7 +6,7 @@ The per message deflate compression option is supported. It can lead to very nic
|
||||
|
||||
### TLS/SSL
|
||||
|
||||
Connections can be optionally secured and encrypted with TLS/SSL when using a wss:// endpoint, or using normal un-encrypted socket with ws:// endpoints. AppleSSL is used on iOS and macOS, OpenSSL is used on Android and Linux, mbedTLS is used on Windows.
|
||||
Connections can be optionally secured and encrypted with TLS/SSL when using a wss:// endpoint, or using normal un-encrypted socket with ws:// endpoints. AppleSSL is used on iOS and macOS, OpenSSL and mbedTLS can be used on Android, Linux and Windows.
|
||||
|
||||
### Polling and background thread work
|
||||
|
||||
|
45
ixbots/CMakeLists.txt
Normal file
45
ixbots/CMakeLists.txt
Normal file
@ -0,0 +1,45 @@
|
||||
#
|
||||
# Author: Benjamin Sergeant
|
||||
# Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
#
|
||||
|
||||
set (IXBOTS_SOURCES
|
||||
ixbots/IXCobraToSentryBot.cpp
|
||||
ixbots/IXCobraToStatsdBot.cpp
|
||||
ixbots/IXQueueManager.cpp
|
||||
)
|
||||
|
||||
set (IXBOTS_HEADERS
|
||||
ixbots/IXCobraToSentryBot.h
|
||||
ixbots/IXCobraToStatsdBot.h
|
||||
ixbots/IXQueueManager.h
|
||||
)
|
||||
|
||||
add_library(ixbots STATIC
|
||||
${IXBOTS_SOURCES}
|
||||
${IXBOTS_HEADERS}
|
||||
)
|
||||
|
||||
find_package(JsonCpp)
|
||||
if (NOT JSONCPP_FOUND)
|
||||
set(JSONCPP_INCLUDE_DIRS ../third_party/jsoncpp)
|
||||
endif()
|
||||
|
||||
find_package(SpdLog)
|
||||
if (NOT SPDLOG_FOUND)
|
||||
set(SPDLOG_INCLUDE_DIRS ../third_party/spdlog/include)
|
||||
endif()
|
||||
|
||||
set(STATSD_CLIENT_INCLUDE_DIRS ../third_party/statsd-client-cpp/src)
|
||||
|
||||
set(IXBOTS_INCLUDE_DIRS
|
||||
.
|
||||
..
|
||||
../ixcore
|
||||
../ixcobra
|
||||
../ixsentry
|
||||
${JSONCPP_INCLUDE_DIRS}
|
||||
${SPDLOG_INCLUDE_DIRS}
|
||||
${STATSD_CLIENT_INCLUDE_DIRS})
|
||||
|
||||
target_include_directories( ixbots PUBLIC ${IXBOTS_INCLUDE_DIRS} )
|
@ -1,17 +1,14 @@
|
||||
/*
|
||||
* ws_cobra_to_sentry.cpp
|
||||
* IXCobraToSentryBot.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include "IXCobraToSentryBot.h"
|
||||
#include "IXQueueManager.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <ixcobra/IXCobraConnection.h>
|
||||
#include <ixsentry/IXSentryClient.h>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
@ -19,101 +16,18 @@
|
||||
|
||||
namespace ix
|
||||
{
|
||||
class QueueManager
|
||||
{
|
||||
public:
|
||||
QueueManager(size_t maxQueueSize, std::atomic<bool>& stop)
|
||||
: _maxQueueSize(maxQueueSize)
|
||||
, _stop(stop)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Value pop();
|
||||
void add(Json::Value msg);
|
||||
|
||||
private:
|
||||
std::map<std::string, std::queue<Json::Value>> _queues;
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _condition;
|
||||
size_t _maxQueueSize;
|
||||
std::atomic<bool>& _stop;
|
||||
};
|
||||
|
||||
Json::Value QueueManager::pop()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
if (_queues.empty())
|
||||
{
|
||||
Json::Value val;
|
||||
return val;
|
||||
}
|
||||
|
||||
std::vector<std::string> games;
|
||||
for (auto it : _queues)
|
||||
{
|
||||
games.push_back(it.first);
|
||||
}
|
||||
|
||||
std::random_shuffle(games.begin(), games.end());
|
||||
std::string game = games[0];
|
||||
|
||||
spdlog::info("Sending event for game '{}'", game);
|
||||
|
||||
_condition.wait(lock, [this] { return !_stop; });
|
||||
|
||||
if (_queues[game].empty())
|
||||
{
|
||||
Json::Value val;
|
||||
return val;
|
||||
}
|
||||
|
||||
auto msg = _queues[game].front();
|
||||
_queues[game].pop();
|
||||
return msg;
|
||||
}
|
||||
|
||||
void QueueManager::add(Json::Value msg)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
std::string game;
|
||||
if (msg.isMember("device") && msg["device"].isMember("game"))
|
||||
{
|
||||
game = msg["device"]["game"].asString();
|
||||
}
|
||||
|
||||
if (game.empty()) return;
|
||||
|
||||
// if the sending is not fast enough there is no point
|
||||
// in queuing too many events.
|
||||
if (_queues[game].size() < _maxQueueSize)
|
||||
{
|
||||
_queues[game].push(msg);
|
||||
_condition.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
int ws_cobra_to_sentry_main(const std::string& appkey,
|
||||
const std::string& endpoint,
|
||||
const std::string& rolename,
|
||||
const std::string& rolesecret,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& dsn,
|
||||
bool verbose,
|
||||
bool strict,
|
||||
int jobs,
|
||||
size_t maxQueueSize,
|
||||
const ix::SocketTLSOptions& tlsOptions)
|
||||
int cobra_to_sentry_bot(const CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
SentryClient& sentryClient,
|
||||
bool verbose,
|
||||
bool strict,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime)
|
||||
{
|
||||
ix::CobraConnection conn;
|
||||
conn.configure(appkey,
|
||||
endpoint,
|
||||
rolename,
|
||||
rolesecret,
|
||||
ix::WebSocketPerMessageDeflateOptions(true),
|
||||
tlsOptions);
|
||||
conn.configure(config);
|
||||
conn.connect();
|
||||
|
||||
Json::FastWriter jsonWriter;
|
||||
@ -125,28 +39,58 @@ namespace ix
|
||||
|
||||
QueueManager queueManager(maxQueueSize, stop);
|
||||
|
||||
auto timer = [&sentCount, &receivedCount] {
|
||||
while (true)
|
||||
auto timer = [&sentCount, &receivedCount, &stop] {
|
||||
while (!stop)
|
||||
{
|
||||
spdlog::info("messages received {} sent {}", receivedCount, sentCount);
|
||||
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
|
||||
spdlog::info("timer thread done");
|
||||
};
|
||||
|
||||
std::thread t1(timer);
|
||||
|
||||
auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat] {
|
||||
std::string state("na");
|
||||
|
||||
if (!enableHeartbeat) return;
|
||||
|
||||
while (!stop)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "messages received " << receivedCount;
|
||||
ss << "messages sent " << sentCount;
|
||||
|
||||
std::string currentState = ss.str();
|
||||
|
||||
if (currentState == state)
|
||||
{
|
||||
spdlog::error("no messages received or sent for 1 minute, exiting");
|
||||
exit(1);
|
||||
}
|
||||
state = currentState;
|
||||
|
||||
auto duration = std::chrono::minutes(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
|
||||
spdlog::info("heartbeat thread done");
|
||||
};
|
||||
|
||||
std::thread t2(heartbeat);
|
||||
|
||||
auto sentrySender =
|
||||
[&queueManager, verbose, &errorSending, &sentCount, &stop, &throttled, &dsn] {
|
||||
SentryClient sentryClient(dsn);
|
||||
[&queueManager, verbose, &errorSending, &sentCount, &stop, &throttled, &sentryClient] {
|
||||
|
||||
while (true)
|
||||
{
|
||||
Json::Value msg = queueManager.pop();
|
||||
|
||||
if (stop) break;
|
||||
if (msg.isNull()) continue;
|
||||
if (stop) return;
|
||||
|
||||
auto ret = sentryClient.send(msg, verbose);
|
||||
HttpResponsePtr response = ret.first;
|
||||
@ -218,17 +162,13 @@ namespace ix
|
||||
++sentCount;
|
||||
}
|
||||
|
||||
if (stop) return;
|
||||
if (stop) break;
|
||||
}
|
||||
|
||||
spdlog::info("sentrySender thread done");
|
||||
};
|
||||
|
||||
// Create a thread pool
|
||||
spdlog::info("Starting {} sentry sender jobs", jobs);
|
||||
std::vector<std::thread> pool;
|
||||
for (int i = 0; i < jobs; i++)
|
||||
{
|
||||
pool.push_back(std::thread(sentrySender));
|
||||
}
|
||||
std::thread t3(sentrySender);
|
||||
|
||||
conn.setEventCallback([&conn,
|
||||
&channel,
|
||||
@ -261,10 +201,10 @@ namespace ix
|
||||
conn.subscribe(channel,
|
||||
filter,
|
||||
[&jsonWriter, verbose, &throttled, &receivedCount, &queueManager](
|
||||
const Json::Value& msg) {
|
||||
const Json::Value& msg, const std::string& position) {
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info(jsonWriter.write(msg));
|
||||
spdlog::info("Subscriber received message {} -> {}", position, jsonWriter.write(msg));
|
||||
}
|
||||
|
||||
// If we cannot send to sentry fast enough, drop the message
|
||||
@ -299,24 +239,42 @@ namespace ix
|
||||
}
|
||||
});
|
||||
|
||||
while (true)
|
||||
// Run forever
|
||||
if (runtime == -1)
|
||||
{
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
while (true)
|
||||
{
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
|
||||
if (strict && errorSending) break;
|
||||
if (strict && errorSending) break;
|
||||
}
|
||||
}
|
||||
// Run for a duration, used by unittesting now
|
||||
else
|
||||
{
|
||||
for (int i = 0 ; i < runtime; ++i)
|
||||
{
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
|
||||
if (strict && errorSending) break;
|
||||
}
|
||||
}
|
||||
|
||||
conn.disconnect();
|
||||
|
||||
//
|
||||
// Cleanup.
|
||||
// join all the bg threads and stop them.
|
||||
//
|
||||
conn.disconnect();
|
||||
stop = true;
|
||||
for (int i = 0; i < jobs; i++)
|
||||
{
|
||||
spdlog::error("joining thread {}", i);
|
||||
pool[i].join();
|
||||
}
|
||||
|
||||
return (strict && errorSending) ? 1 : 0;
|
||||
t1.join();
|
||||
if (t2.joinable()) t2.join();
|
||||
spdlog::info("heartbeat thread done");
|
||||
|
||||
t3.join();
|
||||
|
||||
return (strict && errorSending) ? -1 : (int) sentCount;
|
||||
}
|
||||
} // namespace ix
|
23
ixbots/ixbots/IXCobraToSentryBot.h
Normal file
23
ixbots/ixbots/IXCobraToSentryBot.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* IXCobraToSentryBot.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <ixsentry/IXSentryClient.h>
|
||||
#include <string>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
int cobra_to_sentry_bot(const CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
SentryClient& sentryClient,
|
||||
bool verbose,
|
||||
bool strict,
|
||||
size_t maxQueueSize,
|
||||
bool enableHeartbeat,
|
||||
int runtime);
|
||||
} // namespace ix
|
@ -1,9 +1,12 @@
|
||||
/*
|
||||
* ws_cobra_to_statsd.cpp
|
||||
* IXCobraToStatsdBot.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXCobraToStatsdBot.h"
|
||||
#include "IXQueueManager.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
@ -17,59 +20,6 @@
|
||||
#include <statsd_client.h>
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
class QueueManager
|
||||
{
|
||||
public:
|
||||
QueueManager(size_t maxQueueSize, std::atomic<bool>& stop)
|
||||
: _maxQueueSize(maxQueueSize)
|
||||
, _stop(stop)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Value pop();
|
||||
void add(Json::Value msg);
|
||||
|
||||
private:
|
||||
std::queue<Json::Value> _queue;
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _condition;
|
||||
size_t _maxQueueSize;
|
||||
std::atomic<bool>& _stop;
|
||||
};
|
||||
|
||||
Json::Value QueueManager::pop()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
if (_queue.empty())
|
||||
{
|
||||
Json::Value val;
|
||||
return val;
|
||||
}
|
||||
|
||||
_condition.wait(lock, [this] { return !_stop; });
|
||||
|
||||
auto msg = _queue.front();
|
||||
_queue.pop();
|
||||
return msg;
|
||||
}
|
||||
|
||||
void QueueManager::add(Json::Value msg)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
// if the sending is not fast enough there is no point
|
||||
// in queuing too many events.
|
||||
if (_queue.size() < _maxQueueSize)
|
||||
{
|
||||
_queue.push(msg);
|
||||
_condition.notify_one();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace ix
|
||||
{
|
||||
// fields are command line argument that can be specified multiple times
|
||||
@ -109,26 +59,17 @@ namespace ix
|
||||
return val.asString();
|
||||
}
|
||||
|
||||
int ws_cobra_to_statsd_main(const std::string& appkey,
|
||||
const std::string& endpoint,
|
||||
const std::string& rolename,
|
||||
const std::string& rolesecret,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& host,
|
||||
int port,
|
||||
const std::string& prefix,
|
||||
const std::string& fields,
|
||||
bool verbose,
|
||||
const ix::SocketTLSOptions& tlsOptions)
|
||||
int cobra_to_statsd_bot(const ix::CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& host,
|
||||
int port,
|
||||
const std::string& prefix,
|
||||
const std::string& fields,
|
||||
bool verbose)
|
||||
{
|
||||
ix::CobraConnection conn;
|
||||
conn.configure(appkey,
|
||||
endpoint,
|
||||
rolename,
|
||||
rolesecret,
|
||||
ix::WebSocketPerMessageDeflateOptions(true),
|
||||
tlsOptions);
|
||||
conn.configure(config);
|
||||
conn.connect();
|
||||
|
||||
auto tokens = parseFields(fields);
|
||||
@ -153,6 +94,31 @@ namespace ix
|
||||
|
||||
std::thread t1(timer);
|
||||
|
||||
auto heartbeat = [&sentCount, &receivedCount] {
|
||||
std::string state("na");
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "messages received " << receivedCount;
|
||||
ss << "messages sent " << sentCount;
|
||||
|
||||
std::string currentState = ss.str();
|
||||
|
||||
if (currentState == state)
|
||||
{
|
||||
spdlog::error("no messages received or sent for 1 minute, exiting");
|
||||
exit(1);
|
||||
}
|
||||
state = currentState;
|
||||
|
||||
auto duration = std::chrono::minutes(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
};
|
||||
|
||||
std::thread t2(heartbeat);
|
||||
|
||||
auto statsdSender = [&queueManager, &host, &port, &sentCount, &tokens, &prefix, &stop] {
|
||||
// statsd client
|
||||
// test with netcat as a server: `nc -ul 8125`
|
||||
@ -184,7 +150,7 @@ namespace ix
|
||||
}
|
||||
};
|
||||
|
||||
std::thread t2(statsdSender);
|
||||
std::thread t3(statsdSender);
|
||||
|
||||
conn.setEventCallback(
|
||||
[&conn, &channel, &filter, &jsonWriter, verbose, &queueManager, &receivedCount](
|
||||
@ -212,10 +178,10 @@ namespace ix
|
||||
conn.subscribe(channel,
|
||||
filter,
|
||||
[&jsonWriter, &queueManager, verbose, &receivedCount](
|
||||
const Json::Value& msg) {
|
||||
const Json::Value& msg, const std::string& position) {
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info(jsonWriter.write(msg));
|
||||
spdlog::info("Subscriber received message {} -> {}", position, jsonWriter.write(msg));
|
||||
}
|
||||
|
||||
receivedCount++;
|
22
ixbots/ixbots/IXCobraToStatsdBot.h
Normal file
22
ixbots/ixbots/IXCobraToStatsdBot.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* IXCobraToStatsdBot.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <string>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
int cobra_to_statsd_bot(const ix::CobraConfig& config,
|
||||
const std::string& channel,
|
||||
const std::string& filter,
|
||||
const std::string& host,
|
||||
int port,
|
||||
const std::string& prefix,
|
||||
const std::string& fields,
|
||||
bool verbose);
|
||||
} // namespace ix
|
66
ixbots/ixbots/IXQueueManager.cpp
Normal file
66
ixbots/ixbots/IXQueueManager.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* IXQueueManager.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXQueueManager.h"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
Json::Value QueueManager::pop()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
if (_queues.empty())
|
||||
{
|
||||
Json::Value val;
|
||||
return val;
|
||||
}
|
||||
|
||||
std::vector<std::string> games;
|
||||
for (auto it : _queues)
|
||||
{
|
||||
games.push_back(it.first);
|
||||
}
|
||||
|
||||
std::random_shuffle(games.begin(), games.end());
|
||||
std::string game = games[0];
|
||||
|
||||
auto duration = std::chrono::seconds(1);
|
||||
_condition.wait_for(lock, duration);
|
||||
|
||||
if (_queues[game].empty())
|
||||
{
|
||||
Json::Value val;
|
||||
return val;
|
||||
}
|
||||
|
||||
auto msg = _queues[game].front();
|
||||
_queues[game].pop();
|
||||
return msg;
|
||||
}
|
||||
|
||||
void QueueManager::add(Json::Value msg)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
std::string game;
|
||||
if (msg.isMember("device") && msg["device"].isMember("game"))
|
||||
{
|
||||
game = msg["device"]["game"].asString();
|
||||
}
|
||||
|
||||
if (game.empty()) return;
|
||||
|
||||
// if the sending is not fast enough there is no point
|
||||
// in queuing too many events.
|
||||
if (_queues[game].size() < _maxQueueSize)
|
||||
{
|
||||
_queues[game].push(msg);
|
||||
_condition.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
38
ixbots/ixbots/IXQueueManager.h
Normal file
38
ixbots/ixbots/IXQueueManager.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* IXQueueManager.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <atomic>
|
||||
#include <json/json.h>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
#include <map>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
class QueueManager
|
||||
{
|
||||
public:
|
||||
QueueManager(size_t maxQueueSize, std::atomic<bool>& stop)
|
||||
: _maxQueueSize(maxQueueSize)
|
||||
, _stop(stop)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Value pop();
|
||||
void add(Json::Value msg);
|
||||
|
||||
private:
|
||||
std::map<std::string, std::queue<Json::Value>> _queues;
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _condition;
|
||||
size_t _maxQueueSize;
|
||||
std::atomic<bool>& _stop;
|
||||
};
|
||||
}
|
@ -13,6 +13,7 @@ set (IXCOBRA_HEADERS
|
||||
ixcobra/IXCobraConnection.h
|
||||
ixcobra/IXCobraMetricsThreadedPublisher.h
|
||||
ixcobra/IXCobraMetricsPublisher.h
|
||||
ixcobra/IXCobraConfig.h
|
||||
)
|
||||
|
||||
add_library(ixcobra STATIC
|
||||
|
35
ixcobra/ixcobra/IXCobraConfig.h
Normal file
35
ixcobra/ixcobra/IXCobraConfig.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* IXCobraConfig.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h>
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
struct CobraConfig
|
||||
{
|
||||
std::string appkey;
|
||||
std::string endpoint;
|
||||
std::string rolename;
|
||||
std::string rolesecret;
|
||||
WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions;
|
||||
SocketTLSOptions socketTLSOptions;
|
||||
|
||||
CobraConfig(const std::string& a = std::string(),
|
||||
const std::string& e = std::string(),
|
||||
const std::string& r = std::string(),
|
||||
const std::string& s = std::string())
|
||||
: appkey(a)
|
||||
, endpoint(e)
|
||||
, rolename(r)
|
||||
, rolesecret(s)
|
||||
{
|
||||
;
|
||||
}
|
||||
};
|
||||
} // namespace ix
|
@ -265,7 +265,25 @@ namespace ix
|
||||
_webSocket->setUrl(url);
|
||||
_webSocket->setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
|
||||
_webSocket->setTLSOptions(socketTLSOptions);
|
||||
|
||||
// Send a websocket ping every N seconds (N = 30) now
|
||||
// This should keep the connection open and prevent some load balancers such as
|
||||
// the Amazon one from shutting it down
|
||||
_webSocket->setPingInterval(kPingIntervalSecs);
|
||||
|
||||
// If we don't receive a pong back, declare loss after 3 * N seconds
|
||||
// (will be 90s now), and close and restart the connection
|
||||
_webSocket->setPingTimeout(3 * kPingIntervalSecs);
|
||||
}
|
||||
|
||||
void CobraConnection::configure(const ix::CobraConfig& config)
|
||||
{
|
||||
configure(config.appkey,
|
||||
config.endpoint,
|
||||
config.rolename,
|
||||
config.rolesecret,
|
||||
config.webSocketPerMessageDeflateOptions,
|
||||
config.socketTLSOptions);
|
||||
}
|
||||
|
||||
//
|
||||
@ -423,9 +441,12 @@ namespace ix
|
||||
if (!body.isMember("messages")) return false;
|
||||
Json::Value messages = body["messages"];
|
||||
|
||||
if (!body.isMember("position")) return false;
|
||||
std::string position = body["position"].asString();
|
||||
|
||||
for (auto&& msg : messages)
|
||||
{
|
||||
cb->second(msg);
|
||||
cb->second(msg, position);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -506,7 +527,7 @@ namespace ix
|
||||
if (_messageQueue.empty()) return true;
|
||||
|
||||
auto&& msg = _messageQueue.back();
|
||||
if (!publishMessage(msg))
|
||||
if (!_authenticated || !publishMessage(msg))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include <unordered_map>
|
||||
#include <limits>
|
||||
|
||||
#include "IXCobraConfig.h"
|
||||
|
||||
namespace ix
|
||||
{
|
||||
class WebSocket;
|
||||
@ -40,7 +42,7 @@ namespace ix
|
||||
CobraConnection_PublishMode_Batch = 1
|
||||
};
|
||||
|
||||
using SubscriptionCallback = std::function<void(const Json::Value&)>;
|
||||
using SubscriptionCallback = std::function<void(const Json::Value&, const std::string&)>;
|
||||
using EventCallback = std::function<void(CobraConnectionEventType,
|
||||
const std::string&,
|
||||
const WebSocketHttpHeaders&,
|
||||
@ -67,6 +69,8 @@ namespace ix
|
||||
const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions,
|
||||
const SocketTLSOptions& socketTLSOptions);
|
||||
|
||||
void configure(const ix::CobraConfig& config);
|
||||
|
||||
/// Set the traffic tracker callback
|
||||
static void setTrafficTrackerCallback(const TrafficTrackerCallback& callback);
|
||||
|
||||
|
@ -40,6 +40,11 @@ namespace ix
|
||||
}
|
||||
}
|
||||
|
||||
void SentryClient::setTLSOptions(const SocketTLSOptions& tlsOptions)
|
||||
{
|
||||
_httpClient->setTLSOptions(tlsOptions);
|
||||
}
|
||||
|
||||
int64_t SentryClient::getTimestamp()
|
||||
{
|
||||
const auto tp = std::chrono::system_clock::now();
|
||||
@ -166,6 +171,17 @@ namespace ix
|
||||
tags.append(tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (msg["data"]["info"].isMember("level_str"))
|
||||
{
|
||||
// https://docs.sentry.io/enriching-error-data/context/?platform=python#setting-the-level
|
||||
std::string level = msg["data"]["info"]["level_str"].asString();
|
||||
if (level == "critical")
|
||||
{
|
||||
level = "fatal";
|
||||
}
|
||||
payload["level"] = level;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <ixwebsocket/IXHttpClient.h>
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
#include <json/json.h>
|
||||
#include <regex>
|
||||
#include <memory>
|
||||
@ -24,6 +25,9 @@ namespace ix
|
||||
|
||||
Json::Value parseLuaStackTrace(const std::string& stack);
|
||||
|
||||
// Mostly for testing
|
||||
void setTLSOptions(const SocketTLSOptions& tlsOptions);
|
||||
|
||||
void uploadMinidump(
|
||||
const std::string& sentryMetadata,
|
||||
const std::string& minidumpBytes,
|
||||
|
@ -32,6 +32,7 @@ namespace snake
|
||||
|
||||
// Misc
|
||||
bool verbose;
|
||||
bool disablePong;
|
||||
};
|
||||
|
||||
bool isAppKeyValid(const AppConfig& appConfig, std::string appkey);
|
||||
|
@ -17,8 +17,8 @@
|
||||
|
||||
namespace ix
|
||||
{
|
||||
RedisServer::RedisServer(int port, const std::string& host, int backlog, size_t maxConnections)
|
||||
: SocketServer(port, host, backlog, maxConnections)
|
||||
RedisServer::RedisServer(int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
|
||||
: SocketServer(port, host, backlog, maxConnections, addressFamily)
|
||||
, _connectedClientsCount(0)
|
||||
, _stopHandlingConnections(false)
|
||||
{
|
||||
|
@ -25,7 +25,8 @@ namespace ix
|
||||
RedisServer(int port = SocketServer::kDefaultPort,
|
||||
const std::string& host = SocketServer::kDefaultHost,
|
||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections);
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections,
|
||||
int addressFamily = SocketServer::kDefaultAddressFamily);
|
||||
virtual ~RedisServer();
|
||||
virtual void stop() final;
|
||||
|
||||
|
@ -189,7 +189,7 @@ namespace snake
|
||||
nlohmann::json response = {
|
||||
{"action", "rtm/subscription/data"},
|
||||
{"id", id++},
|
||||
{"body", {{"subscription_id", subscriptionId}, {"messages", {msg}}}}};
|
||||
{"body", {{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}};
|
||||
|
||||
ws->sendText(response.dump());
|
||||
};
|
||||
|
@ -21,6 +21,15 @@ namespace snake
|
||||
, _server(appConfig.port, appConfig.hostname)
|
||||
{
|
||||
_server.setTLSOptions(appConfig.socketTLSOptions);
|
||||
|
||||
if (appConfig.disablePong)
|
||||
{
|
||||
_server.disablePong();
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Listening on " << appConfig.hostname << ":" << appConfig.port;
|
||||
ix::IXCoreLogger::Log(ss.str().c_str());
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -648,7 +648,7 @@ namespace ix
|
||||
<< it.second << "\r\n";
|
||||
}
|
||||
|
||||
ss << "--" << multipartBoundary << "\r\n";
|
||||
ss << "--" << multipartBoundary << "--\r\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
@ -42,8 +42,9 @@ namespace
|
||||
|
||||
namespace ix
|
||||
{
|
||||
HttpServer::HttpServer(int port, const std::string& host, int backlog, size_t maxConnections)
|
||||
: SocketServer(port, host, backlog, maxConnections)
|
||||
HttpServer::HttpServer(
|
||||
int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
|
||||
: SocketServer(port, host, backlog, maxConnections, addressFamily)
|
||||
, _connectedClientsCount(0)
|
||||
{
|
||||
setDefaultConnectionCallback();
|
||||
|
@ -28,7 +28,8 @@ namespace ix
|
||||
HttpServer(int port = SocketServer::kDefaultPort,
|
||||
const std::string& host = SocketServer::kDefaultHost,
|
||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections);
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections,
|
||||
int addressFamily = SocketServer::kDefaultAddressFamily);
|
||||
virtual ~HttpServer();
|
||||
virtual void stop() final;
|
||||
|
||||
|
@ -54,14 +54,17 @@ namespace ix
|
||||
// to ::poll does fix that.
|
||||
//
|
||||
// However poll isn't as portable as select and has bugs on Windows, so we
|
||||
// should write a shim to fallback to select on those platforms. See
|
||||
// have a shim to fallback to select on those platforms. See
|
||||
// https://github.com/mpv-player/mpv/pull/5203/files for such a select wrapper.
|
||||
//
|
||||
nfds_t nfds = 1;
|
||||
struct pollfd fds[2];
|
||||
memset(fds, 0, sizeof(fds));
|
||||
|
||||
fds[0].fd = sockfd;
|
||||
fds[0].events = (readyToRead) ? POLLIN : POLLOUT;
|
||||
|
||||
// this is ignored by poll, but our select based poll wrapper on Windows needs it
|
||||
fds[0].events |= POLLERR;
|
||||
|
||||
// File descriptor used to interrupt select when needed
|
||||
@ -132,6 +135,11 @@ namespace ix
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (sockfd != -1 && (fds[0].revents & POLLERR || fds[0].revents & POLLHUP ||
|
||||
fds[0].revents & POLLNVAL))
|
||||
{
|
||||
pollResult = PollResultType::Error;
|
||||
}
|
||||
|
||||
return pollResult;
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ namespace ix
|
||||
virtual void close();
|
||||
|
||||
virtual ssize_t send(char* buffer, size_t length);
|
||||
virtual ssize_t send(const std::string& buffer);
|
||||
ssize_t send(const std::string& buffer);
|
||||
virtual ssize_t recv(void* buffer, size_t length);
|
||||
|
||||
// Blocking and cancellable versions, working with socket that can be set
|
||||
|
@ -265,11 +265,6 @@ namespace ix
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t SocketAppleSSL::send(const std::string& buffer)
|
||||
{
|
||||
return send((char*) &buffer[0], buffer.size());
|
||||
}
|
||||
|
||||
// No wait support
|
||||
ssize_t SocketAppleSSL::recv(void* buf, size_t nbyte)
|
||||
{
|
||||
|
@ -30,7 +30,6 @@ namespace ix
|
||||
virtual void close() final;
|
||||
|
||||
virtual ssize_t send(char* buffer, size_t length) final;
|
||||
virtual ssize_t send(const std::string& buffer) final;
|
||||
virtual ssize_t recv(void* buffer, size_t length) final;
|
||||
|
||||
private:
|
||||
|
@ -14,8 +14,6 @@
|
||||
#include "IXSocketOpenSSL.h"
|
||||
#elif __APPLE__
|
||||
#include "IXSocketAppleSSL.h"
|
||||
#elif defined(_WIN32)
|
||||
#include "IXSocketSChannel.h"
|
||||
#endif
|
||||
|
||||
#else
|
||||
@ -46,8 +44,6 @@ namespace ix
|
||||
socket = std::make_shared<SocketMbedTLS>(tlsOptions, fd);
|
||||
#elif defined(IXWEBSOCKET_USE_OPEN_SSL)
|
||||
socket = std::make_shared<SocketOpenSSL>(tlsOptions, fd);
|
||||
#elif defined(_WIN32)
|
||||
socket = std::make_shared<SocketSChannel>(tlsOptions, fd);
|
||||
#elif defined(__APPLE__)
|
||||
socket = std::make_shared<SocketAppleSSL>(tlsOptions, fd);
|
||||
#endif
|
||||
|
@ -230,35 +230,23 @@ namespace ix
|
||||
|
||||
ssize_t SocketMbedTLS::send(char* buf, size_t nbyte)
|
||||
{
|
||||
ssize_t sent = 0;
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
while (nbyte > 0)
|
||||
ssize_t res = mbedtls_ssl_write(&_ssl, (unsigned char*) buf, nbyte);
|
||||
|
||||
if (res > 0)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
ssize_t res = mbedtls_ssl_write(&_ssl, (unsigned char*) buf, nbyte);
|
||||
|
||||
if (res > 0)
|
||||
{
|
||||
nbyte -= res;
|
||||
sent += res;
|
||||
}
|
||||
else if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE)
|
||||
{
|
||||
errno = EWOULDBLOCK;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
else if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE)
|
||||
{
|
||||
errno = EWOULDBLOCK;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
ssize_t SocketMbedTLS::send(const std::string& buffer)
|
||||
{
|
||||
return send((char*) &buffer[0], buffer.size());
|
||||
}
|
||||
|
||||
ssize_t SocketMbedTLS::recv(void* buf, size_t nbyte)
|
||||
|
@ -35,7 +35,6 @@ namespace ix
|
||||
virtual void close() final;
|
||||
|
||||
virtual ssize_t send(char* buffer, size_t length) final;
|
||||
virtual ssize_t send(const std::string& buffer) final;
|
||||
virtual ssize_t recv(void* buffer, size_t length) final;
|
||||
|
||||
private:
|
||||
|
@ -131,8 +131,14 @@ namespace ix
|
||||
SSL_CTX_set_mode(ctx,
|
||||
SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
|
||||
SSL_CTX_set_options(
|
||||
ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
int options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_CIPHER_SERVER_PREFERENCE;
|
||||
|
||||
#ifdef SSL_OP_NO_TLSv1_3
|
||||
// (partially?) work around hang in openssl 1.1.1b, by disabling TLS V1.3
|
||||
// https://github.com/openssl/openssl/issues/7967
|
||||
options |= SSL_OP_NO_TLSv1_3;
|
||||
#endif
|
||||
SSL_CTX_set_options(ctx, options);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
@ -603,42 +609,30 @@ namespace ix
|
||||
|
||||
ssize_t SocketOpenSSL::send(char* buf, size_t nbyte)
|
||||
{
|
||||
ssize_t sent = 0;
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
while (nbyte > 0)
|
||||
if (_ssl_connection == nullptr || _ssl_context == nullptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
if (_ssl_connection == nullptr || _ssl_context == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ERR_clear_error();
|
||||
ssize_t write_result = SSL_write(_ssl_connection, buf + sent, (int) nbyte);
|
||||
int reason = SSL_get_error(_ssl_connection, (int) write_result);
|
||||
|
||||
if (reason == SSL_ERROR_NONE)
|
||||
{
|
||||
nbyte -= write_result;
|
||||
sent += write_result;
|
||||
}
|
||||
else if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
errno = EWOULDBLOCK;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
ssize_t SocketOpenSSL::send(const std::string& buffer)
|
||||
{
|
||||
return send((char*) &buffer[0], buffer.size());
|
||||
ERR_clear_error();
|
||||
ssize_t write_result = SSL_write(_ssl_connection, buf, (int) nbyte);
|
||||
int reason = SSL_get_error(_ssl_connection, (int) write_result);
|
||||
|
||||
if (reason == SSL_ERROR_NONE)
|
||||
{
|
||||
return write_result;
|
||||
}
|
||||
else if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
errno = EWOULDBLOCK;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t SocketOpenSSL::recv(void* buf, size_t nbyte)
|
||||
|
@ -33,7 +33,6 @@ namespace ix
|
||||
virtual void close() final;
|
||||
|
||||
virtual ssize_t send(char* buffer, size_t length) final;
|
||||
virtual ssize_t send(const std::string& buffer) final;
|
||||
virtual ssize_t recv(void* buffer, size_t length) final;
|
||||
|
||||
private:
|
||||
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* IXSocketSChannel.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||
*
|
||||
* See https://docs.microsoft.com/en-us/windows/desktop/WinSock/using-secure-socket-extensions
|
||||
*
|
||||
* https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/netds/winsock/securesocket/stcpclient/tcpclient.c
|
||||
*
|
||||
* This is the right example to look at:
|
||||
* https://www.codeproject.com/Articles/1000189/A-Working-TCP-Client-and-Server-With-SSL
|
||||
*
|
||||
* Similar code is available from this git repo
|
||||
* https://github.com/david-maw/StreamSSL
|
||||
*/
|
||||
#include "IXSocketSChannel.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <WS2tcpip.h>
|
||||
#include <WinSock2.h>
|
||||
#include <basetsd.h>
|
||||
#include <io.h>
|
||||
#include <schannel.h>
|
||||
#include <ws2def.h>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#ifndef UNICODE
|
||||
#define UNICODE
|
||||
#endif
|
||||
|
||||
#include <mstcpip.h>
|
||||
#include <ntdsapi.h>
|
||||
#include <rpc.h>
|
||||
#include <stdio.h>
|
||||
#include <tchar.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#define RECV_DATA_BUF_SIZE 256
|
||||
|
||||
// Link with ws2_32.lib
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
|
||||
// link with fwpuclnt.lib for Winsock secure socket extensions
|
||||
#pragma comment(lib, "fwpuclnt.lib")
|
||||
|
||||
// link with ntdsapi.lib for DsMakeSpn function
|
||||
#pragma comment(lib, "ntdsapi.lib")
|
||||
|
||||
// The following function assumes that Winsock
|
||||
// has already been initialized
|
||||
|
||||
|
||||
#else
|
||||
#error("This file should only be built on Windows")
|
||||
#endif
|
||||
|
||||
namespace ix
|
||||
{
|
||||
SocketSChannel::SocketSChannel()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
SocketSChannel::~SocketSChannel()
|
||||
{
|
||||
}
|
||||
|
||||
bool SocketSChannel::connect(const std::string& host, int port, std::string& errMsg)
|
||||
{
|
||||
return Socket::connect(host, port, errMsg, nullptr);
|
||||
}
|
||||
|
||||
|
||||
void SocketSChannel::secureSocket()
|
||||
{
|
||||
// there will be a lot to do here ...
|
||||
}
|
||||
|
||||
void SocketSChannel::close()
|
||||
{
|
||||
Socket::close();
|
||||
}
|
||||
|
||||
ssize_t SocketSChannel::send(char* buf, size_t nbyte)
|
||||
{
|
||||
return Socket::send(buf, nbyte);
|
||||
}
|
||||
|
||||
ssize_t SocketSChannel::send(const std::string& buffer)
|
||||
{
|
||||
return Socket::send(buffer);
|
||||
}
|
||||
|
||||
ssize_t SocketSChannel::recv(void* buf, size_t nbyte)
|
||||
{
|
||||
return Socket::recv(buf, nbyte);
|
||||
}
|
||||
|
||||
} // namespace ix
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* IXSocketSChannel.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IXSocket.h"
|
||||
|
||||
namespace ix
|
||||
{
|
||||
class SocketSChannel final : public Socket
|
||||
{
|
||||
public:
|
||||
SocketSChannel();
|
||||
~SocketSChannel();
|
||||
|
||||
virtual bool connect(const std::string& host, int port, std::string& errMsg) final;
|
||||
virtual void close() final;
|
||||
|
||||
// The important override
|
||||
virtual void secureSocket() final;
|
||||
|
||||
virtual ssize_t send(char* buffer, size_t length) final;
|
||||
virtual ssize_t send(const std::string& buffer) final;
|
||||
virtual ssize_t recv(void* buffer, size_t length) final;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace ix
|
@ -7,6 +7,7 @@
|
||||
#include "IXSocketServer.h"
|
||||
|
||||
#include "IXNetSystem.h"
|
||||
#include "IXSetThreadName.h"
|
||||
#include "IXSocket.h"
|
||||
#include "IXSocketConnect.h"
|
||||
#include "IXSocketFactory.h"
|
||||
@ -21,15 +22,15 @@ namespace ix
|
||||
const std::string SocketServer::kDefaultHost("127.0.0.1");
|
||||
const int SocketServer::kDefaultTcpBacklog(5);
|
||||
const size_t SocketServer::kDefaultMaxConnections(32);
|
||||
const int SocketServer::kDefaultAddressFamily(AF_INET);
|
||||
|
||||
SocketServer::SocketServer(int port,
|
||||
const std::string& host,
|
||||
int backlog,
|
||||
size_t maxConnections)
|
||||
SocketServer::SocketServer(
|
||||
int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
|
||||
: _port(port)
|
||||
, _host(host)
|
||||
, _backlog(backlog)
|
||||
, _maxConnections(maxConnections)
|
||||
, _addressFamily(addressFamily)
|
||||
, _serverFd(-1)
|
||||
, _stop(false)
|
||||
, _stopGc(false)
|
||||
@ -56,10 +57,15 @@ namespace ix
|
||||
|
||||
std::pair<bool, std::string> SocketServer::listen()
|
||||
{
|
||||
struct sockaddr_in server; // server address information
|
||||
if (_addressFamily != AF_INET && _addressFamily != AF_INET6)
|
||||
{
|
||||
std::string errMsg("SocketServer::listen() AF_INET and AF_INET6 are currently "
|
||||
"the only supported address families");
|
||||
return std::make_pair(false, errMsg);
|
||||
}
|
||||
|
||||
// Get a socket for accepting connections.
|
||||
if ((_serverFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
if ((_serverFd = socket(_addressFamily, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SocketServer::listen() error creating socket): " << strerror(Socket::getErrno());
|
||||
@ -79,27 +85,63 @@ namespace ix
|
||||
return std::make_pair(false, ss.str());
|
||||
}
|
||||
|
||||
// Bind the socket to the server address.
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons(_port);
|
||||
|
||||
// Using INADDR_ANY trigger a pop-up box as binding to any address is detected
|
||||
// by the osx firewall. We need to codesign the binary with a self-signed cert
|
||||
// to allow that, but this is a bit of a pain. (this is what node or python would do).
|
||||
//
|
||||
// Using INADDR_LOOPBACK also does not work ... while it should.
|
||||
// We default to 127.0.0.1 (localhost)
|
||||
//
|
||||
server.sin_addr.s_addr = inet_addr(_host.c_str());
|
||||
|
||||
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
|
||||
if (_addressFamily == AF_INET)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SocketServer::listen() error calling bind "
|
||||
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
|
||||
struct sockaddr_in server;
|
||||
server.sin_family = _addressFamily;
|
||||
server.sin_port = htons(_port);
|
||||
|
||||
Socket::closeSocket(_serverFd);
|
||||
return std::make_pair(false, ss.str());
|
||||
if (inet_pton(_addressFamily, _host.c_str(), &server.sin_addr.s_addr) <= 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SocketServer::listen() error calling inet_pton "
|
||||
<< "at address " << _host << ":" << _port << " : "
|
||||
<< strerror(Socket::getErrno());
|
||||
|
||||
Socket::closeSocket(_serverFd);
|
||||
return std::make_pair(false, ss.str());
|
||||
}
|
||||
|
||||
// Bind the socket to the server address.
|
||||
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SocketServer::listen() error calling bind "
|
||||
<< "at address " << _host << ":" << _port << " : "
|
||||
<< strerror(Socket::getErrno());
|
||||
|
||||
Socket::closeSocket(_serverFd);
|
||||
return std::make_pair(false, ss.str());
|
||||
}
|
||||
}
|
||||
else // AF_INET6
|
||||
{
|
||||
struct sockaddr_in6 server;
|
||||
server.sin6_family = _addressFamily;
|
||||
server.sin6_port = htons(_port);
|
||||
|
||||
if (inet_pton(_addressFamily, _host.c_str(), &server.sin6_addr) <= 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SocketServer::listen() error calling inet_pton "
|
||||
<< "at address " << _host << ":" << _port << " : "
|
||||
<< strerror(Socket::getErrno());
|
||||
|
||||
Socket::closeSocket(_serverFd);
|
||||
return std::make_pair(false, ss.str());
|
||||
}
|
||||
|
||||
// Bind the socket to the server address.
|
||||
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "SocketServer::listen() error calling bind "
|
||||
<< "at address " << _host << ":" << _port << " : "
|
||||
<< strerror(Socket::getErrno());
|
||||
|
||||
Socket::closeSocket(_serverFd);
|
||||
return std::make_pair(false, ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@ -206,6 +248,8 @@ namespace ix
|
||||
// Set the socket to non blocking mode, so that accept calls are not blocking
|
||||
SocketConnect::configure(_serverFd);
|
||||
|
||||
setThreadName("SocketServer::listen");
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (_stop) return;
|
||||
@ -306,6 +350,8 @@ namespace ix
|
||||
|
||||
void SocketServer::runGC()
|
||||
{
|
||||
setThreadName("SocketServer::GC");
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// Garbage collection to shutdown/join threads for closed connections.
|
||||
|
@ -36,7 +36,8 @@ namespace ix
|
||||
SocketServer(int port = SocketServer::kDefaultPort,
|
||||
const std::string& host = SocketServer::kDefaultHost,
|
||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections);
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections,
|
||||
int addressFamily = SocketServer::kDefaultAddressFamily);
|
||||
virtual ~SocketServer();
|
||||
virtual void stop();
|
||||
|
||||
@ -49,6 +50,7 @@ namespace ix
|
||||
const static std::string kDefaultHost;
|
||||
const static int kDefaultTcpBacklog;
|
||||
const static size_t kDefaultMaxConnections;
|
||||
const static int kDefaultAddressFamily;
|
||||
|
||||
void start();
|
||||
std::pair<bool, std::string> listen();
|
||||
@ -69,6 +71,7 @@ namespace ix
|
||||
std::string _host;
|
||||
int _backlog;
|
||||
size_t _maxConnections;
|
||||
int _addressFamily;
|
||||
|
||||
// socket for accepting connections
|
||||
int _serverFd;
|
||||
|
@ -134,6 +134,13 @@ namespace ix
|
||||
_enablePong = false;
|
||||
}
|
||||
|
||||
void WebSocket::enablePerMessageDeflate()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configMutex);
|
||||
WebSocketPerMessageDeflateOptions perMessageDeflateOptions(true);
|
||||
_perMessageDeflateOptions = perMessageDeflateOptions;
|
||||
}
|
||||
|
||||
void WebSocket::disablePerMessageDeflate()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configMutex);
|
||||
@ -169,6 +176,7 @@ namespace ix
|
||||
// wait until working thread will exit
|
||||
// it will exit after close operation is finished
|
||||
_stop = true;
|
||||
_sleepCondition.notify_one();
|
||||
_thread.join();
|
||||
_stop = false;
|
||||
}
|
||||
@ -190,9 +198,19 @@ namespace ix
|
||||
auto subProtocols = getSubProtocols();
|
||||
if (!subProtocols.empty())
|
||||
{
|
||||
//
|
||||
// Sub Protocol strings are comma separated.
|
||||
// Python code to do that is:
|
||||
// >>> ','.join(['json', 'msgpack'])
|
||||
// 'json,msgpack'
|
||||
//
|
||||
int i = 0;
|
||||
for (auto subProtocol : subProtocols)
|
||||
{
|
||||
subProtocolsHeader += ",";
|
||||
if (i++ != 0)
|
||||
{
|
||||
subProtocolsHeader += ",";
|
||||
}
|
||||
subProtocolsHeader += subProtocol;
|
||||
}
|
||||
headers["Sec-WebSocket-Protocol"] = subProtocolsHeader;
|
||||
@ -282,8 +300,13 @@ namespace ix
|
||||
// Only sleep if we are retrying
|
||||
if (duration.count() > 0)
|
||||
{
|
||||
// to do: make sleeping conditional
|
||||
std::this_thread::sleep_for(duration);
|
||||
std::unique_lock<std::mutex> lock(_sleepMutex);
|
||||
_sleepCondition.wait_for(lock, duration);
|
||||
}
|
||||
|
||||
if (_stop)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to connect synchronously
|
||||
@ -389,7 +412,7 @@ namespace ix
|
||||
WebSocketCloseInfo(),
|
||||
binary));
|
||||
|
||||
WebSocket::invokeTrafficTrackerCallback(msg.size(), true);
|
||||
WebSocket::invokeTrafficTrackerCallback(wireSize, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "IXWebSocketSendInfo.h"
|
||||
#include "IXWebSocketTransport.h"
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@ -56,6 +57,7 @@ namespace ix
|
||||
void setPingTimeout(int pingTimeoutSecs);
|
||||
void enablePong();
|
||||
void disablePong();
|
||||
void enablePerMessageDeflate();
|
||||
void disablePerMessageDeflate();
|
||||
void addSubProtocol(const std::string& subProtocol);
|
||||
|
||||
@ -70,7 +72,7 @@ namespace ix
|
||||
WebSocketInitResult connect(int timeoutSecs);
|
||||
void run();
|
||||
|
||||
// send is in binary mode by default
|
||||
// send is in text mode by default
|
||||
WebSocketSendInfo send(const std::string& data,
|
||||
bool binary = false,
|
||||
const OnProgressCallback& onProgressCallback = nullptr);
|
||||
@ -140,6 +142,10 @@ namespace ix
|
||||
static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries;
|
||||
uint32_t _maxWaitBetweenReconnectionRetries;
|
||||
|
||||
// Make the sleeping in the automatic reconnection cancellable
|
||||
std::mutex _sleepMutex;
|
||||
std::condition_variable _sleepCondition;
|
||||
|
||||
std::atomic<int> _handshakeTimeoutSecs;
|
||||
static const int kDefaultHandShakeTimeoutSecs;
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "IXUserAgent.h"
|
||||
#include "libwshandshake.hpp"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
@ -335,7 +336,7 @@ namespace ix
|
||||
std::string header = headers["sec-websocket-extensions"];
|
||||
WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(header);
|
||||
|
||||
// If the client has requested that extension, enable it.
|
||||
// If the client has requested that extension,
|
||||
if (webSocketPerMessageDeflateOptions.enabled())
|
||||
{
|
||||
_enablePerMessageDeflate = true;
|
||||
|
@ -66,12 +66,23 @@ namespace ix
|
||||
{
|
||||
line[i] = '\0';
|
||||
std::string lineStr(line);
|
||||
// colon is ':', colon+1 is ' ', colon+2 is the start of the value.
|
||||
// colon is ':', usually colon+1 is ' ', and colon+2 is the start of the value.
|
||||
// some webservers do not put a space after the colon character, so
|
||||
// the start of the value might be farther than colon+2.
|
||||
// The spec says that space after the : should be discarded.
|
||||
// i is end of string (\0), i-colon is length of string minus key;
|
||||
// subtract 1 for '\0', 1 for '\n', 1 for '\r',
|
||||
// 1 for the ' ' after the ':', and total is -4
|
||||
// since we use an std::string later on and don't account for '\0',
|
||||
// plus the optional first space, total is -2
|
||||
int start = colon + 1;
|
||||
while (lineStr[start] == ' ')
|
||||
{
|
||||
start++;
|
||||
}
|
||||
|
||||
std::string name(lineStr.substr(0, colon));
|
||||
std::string value(lineStr.substr(colon + 2, i - colon - 4));
|
||||
std::string value(lineStr.substr(start, lineStr.size() - start - 2));
|
||||
|
||||
headers[name] = value;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "IXWebSocketServer.h"
|
||||
|
||||
#include "IXNetSystem.h"
|
||||
#include "IXSetThreadName.h"
|
||||
#include "IXSocketConnect.h"
|
||||
#include "IXWebSocket.h"
|
||||
#include "IXWebSocketTransport.h"
|
||||
@ -23,10 +24,12 @@ namespace ix
|
||||
const std::string& host,
|
||||
int backlog,
|
||||
size_t maxConnections,
|
||||
int handshakeTimeoutSecs)
|
||||
: SocketServer(port, host, backlog, maxConnections)
|
||||
int handshakeTimeoutSecs,
|
||||
int addressFamily)
|
||||
: SocketServer(port, host, backlog, maxConnections, addressFamily)
|
||||
, _handshakeTimeoutSecs(handshakeTimeoutSecs)
|
||||
, _enablePong(kDefaultEnablePong)
|
||||
, _enablePerMessageDeflate(true)
|
||||
{
|
||||
}
|
||||
|
||||
@ -58,6 +61,11 @@ namespace ix
|
||||
_enablePong = false;
|
||||
}
|
||||
|
||||
void WebSocketServer::disablePerMessageDeflate()
|
||||
{
|
||||
_enablePerMessageDeflate = false;
|
||||
}
|
||||
|
||||
void WebSocketServer::setOnConnectionCallback(const OnConnectionCallback& callback)
|
||||
{
|
||||
_onConnectionCallback = callback;
|
||||
@ -66,15 +74,21 @@ namespace ix
|
||||
void WebSocketServer::handleConnection(std::shared_ptr<Socket> socket,
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
{
|
||||
setThreadName("WebSocketServer::" + connectionState->getId());
|
||||
|
||||
auto webSocket = std::make_shared<WebSocket>();
|
||||
_onConnectionCallback(webSocket, connectionState);
|
||||
|
||||
webSocket->disableAutomaticReconnection();
|
||||
|
||||
if (_enablePong)
|
||||
{
|
||||
webSocket->enablePong();
|
||||
}
|
||||
else
|
||||
{
|
||||
webSocket->disablePong();
|
||||
}
|
||||
|
||||
// Add this client to our client set
|
||||
{
|
||||
@ -106,7 +120,6 @@ namespace ix
|
||||
}
|
||||
}
|
||||
|
||||
logInfo("WebSocketServer::handleConnection() done");
|
||||
connectionState->setTerminated();
|
||||
}
|
||||
|
||||
|
@ -29,29 +29,33 @@ namespace ix
|
||||
const std::string& host = SocketServer::kDefaultHost,
|
||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections,
|
||||
int handshakeTimeoutSecs = WebSocketServer::kDefaultHandShakeTimeoutSecs);
|
||||
int handshakeTimeoutSecs = WebSocketServer::kDefaultHandShakeTimeoutSecs,
|
||||
int addressFamily = SocketServer::kDefaultAddressFamily);
|
||||
virtual ~WebSocketServer();
|
||||
virtual void stop() final;
|
||||
|
||||
void enablePong();
|
||||
void disablePong();
|
||||
void disablePerMessageDeflate();
|
||||
|
||||
void setOnConnectionCallback(const OnConnectionCallback& callback);
|
||||
|
||||
// Get all the connected clients
|
||||
std::set<std::shared_ptr<WebSocket>> getClients();
|
||||
|
||||
const static int kDefaultHandShakeTimeoutSecs;
|
||||
|
||||
private:
|
||||
// Member variables
|
||||
int _handshakeTimeoutSecs;
|
||||
bool _enablePong;
|
||||
bool _enablePerMessageDeflate;
|
||||
|
||||
OnConnectionCallback _onConnectionCallback;
|
||||
|
||||
std::mutex _clientsMutex;
|
||||
std::set<std::shared_ptr<WebSocket>> _clients;
|
||||
|
||||
const static int kDefaultHandShakeTimeoutSecs;
|
||||
const static bool kDefaultEnablePong;
|
||||
|
||||
// Methods
|
||||
|
@ -350,28 +350,9 @@ namespace ix
|
||||
}
|
||||
else if (pollResult == PollResultType::ReadyForRead)
|
||||
{
|
||||
while (true)
|
||||
if (!receiveFromSocket())
|
||||
{
|
||||
ssize_t ret = _socket->recv((char*) &_readbuf[0], _readbuf.size());
|
||||
|
||||
if (ret < 0 && Socket::isWaitNeeded())
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (ret <= 0)
|
||||
{
|
||||
// if there are received data pending to be processed, then delay the abnormal
|
||||
// closure to after dispatch (other close code/reason could be read from the
|
||||
// buffer)
|
||||
|
||||
closeSocket();
|
||||
|
||||
return PollResult::AbnormalClose;
|
||||
}
|
||||
else
|
||||
{
|
||||
_rxbuf.insert(_rxbuf.end(), _readbuf.begin(), _readbuf.begin() + ret);
|
||||
}
|
||||
return PollResult::AbnormalClose;
|
||||
}
|
||||
}
|
||||
else if (pollResult == PollResultType::Error)
|
||||
@ -739,7 +720,7 @@ namespace ix
|
||||
|
||||
// if an abnormal closure was raised in poll, and nothing else triggered a CLOSED state in
|
||||
// the received and processed data then close the connection
|
||||
if (pollResult == PollResult::AbnormalClose)
|
||||
if (pollResult != PollResult::Succeeded)
|
||||
{
|
||||
_rxbuf.clear();
|
||||
|
||||
@ -1053,19 +1034,17 @@ namespace ix
|
||||
wsheader_type::TEXT_FRAME, message, _enablePerMessageDeflate, onProgressCallback);
|
||||
}
|
||||
|
||||
ssize_t WebSocketTransport::send()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_socketMutex);
|
||||
return _socket->send((char*) &_txbuf[0], _txbuf.size());
|
||||
}
|
||||
|
||||
bool WebSocketTransport::sendOnSocket()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_txbufMutex);
|
||||
|
||||
while (_txbuf.size())
|
||||
{
|
||||
ssize_t ret = send();
|
||||
ssize_t ret = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_socketMutex);
|
||||
ret = _socket->send((char*) &_txbuf[0], _txbuf.size());
|
||||
}
|
||||
|
||||
if (ret < 0 && Socket::isWaitNeeded())
|
||||
{
|
||||
@ -1086,6 +1065,34 @@ namespace ix
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebSocketTransport::receiveFromSocket()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ssize_t ret = _socket->recv((char*) &_readbuf[0], _readbuf.size());
|
||||
|
||||
if (ret < 0 && Socket::isWaitNeeded())
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (ret <= 0)
|
||||
{
|
||||
// if there are received data pending to be processed, then delay the abnormal
|
||||
// closure to after dispatch (other close code/reason could be read from the
|
||||
// buffer)
|
||||
|
||||
closeSocket();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_rxbuf.insert(_rxbuf.end(), _readbuf.begin(), _readbuf.begin() + ret);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebSocketTransport::sendCloseFrame(uint16_t code, const std::string& reason)
|
||||
{
|
||||
bool compress = false;
|
||||
|
@ -99,7 +99,6 @@ namespace ix
|
||||
bool remote = false);
|
||||
|
||||
void closeSocket();
|
||||
ssize_t send();
|
||||
|
||||
ReadyState getReadyState() const;
|
||||
void setReadyState(ReadyState readyState);
|
||||
@ -245,6 +244,8 @@ namespace ix
|
||||
|
||||
bool flushSendBuffer();
|
||||
bool sendOnSocket();
|
||||
bool receiveFromSocket();
|
||||
|
||||
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
||||
const std::string& message,
|
||||
bool compress,
|
||||
|
@ -6,4 +6,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define IX_WEBSOCKET_VERSION "7.9.3"
|
||||
#define IX_WEBSOCKET_VERSION "8.2.3"
|
||||
|
3
makefile
3
makefile
@ -49,6 +49,9 @@ BUILD := ${NAME}:build
|
||||
print_version:
|
||||
@echo 'IXWebSocket version =>' ${TAG}
|
||||
|
||||
set_version:
|
||||
sh tools/update_version.sh ${VERSION}
|
||||
|
||||
docker_test:
|
||||
docker build -f docker/Dockerfile.debian -t bsergean/ixwebsocket_test:build .
|
||||
|
||||
|
20
test/.certs/selfsigned-client-crt.pem
Normal file
20
test/.certs/selfsigned-client-crt.pem
Normal file
@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQTCCAimgAwIBAgIUNJBwOQdDle1TI/MHGd+cSpxIllwwDQYJKoZIhvcNAQEL
|
||||
BQAwSDEUMBIGA1UECgwLbWFjaGluZXpvbmUxFDASBgNVBAoMC0lYV2ViU29ja2V0
|
||||
MRowGAYDVQQDDBFzZWxmc2lnbmVkLWNsaWVudDAeFw0yMDAzMTIyMzA0MzdaFw0y
|
||||
MTAzMTIyMzA0MzdaMEgxFDASBgNVBAoMC21hY2hpbmV6b25lMRQwEgYDVQQKDAtJ
|
||||
WFdlYlNvY2tldDEaMBgGA1UEAwwRc2VsZnNpZ25lZC1jbGllbnQwggEiMA0GCSqG
|
||||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7q6W0f5vRSHaNOuM1VQpY0rC0a5u04J5Z
|
||||
nssUD1QfgilY1UEaaR/4K6ILE4oClqeDsQy/7+04Wt6i/ttceB/k1Jk6n0kgdtvA
|
||||
CsX1H+nA7JL7ANBZvQ6W2E1mwJieTDSVDgL4YB9qzJQu3PdwZJgm5GTlVK66DMr1
|
||||
IH2EYwu73M/ZwOzfgyd7m0TcgkRV8OHiD1dVDERNQR9gzDUsBtCoWPmzXxgPMOSE
|
||||
Oq1sEhNC0bPaG3zTDvCv0t4Hti33po/U8PZwOtz2b8StSjS5BnvEDnksAtEZuNEu
|
||||
4B3KJN4Oxrtgh7DYdiF7S9Gh0dN6yqtRfDWkGyC9WkyoqpFKCM4fAgMBAAGjIzAh
|
||||
MB8GA1UdEQQYMBaCCWxvY2FsaG9zdIIJMTI3LjAuMC4xMA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQB4oIutDYbCRfsyWRAiAY+D9rhYsJYlsQjyml1q2+pCv7BJ1kWsKk7m2VMX
|
||||
Tl6CM+PI0zXPpLN6Ot79jf/jxEbDMvqrBgGpYfddvLhyTFnzIZpG8d63RvzPADF6
|
||||
lV3x34eZf/EdtrWgZAHK+5oZjtzePGHwKDFIPva9nvJXYIxNwKYWGRX8HSm0OZi2
|
||||
FQiaOt6WYLo7ZdefNPS9nugFRM6hfztJe6WvvglKm+BTnHbCSKj5xRuT9iA80+jX
|
||||
Zij7po8opY3S+zEZ0eNUCHxMBQ+2Jdq3HxggJ2cFQVRHdvKfwzmavVeGgni75d16
|
||||
+xFD5nS3g3eIEME+lZ8c8GbL0AJ4
|
||||
-----END CERTIFICATE-----
|
27
test/.certs/selfsigned-client-key.pem
Normal file
27
test/.certs/selfsigned-client-key.pem
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAu6ultH+b0Uh2jTrjNVUKWNKwtGubtOCeWZ7LFA9UH4IpWNVB
|
||||
Gmkf+CuiCxOKApang7EMv+/tOFreov7bXHgf5NSZOp9JIHbbwArF9R/pwOyS+wDQ
|
||||
Wb0OlthNZsCYnkw0lQ4C+GAfasyULtz3cGSYJuRk5VSuugzK9SB9hGMLu9zP2cDs
|
||||
34Mne5tE3IJEVfDh4g9XVQxETUEfYMw1LAbQqFj5s18YDzDkhDqtbBITQtGz2ht8
|
||||
0w7wr9LeB7Yt96aP1PD2cDrc9m/ErUo0uQZ7xA55LALRGbjRLuAdyiTeDsa7YIew
|
||||
2HYhe0vRodHTesqrUXw1pBsgvVpMqKqRSgjOHwIDAQABAoIBAQC2q7IESj2x7TWv
|
||||
7ITiEZ+bq6DiTOfnnMeldkI3iWAZt0lltVXETlUW6+mznFY2hMwTDE/bt78Qnqqc
|
||||
vzNoA2kQBLwNaqP0XJ0zhYkAOwr9hYjflwA2iSZdP7e/b3JeitCX0WakunN6Mh1+
|
||||
rAiRtui+2os3CkF0yST4iqKCLSJrvSvvK5fU92aKEaE3k9kznBljvVOJIIRQBUPz
|
||||
G8tvtPgpLALrT7XMnGfaCyGS8c1IbFMm84KTxAlVV0bnuGgeYQ2VupqUmZpJjcJ0
|
||||
B08hr7vPfxz3UXSOKwYY8TRfmF3X370ky5Ov2I9ddg27V1QoeRTWlL7VMxRtiSer
|
||||
hoM5SPKpAoGBAO0vBd1Z6425wGT0PClUbJAVm2OBYMDnl2RmhBK5TAxrKjs9ag08
|
||||
65jfVCMD8wDMDhEbvbmzkgRa9BC6AY97JBmyr4m9oGfA7oenuou+a9LYAKqtO0ts
|
||||
hxHf2LnpC1HCyh4+l5gohjlUG7gSVu/oBhNTJNKmqUKuQ8v1b6My/JR9AoGBAMqP
|
||||
DugL9DusECncKHQbaIEzvEBe+QErcUxXxq+G4LLvFTZVvthHbrZ/0cxm5Ve6rfd2
|
||||
krqjYFA3WPOuTcKEUouNeRK2A4V6PbnSdpf0kagN6KbEjK66ZSZs8wnWitghqo7J
|
||||
n2IHcSDEEACTyjS7K8HjPx0fQGU1tzkG/7/xs3vLAoGAI61JEoyuE/l26TibvBPI
|
||||
6Lt3TjZt2VZ8vUt2XmKk/9E23wZT533canhdbY7whJQtIYGsvjw2oJUV1VZFWdHK
|
||||
EluAcBWoBTNOLfWa595S1bpMD2BTZPsELjofnYdifn/wazA7GVYvKnxuVvfbP+cE
|
||||
0u9UwKL1HuSbqhhXHJNUzvkCgYAeFRLsqWHTPuGDpfuoCq4BijJqCPDIGLCR2vNZ
|
||||
/BkA2fr3f9KBAlLR7be1uI5U8heGCekOqNbT8vRV9Ev+GHK94PvbKIbrWtUx9KzC
|
||||
MoMzRyWHJueRx4LgKwwJKQCjypQu8oimIV7Os++AdnJwVF/SQrKL26lPnqOgZ4ax
|
||||
9e5m8wKBgQCF626EmJk34+WTGEa5gdTx567Y+1EAbag+7fQSskwiRPvRN2fcg3H8
|
||||
ynUAtIgWbrecgKhblXxc7zwJrl41P71uQzCFspgvOPXMxL2xqN+tnTfuz84OXk26
|
||||
h1xSdS3e+JYsWUIxqbH1W59S+dC7KtklBAcUxb8DNpDoVjVBeAEqzw==
|
||||
-----END RSA PRIVATE KEY-----
|
21
test/.certs/trusted-ca-crt.pem
Normal file
21
test/.certs/trusted-ca-crt.pem
Normal file
@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDYzCCAkugAwIBAgIUD0V1mxoZF9TNpsoyvuHU2zhrg0wwDQYJKoZIhvcNAQEL
|
||||
BQAwQTEUMBIGA1UECgwLbWFjaGluZXpvbmUxFDASBgNVBAoMC0lYV2ViU29ja2V0
|
||||
MRMwEQYDVQQDDAp0cnVzdGVkLWNhMB4XDTIwMDMxMjIzMDQzN1oXDTMwMDMxMDIz
|
||||
MDQzN1owQTEUMBIGA1UECgwLbWFjaGluZXpvbmUxFDASBgNVBAoMC0lYV2ViU29j
|
||||
a2V0MRMwEQYDVQQDDAp0cnVzdGVkLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEAr2nVpfIzxsxK76Va+HaBfZ7aqjk90zipzH3/CWuMMN9wzBhg2HPE
|
||||
cRreq1vKm2M/L9CZH6y6fnr68n8lW4rDATmbH0GeY4OqI9jw/mfjL4jUsAxwRi4X
|
||||
kkk4G2nz1G81LvWLFXXAZlOxeHSZtpPh5OP1tNGiJNL4eGVxjlwFJIFwDvweJ/tW
|
||||
J7dh/FTzO0jqh8FheJTeJO64Gflqfln64WRUOPSpO7v4KmyesM/BGwGMfZjcwhs/
|
||||
KZT+OKXpPgYhdmAZJE24ftwWTP84DP9wnJbNqTRt0r5ud+q8EusKIjw/Pbf/tPUF
|
||||
7J0bkMp4y5/+7MMuIxeZ+s2uHdp6hmwdJQIDAQABo1MwUTAdBgNVHQ4EFgQUPARq
|
||||
Vm19yGgWqEnpNT1ILIkfWhEwHwYDVR0jBBgwFoAUPARqVm19yGgWqEnpNT1ILIkf
|
||||
WhEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEIsTvJhs6r2r
|
||||
x1xrHKaGo4sSuywJiZqMabvC9g22Xw3Cno5qGVFYWi4k0qjX/j9DN36DyOY1rei+
|
||||
kNBnOnLdtdNDltcvaLeA/9SeIhxRYOwjXpPzy9AqHpGZPui988qtptA+DI+IOLAm
|
||||
mQyssYC4doDcohMXaI7KumKHojTDAPrF2INJRTF9zWgbsFjvSWU5CY5CNERWCydh
|
||||
OXfzFylifScNOppioZL9VTa6At7R+MGg834kMi6WDIvtD6Ibn+pw0bV60aiMhBe8
|
||||
8qgZ8lxjGOHlvQrjqdk65smhfaECJcFJxybOSA3Z1f+Y9j/p0e0hyUJM/b/NouaE
|
||||
64H6vXczLQ==
|
||||
-----END CERTIFICATE-----
|
1
test/.certs/trusted-ca-crt.srl
Normal file
1
test/.certs/trusted-ca-crt.srl
Normal file
@ -0,0 +1 @@
|
||||
297E3BFAD1F1F96A60A2AF0F48B092E705C0C68A
|
27
test/.certs/trusted-ca-key.pem
Normal file
27
test/.certs/trusted-ca-key.pem
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAr2nVpfIzxsxK76Va+HaBfZ7aqjk90zipzH3/CWuMMN9wzBhg
|
||||
2HPEcRreq1vKm2M/L9CZH6y6fnr68n8lW4rDATmbH0GeY4OqI9jw/mfjL4jUsAxw
|
||||
Ri4Xkkk4G2nz1G81LvWLFXXAZlOxeHSZtpPh5OP1tNGiJNL4eGVxjlwFJIFwDvwe
|
||||
J/tWJ7dh/FTzO0jqh8FheJTeJO64Gflqfln64WRUOPSpO7v4KmyesM/BGwGMfZjc
|
||||
whs/KZT+OKXpPgYhdmAZJE24ftwWTP84DP9wnJbNqTRt0r5ud+q8EusKIjw/Pbf/
|
||||
tPUF7J0bkMp4y5/+7MMuIxeZ+s2uHdp6hmwdJQIDAQABAoIBAH4sPkUTJjMEl5Iw
|
||||
+nJlq1bUgKyYZ+QaiehRaLU56qjsz5G+p0qKWu6QSUIw0Fdc2AJopPunnq2DgCYV
|
||||
VqW19fZXnUCqTmd+OU93qEEWMM/sODA5gji4xrOufvEZEQ3ov/R7IgPZov73jFv8
|
||||
YuR1ErM1VXMuptad+aOANGIVxo0ubDEXKK/zhOfUUXQy7ZsEruJCCIpigULU159r
|
||||
sVOq2lwgLz+hClFBIq0IKAKqiPWpw2GtHtU5WtAo3qZEMJkNM5SppjDmS2Wy3qN6
|
||||
Gq6sXtlAmLFZAyVpXXklQK3mCaAs5gcV94nm+r++F884obaOtJ126uDdIKlL+A6k
|
||||
l41DXwECgYEA4KAswbdoa18J1Ql2QtwW3+knEaUO62JH11RO5VV02uiYv4v4mHmA
|
||||
prnl1jsfgbc3qfIlZWDLlNRovKCfQSj/HzOe4Hd+gEPiSYjA77PRqQeYTPXTf0Ml
|
||||
IQ3j9z1CdBWNoKJ18CEiIncvjpDYkdFf3RsawcnYXklXRjmm6kIJJzUCgYEAx+oA
|
||||
gm/xXK28P/CFksZzsseF5i/1MPdniyP3oY34DlEmDvl9ZA1Z52De8vojfNd9X12M
|
||||
ccjiGMMGgknJqncCB+uTWYFy2pWnr9dVVxf+oirAlT1Z03AkT5gxmIZ3FUQw8VkB
|
||||
HjKJYD1mpTwoSlc+DR3R0xNdl84nkUI2hxGErDECgYEAjdsZ6MyXGRfP8cYj9V1g
|
||||
5M8taStAHM7YZ9hKavJo9cZmkLEoscIpySElUQHNh/HZKW5Ox5M1fiwWaOlXKaNm
|
||||
WqIS99b/AKneQmomzjpVcdXmDNRCWOBilllbWkxJp13lL0jqClgiYnm6guJeotgD
|
||||
HnN7ll6OUh0nDKZkDxTdCvECgYEAtlQZet2WCKz70GURrjgJNbj7ymFbAvniGekH
|
||||
5PSSlJw2Vdn+Hs5+fKTBMmIpE6eF1QCBIxXQAD1/Jj0eDLbVx1t33F5P3kQ32AxQ
|
||||
7UoZFtZfJr35uvnAZEeulCmvWloDOVuvxVbaLEhT4cfoB0VidpwHzrcO2XFQbQ8y
|
||||
pCW6F0ECgYBbO0NU/Jlu3acIzGwAv69CMo8udwnWrhzGStZD67swdQ/yxHVpx2RH
|
||||
0sNk6UfLku8Mal7Pp+RglAmsOZEjSgk1V92J9lXYjYD8IUNwNyRRCpQ8xu0KPgDM
|
||||
XGeUca/Ao7jRVcsPOiqFH7wgfEjyzpO85X/K9BoBnA0EcUTOScaqmw==
|
||||
-----END RSA PRIVATE KEY-----
|
20
test/.certs/trusted-client-crt.pem
Normal file
20
test/.certs/trusted-client-crt.pem
Normal file
@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDNzCCAh+gAwIBAgIUKX47+tHx+Wpgoq8PSLCS5wXAxoowDQYJKoZIhvcNAQEL
|
||||
BQAwQTEUMBIGA1UECgwLbWFjaGluZXpvbmUxFDASBgNVBAoMC0lYV2ViU29ja2V0
|
||||
MRMwEQYDVQQDDAp0cnVzdGVkLWNhMB4XDTIwMDMxMjIzMDQzN1oXDTIxMDMxMjIz
|
||||
MDQzN1owRTEUMBIGA1UECgwLbWFjaGluZXpvbmUxFDASBgNVBAoMC0lYV2ViU29j
|
||||
a2V0MRcwFQYDVQQDDA50cnVzdGVkLWNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggEPADCCAQoCggEBALijaV0JhdoRAXnD5fX5W/9nZFb6jor6lIGW56Mdn+11ICYw
|
||||
GoJ7ATnygUwfBMepoD5RfJ5pkNxYewo8N5JR+8rb4V9atJCSYQLT8P7Dm2YNMtkq
|
||||
mNiRuRLrTqoPYajEzz5ENWSNnsjUB1GMGEpcCvRDgsTF24OsVV9BmLV166BEye7w
|
||||
ah+jk1YYJHbEnNT4wzr4drJSGEYh2aRO72yY+ROe49Tz/GVVXfCamcj88z5hOS/+
|
||||
+nCF/odLLB9Ij4xhR8WwTrwE/TxlkIQRBBPTsNetZjvMQZT+TkKw9nNjdoHiDlz9
|
||||
BLOYxovUIB8OtOQQfour8V7nwZ2bL9Pp51mnmBsCAwEAAaMjMCEwHwYDVR0RBBgw
|
||||
FoIJbG9jYWxob3N0ggkxMjcuMC4wLjEwDQYJKoZIhvcNAQELBQADggEBAFTus7o2
|
||||
fQuSMk52qXUESVWG4ygvd2scV58zRrLxZL7Ug9p4DIJo0cY59l3Vhwn2xDSYlAFi
|
||||
1h/qSEGkR2a0U2LzMK7BPSkqqYceSwnUvnwHvCwgH9aL1Rvk/4f1sFfsKegjScle
|
||||
wraYsRmpidEZJYICvokHev36mX3fHaZZEU+WIoTvChgu0OtD+qkI4DECywLgtB92
|
||||
/geabKC3C5JgiW0Jz8AScWoO2uKHFeuD2nfI1SiAbfMIAmG3RTanbZ8JMEVomVep
|
||||
txMNGnojun923KTEScnH3cQfnkJjm2AM5yKgT6I/OHELe9Gg7R0IOJbiPmSru7/k
|
||||
x5tBp3iMsZZ26VE=
|
||||
-----END CERTIFICATE-----
|
27
test/.certs/trusted-client-key.pem
Normal file
27
test/.certs/trusted-client-key.pem
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAuKNpXQmF2hEBecPl9flb/2dkVvqOivqUgZbnox2f7XUgJjAa
|
||||
gnsBOfKBTB8Ex6mgPlF8nmmQ3Fh7Cjw3klH7ytvhX1q0kJJhAtPw/sObZg0y2SqY
|
||||
2JG5EutOqg9hqMTPPkQ1ZI2eyNQHUYwYSlwK9EOCxMXbg6xVX0GYtXXroETJ7vBq
|
||||
H6OTVhgkdsSc1PjDOvh2slIYRiHZpE7vbJj5E57j1PP8ZVVd8JqZyPzzPmE5L/76
|
||||
cIX+h0ssH0iPjGFHxbBOvAT9PGWQhBEEE9Ow161mO8xBlP5OQrD2c2N2geIOXP0E
|
||||
s5jGi9QgHw605BB+i6vxXufBnZsv0+nnWaeYGwIDAQABAoIBAFQ2XAEOLdmW9ghW
|
||||
fBUjRX2I56/wGYFz5rXwYPf5tA625BHm0MCAX7/RRn20jBaQ3EBwJBmQZnzJclzp
|
||||
uCLpd6E/hlxaX46s5MhIaFuaVc9G59E653mnhTUG09smptE16pwouf2BxlEsu6XK
|
||||
8u0/a9Oa0xLydztoJ4wJvB/Ph8eRsbdbfL/ZAe+vk+bEp9ugyec3B5KTc+hWRneH
|
||||
BRfe239OX4mEhxNoO1tPJz1hJLjJH5F/iE1wkjSzLr1SI/cSbcbnyYj/kyXmktZw
|
||||
uaeFptkT6rB9GO0YunEPzzuQ4EEPpK9F63uu74dGqyW56STq26km7diAHhEpFdp1
|
||||
7X0rfHECgYEA5YPtjdqKEn5pQEdehqFnzi3IIu593o7baM6qEyFpMsTP53QCjUKX
|
||||
rrImyr2opfFKrXrI0IYXlDgOZApb2sKLoeP/wpfZiGSyqrzj+Y49cNRHjH643ClL
|
||||
Ri5eO6TRBukAW1gQFwuVBPbcnswaU6Ah85uTxqj+hO0g18rkuVdf72MCgYEAzfHH
|
||||
lb9TMf4DZEoL7GMpc4gDG9V66UWWzXyJB4CWHd6QX1vl6Ow5wE7q3fewD4SNgvDs
|
||||
DHZ8oqK2OMKJH/h/tqxyu+g1huajOhPqy1TIt5ncMjS0sguQ+7bQeHASKLxHhjPC
|
||||
YdqGMxOBQI5olWGq5U9Td5TYE95qk50KoIyNnekCgYEAkhMwa1tPC0w3UrjZuZga
|
||||
yEetHEZsB+0mSgNWjYxzNuO6atYUFbHvdjlepSSmpM74t4bxLn5ZnXU7+4H4SjgN
|
||||
xMCm9EPPKJbme/Jyqk9UXW5OB2ZT45PIm+dBBHb2ro43MuvOecxeUOWJLuw6SUUe
|
||||
trwrBoJiU1nU0GMKxceNgH8CgYEAzeNMpDG9S7ply6qXVwEf3Kd6bCY1leaDR/Wb
|
||||
zMtJyJzL+vmV1RHs/ownFDfeZPUgwGp5olAGdFV1FTOvAS5fB9JJdgBFGxOS1ao5
|
||||
zoN5kswYLn0wtNsJXAy9R9rK3Ly2SL2QNGHSTlfOnSqB9e3JeyyeBmvgxaRTKjYS
|
||||
/MTng5kCgYAIL79seoBnd9ZSp8A7QUBighxBn6DwrvLgexaysmC0zYqxbatczHk9
|
||||
iFbQRmPnFHhUt4URhxyhCoTgd7F0JpxklQODNfseVwtDiDMj8Fu8Tfmn6+9GdFRv
|
||||
0QEU+dR3gi98bO6G4IuAFGO9emXho3Snu6odRmh4HZVNOdLCuQe1Cw==
|
||||
-----END RSA PRIVATE KEY-----
|
20
test/.certs/trusted-server-crt.pem
Normal file
20
test/.certs/trusted-server-crt.pem
Normal file
@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDNzCCAh+gAwIBAgIUKX47+tHx+Wpgoq8PSLCS5wXAxokwDQYJKoZIhvcNAQEL
|
||||
BQAwQTEUMBIGA1UECgwLbWFjaGluZXpvbmUxFDASBgNVBAoMC0lYV2ViU29ja2V0
|
||||
MRMwEQYDVQQDDAp0cnVzdGVkLWNhMB4XDTIwMDMxMjIzMDQzN1oXDTIxMDMxMjIz
|
||||
MDQzN1owRTEUMBIGA1UECgwLbWFjaGluZXpvbmUxFDASBgNVBAoMC0lYV2ViU29j
|
||||
a2V0MRcwFQYDVQQDDA50cnVzdGVkLXNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggEPADCCAQoCggEBAMK5XAcHJwVSor1SfoMM5H5aNfNnM4JKq8kfAOl6KlXCsgs3
|
||||
bBcrJ24gEG6/goxkgLxhC1SXdbebt3Jay2lxAa9/7Uj87yztozSsctMkxXE0u3R+
|
||||
ih+9sP7ctpZ1hrF2Gv+ztd49/mXe1iRLPhkPijGpPlNsfie/TYybrw3WQlGH8jUm
|
||||
MnW12QUOzoBrIOCO6uIxFBJ1qiMq5mIBLlYOMj+MQubnQdvaQPNf1zaZWsCVGyTv
|
||||
95roHAb/s70Ie4r4ATcubtZs/ftjvzSmJegodTprPAedkrJ/k6Od9as7hpL37605
|
||||
haBU5pMyPNMWYi1MwYc9k0R0IpCKdyeX0huHfpkCAwEAAaMjMCEwHwYDVR0RBBgw
|
||||
FoIJbG9jYWxob3N0ggkxMjcuMC4wLjEwDQYJKoZIhvcNAQELBQADggEBAI41ZI4Z
|
||||
WbPFB1e+wIWQE7O2rJMeTEImjBOtcJEN3bqhsdE3Zqk0fPaE6jNz0Fp4IXqUYXzo
|
||||
SGsgBroV6sgknuLo8HdcTLcg8p9qZ3FGFHFQD1QYINn4ykupJZE2KcrIV8BZ/Tiv
|
||||
ciFrJ7i/qwpOrTRBV/w47yP3WZ3v8UdBnj5URD0v/yaAfkaReDO59Dlht/wyItQi
|
||||
GkDczMqMF1GTqcLqBwZdfpHq7B/UI8sp58a6eR9lOgryYCr+QJn7TcZrYzkcSWzg
|
||||
KE6VuzK6+NElvtg1hSST2Rc/RuuKzexsO/PLesVzaU/6NdDwXmpuSxeCiWm1mosA
|
||||
xfQZ9fSOQG6reFk=
|
||||
-----END CERTIFICATE-----
|
27
test/.certs/trusted-server-key.pem
Normal file
27
test/.certs/trusted-server-key.pem
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAwrlcBwcnBVKivVJ+gwzkflo182czgkqryR8A6XoqVcKyCzds
|
||||
FysnbiAQbr+CjGSAvGELVJd1t5u3clrLaXEBr3/tSPzvLO2jNKxy0yTFcTS7dH6K
|
||||
H72w/ty2lnWGsXYa/7O13j3+Zd7WJEs+GQ+KMak+U2x+J79NjJuvDdZCUYfyNSYy
|
||||
dbXZBQ7OgGsg4I7q4jEUEnWqIyrmYgEuVg4yP4xC5udB29pA81/XNplawJUbJO/3
|
||||
mugcBv+zvQh7ivgBNy5u1mz9+2O/NKYl6Ch1Oms8B52Ssn+To531qzuGkvfvrTmF
|
||||
oFTmkzI80xZiLUzBhz2TRHQikIp3J5fSG4d+mQIDAQABAoIBACFw4dwXH11rpqUq
|
||||
4K0y7p7AcVl+1LrAhiYBHA/8uf6GdDs25mpIL/paqVfLrejcbbtsUxzQ8hd5N5T9
|
||||
AMf371kreB27ynuFyCyInSOjwgDCFJtaC/CNjDMIxpaqUlpxtQtK2qXzMZhfH5mW
|
||||
DnERWSNUNG7xR+0djnziU7rlm/gSOxUA2gS/5ik9JXAx0yoML7HWlBbk0PJ5o8Ac
|
||||
sy6w/YwJVZAjXyUuYptPy2bK8WpGAsthw6RmW1fdOdDAUC3wz7TIKLuPD0AP9g7j
|
||||
u8grDYtD+U3ls1Z1Grow2UUG8CedotzVE8KIhDWi35aNiGIuaMFnnLf2OO/Mgd6G
|
||||
V82kkLECgYEA4sBtNsmlfFteFmHPS0s8wzg7lzLN15yifk7kO9GQcsbymXVSgU93
|
||||
XnvADAflly382pyMpr7Fb6V126iOx+YQhr6ya116S4UtAKq4kCau3Im6OedKefwx
|
||||
B71rST+vuAlUcv2ZcAVRJK8GtQQvwcAeI24ShPOXC+vyFAaaZ5eZo/0CgYEA29dZ
|
||||
LcREVlv2Tgy/YJVnZ7EYRGiheuF0rl0d0+Stggj34fSS1cexv3gMF13HBk2HpXfW
|
||||
3LfJyj2iGRZE9OUjN0ozVIVZWgZS/cwZbEUyl3o6IK0kPE4fZBaE16Onh4OGXKwr
|
||||
XTG+EjmIJRVRawECbLMONj5rHQLdcy+5YIH28c0CgYEA1XSL2y2MCTsBoVRGDf0v
|
||||
oB7JihYbTEN5fCnMFLu8nS/HpMqa9nvWRS19plWwvdZe13TTuwyPVACQqE1Oy8M5
|
||||
/354+zUuMPWXXa9YuuqPZbCJjIS8yYSsqzqXSocXZcnyo6Uz0g5PSpcxWyorwtqW
|
||||
BIhUCrA8ms5sPonQxIAj9AkCgYBd/6g7722g11VrbfvuWjOKnKhZp7tUBU6Ut2/n
|
||||
iCHANgF3ddHK4sXXrobM/uX4hfH4CFOwsEzx0oSa4XC+nbL/ExT7kMDxwz59EmXU
|
||||
a4oERtjP2/hgaK73ZsGKSol5Yf1zZpJsGLbCqCLUaFcVv6q/u5faDbpS/0Sc2c0T
|
||||
vL5QCQKBgQCL6ySxvEb5+zst/kRxXcnefXjoB+LSYsU4zy8WfkcP4r38AAQ2Hn+F
|
||||
f3/9BUX+2gNr99VDMjI+TUEf+NdQA/nFu4RbFvJ9Wpw9pXkIJpJkZ9g3Why4Ziji
|
||||
h0IrXm5JCet71+EIMwP0LhKJKrXZudlzP4DYMWmA7Avqb3HIdPXd7Q==
|
||||
-----END RSA PRIVATE KEY-----
|
21
test/.certs/untrusted-ca-crt.pem
Normal file
21
test/.certs/untrusted-ca-crt.pem
Normal file
@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDZzCCAk+gAwIBAgIUBEbp5x1IlwAV6OcQ/4xHk1Y+K4UwDQYJKoZIhvcNAQEL
|
||||
BQAwQzEUMBIGA1UECgwLbWFjaGluZXpvbmUxFDASBgNVBAoMC0lYV2ViU29ja2V0
|
||||
MRUwEwYDVQQDDAx1bnRydXN0ZWQtY2EwHhcNMjAwMzEyMjMwNDM3WhcNMzAwMzEw
|
||||
MjMwNDM3WjBDMRQwEgYDVQQKDAttYWNoaW5lem9uZTEUMBIGA1UECgwLSVhXZWJT
|
||||
b2NrZXQxFTATBgNVBAMMDHVudHJ1c3RlZC1jYTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggEPADCCAQoCggEBAMmmYROZf/Kg46b/0Zvq4pUY8ghUEA+eYit8dLyUZ/onoW4l
|
||||
xl3CK5NhJIer62Olv7QIu8WhU/hYoeE+8lLva9v0HaJgGjKmPQ3tyej319PzIc7o
|
||||
uKatrQ0BAi/KReBQOoqAGqa+DBIGAHoi29x4wZ/ZGSjeVManNb58Lz3+caFlZRCW
|
||||
8vcrE5J8OcpD+0O/CKM1UJDlTVFSBJS229my5WjxQnfNZeuxRnMxOCah/qaJsZZr
|
||||
FdRd0th2mRZtpjM8vZfXuoUcK+XVSENuJKdqFR4hXQU5Xq62ofxz+IiToPHO24zi
|
||||
S1lp7ggeIrgZXaz2I+7LmIy6gnZWP6oXE8XcyW8CAwEAAaNTMFEwHQYDVR0OBBYE
|
||||
FJLe6w7SsBTwFnIQYjjjH16p/3zDMB8GA1UdIwQYMBaAFJLe6w7SsBTwFnIQYjjj
|
||||
H16p/3zDMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGrHNKNe
|
||||
5UqrNPdIXlGwpabOdrhmAc9yN/tXiB386lByktIeOShS6pvD+UuV14PcTXUFGCwW
|
||||
2o8I5OE/+O8w+InyWyV7qC7dgeWyEL4qDAuIYmxs71T2VOv/eekYp1Zq/o3kL3hI
|
||||
f0oxonJVZXkR4p39L4TCS3z6EiWRJxWlI4LVNcvWgkwJB8w7wIxSbql0Y/EO9yoU
|
||||
07u8QHVj7Nth7YteacOpj8jEy42SuWq5sdW7ccMgEfptRSYiVAmgD7mOCaELCBHz
|
||||
NVqyLRPkvWqX7apqDy9vR3ZnMiHWEpTPeQqK12GJbVMW53AVEDWKiL0bhrjnY/uS
|
||||
dwnpMp7fEUJLXQk=
|
||||
-----END CERTIFICATE-----
|
1
test/.certs/untrusted-ca-crt.srl
Normal file
1
test/.certs/untrusted-ca-crt.srl
Normal file
@ -0,0 +1 @@
|
||||
5CB637D0B24622D344F4C956FE5930B22CF87221
|
27
test/.certs/untrusted-ca-key.pem
Normal file
27
test/.certs/untrusted-ca-key.pem
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAyaZhE5l/8qDjpv/Rm+rilRjyCFQQD55iK3x0vJRn+iehbiXG
|
||||
XcIrk2Ekh6vrY6W/tAi7xaFT+Fih4T7yUu9r2/QdomAaMqY9De3J6PfX0/Mhzui4
|
||||
pq2tDQECL8pF4FA6ioAapr4MEgYAeiLb3HjBn9kZKN5Uxqc1vnwvPf5xoWVlEJby
|
||||
9ysTknw5ykP7Q78IozVQkOVNUVIElLbb2bLlaPFCd81l67FGczE4JqH+pomxlmsV
|
||||
1F3S2HaZFm2mMzy9l9e6hRwr5dVIQ24kp2oVHiFdBTlerrah/HP4iJOg8c7bjOJL
|
||||
WWnuCB4iuBldrPYj7suYjLqCdlY/qhcTxdzJbwIDAQABAoIBAQCWRBLRLTDoWDZs
|
||||
6vODEczZOGacCDCTwv3609qV8K1u/3tPfnzMv3YDdH9pTpaxggFSIrPyeN7/EOVI
|
||||
2cRwQxQIK2it6Jl9Jt4WdB1jKtW9js+hxVBcfM2ZBChh/oSFvKNzNDUoDjUmdSyD
|
||||
11gpeh8ng/s4tj1Mb6wgD6CQvPxmPLsJZ3swxdSFgR5hpXXELtAK+oOlP0Y6SFpi
|
||||
d5AyiaMP9imBKQV7qgJSiKWVtSAvMhfCOPaeYM9wPCA9nha6dYGC8Fgh9FklOf2+
|
||||
fj+0dqmbWwa3xuEBfZ7oS+uKnzzcBvTxtNz/U8b9bPzTtoJU6Z6P3wLIB5x8DgQ3
|
||||
NcDqVbtRAoGBAOnEl1hfHsm1Ni0flugvNSY5pRF9CGQjbTk2tQxEfsPc7LiNZxjF
|
||||
NFyJK2wVs17bsCI4PUO9nnMjnCi86SMKj0ifVoroYlMkt4ruY9iQPTLbrJpTBF/X
|
||||
LkU77s6TSeOQdzUlVPcIXfTCYwguicpIP6kOcohHplmzdurWtl723GqHAoGBANzT
|
||||
1G2h8dS7UtR0GRO4u9QM8jhRFszariovI6eOEKPaVhBhPeiwwcWRq40un7koCLzU
|
||||
WA5CV6h1fGQVvN8pjpZdXYUAa26jlnISQLvNgNvwD2b5UjRi4tH2QuV0LOAMiMGs
|
||||
vcQtpjM12RNfii/Tdun0mYZ9pcb65T4p5VubM9vZAoGADl4i3y+ZeNRGbCeQ4txj
|
||||
6+GHH7gLl/wFborKPeLH18nwUrd+KquUOEvF+3Kp/56JCNFkEpHI91Ks+mQCAEFZ
|
||||
5SDF9Ourf2i2Tzevs1PKLyIJTcLkde+HzIGOf+vVksMCUKXmvvgori50X8Bcf65J
|
||||
G17j8zRUKRc6q9xegR+zFGkCgYEAtA2UG3/76nSCaO/wsn/hxlh39ytG5+k2MPcW
|
||||
nzvanX8cxWZEUEIu/KR1uDvXx+S4mx6YXagCSTziG8kNovgDZt7hrdxVvHRt6ryv
|
||||
Q3GgK7RlGpUXTdeDEac1jFlZbaVKrH/oitidtwuk34L67VwCjWf+9gXk8YUI/dKz
|
||||
TCoT8qECgYEAyUbioIZuc6iWF2oIk3VuPdWHUvhuhzvYr+gb++P/xIXraEUMI81c
|
||||
UFUDOw+jVVF6H0aioD1rRUczF9vJVE1pUZrXHbAViPECr6QgZTl1mluHtxnT8Asq
|
||||
7sXS69HdlV+k+P+YZ51qRXRLKZsjwSJwn8fCRWS+7HPdQ3ogIWp1Q+A=
|
||||
-----END RSA PRIVATE KEY-----
|
20
test/.certs/untrusted-client-crt.pem
Normal file
20
test/.certs/untrusted-client-crt.pem
Normal file
@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDOzCCAiOgAwIBAgIUXLY30LJGItNE9MlW/lkwsiz4ciEwDQYJKoZIhvcNAQEL
|
||||
BQAwQzEUMBIGA1UECgwLbWFjaGluZXpvbmUxFDASBgNVBAoMC0lYV2ViU29ja2V0
|
||||
MRUwEwYDVQQDDAx1bnRydXN0ZWQtY2EwHhcNMjAwMzEyMjMwNDM3WhcNMjEwMzEy
|
||||
MjMwNDM3WjBHMRQwEgYDVQQKDAttYWNoaW5lem9uZTEUMBIGA1UECgwLSVhXZWJT
|
||||
b2NrZXQxGTAXBgNVBAMMEHVudHJ1c3RlZC1jbGllbnQwggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQC5SOVG06/37lekGxkBJUt7AN3Xw708jN8XI7DR1sq+
|
||||
NPeGN/wEfCUSIHJXQq1fqBJQkYKpyYa9EkvQs2RhrOXahul3ZdX1kP3zxQLvvbxU
|
||||
EcB2gMS4B61EqnmBHRMsj+dI91++YSEFE1hkolD3+gQtm0+FVbPoXt5Y3rBAF/l0
|
||||
UMvrBsgraB12OHUlqqj8WkUIul37u8XcnsnPWKoigWb2k+/W47LCGsd+haRnulIK
|
||||
ADQOsjNs7wy3IV9d8zCifEV0YUT5ZPBg2K2f1lpYfOSobK7JLqgV03HVrkROQfej
|
||||
FTvMRtDAxlsa6bHLrGUeBhCNaO7SLj16oo5nMCq4DnTjAgMBAAGjIzAhMB8GA1Ud
|
||||
EQQYMBaCCWxvY2FsaG9zdIIJMTI3LjAuMC4xMA0GCSqGSIb3DQEBCwUAA4IBAQDH
|
||||
7XbX6dCzUCGj91835gvTPr5FgKrTqocVQ+EtCxJxRVvqB4zj7/80SHxByyWz9XJQ
|
||||
IBZmDz298nVqfW6uegq3qU29sG9OAOOg6I0SpWOL9qq/ZKMoEqRv6fHnjHhRiOwT
|
||||
isqdZISh1vhoIvcUpNsm1PwpaDxerjeE3oPyuNO0P0lKI5jykO3orDANGvyC8fzx
|
||||
jxlDsSXCgmcaPh99752vBe8UlY1M8t4GxsJAV8DXxdDZCYIWMe+/C5aQ2xDvj3+l
|
||||
vYht9+yc6ebl5uGOttgWSYPxdryCynDKsdBfXxet9Ix/qdsLF9hwU2JokVDh50J+
|
||||
er36eML3WvEO2HuBKTq8
|
||||
-----END CERTIFICATE-----
|
27
test/.certs/untrusted-client-key.pem
Normal file
27
test/.certs/untrusted-client-key.pem
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAuUjlRtOv9+5XpBsZASVLewDd18O9PIzfFyOw0dbKvjT3hjf8
|
||||
BHwlEiByV0KtX6gSUJGCqcmGvRJL0LNkYazl2obpd2XV9ZD988UC7728VBHAdoDE
|
||||
uAetRKp5gR0TLI/nSPdfvmEhBRNYZKJQ9/oELZtPhVWz6F7eWN6wQBf5dFDL6wbI
|
||||
K2gddjh1Jaqo/FpFCLpd+7vF3J7Jz1iqIoFm9pPv1uOywhrHfoWkZ7pSCgA0DrIz
|
||||
bO8MtyFfXfMwonxFdGFE+WTwYNitn9ZaWHzkqGyuyS6oFdNx1a5ETkH3oxU7zEbQ
|
||||
wMZbGumxy6xlHgYQjWju0i49eqKOZzAquA504wIDAQABAoIBAHle6OG2dUShmkNj
|
||||
hMOdXI5ciPV3wRRS6yhLNt6eJvzl0WbYcXu2nsn6+ytyAAPzItwoFUGHQ33C6Grz
|
||||
uEPLcF3vliuiR7+ulMwEN+I3lZA0eLCntTUfwj6CtUkAdLjyIv1HHi6ljW23uGVj
|
||||
dkqaOfZuEG81Lr5+toPci/PQQJYR4btVJJHCXJ6KVx6w8i++fwcRwby9riNhWAzk
|
||||
8OhUiSTTsx9sioBk62QRB8Qs0LVR5tGbDSrpQW5Ns9KnH7sayInwEN94PTsPKcqY
|
||||
i/oNNZG+qvSf8jG8QiIMdyGx/goVKuQVx5Gev8my5mnfuVM/oXB20T56z2iII64V
|
||||
kNh/sNECgYEA3qjAPqTY0rnu2tmvQN1PwAfyENz3XmTVWpVtBvedPj3qiV3mXJGZ
|
||||
qQoS0wb/2t/D05GhTxBJARk8foorNzGchMVtECMlGxDAs1vBw6dwSK5hJnw47PQQ
|
||||
Q68Vz/zwvrzJgmeijPow87PdpomYECgerTa6BynH8W0ffuSNIJC8Ke8CgYEA1Qd2
|
||||
FpwFjUFqhYbcvR3VG8qAMIF8RKLzmQDDZh7liKMeHLypdXRz1ZEa6NFkvsg7qRZh
|
||||
ahe/ULubnRhdOxs0JVyZPS0dU3ZmHT9bBcIuLzC//5e1ictXUZspFzIHE9T4suLC
|
||||
Xnh2vqQzlEy3iZLx5B6FMzc3ws7LM7q7L2AfqE0CgYAMkvEQWJTaCaAAgeyQuC7J
|
||||
xGkaJLBfh0g5LlkS3Kbnne2Bxmi874gC8MuxWSLXxG01pHK8mUnWIwu0ha79FfMl
|
||||
2FRZZfKxfZe0SUk++FSx9g8MclVwpDPK7rdHoJwj2Vtz3tBiL7rV+GFbB0gsGWfq
|
||||
Fj4ZK3XcH3J44wVJQoMtxwKBgQDM/ZkMuKY+/yvZwaS39vUTARHJm1BRW9y85pcg
|
||||
tap6iTx4urL2a1Drue4DCzu+uj9uvjKPPLrEnUNpMADG166eJTTwQXFu1wf8LPMR
|
||||
34FBt8+JzBrMtfcYeA5aW7Gjy9Rljv8qmRDq8mcP1aLnp5dMxHG4jvIBa6zt4kot
|
||||
lHniIQKBgQDWuMWA2Q1kcKKy7OJszp60jO+ftq306QMoDsPNFLUUUtCxNSrpAeC2
|
||||
MVvI4kzIn+6hYsMdRsqDSadosuKE4ZzCPIfuyadiAKTAO5esBJs7KAQFMJXSnfY7
|
||||
+Zs1QUcdLZAWivO7j3ZASbR8L/1mawlBMgyIaT9YKp1+iW+uzaYgUQ==
|
||||
-----END RSA PRIVATE KEY-----
|
@ -55,12 +55,13 @@ set (SOURCES
|
||||
IXDNSLookupTest.cpp
|
||||
IXWebSocketSubProtocolTest.cpp
|
||||
IXSentryClientTest.cpp
|
||||
IXWebSocketChatTest.cpp
|
||||
IXCobraToSentryBotTest.cpp
|
||||
)
|
||||
|
||||
# Some unittest don't work on windows yet
|
||||
if (UNIX)
|
||||
list(APPEND SOURCES
|
||||
IXWebSocketChatTest.cpp
|
||||
IXWebSocketCloseTest.cpp
|
||||
)
|
||||
endif()
|
||||
@ -99,6 +100,7 @@ target_link_libraries(ixwebsocket_unittest ixwebsocket)
|
||||
target_link_libraries(ixwebsocket_unittest ixcrypto)
|
||||
target_link_libraries(ixwebsocket_unittest ixcore)
|
||||
target_link_libraries(ixwebsocket_unittest ixsentry)
|
||||
target_link_libraries(ixwebsocket_unittest ixbots)
|
||||
|
||||
target_link_libraries(ixwebsocket_unittest spdlog)
|
||||
|
||||
|
@ -122,31 +122,32 @@ namespace
|
||||
void CobraChat::subscribe(const std::string& channel)
|
||||
{
|
||||
std::string filter;
|
||||
_conn.subscribe(channel, filter, [this](const Json::Value& msg) {
|
||||
spdlog::info("receive {}", msg.toStyledString());
|
||||
_conn.subscribe(
|
||||
channel, filter, [this](const Json::Value& msg, const std::string& /*position*/) {
|
||||
spdlog::info("receive {}", msg.toStyledString());
|
||||
|
||||
if (!msg.isObject()) return;
|
||||
if (!msg.isMember("user")) return;
|
||||
if (!msg.isMember("text")) return;
|
||||
if (!msg.isMember("session")) return;
|
||||
if (!msg.isObject()) return;
|
||||
if (!msg.isMember("user")) return;
|
||||
if (!msg.isMember("text")) return;
|
||||
if (!msg.isMember("session")) return;
|
||||
|
||||
std::string msg_user = msg["user"].asString();
|
||||
std::string msg_text = msg["text"].asString();
|
||||
std::string msg_session = msg["session"].asString();
|
||||
std::string msg_user = msg["user"].asString();
|
||||
std::string msg_text = msg["text"].asString();
|
||||
std::string msg_session = msg["session"].asString();
|
||||
|
||||
// We are not interested in messages
|
||||
// from a different session.
|
||||
if (msg_session != _session) return;
|
||||
// We are not interested in messages
|
||||
// from a different session.
|
||||
if (msg_session != _session) return;
|
||||
|
||||
// We are not interested in our own messages
|
||||
if (msg_user == _user) return;
|
||||
// We are not interested in our own messages
|
||||
if (msg_user == _user) return;
|
||||
|
||||
_receivedQueue.push(msg);
|
||||
_receivedQueue.push(msg);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::endl << msg_user << " > " << msg_text << std::endl << _user << " > ";
|
||||
log(ss.str());
|
||||
});
|
||||
std::stringstream ss;
|
||||
ss << std::endl << msg_user << " > " << msg_text << std::endl << _user << " > ";
|
||||
log(ss.str());
|
||||
});
|
||||
}
|
||||
|
||||
void CobraChat::sendMessage(const std::string& text)
|
||||
@ -218,7 +219,7 @@ namespace
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Published)
|
||||
{
|
||||
Logger() << "Subscriber: published message acked: " << msgId;
|
||||
TLogger() << "Subscriber: published message acked: " << msgId;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -79,7 +79,7 @@ namespace
|
||||
CobraConnection::MsgId msgId) {
|
||||
if (eventType == ix::CobraConnection_EventType_Open)
|
||||
{
|
||||
Logger() << "Subscriber connected:";
|
||||
TLogger() << "Subscriber connected:";
|
||||
for (auto&& it : headers)
|
||||
{
|
||||
log("Headers " + it.first + " " + it.second);
|
||||
@ -87,47 +87,48 @@ namespace
|
||||
}
|
||||
if (eventType == ix::CobraConnection_EventType_Error)
|
||||
{
|
||||
Logger() << "Subscriber error:" << errMsg;
|
||||
TLogger() << "Subscriber error:" << errMsg;
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Authenticated)
|
||||
{
|
||||
log("Subscriber authenticated");
|
||||
std::string filter;
|
||||
conn.subscribe(CHANNEL, filter, [](const Json::Value& msg) {
|
||||
log(msg.toStyledString());
|
||||
conn.subscribe(
|
||||
CHANNEL, filter, [](const Json::Value& msg, const std::string& /*position*/) {
|
||||
log(msg.toStyledString());
|
||||
|
||||
std::string id = msg["id"].asString();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(gProtectIds);
|
||||
gIds.insert(id);
|
||||
}
|
||||
std::string id = msg["id"].asString();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(gProtectIds);
|
||||
gIds.insert(id);
|
||||
}
|
||||
|
||||
gMessageCount++;
|
||||
});
|
||||
gMessageCount++;
|
||||
});
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Subscribed)
|
||||
{
|
||||
Logger() << "Subscriber: subscribed to channel " << subscriptionId;
|
||||
TLogger() << "Subscriber: subscribed to channel " << subscriptionId;
|
||||
if (subscriptionId == CHANNEL)
|
||||
{
|
||||
gSubscriberConnectedAndSubscribed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger() << "Subscriber: unexpected channel " << subscriptionId;
|
||||
TLogger() << "Subscriber: unexpected channel " << subscriptionId;
|
||||
}
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
|
||||
{
|
||||
Logger() << "Subscriber: ununexpected from channel " << subscriptionId;
|
||||
TLogger() << "Subscriber: ununexpected from channel " << subscriptionId;
|
||||
if (subscriptionId != CHANNEL)
|
||||
{
|
||||
Logger() << "Subscriber: unexpected channel " << subscriptionId;
|
||||
TLogger() << "Subscriber: unexpected channel " << subscriptionId;
|
||||
}
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Published)
|
||||
{
|
||||
Logger() << "Subscriber: published message acked: " << msgId;
|
||||
TLogger() << "Subscriber: published message acked: " << msgId;
|
||||
}
|
||||
});
|
||||
|
||||
|
211
test/IXCobraToSentryBotTest.cpp
Normal file
211
test/IXCobraToSentryBotTest.cpp
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* cmd_satori_chat.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2017 Machine Zone. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXTest.h"
|
||||
#include "catch.hpp"
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <ixbots/IXCobraToSentryBot.h>
|
||||
#include <ixcobra/IXCobraConnection.h>
|
||||
#include <ixcobra/IXCobraMetricsPublisher.h>
|
||||
#include <ixcrypto/IXUuid.h>
|
||||
#include <ixsentry/IXSentryClient.h>
|
||||
#include <ixsnake/IXRedisServer.h>
|
||||
#include <ixsnake/IXSnakeServer.h>
|
||||
#include <ixwebsocket/IXHttpServer.h>
|
||||
#include <ixwebsocket/IXUserAgent.h>
|
||||
|
||||
using namespace ix;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::atomic<size_t> incomingBytes(0);
|
||||
std::atomic<size_t> outgoingBytes(0);
|
||||
|
||||
void setupTrafficTrackerCallback()
|
||||
{
|
||||
ix::CobraConnection::setTrafficTrackerCallback([](size_t size, bool incoming) {
|
||||
if (incoming)
|
||||
{
|
||||
incomingBytes += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
outgoingBytes += size;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void runPublisher(const ix::CobraConfig& config, const std::string& channel)
|
||||
{
|
||||
ix::CobraMetricsPublisher cobraMetricsPublisher;
|
||||
|
||||
SocketTLSOptions socketTLSOptions;
|
||||
bool perMessageDeflate = true;
|
||||
cobraMetricsPublisher.configure(config.appkey,
|
||||
config.endpoint,
|
||||
channel,
|
||||
config.rolename,
|
||||
config.rolesecret,
|
||||
perMessageDeflate,
|
||||
socketTLSOptions);
|
||||
cobraMetricsPublisher.setSession(uuid4());
|
||||
cobraMetricsPublisher.enable(true); // disabled by default, needs to be enabled to be active
|
||||
|
||||
Json::Value msg;
|
||||
msg["fps"] = 60;
|
||||
|
||||
cobraMetricsPublisher.setGenericAttributes("game", "ody");
|
||||
|
||||
// Wait a bit
|
||||
ix::msleep(500);
|
||||
|
||||
// publish some messages
|
||||
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #1)
|
||||
cobraMetricsPublisher.push("sms_metric_B_id", msg); // (msg #2)
|
||||
ix::msleep(500);
|
||||
|
||||
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #3)
|
||||
cobraMetricsPublisher.push("sms_metric_D_id", msg); // (msg #4)
|
||||
ix::msleep(500);
|
||||
|
||||
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #4)
|
||||
cobraMetricsPublisher.push("sms_metric_F_id", msg); // (msg #5)
|
||||
ix::msleep(500);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Cobra_to_sentry_bot", "[foo]")
|
||||
{
|
||||
SECTION("Exchange and count sent/received messages.")
|
||||
{
|
||||
int port = getFreePort();
|
||||
snake::AppConfig appConfig = makeSnakeServerConfig(port);
|
||||
|
||||
// Start a redis server
|
||||
ix::RedisServer redisServer(appConfig.redisPort);
|
||||
auto res = redisServer.listen();
|
||||
REQUIRE(res.first);
|
||||
redisServer.start();
|
||||
|
||||
// Start a snake server
|
||||
snake::SnakeServer snakeServer(appConfig);
|
||||
snakeServer.run();
|
||||
|
||||
// Start a fake sentry http server
|
||||
SocketTLSOptions tlsOptionsServer;
|
||||
tlsOptionsServer.certFile = ".certs/trusted-server-crt.pem";
|
||||
tlsOptionsServer.keyFile = ".certs/trusted-server-key.pem";
|
||||
tlsOptionsServer.caFile = ".certs/trusted-ca-crt.pem";
|
||||
|
||||
int sentryPort = getFreePort();
|
||||
ix::HttpServer sentryServer(sentryPort, "127.0.0.1");
|
||||
sentryServer.setTLSOptions(tlsOptionsServer);
|
||||
|
||||
sentryServer.setOnConnectionCallback(
|
||||
[](HttpRequestPtr request,
|
||||
std::shared_ptr<ConnectionState> /*connectionState*/) -> HttpResponsePtr {
|
||||
WebSocketHttpHeaders headers;
|
||||
headers["Server"] = userAgent();
|
||||
|
||||
// Log request
|
||||
std::stringstream ss;
|
||||
ss << request->method << " " << request->headers["User-Agent"] << " "
|
||||
<< request->uri;
|
||||
|
||||
if (request->method == "POST")
|
||||
{
|
||||
return std::make_shared<HttpResponse>(
|
||||
200, "OK", HttpErrorCode::Ok, headers, std::string());
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_shared<HttpResponse>(
|
||||
405, "OK", HttpErrorCode::Invalid, headers, std::string("Invalid method"));
|
||||
}
|
||||
});
|
||||
|
||||
res = sentryServer.listen();
|
||||
REQUIRE(res.first);
|
||||
sentryServer.start();
|
||||
|
||||
setupTrafficTrackerCallback();
|
||||
|
||||
// Run the bot for a small amount of time
|
||||
std::string channel = ix::generateSessionId();
|
||||
std::string appkey("FC2F10139A2BAc53BB72D9db967b024f");
|
||||
std::string role = "_sub";
|
||||
std::string secret = "66B1dA3ED5fA074EB5AE84Dd8CE3b5ba";
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "ws://localhost:" << port;
|
||||
std::string endpoint = ss.str();
|
||||
|
||||
ix::CobraConfig config;
|
||||
config.endpoint = endpoint;
|
||||
config.appkey = appkey;
|
||||
config.rolename = role;
|
||||
config.rolesecret = secret;
|
||||
|
||||
std::thread publisherThread(runPublisher, config, channel);
|
||||
|
||||
std::string filter;
|
||||
bool verbose = true;
|
||||
bool strict = true;
|
||||
size_t maxQueueSize = 10;
|
||||
bool enableHeartbeat = false;
|
||||
|
||||
// FIXME: try to get this working with https instead of http
|
||||
// to regress the TLS 1.3 OpenSSL bug
|
||||
// -> https://github.com/openssl/openssl/issues/7967
|
||||
// https://xxxxx:yyyyyy@sentry.io/1234567
|
||||
std::stringstream oss;
|
||||
std::string scheme("http://");
|
||||
|
||||
oss << scheme << "xxxxxxx:yyyyyyy@localhost:" << sentryPort << "/1234567";
|
||||
std::string dsn = oss.str();
|
||||
|
||||
SocketTLSOptions tlsOptionsClient;
|
||||
tlsOptionsClient.certFile = ".certs/trusted-client-crt.pem";
|
||||
tlsOptionsClient.keyFile = ".certs/trusted-client-key.pem";
|
||||
tlsOptionsClient.caFile = ".certs/trusted-ca-crt.pem";
|
||||
|
||||
SentryClient sentryClient(dsn);
|
||||
sentryClient.setTLSOptions(tlsOptionsClient);
|
||||
|
||||
// Only run the bot for 3 seconds
|
||||
int runtime = 3;
|
||||
|
||||
int sentCount = cobra_to_sentry_bot(config,
|
||||
channel,
|
||||
filter,
|
||||
sentryClient,
|
||||
verbose,
|
||||
strict,
|
||||
maxQueueSize,
|
||||
enableHeartbeat,
|
||||
runtime);
|
||||
//
|
||||
// We want at least 2 messages to be sent
|
||||
//
|
||||
REQUIRE(sentCount >= 2);
|
||||
|
||||
// Give us 1s for all messages to be received
|
||||
ix::msleep(1000);
|
||||
|
||||
spdlog::info("Incoming bytes {}", incomingBytes);
|
||||
spdlog::info("Outgoing bytes {}", outgoingBytes);
|
||||
|
||||
spdlog::info("Stopping snake server...");
|
||||
snakeServer.stop();
|
||||
|
||||
spdlog::info("Stopping redis server...");
|
||||
redisServer.stop();
|
||||
|
||||
publisherThread.join();
|
||||
sentryServer.stop();
|
||||
}
|
||||
}
|
@ -59,10 +59,14 @@ TEST_CASE("http server", "[httpd]")
|
||||
|
||||
REQUIRE(response->errorCode == HttpErrorCode::Ok);
|
||||
REQUIRE(response->statusCode == 200);
|
||||
REQUIRE(response->headers["Accept-Encoding"] == "gzip");
|
||||
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("http server redirection", "[httpd_redirect]")
|
||||
{
|
||||
SECTION("Connect to a local HTTP server, with redirection enabled")
|
||||
{
|
||||
int port = getFreePort();
|
||||
|
@ -29,17 +29,17 @@ namespace ix
|
||||
makeCancellationRequestWithTimeout(timeoutSecs, requestInitCancellation);
|
||||
|
||||
bool success = socket->connect(host, port, errMsg, isCancellationRequested);
|
||||
Logger() << "errMsg: " << errMsg;
|
||||
TLogger() << "errMsg: " << errMsg;
|
||||
REQUIRE(success);
|
||||
|
||||
Logger() << "Sending request: " << request << "to " << host << ":" << port;
|
||||
TLogger() << "Sending request: " << request << "to " << host << ":" << port;
|
||||
REQUIRE(socket->writeBytes(request, isCancellationRequested));
|
||||
|
||||
auto lineResult = socket->readLine(isCancellationRequested);
|
||||
auto lineValid = lineResult.first;
|
||||
auto line = lineResult.second;
|
||||
|
||||
Logger() << "read error: " << strerror(Socket::getErrno());
|
||||
TLogger() << "read error: " << strerror(Socket::getErrno());
|
||||
|
||||
REQUIRE(lineValid);
|
||||
|
||||
|
@ -24,7 +24,7 @@ namespace ix
|
||||
{
|
||||
std::atomic<size_t> incomingBytes(0);
|
||||
std::atomic<size_t> outgoingBytes(0);
|
||||
std::mutex Logger::_mutex;
|
||||
std::mutex TLogger::_mutex;
|
||||
std::stack<int> freePorts;
|
||||
|
||||
void setupWebSocketTrafficTrackerCallback()
|
||||
@ -43,9 +43,9 @@ namespace ix
|
||||
|
||||
void reportWebSocketTraffic()
|
||||
{
|
||||
Logger() << incomingBytes;
|
||||
Logger() << "Incoming bytes: " << incomingBytes;
|
||||
Logger() << "Outgoing bytes: " << outgoingBytes;
|
||||
TLogger() << incomingBytes;
|
||||
TLogger() << "Incoming bytes: " << incomingBytes;
|
||||
TLogger() << "Outgoing bytes: " << outgoingBytes;
|
||||
}
|
||||
|
||||
void msleep(int ms)
|
||||
@ -65,7 +65,7 @@ namespace ix
|
||||
|
||||
void log(const std::string& msg)
|
||||
{
|
||||
Logger() << msg;
|
||||
TLogger() << msg;
|
||||
}
|
||||
|
||||
void hexDump(const std::string& prefix, const std::string& s)
|
||||
@ -90,17 +90,17 @@ namespace ix
|
||||
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New connection";
|
||||
Logger() << "Uri: " << msg->openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
TLogger() << "New connection";
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
Logger() << "Closed connection";
|
||||
TLogger() << "Closed connection";
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
@ -118,7 +118,7 @@ namespace ix
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
{
|
||||
Logger() << res.second;
|
||||
TLogger() << res.second;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -28,11 +28,11 @@ namespace ix
|
||||
void setupWebSocketTrafficTrackerCallback();
|
||||
void reportWebSocketTraffic();
|
||||
|
||||
struct Logger
|
||||
struct TLogger
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
Logger& operator<<(T const& obj)
|
||||
TLogger& operator<<(T const& obj)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
|
@ -199,13 +199,13 @@ namespace
|
||||
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New connection";
|
||||
Logger() << "id: " << connectionState->getId();
|
||||
Logger() << "Uri: " << msg->openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
TLogger() << "New connection";
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
|
@ -180,13 +180,13 @@ namespace
|
||||
&mutexWrite](const ix::WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New server connection";
|
||||
Logger() << "id: " << connectionState->getId();
|
||||
Logger() << "Uri: " << msg->openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
TLogger() << "New server connection";
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
|
@ -23,23 +23,23 @@ namespace
|
||||
[connectionState, &server](const WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New connection";
|
||||
TLogger() << "New connection";
|
||||
connectionState->computeId();
|
||||
Logger() << "id: " << connectionState->getId();
|
||||
Logger() << "Uri: " << msg->openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto&& it : msg->openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
Logger() << "Closed connection";
|
||||
TLogger() << "Closed connection";
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
Logger() << "Message received: " << msg->str;
|
||||
TLogger() << "Message received: " << msg->str;
|
||||
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
@ -52,7 +52,7 @@ namespace
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
{
|
||||
Logger() << res.second;
|
||||
TLogger() << res.second;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -140,13 +140,13 @@ namespace
|
||||
const ix::WebSocketCloseInfo& closeInfo) {
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New server connection";
|
||||
Logger() << "id: " << connectionState->getId();
|
||||
Logger() << "Uri: " << openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
TLogger() << "New server connection";
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
|
@ -169,13 +169,13 @@ namespace
|
||||
const ix::WebSocketCloseInfo& closeInfo) {
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New server connection";
|
||||
Logger() << "id: " << connectionState->getId();
|
||||
Logger() << "Uri: " << openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
TLogger() << "New server connection";
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
|
@ -40,21 +40,21 @@ namespace ix
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New connection";
|
||||
TLogger() << "New connection";
|
||||
connectionState->computeId();
|
||||
Logger() << "id: " << connectionState->getId();
|
||||
Logger() << "Uri: " << msg->openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
|
||||
connectionId = connectionState->getId();
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
Logger() << "Closed connection";
|
||||
TLogger() << "Closed connection";
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
@ -72,7 +72,7 @@ namespace ix
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
{
|
||||
Logger() << res.second;
|
||||
TLogger() << res.second;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@ TEST_CASE("Websocket_server", "[websocket_server]")
|
||||
bool success = socket->connect(host, port, errMsg, isCancellationRequested);
|
||||
REQUIRE(success);
|
||||
|
||||
Logger() << "writeBytes";
|
||||
TLogger() << "writeBytes";
|
||||
socket->writeBytes("GET /\r\n", isCancellationRequested);
|
||||
|
||||
auto lineResult = socket->readLine(isCancellationRequested);
|
||||
|
@ -23,13 +23,13 @@ bool startServer(ix::WebSocketServer& server, std::string& subProtocols)
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New connection";
|
||||
Logger() << "id: " << connectionState->getId();
|
||||
Logger() << "Uri: " << msg->openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
TLogger() << "New connection";
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
|
||||
subProtocols = msg->openInfo.headers["Sec-WebSocket-Protocol"];
|
||||
|
@ -1,26 +0,0 @@
|
||||
name: C++ CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Clone source
|
||||
run: git clone --recursive https://github.com/uNetworking/uWebSockets.git
|
||||
- name: Build source
|
||||
run: make -C uWebSockets
|
||||
|
||||
|
||||
build_osx:
|
||||
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Clone source
|
||||
run: git clone --recursive https://github.com/uNetworking/uWebSockets.git
|
||||
- name: Build source
|
||||
run: make -C uWebSockets
|
||||
|
3
test/compatibility/C/uWebSockets/.gitmodules
vendored
3
test/compatibility/C/uWebSockets/.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "uSockets"]
|
||||
path = uSockets
|
||||
url = https://github.com/uNetworking/uSockets.git
|
@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. 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.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You 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 the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You 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 such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) 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. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -1,40 +0,0 @@
|
||||
EXAMPLE_FILES := HelloWorld EchoServer BroadcastingEchoServer
|
||||
THREADED_EXAMPLE_FILES := HelloWorldThreaded EchoServerThreaded
|
||||
override CXXFLAGS += -lpthread -Wconversion -std=c++17 -Isrc -IuSockets/src
|
||||
override LDFLAGS += uSockets/*.o -lz
|
||||
|
||||
# WITH_OPENSSL=1 enables OpenSSL 1.1+ support
|
||||
ifeq ($(WITH_OPENSSL),1)
|
||||
# With problems on macOS, make sure to pass needed LDFLAGS required to find these
|
||||
override LDFLAGS += -lssl -lcrypto
|
||||
else
|
||||
# WITH_WOLFSSL=1 enables WolfSSL 4.2.0 support (mutually exclusive with OpenSSL)
|
||||
ifeq ($(WITH_WOLFSSL),1)
|
||||
override LDFLAGS += -L/usr/local/lib -lwolfssl
|
||||
endif
|
||||
endif
|
||||
|
||||
# WITH_LIBUV=1 builds with libuv as event-loop
|
||||
ifeq ($(WITH_LIBUV),1)
|
||||
override LDFLAGS += -luv
|
||||
endif
|
||||
|
||||
# WITH_ASAN builds with sanitizers
|
||||
ifeq ($(WITH_ASAN),1)
|
||||
override CXXFLAGS += -fsanitize=address
|
||||
override LDFLAGS += -lasan
|
||||
endif
|
||||
|
||||
.PHONY: examples
|
||||
examples:
|
||||
cd uSockets && make
|
||||
$(foreach FILE,$(EXAMPLE_FILES),$(CXX) -flto -O3 $(CXXFLAGS) examples/$(FILE).cpp -o $(FILE) $(LDFLAGS);)
|
||||
$(foreach FILE,$(THREADED_EXAMPLE_FILES),$(CXX) -pthread -flto -O3 $(CXXFLAGS) examples/$(FILE).cpp -o $(FILE) $(LDFLAGS);)
|
||||
|
||||
all:
|
||||
make examples
|
||||
make -C fuzzing
|
||||
make -C benchmarks
|
||||
clean:
|
||||
rm -rf $(EXAMPLE_FILES) $(THREADED_EXAMPLE_FILES)
|
||||
rm -rf fuzzing/*.o benchmarks/*.o
|
@ -1,65 +0,0 @@
|
||||
<div align="center">
|
||||
<img src="misc/logo.svg" height="180" />
|
||||
|
||||
*µWebSockets™ (it's "[micro](https://en.wikipedia.org/wiki/Micro-)") is simple, secure*<sup>[[1]](fuzzing)</sup> *& standards compliant*<sup>[[2]](https://unetworking.github.io/uWebSockets.js/report.pdf)</sup> *web I/O for the most demanding*<sup>[[3]](benchmarks)</sup> *of applications.*
|
||||
|
||||
• [Read more](misc/READMORE.md) • [Read about uSockets](https://github.com/uNetworking/uSockets) • [See uWebSockets.js](https://github.com/uNetworking/uWebSockets.js)
|
||||
|
||||
*© 2016-2019, >39,632,272 downloads*
|
||||
|
||||
</div>
|
||||
|
||||
#### Express yourself briefly.
|
||||
```c++
|
||||
uWS::SSLApp({
|
||||
|
||||
/* There are tons of SSL options */
|
||||
.cert_file_name = "cert.pem",
|
||||
.key_file_name = "key.pem"
|
||||
|
||||
}).get("/hello", [](auto *res, auto *req) {
|
||||
|
||||
/* You can efficiently stream huge files too */
|
||||
res->writeHeader("Content-Type", "text/html; charset=utf-8")->end("Hello HTTP!");
|
||||
|
||||
}).ws<UserData>("/*", {
|
||||
|
||||
/* Just a few of the available handlers */
|
||||
.open = [](auto *ws, auto *req) {
|
||||
ws->subscribe("buzzword weekly");
|
||||
},
|
||||
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
|
||||
ws->send(message, opCode);
|
||||
}
|
||||
|
||||
}).listen(9001, [](auto *token) {
|
||||
|
||||
if (token) {
|
||||
std::cout << "Listening on port " << 9001 << std::endl;
|
||||
}
|
||||
|
||||
}).run();
|
||||
```
|
||||
Don't miss the [user manual](https://github.com/uNetworking/uWebSockets/blob/master/misc/READMORE.md#user-manual), the [C++ examples](https://github.com/uNetworking/uWebSockets/tree/master/examples) or the [JavaScript examples](https://github.com/uNetworking/uWebSockets.js/tree/master/examples). JavaScript examples are very applicable to C++ developers, so go through them as well.
|
||||
|
||||
#### Pay what you want.
|
||||
A free & open source ([permissive](LICENSE)) project since 2016. Kindly sponsored by [BitMEX](https://bitmex.com), [Bitfinex](https://bitfinex.com) & [Coinbase](https://www.coinbase.com/) in 2018 and/or 2019. Individual donations are always accepted via [PayPal](https://paypal.me/uwebsockets).
|
||||
|
||||
<div align="center"><img src="misc/2018.png"/></div>
|
||||
|
||||
*Code is provided as-is, do not expect or demand **free** consulting services, personal tutoring, advice or debugging.*
|
||||
|
||||
#### Deploy like a boss.
|
||||
Commercial support is available via a per-hourly consulting plan or as otherwise negotiated. If you're stuck, worried about design or just in need of help don't hesitate throwing [me, the author](https://github.com/alexhultman) a mail and we'll figure out what's best for both parties. I want your business to have a proper understanding of the problem before rushing in to one of the many pitfalls.
|
||||
|
||||
#### Excel across the board.
|
||||
All that glitters is not gold. Especially so in a market driven by flashy logos, hype and pointless badges.
|
||||
|
||||
Http | WebSockets
|
||||
--- | ---
|
||||
 | 
|
||||
|
||||
#### Keep it legal.
|
||||
Intellectual property, all rights reserved.
|
||||
|
||||
*You are forbidden to use logos, product names, texts, names or otherwise perceived brand identity, of copyright holder, in any way that might state or imply that the copyright holder endorses your distribution or in any way that might state or imply that you created the original software. Modified distributions must carry, from the original distribution, significantly different names and must not be confused with the original distribution.*
|
@ -1,4 +0,0 @@
|
||||
default:
|
||||
clang -flto -O3 -DLIBUS_USE_OPENSSL -I../uSockets/src ../uSockets/src/*.c ../uSockets/src/eventing/*.c ../uSockets/src/crypto/*.c broadcast_test.c -o broadcast_test -lssl -lcrypto
|
||||
clang -flto -O3 -DLIBUS_USE_OPENSSL -I../uSockets/src ../uSockets/src/*.c ../uSockets/src/eventing/*.c ../uSockets/src/crypto/*.c load_test.c -o load_test -lssl -lcrypto
|
||||
clang -flto -O3 -DLIBUS_USE_OPENSSL -I../uSockets/src ../uSockets/src/*.c ../uSockets/src/eventing/*.c ../uSockets/src/crypto/*.c scale_test.c -o scale_test -lssl -lcrypto
|
@ -1,17 +0,0 @@
|
||||
# Benchmark-driven development
|
||||
|
||||
Just like testing code for correctness and stability, testing for performance is just as important if performance is a goal. You cannot really argue or reason about performance without having tests for it.
|
||||
|
||||
* Do not trust anyone who claims performance of any kind unless they provide benchmarks. Do not listen to people who talk about performance without having actual scientific data to back their claims up.
|
||||
* Never accept absolute numbers without a direct comparison with an alternative solution. Many projects can give you a number, X, which can be "50 billion messages per second". How much is this? What kind of worth does this number have? Impossible to know without a comparison. Absolute numbers mean nothing, relative comparisons are what you should look for.
|
||||
* Make sure to benchmark the correct thing. This is an extremely common mistake, done by many of the most well-known developers out there. If you measure for CPU-time efficiency (which you do) then normalizing for spent CPU-time is the difference between a completely invalid, botched and bogus test and something that might be valid.
|
||||
|
||||
Here are the current relative comparisons:
|
||||
|
||||
Http | WebSockets
|
||||
--- | ---
|
||||
 | 
|
||||
|
||||
Over the period of a few years I have never come across any web server which can score as high as µWebSockets do. This is not to say that µWebSockets is fastest, as "fastest" is a silly superlative nobody should *ever* use.
|
||||
|
||||
Never trust anyone using superlatives to describe their work; they are more often wrong than right.
|
@ -1,196 +0,0 @@
|
||||
/* This benchmark establishes _connections_ number of WebSocket
|
||||
clients, then iteratively performs the following:
|
||||
|
||||
1. Send one message for every client.
|
||||
2. Wait for the quadratic (_connections_^2) amount of responses from the server.
|
||||
3. Once received all expected bytes, repeat by going to step 1.
|
||||
|
||||
Every 4 seconds we print the current average "iterations per second".
|
||||
*/
|
||||
|
||||
#include <libusockets.h>
|
||||
int SSL;
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
unsigned char web_socket_request[26] = {130, 128 | 20, 1, 2, 3, 4};
|
||||
|
||||
char request[] = "GET / HTTP/1.1\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
|
||||
"Host: server.example.com\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n\r\n";
|
||||
char *host;
|
||||
int port;
|
||||
int connections;
|
||||
|
||||
int satisfied_sockets;
|
||||
int iterations;
|
||||
|
||||
struct http_socket {
|
||||
/* How far we have streamed our websocket request */
|
||||
int offset;
|
||||
|
||||
/* How far we have streamed our upgrade request */
|
||||
int upgrade_offset;
|
||||
|
||||
/* Are we upgraded? */
|
||||
int is_upgraded;
|
||||
|
||||
/* Bytes received */
|
||||
int bytes_received;
|
||||
};
|
||||
|
||||
/* We track upgraded websockets */
|
||||
void **web_sockets;
|
||||
int num_web_sockets;
|
||||
|
||||
/* We don't need any of these */
|
||||
void noop(struct us_loop_t *loop) {
|
||||
|
||||
}
|
||||
|
||||
void start_iteration() {
|
||||
for (int i = 0; i < num_web_sockets; i++) {
|
||||
struct us_socket_t *s = (struct us_socket_t *) web_sockets[i];
|
||||
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
|
||||
|
||||
http_socket->offset = us_socket_write(SSL, s, (char *) web_socket_request, sizeof(web_socket_request), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void next_connection(struct us_socket_t *s) {
|
||||
/* Add this connection to our array */
|
||||
web_sockets[num_web_sockets++] = s;
|
||||
|
||||
/* We could wait with this until properly upgraded */
|
||||
if (--connections) {
|
||||
us_socket_context_connect(SSL, us_socket_context(SSL, s), host, port, 0, sizeof(struct http_socket));
|
||||
} else {
|
||||
printf("Running benchmark now...\n");
|
||||
start_iteration();
|
||||
|
||||
us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY);
|
||||
}
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) {
|
||||
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
|
||||
|
||||
/* Are we still not upgraded yet? */
|
||||
if (http_socket->upgrade_offset < sizeof(request) - 1) {
|
||||
http_socket->upgrade_offset += us_socket_write(SSL, s, request + http_socket->upgrade_offset, sizeof(request) - 1 - http_socket->upgrade_offset, 0);
|
||||
} else {
|
||||
/* Stream whatever is remaining of the request */
|
||||
http_socket->offset += us_socket_write(SSL, s, (char *) web_socket_request + http_socket->offset, sizeof(web_socket_request) - http_socket->offset, 0);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_close(struct us_socket_t *s) {
|
||||
|
||||
printf("Client was disconnected, exiting!\n");
|
||||
exit(-1);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_end(struct us_socket_t *s) {
|
||||
return us_socket_close(SSL, s);
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int length) {
|
||||
/* Get socket extension and the socket's context's extension */
|
||||
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
|
||||
|
||||
/* Are we already upgraded? */
|
||||
if (http_socket->is_upgraded) {
|
||||
http_socket->bytes_received += length;
|
||||
|
||||
if (http_socket->bytes_received == (sizeof(web_socket_request) - 4) * num_web_sockets) {
|
||||
satisfied_sockets++;
|
||||
http_socket->bytes_received = 0;
|
||||
|
||||
if (satisfied_sockets == num_web_sockets) {
|
||||
iterations++;
|
||||
satisfied_sockets = 0;
|
||||
|
||||
start_iteration();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* We assume the server is not sending anything immediately following upgrade and that we get rnrn in one chunk */
|
||||
if (length >= 4 && data[length - 1] == '\n' && data[length - 2] == '\r' && data[length - 3] == '\n' && data[length - 4] == '\r') {
|
||||
http_socket->is_upgraded = 1;
|
||||
next_connection(s);
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) {
|
||||
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
|
||||
|
||||
/* Reset offsets */
|
||||
http_socket->offset = 0;
|
||||
http_socket->is_upgraded = 0;
|
||||
http_socket->bytes_received = 0;
|
||||
|
||||
/* Send an upgrade request */
|
||||
http_socket->upgrade_offset = us_socket_write(SSL, s, request, sizeof(request) - 1, 0);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) {
|
||||
/* Print current statistics */
|
||||
printf("Iterations/second (%d clients): %f\n", num_web_sockets, ((float)iterations) / LIBUS_TIMEOUT_GRANULARITY);
|
||||
|
||||
iterations = 0;
|
||||
us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
/* Parse host and port */
|
||||
if (argc != 5) {
|
||||
printf("Usage: connections host port ssl\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
port = atoi(argv[3]);
|
||||
host = malloc(strlen(argv[2]) + 1);
|
||||
memcpy(host, argv[2], strlen(argv[2]) + 1);
|
||||
connections = atoi(argv[1]);
|
||||
SSL = atoi(argv[4]);
|
||||
|
||||
/* Allocate room for every socket */
|
||||
web_sockets = (void **) malloc(sizeof(void *) * connections);
|
||||
|
||||
/* Create the event loop */
|
||||
struct us_loop_t *loop = us_create_loop(0, noop, noop, noop, 0);
|
||||
|
||||
/* Create a socket context for HTTP */
|
||||
struct us_socket_context_options_t options = {};
|
||||
struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, 0, options);
|
||||
|
||||
/* Set up event handlers */
|
||||
us_socket_context_on_open(SSL, http_context, on_http_socket_open);
|
||||
us_socket_context_on_data(SSL, http_context, on_http_socket_data);
|
||||
us_socket_context_on_writable(SSL, http_context, on_http_socket_writable);
|
||||
us_socket_context_on_close(SSL, http_context, on_http_socket_close);
|
||||
us_socket_context_on_timeout(SSL, http_context, on_http_socket_timeout);
|
||||
us_socket_context_on_end(SSL, http_context, on_http_socket_end);
|
||||
|
||||
/* Start making HTTP connections */
|
||||
us_socket_context_connect(SSL, http_context, host, port, 0, sizeof(struct http_socket));
|
||||
|
||||
us_loop_run(loop);
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
/* This is a simple yet efficient WebSocket server benchmark much like WRK */
|
||||
|
||||
#include <libusockets.h>
|
||||
int SSL;
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// request eller upgradeRequest samt webSocketFrame
|
||||
|
||||
unsigned char web_socket_request[26] = {130, 128 | 20, 1, 2, 3, 4};
|
||||
|
||||
char request[] = "GET / HTTP/1.1\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
|
||||
"Host: server.example.com\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n\r\n";
|
||||
char *host;
|
||||
int port;
|
||||
int connections;
|
||||
|
||||
int responses;
|
||||
|
||||
struct http_socket {
|
||||
/* How far we have streamed our websocket request */
|
||||
int offset;
|
||||
|
||||
/* How far we have streamed our upgrade request */
|
||||
int upgrade_offset;
|
||||
};
|
||||
|
||||
/* We don't need any of these */
|
||||
void on_wakeup(struct us_loop_t *loop) {
|
||||
|
||||
}
|
||||
|
||||
void on_pre(struct us_loop_t *loop) {
|
||||
|
||||
}
|
||||
|
||||
/* This is not HTTP POST, it is merely an event emitted post loop iteration */
|
||||
void on_post(struct us_loop_t *loop) {
|
||||
|
||||
}
|
||||
|
||||
void next_connection(struct us_socket_t *s) {
|
||||
/* We could wait with this until properly upgraded */
|
||||
if (--connections) {
|
||||
us_socket_context_connect(SSL, us_socket_context(SSL, s), host, port, 0, sizeof(struct http_socket));
|
||||
} else {
|
||||
printf("Running benchmark now...\n");
|
||||
|
||||
us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY);
|
||||
}
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) {
|
||||
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
|
||||
|
||||
/* Are we still not upgraded yet? */
|
||||
if (http_socket->upgrade_offset < sizeof(request) - 1) {
|
||||
http_socket->upgrade_offset += us_socket_write(SSL, s, request + http_socket->upgrade_offset, sizeof(request) - 1 - http_socket->upgrade_offset, 0);
|
||||
|
||||
/* Now we should be */
|
||||
if (http_socket->upgrade_offset == sizeof(request) - 1) {
|
||||
next_connection(s);
|
||||
}
|
||||
} else {
|
||||
/* Stream whatever is remaining of the request */
|
||||
http_socket->offset += us_socket_write(SSL, s, (char *) web_socket_request + http_socket->offset, sizeof(web_socket_request) - http_socket->offset, 0);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_close(struct us_socket_t *s) {
|
||||
|
||||
printf("Closed!\n");
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_end(struct us_socket_t *s) {
|
||||
return us_socket_close(SSL, s);
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int length) {
|
||||
/* Get socket extension and the socket's context's extension */
|
||||
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
|
||||
//struct http_context *http_context = (struct http_context *) us_socket_context_ext(SSL, us_socket_context(SSL, s));
|
||||
|
||||
/* We treat all data events as a response */
|
||||
http_socket->offset = us_socket_write(SSL, s, (char *) web_socket_request, sizeof(web_socket_request), 0);
|
||||
|
||||
/* */
|
||||
responses++;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) {
|
||||
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
|
||||
|
||||
/* Reset offsets */
|
||||
http_socket->offset = 0;
|
||||
|
||||
/* Send an upgrade request */
|
||||
http_socket->upgrade_offset = us_socket_write(SSL, s, request, sizeof(request) - 1, 0);
|
||||
if (http_socket->upgrade_offset == sizeof(request) - 1) {
|
||||
next_connection(s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) {
|
||||
/* Print current statistics */
|
||||
printf("Msg/sec: %f\n", ((float)responses) / LIBUS_TIMEOUT_GRANULARITY);
|
||||
|
||||
responses = 0;
|
||||
us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
/* Parse host and port */
|
||||
if (argc != 5) {
|
||||
printf("Usage: connections host port ssl\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
port = atoi(argv[3]);
|
||||
host = malloc(strlen(argv[2]) + 1);
|
||||
memcpy(host, argv[2], strlen(argv[2]) + 1);
|
||||
connections = atoi(argv[1]);
|
||||
SSL = atoi(argv[4]);
|
||||
|
||||
/* Create the event loop */
|
||||
struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0);
|
||||
|
||||
/* Create a socket context for HTTP */
|
||||
struct us_socket_context_options_t options = {};
|
||||
struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, 0, options);
|
||||
|
||||
/* Set up event handlers */
|
||||
us_socket_context_on_open(SSL, http_context, on_http_socket_open);
|
||||
us_socket_context_on_data(SSL, http_context, on_http_socket_data);
|
||||
us_socket_context_on_writable(SSL, http_context, on_http_socket_writable);
|
||||
us_socket_context_on_close(SSL, http_context, on_http_socket_close);
|
||||
us_socket_context_on_timeout(SSL, http_context, on_http_socket_timeout);
|
||||
us_socket_context_on_end(SSL, http_context, on_http_socket_end);
|
||||
|
||||
/* Start making HTTP connections */
|
||||
us_socket_context_connect(SSL, http_context, host, port, 0, sizeof(struct http_socket));
|
||||
|
||||
us_loop_run(loop);
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
/* This is a scalability test for testing million(s) of pinging connections */
|
||||
|
||||
#include <libusockets.h>
|
||||
int SSL;
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
unsigned char web_socket_request[26] = {130, 128 | 20, 1, 2, 3, 4};
|
||||
|
||||
char request[] = "GET / HTTP/1.1\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
|
||||
"Host: server.example.com\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n\r\n";
|
||||
char *host;
|
||||
int port;
|
||||
int connections;
|
||||
|
||||
/* Send ping every 16 seconds */
|
||||
int WEBSOCKET_PING_INTERVAL = 16;
|
||||
|
||||
/* We only establish 20k connections per address */
|
||||
int CONNECTIONS_PER_ADDRESS = 20000;
|
||||
|
||||
/* How many connections a time */
|
||||
int BATCH_CONNECT = 1;
|
||||
|
||||
/* Currently open and alive connections */
|
||||
int opened_connections;
|
||||
/* Dead connections */
|
||||
int closed_connections;
|
||||
|
||||
struct http_socket {
|
||||
/* How far we have streamed our websocket request */
|
||||
int offset;
|
||||
|
||||
/* How far we have streamed our upgrade request */
|
||||
int upgrade_offset;
|
||||
};
|
||||
|
||||
/* We don't need any of these */
|
||||
void on_wakeup(struct us_loop_t *loop) {
|
||||
|
||||
}
|
||||
|
||||
void on_pre(struct us_loop_t *loop) {
|
||||
|
||||
}
|
||||
|
||||
/* This is not HTTP POST, it is merely an event emitted post loop iteration */
|
||||
void on_post(struct us_loop_t *loop) {
|
||||
|
||||
}
|
||||
|
||||
void next_connection(struct us_socket_t *s) {
|
||||
/* We could wait with this until properly upgraded */
|
||||
if (--connections/* > BATCH_CONNECT*/) {
|
||||
/* Swap address */
|
||||
int address = opened_connections / CONNECTIONS_PER_ADDRESS + 1;
|
||||
char buf[16];
|
||||
sprintf(buf, "127.0.0.%d", address);
|
||||
|
||||
us_socket_context_connect(SSL, us_socket_context(SSL, s), buf, port, 0, sizeof(struct http_socket));
|
||||
}
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) {
|
||||
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
|
||||
|
||||
/* Are we still not upgraded yet? */
|
||||
if (http_socket->upgrade_offset < sizeof(request) - 1) {
|
||||
http_socket->upgrade_offset += us_socket_write(SSL, s, request + http_socket->upgrade_offset, sizeof(request) - 1 - http_socket->upgrade_offset, 0);
|
||||
|
||||
/* Now we should be */
|
||||
if (http_socket->upgrade_offset == sizeof(request) - 1) {
|
||||
next_connection(s);
|
||||
|
||||
/* Make sure to send ping */
|
||||
us_socket_timeout(SSL, s, WEBSOCKET_PING_INTERVAL);
|
||||
}
|
||||
} else {
|
||||
/* Stream whatever is remaining of the request */
|
||||
http_socket->offset += us_socket_write(SSL, s, (char *) web_socket_request + http_socket->offset, sizeof(web_socket_request) - http_socket->offset, 0);
|
||||
if (http_socket->offset == sizeof(web_socket_request)) {
|
||||
/* Reset timeout if we managed to */
|
||||
us_socket_timeout(SSL, s, WEBSOCKET_PING_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_close(struct us_socket_t *s) {
|
||||
|
||||
closed_connections++;
|
||||
if (closed_connections % 1000 == 0) {
|
||||
printf("Alive: %d, dead: %d\n", opened_connections, closed_connections);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_end(struct us_socket_t *s) {
|
||||
return us_socket_close(SSL, s);
|
||||
}
|
||||
|
||||
// should never get a response!
|
||||
struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int length) {
|
||||
return s;
|
||||
}
|
||||
|
||||
struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) {
|
||||
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
|
||||
|
||||
/* Display number of opened connections */
|
||||
opened_connections++;
|
||||
if (opened_connections % 1000 == 0) {
|
||||
printf("Alive: %d, dead: %d\n", opened_connections, closed_connections);
|
||||
}
|
||||
|
||||
/* Send an upgrade request */
|
||||
http_socket->upgrade_offset = us_socket_write(SSL, s, request, sizeof(request) - 1, 0);
|
||||
if (http_socket->upgrade_offset == sizeof(request) - 1) {
|
||||
next_connection(s);
|
||||
|
||||
/* Make sure to send ping */
|
||||
us_socket_timeout(SSL, s, WEBSOCKET_PING_INTERVAL);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// here we should send a message as ping (part of the test)
|
||||
struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) {
|
||||
struct http_socket *http_socket = (struct http_socket *) us_socket_ext(SSL, s);
|
||||
|
||||
/* Send ping here */
|
||||
http_socket->offset = us_socket_write(SSL, s, (char *) web_socket_request, sizeof(web_socket_request), 0);
|
||||
if (http_socket->offset == sizeof(web_socket_request)) {
|
||||
/* Reset timeout if we managed to */
|
||||
us_socket_timeout(SSL, s, WEBSOCKET_PING_INTERVAL);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
/* Parse host and port */
|
||||
if (argc != 5) {
|
||||
printf("Usage: connections host port ssl\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
port = atoi(argv[3]);
|
||||
host = malloc(strlen(argv[2]) + 1);
|
||||
memcpy(host, argv[2], strlen(argv[2]) + 1);
|
||||
connections = atoi(argv[1]);
|
||||
SSL = atoi(argv[4]);
|
||||
|
||||
/* Create the event loop */
|
||||
struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0);
|
||||
|
||||
/* Create a socket context for HTTP */
|
||||
struct us_socket_context_options_t options = {};
|
||||
struct us_socket_context_t *http_context = us_create_socket_context(SSL, loop, 0, options);
|
||||
|
||||
/* Set up event handlers */
|
||||
us_socket_context_on_open(SSL, http_context, on_http_socket_open);
|
||||
us_socket_context_on_data(SSL, http_context, on_http_socket_data);
|
||||
us_socket_context_on_writable(SSL, http_context, on_http_socket_writable);
|
||||
us_socket_context_on_close(SSL, http_context, on_http_socket_close);
|
||||
us_socket_context_on_timeout(SSL, http_context, on_http_socket_timeout);
|
||||
us_socket_context_on_end(SSL, http_context, on_http_socket_end);
|
||||
|
||||
/* Start making HTTP connections */
|
||||
for (int i = 0; i < BATCH_CONNECT; i++) {
|
||||
us_socket_context_connect(SSL, http_context, host, port, 0, sizeof(struct http_socket));
|
||||
}
|
||||
|
||||
us_loop_run(loop);
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
#include "App.h"
|
||||
|
||||
struct us_listen_socket_t *listen_socket;
|
||||
|
||||
int main() {
|
||||
/* ws->getUserData returns one of these */
|
||||
struct PerSocketData {
|
||||
|
||||
};
|
||||
|
||||
/* Very simple WebSocket broadcasting echo server */
|
||||
uWS::App().ws<PerSocketData>("/*", {
|
||||
/* Settings */
|
||||
.compression = uWS::SHARED_COMPRESSOR,
|
||||
.maxPayloadLength = 16 * 1024 * 1024,
|
||||
.idleTimeout = 10,
|
||||
.maxBackpressure = 1 * 1024 * 1204,
|
||||
/* Handlers */
|
||||
.open = [](auto *ws, auto *req) {
|
||||
/* Let's make every connection subscribe to the "broadcast" topic */
|
||||
ws->subscribe("broadcast");
|
||||
},
|
||||
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
|
||||
/* Exit gracefully if we get a closedown message (ASAN debug) */
|
||||
if (message == "closedown") {
|
||||
/* Bye bye */
|
||||
us_listen_socket_close(0, listen_socket);
|
||||
ws->close();
|
||||
}
|
||||
|
||||
/* Simply broadcast every single message we get */
|
||||
ws->publish("broadcast", message, opCode);
|
||||
},
|
||||
.drain = [](auto *ws) {
|
||||
/* Check getBufferedAmount here */
|
||||
},
|
||||
.ping = [](auto *ws) {
|
||||
|
||||
},
|
||||
.pong = [](auto *ws) {
|
||||
|
||||
},
|
||||
.close = [](auto *ws, int code, std::string_view message) {
|
||||
/* We automatically unsubscribe from any topic here */
|
||||
}
|
||||
}).listen(9001, [](auto *token) {
|
||||
listen_socket = token;
|
||||
if (token) {
|
||||
std::cout << "Listening on port " << 9001 << std::endl;
|
||||
}
|
||||
}).run();
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/* We simply call the root header file "App.h", giving you uWS::App and uWS::SSLApp */
|
||||
#include "App.h"
|
||||
|
||||
/* This is a simple WebSocket echo server example.
|
||||
* You may compile it with "WITH_OPENSSL=1 make" or with "make" */
|
||||
|
||||
int main() {
|
||||
/* ws->getUserData returns one of these */
|
||||
struct PerSocketData {
|
||||
/* Fill with user data */
|
||||
};
|
||||
|
||||
/* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support.
|
||||
* You may swap to using uWS:App() if you don't need SSL */
|
||||
uWS::SSLApp({
|
||||
/* There are example certificates in uWebSockets.js repo */
|
||||
.key_file_name = "../misc/key.pem",
|
||||
.cert_file_name = "../misc/cert.pem",
|
||||
.passphrase = "1234"
|
||||
}).ws<PerSocketData>("/*", {
|
||||
/* Settings */
|
||||
.compression = uWS::SHARED_COMPRESSOR,
|
||||
.maxPayloadLength = 16 * 1024,
|
||||
.idleTimeout = 10,
|
||||
.maxBackpressure = 1 * 1024 * 1204,
|
||||
/* Handlers */
|
||||
.open = [](auto *ws, auto *req) {
|
||||
/* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */
|
||||
},
|
||||
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
|
||||
ws->send(message, opCode);
|
||||
},
|
||||
.drain = [](auto *ws) {
|
||||
/* Check ws->getBufferedAmount() here */
|
||||
},
|
||||
.ping = [](auto *ws) {
|
||||
/* Not implemented yet */
|
||||
},
|
||||
.pong = [](auto *ws) {
|
||||
/* Not implemented yet */
|
||||
},
|
||||
.close = [](auto *ws, int code, std::string_view message) {
|
||||
/* You may access ws->getUserData() here */
|
||||
}
|
||||
}).listen(9001, [](auto *token) {
|
||||
if (token) {
|
||||
std::cout << "Listening on port " << 9001 << std::endl;
|
||||
}
|
||||
}).run();
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
#include "App.h"
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
|
||||
int main() {
|
||||
/* ws->getUserData returns one of these */
|
||||
struct PerSocketData {
|
||||
|
||||
};
|
||||
|
||||
/* Simple echo websocket server, using multiple threads */
|
||||
std::vector<std::thread *> threads(std::thread::hardware_concurrency());
|
||||
|
||||
std::transform(threads.begin(), threads.end(), threads.begin(), [](std::thread *t) {
|
||||
return new std::thread([]() {
|
||||
|
||||
/* Very simple WebSocket echo server */
|
||||
uWS::App().ws<PerSocketData>("/*", {
|
||||
/* Settings */
|
||||
.compression = uWS::SHARED_COMPRESSOR,
|
||||
.maxPayloadLength = 16 * 1024,
|
||||
.idleTimeout = 10,
|
||||
.maxBackpressure = 1 * 1024 * 1204,
|
||||
/* Handlers */
|
||||
.open = [](auto *ws, auto *req) {
|
||||
|
||||
},
|
||||
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
|
||||
ws->send(message, opCode);
|
||||
},
|
||||
.drain = [](auto *ws) {
|
||||
/* Check getBufferedAmount here */
|
||||
},
|
||||
.ping = [](auto *ws) {
|
||||
|
||||
},
|
||||
.pong = [](auto *ws) {
|
||||
|
||||
},
|
||||
.close = [](auto *ws, int code, std::string_view message) {
|
||||
|
||||
}
|
||||
}).listen(9001, [](auto *token) {
|
||||
if (token) {
|
||||
std::cout << "Thread " << std::this_thread::get_id() << " listening on port " << 9001 << std::endl;
|
||||
} else {
|
||||
std::cout << "Thread " << std::this_thread::get_id() << " failed to listen on port 9001" << std::endl;
|
||||
}
|
||||
}).run();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
std::for_each(threads.begin(), threads.end(), [](std::thread *t) {
|
||||
t->join();
|
||||
});
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#include "App.h"
|
||||
|
||||
/* Note that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support */
|
||||
|
||||
int main() {
|
||||
/* Overly simple hello world app */
|
||||
uWS::SSLApp({
|
||||
.key_file_name = "../misc/key.pem",
|
||||
.cert_file_name = "../misc/cert.pem",
|
||||
.passphrase = "1234"
|
||||
}).get("/*", [](auto *res, auto *req) {
|
||||
res->end("Hello world!");
|
||||
}).listen(3000, [](auto *token) {
|
||||
if (token) {
|
||||
std::cout << "Listening on port " << 3000 << std::endl;
|
||||
}
|
||||
}).run();
|
||||
|
||||
std::cout << "Failed to listen on port 3000" << std::endl;
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#include "App.h"
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
|
||||
int main() {
|
||||
/* Overly simple hello world app, using multiple threads */
|
||||
std::vector<std::thread *> threads(std::thread::hardware_concurrency());
|
||||
|
||||
std::transform(threads.begin(), threads.end(), threads.begin(), [](std::thread *t) {
|
||||
return new std::thread([]() {
|
||||
|
||||
uWS::App().get("/*", [](auto *res, auto *req) {
|
||||
res->end("Hello world!");
|
||||
}).listen(3000, [](auto *token) {
|
||||
if (token) {
|
||||
std::cout << "Thread " << std::this_thread::get_id() << " listening on port " << 3000 << std::endl;
|
||||
} else {
|
||||
std::cout << "Thread " << std::this_thread::get_id() << " failed to listen on port 3000" << std::endl;
|
||||
}
|
||||
}).run();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
std::for_each(threads.begin(), threads.end(), [](std::thread *t) {
|
||||
t->join();
|
||||
});
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
/* This is a simple HTTP(S) web server much like Python's SimpleHTTPServer */
|
||||
|
||||
#include <App.h>
|
||||
|
||||
/* Helpers for this example */
|
||||
#include "helpers/AsyncFileReader.h"
|
||||
#include "helpers/AsyncFileStreamer.h"
|
||||
#include "helpers/Middleware.h"
|
||||
|
||||
/* optparse */
|
||||
#define OPTPARSE_IMPLEMENTATION
|
||||
#include "helpers/optparse.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
int option;
|
||||
struct optparse options;
|
||||
optparse_init(&options, argv);
|
||||
|
||||
struct optparse_long longopts[] = {
|
||||
{"port", 'p', OPTPARSE_REQUIRED},
|
||||
{"help", 'h', OPTPARSE_NONE},
|
||||
{"passphrase", 'a', OPTPARSE_REQUIRED},
|
||||
{"key", 'k', OPTPARSE_REQUIRED},
|
||||
{"cert", 'c', OPTPARSE_REQUIRED},
|
||||
{"dh_params", 'd', OPTPARSE_REQUIRED},
|
||||
{0}
|
||||
};
|
||||
|
||||
int port = 3000;
|
||||
struct us_socket_context_options_t ssl_options = {};
|
||||
|
||||
while ((option = optparse_long(&options, longopts, nullptr)) != -1) {
|
||||
switch (option) {
|
||||
case 'p':
|
||||
port = atoi(options.optarg);
|
||||
break;
|
||||
case 'a':
|
||||
ssl_options.passphrase = options.optarg;
|
||||
break;
|
||||
case 'c':
|
||||
ssl_options.cert_file_name = options.optarg;
|
||||
break;
|
||||
case 'k':
|
||||
ssl_options.key_file_name = options.optarg;
|
||||
break;
|
||||
case 'd':
|
||||
ssl_options.dh_params_file_name = options.optarg;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
fail:
|
||||
std::cout << "Usage: " << argv[0] << " [--help] [--port <port>] [--key <ssl key>] [--cert <ssl cert>] [--passphrase <ssl key passphrase>] [--dh_params <ssl dh params file>] <public root>" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
char *root = optparse_arg(&options);
|
||||
if (!root) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
AsyncFileStreamer asyncFileStreamer(root);
|
||||
|
||||
/* Either serve over HTTP or HTTPS */
|
||||
struct us_socket_context_options_t empty_ssl_options = {};
|
||||
if (memcmp(&ssl_options, &empty_ssl_options, sizeof(empty_ssl_options))) {
|
||||
/* HTTPS */
|
||||
uWS::SSLApp(ssl_options).get("/*", [&asyncFileStreamer](auto *res, auto *req) {
|
||||
serveFile(res, req);
|
||||
asyncFileStreamer.streamFile(res, req->getUrl());
|
||||
}).listen(port, [port, root](auto *token) {
|
||||
if (token) {
|
||||
std::cout << "Serving " << root << " over HTTPS a " << port << std::endl;
|
||||
}
|
||||
}).run();
|
||||
} else {
|
||||
/* HTTP */
|
||||
uWS::App().get("/*", [&asyncFileStreamer](auto *res, auto *req) {
|
||||
serveFile(res, req);
|
||||
asyncFileStreamer.streamFile(res, req->getUrl());
|
||||
}).listen(port, [port, root](auto *token) {
|
||||
if (token) {
|
||||
std::cout << "Serving " << root << " over HTTP a " << port << std::endl;
|
||||
}
|
||||
}).run();
|
||||
}
|
||||
|
||||
std::cout << "Failed to listen to port " << port << std::endl;
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
#include <map>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <future>
|
||||
|
||||
/* This is just a very simple and inefficient demo of async responses,
|
||||
* please do roll your own variant or use a database or Node.js's async
|
||||
* features instead of this really bad demo */
|
||||
struct AsyncFileReader {
|
||||
private:
|
||||
/* The cache we have in memory for this file */
|
||||
std::string cache;
|
||||
int cacheOffset;
|
||||
bool hasCache;
|
||||
|
||||
/* The pending async file read (yes we only support one pending read) */
|
||||
std::function<void(std::string_view)> pendingReadCb;
|
||||
|
||||
int fileSize;
|
||||
std::string fileName;
|
||||
std::ifstream fin;
|
||||
uWS::Loop *loop;
|
||||
|
||||
public:
|
||||
/* Construct a demo async. file reader for fileName */
|
||||
AsyncFileReader(std::string fileName) : fileName(fileName) {
|
||||
fin.open(fileName, std::ios::binary);
|
||||
|
||||
// get fileSize
|
||||
fin.seekg(0, fin.end);
|
||||
fileSize = fin.tellg();
|
||||
|
||||
//std::cout << "File size is: " << fileSize << std::endl;
|
||||
|
||||
// cache up 1 mb!
|
||||
cache.resize(1024 * 1024);
|
||||
|
||||
//std::cout << "Caching 1 MB at offset = " << 0 << std::endl;
|
||||
fin.seekg(0, fin.beg);
|
||||
fin.read(cache.data(), cache.length());
|
||||
cacheOffset = 0;
|
||||
hasCache = true;
|
||||
|
||||
// get loop for thread
|
||||
|
||||
loop = uWS::Loop::get();
|
||||
}
|
||||
|
||||
/* Returns any data already cached for this offset */
|
||||
std::string_view peek(int offset) {
|
||||
/* Did we hit the cache? */
|
||||
if (hasCache && offset >= cacheOffset && ((offset - cacheOffset) < cache.length())) {
|
||||
/* Cache hit */
|
||||
//std::cout << "Cache hit!" << std::endl;
|
||||
|
||||
/*if (fileSize - offset < cache.length()) {
|
||||
std::cout << "LESS THAN WHAT WE HAVE!" << std::endl;
|
||||
}*/
|
||||
|
||||
int chunkSize = std::min<int>(fileSize - offset, cache.length() - offset + cacheOffset);
|
||||
|
||||
return std::string_view(cache.data() + offset - cacheOffset, chunkSize);
|
||||
} else {
|
||||
/* Cache miss */
|
||||
//std::cout << "Cache miss!" << std::endl;
|
||||
return std::string_view(nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Asynchronously request more data at offset */
|
||||
void request(int offset, std::function<void(std::string_view)> cb) {
|
||||
|
||||
// in this case, what do we do?
|
||||
// we need to queue up this chunk request and callback!
|
||||
// if queue is full, either block or close the connection via abort!
|
||||
if (!hasCache) {
|
||||
// already requesting a chunk!
|
||||
std::cout << "ERROR: already requesting a chunk!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// disable cache
|
||||
hasCache = false;
|
||||
|
||||
std::async(std::launch::async, [this, cb, offset]() {
|
||||
//std::cout << "ASYNC Caching 1 MB at offset = " << offset << std::endl;
|
||||
|
||||
|
||||
|
||||
// den har stängts! öppna igen!
|
||||
if (!fin.good()) {
|
||||
fin.close();
|
||||
//std::cout << "Reopening fin!" << std::endl;
|
||||
fin.open(fileName, std::ios::binary);
|
||||
}
|
||||
fin.seekg(offset, fin.beg);
|
||||
fin.read(cache.data(), cache.length());
|
||||
|
||||
cacheOffset = offset;
|
||||
|
||||
loop->defer([this, cb, offset]() {
|
||||
|
||||
int chunkSize = std::min<int>(cache.length(), fileSize - offset);
|
||||
|
||||
// båda dessa sker, wtf?
|
||||
if (chunkSize == 0) {
|
||||
std::cout << "Zero size!?" << std::endl;
|
||||
}
|
||||
|
||||
if (chunkSize != cache.length()) {
|
||||
std::cout << "LESS THAN A CACHE 1 MB!" << std::endl;
|
||||
}
|
||||
|
||||
hasCache = true;
|
||||
cb(std::string_view(cache.data(), chunkSize));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* Abort any pending async. request */
|
||||
void abort() {
|
||||
|
||||
}
|
||||
|
||||
int getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
};
|
@ -1,84 +0,0 @@
|
||||
#include <filesystem>
|
||||
|
||||
struct AsyncFileStreamer {
|
||||
|
||||
std::map<std::string_view, AsyncFileReader *> asyncFileReaders;
|
||||
std::string root;
|
||||
|
||||
AsyncFileStreamer(std::string root) : root(root) {
|
||||
// for all files in this path, init the map of AsyncFileReaders
|
||||
updateRootCache();
|
||||
}
|
||||
|
||||
void updateRootCache() {
|
||||
// todo: if the root folder changes, we want to reload the cache
|
||||
for(auto &p : std::filesystem::recursive_directory_iterator(root)) {
|
||||
std::string url = p.path().string().substr(root.length());
|
||||
if (url == "/index.html") {
|
||||
url = "/";
|
||||
}
|
||||
|
||||
char *key = new char[url.length()];
|
||||
memcpy(key, url.data(), url.length());
|
||||
asyncFileReaders[std::string_view(key, url.length())] = new AsyncFileReader(p.path().string());
|
||||
}
|
||||
}
|
||||
|
||||
template <bool SSL>
|
||||
void streamFile(uWS::HttpResponse<SSL> *res, std::string_view url) {
|
||||
auto it = asyncFileReaders.find(url);
|
||||
if (it == asyncFileReaders.end()) {
|
||||
std::cout << "Did not find file: " << url << std::endl;
|
||||
} else {
|
||||
streamFile(res, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool SSL>
|
||||
static void streamFile(uWS::HttpResponse<SSL> *res, AsyncFileReader *asyncFileReader) {
|
||||
/* Peek from cache */
|
||||
std::string_view chunk = asyncFileReader->peek(res->getWriteOffset());
|
||||
if (!chunk.length() || res->tryEnd(chunk, asyncFileReader->getFileSize()).first) {
|
||||
/* Request new chunk */
|
||||
// todo: we need to abort this callback if peer closed!
|
||||
// this also means Loop::defer needs to support aborting (functions should embedd an atomic boolean abort or something)
|
||||
|
||||
// Loop::defer(f) -> integer
|
||||
// Loop::abort(integer)
|
||||
|
||||
// hmm? no?
|
||||
|
||||
// us_socket_up_ref eftersom vi delar ägandeskapet
|
||||
|
||||
if (chunk.length() < asyncFileReader->getFileSize()) {
|
||||
asyncFileReader->request(res->getWriteOffset(), [res, asyncFileReader](std::string_view chunk) {
|
||||
// check if we were closed in the mean time
|
||||
//if (us_socket_is_closed()) {
|
||||
// free it here
|
||||
//return;
|
||||
//}
|
||||
|
||||
/* We were aborted for some reason */
|
||||
if (!chunk.length()) {
|
||||
// todo: make sure to check for is_closed internally after all callbacks!
|
||||
res->close();
|
||||
} else {
|
||||
AsyncFileStreamer::streamFile(res, asyncFileReader);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
/* We failed writing everything, so let's continue when we can */
|
||||
res->onWritable([res, asyncFileReader](int offset) {
|
||||
|
||||
// här kan skiten avbrytas!
|
||||
|
||||
AsyncFileStreamer::streamFile(res, asyncFileReader);
|
||||
// todo: I don't really know what this is supposed to mean?
|
||||
return false;
|
||||
})->onAborted([]() {
|
||||
std::cout << "ABORTED!" << std::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -1,19 +0,0 @@
|
||||
/* Middleware to fill out content-type */
|
||||
inline bool hasExt(std::string_view file, std::string_view ext) {
|
||||
if (ext.size() > file.size()) {
|
||||
return false;
|
||||
}
|
||||
return std::equal(ext.rbegin(), ext.rend(), file.rbegin());
|
||||
}
|
||||
|
||||
/* This should be a filter / middleware like app.use(handler) */
|
||||
template <bool SSL>
|
||||
uWS::HttpResponse<SSL> *serveFile(uWS::HttpResponse<SSL> *res, uWS::HttpRequest *req) {
|
||||
res->writeStatus(uWS::HTTP_200_OK);
|
||||
|
||||
if (hasExt(req->getUrl(), ".svg")) {
|
||||
res->writeHeader("Content-Type", "image/svg+xml");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user