Compare commits
617 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71ba7e7b1b | ||
|
|
ebe7efa1cc | ||
|
|
22d90c29b4 | ||
|
|
b944f942ee | ||
|
|
550f728165 | ||
|
|
a4b2c61a65 | ||
|
|
5c0135fa5d | ||
|
|
2b5d1eea8d | ||
|
|
d274c0abe5 | ||
|
|
dda2e007a0 | ||
|
|
321a86d9f2 | ||
|
|
ada97046a2 | ||
|
|
6e73a63153 | ||
|
|
cdc223019a | ||
|
|
574f5ce93e | ||
|
|
2996cecee0 | ||
|
|
32bf5c9c09 | ||
|
|
735e5930eb | ||
|
|
748f47b377 | ||
|
|
4cb8ff9f90 | ||
|
|
985cd9f6a2 | ||
|
|
233f0fb1b8 | ||
|
|
03cf43ebaa | ||
|
|
3c4b96024f | ||
|
|
d74e4a7c9c | ||
|
|
bfa2f735f2 | ||
|
|
b6ab8435d7 | ||
|
|
39a64fb4e7 | ||
|
|
d7c14b6f3a | ||
|
|
1880693aef | ||
|
|
dd20342825 | ||
|
|
a268d65c4f | ||
|
|
b397c768e4 | ||
|
|
8e22a7676a | ||
|
|
8a7c536ad5 | ||
|
|
8aad481c69 | ||
|
|
5814e121df | ||
|
|
7adbccbaf7 | ||
|
|
eb10c22db1 | ||
|
|
708f860e3a | ||
|
|
eb30f15363 | ||
|
|
4941d5b56b | ||
|
|
9bbb4741b4 | ||
|
|
282f2feb77 | ||
|
|
60a1f00618 | ||
|
|
9104054ca5 | ||
|
|
d69f144a99 | ||
|
|
929dfbd348 | ||
|
|
3047183fd9 | ||
|
|
ef5e4044f1 | ||
|
|
3779800322 | ||
|
|
986a20fb7d | ||
|
|
8311e1105f | ||
|
|
ba6845925d | ||
|
|
343a0fc073 | ||
|
|
54f8a4d0f3 | ||
|
|
9c36aae4b7 | ||
|
|
b766025a83 | ||
|
|
9b5f76f833 | ||
|
|
d647f484a4 | ||
|
|
8794792baa | ||
|
|
b85768c1f3 | ||
|
|
e6d71bd702 | ||
|
|
258992a160 | ||
|
|
a7bc00e330 | ||
|
|
11a40584e9 | ||
|
|
3e86bdb4d8 | ||
|
|
c817d65695 | ||
|
|
51dee793fe | ||
|
|
457fc4306e | ||
|
|
4f5b003e76 | ||
|
|
5421e27106 | ||
|
|
fe07660f40 | ||
|
|
da2f9e476e | ||
|
|
1a7a7ed1c3 | ||
|
|
413994912d | ||
|
|
01dcf1d0ad | ||
|
|
8e378779c2 | ||
|
|
970b52897c | ||
|
|
412ba04d19 | ||
|
|
bfef4b3e9b | ||
|
|
7bd316f3d0 | ||
|
|
26208363ee | ||
|
|
b1b4bb8850 | ||
|
|
9dd565b6e3 | ||
|
|
924f214303 | ||
|
|
5c1a34e766 | ||
|
|
fa90d06dd5 | ||
|
|
d869054318 | ||
|
|
0cc1ca9a8d | ||
|
|
3701195033 | ||
|
|
f884a56258 | ||
|
|
d79633ff52 | ||
|
|
e0ebc431dc | ||
|
|
131bc6c674 | ||
|
|
10d68cff50 | ||
|
|
996acc5253 | ||
|
|
7c4799d0cf | ||
|
|
c239087332 | ||
|
|
7018e9263d | ||
|
|
4990b4b4b7 | ||
|
|
5064373c23 | ||
|
|
6c93aea59a | ||
|
|
6553cdedab | ||
|
|
a61b2427b0 | ||
|
|
af4ece3d5f | ||
|
|
e64379c3d7 | ||
|
|
5053912534 | ||
|
|
932b1cbc32 | ||
|
|
de36ea7755 | ||
|
|
9f8db2c230 | ||
|
|
3f00e1b321 | ||
|
|
7ab9c119ef | ||
|
|
3f2922b3fa | ||
|
|
509f583dca | ||
|
|
2d01e71286 | ||
|
|
e612154694 | ||
|
|
82fcbe3901 | ||
|
|
dbd2465b56 | ||
|
|
ea79494b29 | ||
|
|
f35aff84c2 | ||
|
|
7b18ae6f16 | ||
|
|
a79c56d06b | ||
|
|
3d6e315a4c | ||
|
|
4c27f9c6ef | ||
|
|
d173a37d17 | ||
|
|
7fd346a2ca | ||
|
|
c673d502b9 | ||
|
|
c43c51362a | ||
|
|
3e86d93d13 | ||
|
|
f6e4e2d0f3 | ||
|
|
01a52aa8bd | ||
|
|
8415bf0823 | ||
|
|
327ff263f5 | ||
|
|
61c418048d | ||
|
|
9720ef8c34 | ||
|
|
978a4f6345 | ||
|
|
80fb03628b | ||
|
|
2480c0342c | ||
|
|
eb6f610a45 | ||
|
|
cb74e4191b | ||
|
|
dfa641ca41 | ||
|
|
969a9f99d5 | ||
|
|
c099b42ba3 | ||
|
|
b8315278cb | ||
|
|
485f8f2411 | ||
|
|
953e4f3841 | ||
|
|
adf65cfe61 | ||
|
|
12c829f6d3 | ||
|
|
913314f1b1 | ||
|
|
ef63f97afe | ||
|
|
bda74db01d | ||
|
|
9ff3ff9446 | ||
|
|
c75d071615 | ||
|
|
b4989130da | ||
|
|
4fc0303bda | ||
|
|
3d9cc51851 | ||
|
|
f69587656f | ||
|
|
d5fc340c30 | ||
|
|
d79a547dc9 | ||
|
|
bd1da4346a | ||
|
|
4c2a608a0c | ||
|
|
ee4eb8deaa | ||
|
|
7196ac8a07 | ||
|
|
c88b09bc6b | ||
|
|
87fab847b8 | ||
|
|
4e6055f084 | ||
|
|
975cf0dae5 | ||
|
|
4854a694cd | ||
|
|
b1f8e986bf | ||
|
|
c5ee208775 | ||
|
|
ddfdacfa49 | ||
|
|
2514ebc20f | ||
|
|
4f9c6540b2 | ||
|
|
21c9a6a1ff | ||
|
|
7f6d413ddd | ||
|
|
88277139e7 | ||
|
|
6cdd3493a1 | ||
|
|
9c91b6f4a6 | ||
|
|
cee838e335 | ||
|
|
d82c82db2c | ||
|
|
ba638ff38e | ||
|
|
da0c6579fa | ||
|
|
52a18c78a5 | ||
|
|
048edec9ed | ||
|
|
af56b7ec0b | ||
|
|
6c3e8482f7 | ||
|
|
390f2c41f6 | ||
|
|
aa04feebb4 | ||
|
|
45f3694f82 | ||
|
|
c5c54b31e2 | ||
|
|
69c84c9597 | ||
|
|
ae63b89cbf | ||
|
|
ff038f98b7 | ||
|
|
e00fd06355 | ||
|
|
521529d24d | ||
|
|
ed0719f2bc | ||
|
|
6a848b1a16 | ||
|
|
c8bcaf8a91 | ||
|
|
8cd0ed0509 | ||
|
|
177d8420a1 | ||
|
|
388a8c007c | ||
|
|
bdefdce1ae | ||
|
|
9e4f93d87e | ||
|
|
0b657d28cf | ||
|
|
c1a09daf15 | ||
|
|
8438df4a95 | ||
|
|
67fd7e3d09 | ||
|
|
d44031615d | ||
|
|
98cc1ec344 | ||
|
|
25b1e0d906 | ||
|
|
05f9f83240 | ||
|
|
fb739dbaec | ||
|
|
50fce538c6 | ||
|
|
3b6597bba9 | ||
|
|
f10720ed69 | ||
|
|
2bc550b2f0 | ||
|
|
ce36b8a6e5 | ||
|
|
560854a961 | ||
|
|
2064462c35 | ||
|
|
07288888ad | ||
|
|
34d392cf3d | ||
|
|
825c3fbbb1 | ||
|
|
00bdf73ec6 | ||
|
|
f44ab9b3da | ||
|
|
a61f2b89be | ||
|
|
b8bafbc291 | ||
|
|
548dfff0ae | ||
|
|
4dd2f3d03d | ||
|
|
6791a8364d | ||
|
|
d1a1c8a158 | ||
|
|
b4d26badf2 | ||
|
|
c5a0673c93 | ||
|
|
ad40bd6a00 | ||
|
|
5c00bbf36b | ||
|
|
9d6f5372a3 | ||
|
|
f06fd934f6 | ||
|
|
80c0cc445e | ||
|
|
762024b890 | ||
|
|
82a90a2325 | ||
|
|
b7cac4f4b8 | ||
|
|
e323374d2a | ||
|
|
ffc294d37e | ||
|
|
fceada9ef4 | ||
|
|
5f0f73fad9 | ||
|
|
530d6ee098 | ||
|
|
420c9759c6 | ||
|
|
2ce7c22218 | ||
|
|
4ef9ed80cd | ||
|
|
44b3fe6277 | ||
|
|
449801990f | ||
|
|
af2928d316 | ||
|
|
d948e38820 | ||
|
|
65218ce222 | ||
|
|
55e99c4030 | ||
|
|
b63d50671d | ||
|
|
eba980846b | ||
|
|
374d058de7 | ||
|
|
31cdcc3c3a | ||
|
|
ad9f6423e2 | ||
|
|
cbca63f091 | ||
|
|
b4748a226c | ||
|
|
5b943d9bb8 | ||
|
|
c86f69a105 | ||
|
|
d39fda0657 | ||
|
|
37f8dc4382 | ||
|
|
3a8adda381 | ||
|
|
8aa38aecaf | ||
|
|
f1dec77f46 | ||
|
|
cddaedaff8 | ||
|
|
cefb5a8822 | ||
|
|
e426a38c3e | ||
|
|
f14accb7b6 | ||
|
|
c5c704cb3b | ||
|
|
115a786581 | ||
|
|
5ef4cfd263 | ||
|
|
03fecb2f78 | ||
|
|
7fc8682a0a | ||
|
|
f1431311a4 | ||
|
|
1d14e051a5 | ||
|
|
97ae6733ed | ||
|
|
1d6b22b5f0 | ||
|
|
1a49076b5b | ||
|
|
d0e4cb3f07 | ||
|
|
e2813d9d4d | ||
|
|
20a7f088ce | ||
|
|
f63ba7d013 | ||
|
|
0a629d7391 | ||
|
|
a609330e4c | ||
|
|
c029597a5a | ||
|
|
30b7732565 | ||
|
|
6650632e7f | ||
|
|
afe627e7af | ||
|
|
67f6ff7fa9 | ||
|
|
c7ed1796a7 | ||
|
|
44c62d838e | ||
|
|
6bb580cda8 | ||
|
|
00a8cb8e5d | ||
|
|
2e34a39673 | ||
|
|
01b90829bc | ||
|
|
e699bd0730 | ||
|
|
961a9379d5 | ||
|
|
ec87b04aff | ||
|
|
aabf752a51 | ||
|
|
afb0674ccb | ||
|
|
ee625232a4 | ||
|
|
52d8dd41f1 | ||
|
|
be07d2d7a9 | ||
|
|
0f1b62c2b3 | ||
|
|
c30906a541 | ||
|
|
3533503323 | ||
|
|
82acdca638 | ||
|
|
a1e56a567b | ||
|
|
0c17d172a2 | ||
|
|
5d8e7c761f | ||
|
|
17fc522b75 | ||
|
|
f1daa5b88b | ||
|
|
fe9a1949a6 | ||
|
|
50cba6db9f | ||
|
|
18592e7f98 | ||
|
|
bd9612b81e | ||
|
|
7ab5fb65b2 | ||
|
|
8df5fedc35 | ||
|
|
4a61f68fa4 | ||
|
|
067890133c | ||
|
|
d3076f5a70 | ||
|
|
ed129f057f | ||
|
|
eab5ea01d7 | ||
|
|
3e287b3a26 | ||
|
|
4f33637b43 | ||
|
|
698a1e51ec | ||
|
|
27c0e1186c | ||
|
|
c54c71a3e5 | ||
|
|
3409c00e6f | ||
|
|
f8ef5fab64 | ||
|
|
5b397d455d | ||
|
|
f977558a28 | ||
|
|
c2e156e0e0 | ||
|
|
7aba2938d3 | ||
|
|
d587548250 | ||
|
|
21f9c51556 | ||
|
|
985ceba525 | ||
|
|
e62a4b02e5 | ||
|
|
ff34749572 | ||
|
|
e5804d4a50 | ||
|
|
3956a2b790 | ||
|
|
b33aa52dc2 | ||
|
|
76230db97f | ||
|
|
a66a013ed7 | ||
|
|
f4b02dfdc1 | ||
|
|
4cf218643e | ||
|
|
8f96b69a25 | ||
|
|
d262033ded | ||
|
|
5745eabe69 | ||
|
|
5f18642271 | ||
|
|
88a9278872 | ||
|
|
9bb3ca8169 | ||
|
|
f2f4728489 | ||
|
|
d1b616286f | ||
|
|
df74526f91 | ||
|
|
9f7ae0737a | ||
|
|
1ebb8412c5 | ||
|
|
7b69999c37 | ||
|
|
c7e959a948 | ||
|
|
ba5884e779 | ||
|
|
cdaa5c48db | ||
|
|
bab5c0e907 | ||
|
|
016838fd10 | ||
|
|
75053bf855 | ||
|
|
ae3a6dd2a9 | ||
|
|
6d963fbe8d | ||
|
|
88f6245c84 | ||
|
|
0e7d2f9f93 | ||
|
|
4e6ded1f36 | ||
|
|
d663588491 | ||
|
|
439caf5b79 | ||
|
|
c4ba43ca6f | ||
|
|
20cba2ecd9 | ||
|
|
0ff2e16d69 | ||
|
|
51607ec752 | ||
|
|
7992b14896 | ||
|
|
7e420aeed3 | ||
|
|
227d2c2050 | ||
|
|
93e53c91f7 | ||
|
|
58cffd3223 | ||
|
|
8f32271e8c | ||
|
|
c8c1c3d376 | ||
|
|
9f512acb42 | ||
|
|
c0b461a3b7 | ||
|
|
74fe5a5029 | ||
|
|
9d0a9d4e23 | ||
|
|
5758769ad3 | ||
|
|
e7eadc3605 | ||
|
|
07c6e58951 | ||
|
|
87994811a1 | ||
|
|
42feb7e8be | ||
|
|
26196b70af | ||
|
|
93a51979c4 | ||
|
|
ad7edc7b27 | ||
|
|
27cd4e6ffe | ||
|
|
cae5a8be1c | ||
|
|
8e10d4e8e7 | ||
|
|
b57f79f438 | ||
|
|
a9cf097951 | ||
|
|
5c3624e1af | ||
|
|
cba9ef8c0b | ||
|
|
4f8407a3a7 | ||
|
|
656e936f49 | ||
|
|
d92c314466 | ||
|
|
b747fb111d | ||
|
|
7e0a0b2d0c | ||
|
|
362d064afa | ||
|
|
1bd88de2e5 | ||
|
|
0b541ffebc | ||
|
|
106be19c3e | ||
|
|
25d72bf881 | ||
|
|
9d5b5297cc | ||
|
|
462884bebb | ||
|
|
b1cc24b795 | ||
|
|
f0eb55b327 | ||
|
|
6dc285b5ca | ||
|
|
07e614eef7 | ||
|
|
916b2a8fd3 | ||
|
|
869f5bb279 | ||
|
|
3e21338f82 | ||
|
|
37bb3c6a77 | ||
|
|
d4ab2fa0e6 | ||
|
|
72d3f4896a | ||
|
|
5e6f973b99 | ||
|
|
7ed77b02ad | ||
|
|
127a64d5a0 | ||
|
|
caa31aafda | ||
|
|
dae318495f | ||
|
|
305a7abcb9 | ||
|
|
219d13b718 | ||
|
|
df20c27696 | ||
|
|
a5a62768c0 | ||
|
|
4001637beb | ||
|
|
47044c05a8 | ||
|
|
a449d82723 | ||
|
|
fee8e97b4e | ||
|
|
72d9ed4056 | ||
|
|
1be1b3a86d | ||
|
|
9452c0a4b6 | ||
|
|
307b729549 | ||
|
|
696239d6e1 | ||
|
|
6929d90353 | ||
|
|
348d032029 | ||
|
|
d1d3fcdfd5 | ||
|
|
abf3a67dd0 | ||
|
|
d87abeecf0 | ||
|
|
4e28e4f741 | ||
|
|
80a55cedeb | ||
|
|
d05c343602 | ||
|
|
33f67386fe | ||
|
|
56d8168dc4 | ||
|
|
5d87cc0558 | ||
|
|
cb41947eb4 | ||
|
|
0857eba17b | ||
|
|
020b0db090 | ||
|
|
bf0760fde4 | ||
|
|
bb8e45383e | ||
|
|
a1df576e4f | ||
|
|
7fb0254794 | ||
|
|
c82d1e52cc | ||
|
|
846151b605 | ||
|
|
e44e31dd5b | ||
|
|
f7b9501662 | ||
|
|
e12fe4cbbb | ||
|
|
49d2e1f135 | ||
|
|
8191fd8e6c | ||
|
|
d73395e1dc | ||
|
|
64d001162b | ||
|
|
bb00a23116 | ||
|
|
63d6e9b91b | ||
|
|
66eed5681a | ||
|
|
8ecdb11979 | ||
|
|
894fcc8e02 | ||
|
|
7f43f0f3ff | ||
|
|
87e03dd1ce | ||
|
|
e5cacb465d | ||
|
|
ee8371f753 | ||
|
|
081723f983 | ||
|
|
b61f36579c | ||
|
|
33f53aa458 | ||
|
|
412ab5f063 | ||
|
|
11e02e901c | ||
|
|
65a8f4cf44 | ||
|
|
27d128bbb4 | ||
|
|
070f9bec58 | ||
|
|
f817032513 | ||
|
|
17abe221c0 | ||
|
|
4a7a81e039 | ||
|
|
37fd4eb643 | ||
|
|
865b0e4c03 | ||
|
|
b324921c1a | ||
|
|
63f72caf30 | ||
|
|
99ac17b90a | ||
|
|
4b0ed9ee88 | ||
|
|
20056f6cda | ||
|
|
3b35279b16 | ||
|
|
27deb44df5 | ||
|
|
24a3ef949b | ||
|
|
bc3e098964 | ||
|
|
c247dcdd7b | ||
|
|
793ae9855e | ||
|
|
9fa426d51b | ||
|
|
cec6288a99 | ||
|
|
9639578c2a | ||
|
|
743ecbd365 | ||
|
|
084c643973 | ||
|
|
824e7682e4 | ||
|
|
f9074684dd | ||
|
|
ddff782133 | ||
|
|
3051152103 | ||
|
|
06026bb47d | ||
|
|
226388ae27 | ||
|
|
ea7548b4cc | ||
|
|
c7486ead96 | ||
|
|
90a291214c | ||
|
|
c111c42a86 | ||
|
|
6fb5b63018 | ||
|
|
ec56dfa35e | ||
|
|
943cd51b67 | ||
|
|
301faa074c | ||
|
|
dc0481e832 | ||
|
|
4f8fcdbaf7 | ||
|
|
b80aa7fee3 | ||
|
|
c384be02c9 | ||
|
|
d17ac3bb40 | ||
|
|
c7554ccac2 | ||
|
|
35ef1c7bae | ||
|
|
d87d0672a8 | ||
|
|
3da42fd1e8 | ||
|
|
503aa61325 | ||
|
|
e4c276d0c2 | ||
|
|
e07f7691a8 | ||
|
|
623ab4a96e | ||
|
|
e1efa337a2 | ||
|
|
549cdf2f7d | ||
|
|
3c522386e9 | ||
|
|
c202aa9ce9 | ||
|
|
e3e28c6231 | ||
|
|
4e05368086 | ||
|
|
e1afe74fe2 | ||
|
|
461acb02f5 | ||
|
|
415edc237c | ||
|
|
e20ecd2574 | ||
|
|
ab477b5631 | ||
|
|
0823d5c7f2 | ||
|
|
1cc6930363 | ||
|
|
4297500928 | ||
|
|
a58f042614 | ||
|
|
469c6bc2b6 | ||
|
|
887074efd2 | ||
|
|
9c2c15ca45 | ||
|
|
1b3b098329 | ||
|
|
6f7075e3aa | ||
|
|
ccbddd8842 | ||
|
|
879dd261c2 | ||
|
|
52f5eb5980 | ||
|
|
ea2f69a0d7 | ||
|
|
9f2064a8ed | ||
|
|
e3750d9ddf | ||
|
|
c1eee3012e | ||
|
|
6b08babbd2 | ||
|
|
215b81342e | ||
|
|
06bfa7e08b | ||
|
|
3d83cbb872 | ||
|
|
8a803b30f6 | ||
|
|
80be649de7 | ||
|
|
9648f950f5 | ||
|
|
6b9ffc8bec | ||
|
|
d903053faf | ||
|
|
676f1b5a26 | ||
|
|
b8dec12f15 | ||
|
|
fc9b223acc | ||
|
|
ba824089d7 | ||
|
|
1a2faf09e0 | ||
|
|
5a43bb8149 | ||
|
|
0104614656 | ||
|
|
77a77f6d2d | ||
|
|
089b9daa1c | ||
|
|
ba34ea4ee8 | ||
|
|
2917b8a005 | ||
|
|
dcf24d45a2 | ||
|
|
75fdb06696 | ||
|
|
e00ad37580 | ||
|
|
5cfb70c2b4 | ||
|
|
2a70c45697 | ||
|
|
c58b00580e | ||
|
|
7c60e69c33 | ||
|
|
33e94891ee | ||
|
|
73e0729f63 | ||
|
|
21c529229c | ||
|
|
63643e6386 | ||
|
|
6cc2edce99 | ||
|
|
d122ff3ca8 | ||
|
|
14c6d526b4 | ||
|
|
28e07bca16 | ||
|
|
faa5f1d802 | ||
|
|
9d3365df54 | ||
|
|
6ff84d34d1 | ||
|
|
b845425cd0 | ||
|
|
89519c88e2 | ||
|
|
ff813bf99d | ||
|
|
cf475bcb50 | ||
|
|
bc80d7c789 | ||
|
|
b7566f6961 | ||
|
|
0542fdb8e4 | ||
|
|
78c474c744 | ||
|
|
88411a1f52 | ||
|
|
ae6cf70bc4 | ||
|
|
68d1281759 | ||
|
|
0308d60cb2 | ||
|
|
59f5fdbb33 | ||
|
|
13184f5f80 |
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/test/www*/dir/*.html text eol=lf
|
||||
/test/www*/dir/*.txt text eol=lf
|
||||
69
.github/workflows/abidiff.yaml
vendored
Normal file
69
.github/workflows/abidiff.yaml
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
# SPDX-FileCopyrightText: 2025 Andrea Pappacoda <andrea@pappacoda.it>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
name: abidiff
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: sh
|
||||
|
||||
jobs:
|
||||
abi:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||
container:
|
||||
image: debian:testing
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: apt -y --update install --no-install-recommends
|
||||
abigail-tools
|
||||
ca-certificates
|
||||
g++
|
||||
git
|
||||
libbrotli-dev
|
||||
libssl-dev
|
||||
meson
|
||||
pkg-config
|
||||
python3
|
||||
zlib1g-dev
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: current
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: previous
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Checkout previous
|
||||
working-directory: previous
|
||||
run: |
|
||||
git switch master
|
||||
git describe --tags --abbrev=0 master | xargs git checkout
|
||||
|
||||
- name: Build current
|
||||
working-directory: current
|
||||
run: |
|
||||
meson setup --buildtype=debug -Dcpp-httplib_compile=true build
|
||||
ninja -C build
|
||||
|
||||
- name: Build previous
|
||||
working-directory: previous
|
||||
run: |
|
||||
meson setup --buildtype=debug -Dcpp-httplib_compile=true build
|
||||
ninja -C build
|
||||
|
||||
- name: Run abidiff
|
||||
run: abidiff
|
||||
--headers-dir1 previous/build
|
||||
--headers-dir2 current/build
|
||||
previous/build/libcpp-httplib.so
|
||||
current/build/libcpp-httplib.so
|
||||
32
.github/workflows/cifuzz.yaml
vendored
Normal file
32
.github/workflows/cifuzz.yaml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: CIFuzz
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'cpp-httplib'
|
||||
dry-run: false
|
||||
language: c++
|
||||
- name: Run Fuzzers
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'cpp-httplib'
|
||||
fuzz-seconds: 600
|
||||
dry-run: false
|
||||
language: c++
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out/artifacts
|
||||
145
.github/workflows/test.yaml
vendored
145
.github/workflows/test.yaml
vendored
@ -1,41 +1,126 @@
|
||||
name: test
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
gtest_filter:
|
||||
description: 'Google Test filter'
|
||||
test_linux:
|
||||
description: 'Test on Linux'
|
||||
type: boolean
|
||||
default: true
|
||||
test_macos:
|
||||
description: 'Test on MacOS'
|
||||
type: boolean
|
||||
default: true
|
||||
test_windows:
|
||||
description: 'Test on Windows'
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
GTEST_FILTER: ${{ github.event.inputs.gtest_filter || '*' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
style-check:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: run style check
|
||||
run: |
|
||||
clang-format --version
|
||||
cd test && make style_check
|
||||
|
||||
ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
(github.event_name == 'push') ||
|
||||
(github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) ||
|
||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.test_linux == 'true')
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: install libraries
|
||||
run: sudo apt-get update && sudo apt-get install -y libbrotli-dev libcurl4-openssl-dev
|
||||
- name: build and run tests
|
||||
run: cd test && make
|
||||
- name: run fuzz test target
|
||||
run: cd test && make fuzz_test
|
||||
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
if: >
|
||||
(github.event_name == 'push') ||
|
||||
(github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) ||
|
||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.test_macos == 'true')
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: build and run tests
|
||||
run: cd test && make
|
||||
- name: run fuzz test target
|
||||
run: cd test && make fuzz_test
|
||||
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
if: >
|
||||
(github.event_name == 'push') ||
|
||||
(github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) ||
|
||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.test_windows == 'true')
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macOS-latest, ubuntu-latest, windows-latest]
|
||||
|
||||
config:
|
||||
- with_ssl: false
|
||||
name: without SSL
|
||||
- with_ssl: true
|
||||
name: with SSL
|
||||
name: windows ${{ matrix.config.name }}
|
||||
steps:
|
||||
- name: prepare git for checkout on windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
- name: Prepare Git for Checkout on Windows
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: install brotli library on ubuntu
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: sudo apt update && sudo apt-get install -y libbrotli-dev
|
||||
- name: install brotli library on macOS
|
||||
if: matrix.os == 'macOS-latest'
|
||||
run: brew install brotli
|
||||
- name: make
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: cd test && make
|
||||
- name: check fuzz test target
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: cd test && make -f Makefile.fuzz_test
|
||||
- name: setup msbuild on windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
- name: make-windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
cd test
|
||||
msbuild.exe test.sln /verbosity:minimal /t:Build "/p:Configuration=Release;Platform=x64"
|
||||
x64\Release\test.exe
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Export GitHub Actions cache environment variables
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
|
||||
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
||||
- name: Setup msbuild on windows
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
- name: Install vcpkg dependencies
|
||||
run: vcpkg install gtest curl zlib brotli
|
||||
- name: Install OpenSSL
|
||||
if: ${{ matrix.config.with_ssl }}
|
||||
run: choco install openssl
|
||||
- name: Configure CMake ${{ matrix.config.name }}
|
||||
run: >
|
||||
cmake -B build -S .
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake
|
||||
-DHTTPLIB_TEST=ON
|
||||
-DHTTPLIB_REQUIRE_ZLIB=ON
|
||||
-DHTTPLIB_REQUIRE_BROTLI=ON
|
||||
-DHTTPLIB_REQUIRE_OPENSSL=${{ matrix.config.with_ssl && 'ON' || 'OFF' }}
|
||||
- name: Build ${{ matrix.config.name }}
|
||||
run: cmake --build build --config Release -- /v:m /clp:ShowCommandLine
|
||||
- name: Run tests ${{ matrix.config.name }}
|
||||
run: ctest --output-on-failure --test-dir build -C Release
|
||||
|
||||
env:
|
||||
VCPKG_ROOT: "C:/vcpkg"
|
||||
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@ -9,16 +9,27 @@ example/benchmark
|
||||
example/redirect
|
||||
example/sse*
|
||||
example/upload
|
||||
example/one_time_request
|
||||
example/server_and_client
|
||||
example/*.pem
|
||||
test/httplib.cc
|
||||
test/httplib.h
|
||||
test/test
|
||||
test/server_fuzzer
|
||||
test/test_proxy
|
||||
test/test_split
|
||||
test/test.xcodeproj/xcuser*
|
||||
test/test.xcodeproj/*/xcuser*
|
||||
test/*.o
|
||||
test/*.pem
|
||||
test/*.srl
|
||||
test/_build_*
|
||||
work/
|
||||
benchmark/server*
|
||||
|
||||
*.swp
|
||||
|
||||
build/
|
||||
Debug
|
||||
Release
|
||||
*.vcxproj.user
|
||||
@ -28,5 +39,7 @@ Release
|
||||
*.db
|
||||
ipch
|
||||
*.dSYM
|
||||
*.pyc
|
||||
.*
|
||||
!/.gitattributes
|
||||
!/.travis.yml
|
||||
|
||||
198
CMakeLists.txt
198
CMakeLists.txt
@ -3,18 +3,21 @@
|
||||
* BUILD_SHARED_LIBS (default off) builds as a shared library (if HTTPLIB_COMPILE is ON)
|
||||
* HTTPLIB_USE_OPENSSL_IF_AVAILABLE (default on)
|
||||
* HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on)
|
||||
* HTTPLIB_USE_BROTLI_IF_AVAILABLE (default on)
|
||||
* HTTPLIB_REQUIRE_OPENSSL (default off)
|
||||
* HTTPLIB_REQUIRE_ZLIB (default off)
|
||||
* HTTPLIB_USE_BROTLI_IF_AVAILABLE (default on)
|
||||
* HTTPLIB_REQUIRE_BROTLI (default off)
|
||||
* HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN (default on)
|
||||
* HTTPLIB_COMPILE (default off)
|
||||
* HTTPLIB_INSTALL (default on)
|
||||
* HTTPLIB_TEST (default off)
|
||||
* BROTLI_USE_STATIC_LIBS - tells Cmake to use the static Brotli libs (only works if you have them installed).
|
||||
* OPENSSL_USE_STATIC_LIBS - tells Cmake to use the static OpenSSL libs (only works if you have them installed).
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
After installation with Cmake, a find_package(httplib) is available.
|
||||
This creates a httplib::httplib target (if found).
|
||||
After installation with Cmake, a find_package(httplib COMPONENTS OpenSSL ZLIB Brotli) is available.
|
||||
This creates a httplib::httplib target (if found and if listed components are supported).
|
||||
It can be linked like so:
|
||||
|
||||
target_link_libraries(your_exe httplib::httplib)
|
||||
@ -42,6 +45,7 @@
|
||||
* HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL support is enabled.
|
||||
* HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled.
|
||||
* HTTPLIB_IS_USING_BROTLI - a bool for if Brotli support is enabled.
|
||||
* HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN - a bool for if support of loading system certs from the Apple Keychain is enabled.
|
||||
* HTTPLIB_IS_COMPILED - a bool for if the library is compiled, or otherwise header-only.
|
||||
* HTTPLIB_INCLUDE_DIR - the root path to httplib's header (e.g. /usr/include).
|
||||
* HTTPLIB_LIBRARY - the full path to the library if compiled (e.g. /usr/lib/libhttplib.so).
|
||||
@ -55,58 +59,48 @@
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
FindPython3 requires Cmake v3.12
|
||||
ARCH_INDEPENDENT option of write_basic_package_version_file() requires Cmake v3.14
|
||||
]]
|
||||
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR)
|
||||
|
||||
# On systems without Git installed, there were errors since execute_process seemed to not throw an error without it?
|
||||
find_package(Git QUIET)
|
||||
if(Git_FOUND)
|
||||
# Gets the latest tag as a string like "v0.6.6"
|
||||
# Can silently fail if git isn't on the system
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE _raw_version_string
|
||||
ERROR_VARIABLE _git_tag_error
|
||||
)
|
||||
endif()
|
||||
# Get the CPPHTTPLIB_VERSION value and use it as a version
|
||||
# This gets the string with the CPPHTTPLIB_VERSION value from the header.
|
||||
# This is so the maintainer doesn't actually need to update this manually.
|
||||
file(STRINGS httplib.h _raw_version_string REGEX "CPPHTTPLIB_VERSION \"([0-9]+\\.[0-9]+\\.[0-9]+)\"")
|
||||
|
||||
# execute_process can fail silenty, so check for an error
|
||||
# if there was an error, just use the user agent as a version
|
||||
if(_git_tag_error OR NOT Git_FOUND)
|
||||
message(WARNING "cpp-httplib failed to find the latest Git tag, falling back to using user agent as the version.")
|
||||
# Get the user agent and use it as a version
|
||||
# This gets the string with the user agent from the header.
|
||||
# This is so the maintainer doesn't actually need to update this manually.
|
||||
file(STRINGS httplib.h _raw_version_string REGEX "User\-Agent.*cpp\-httplib/([0-9]+\.?)+")
|
||||
endif()
|
||||
# Needed since git tags have "v" prefixing them.
|
||||
# Also used if the fallback to user agent string is being used.
|
||||
# Extracts just the version string itself from the whole string contained in _raw_version_string
|
||||
# since _raw_version_string would contain the entire line of code where it found the version string
|
||||
string(REGEX MATCH "([0-9]+\\.?)+" _httplib_version "${_raw_version_string}")
|
||||
|
||||
project(httplib VERSION ${_httplib_version} LANGUAGES CXX)
|
||||
project(httplib
|
||||
VERSION ${_httplib_version}
|
||||
LANGUAGES CXX
|
||||
DESCRIPTION "A C++ header-only HTTP/HTTPS server and client library."
|
||||
HOMEPAGE_URL "https://github.com/yhirose/cpp-httplib"
|
||||
)
|
||||
|
||||
# Change as needed to set an OpenSSL minimum version.
|
||||
# This is used in the installed Cmake config file.
|
||||
set(_HTTPLIB_OPENSSL_MIN_VER "1.1.1")
|
||||
set(_HTTPLIB_OPENSSL_MIN_VER "3.0.0")
|
||||
|
||||
# Lets you disable C++ exception during CMake configure time.
|
||||
# The value is used in the install CMake config file.
|
||||
option(HTTPLIB_NO_EXCEPTIONS "Disable the use of C++ exceptions" OFF)
|
||||
# Allow for a build to require OpenSSL to pass, instead of just being optional
|
||||
option(HTTPLIB_REQUIRE_OPENSSL "Requires OpenSSL to be found & linked, or fails build." OFF)
|
||||
option(HTTPLIB_REQUIRE_ZLIB "Requires ZLIB to be found & linked, or fails build." OFF)
|
||||
# Allow for a build to casually enable OpenSSL/ZLIB support, but silenty continue if not found.
|
||||
# Allow for a build to casually enable OpenSSL/ZLIB support, but silently continue if not found.
|
||||
# Make these options so their automatic use can be specifically disabled (as needed)
|
||||
option(HTTPLIB_USE_OPENSSL_IF_AVAILABLE "Uses OpenSSL (if available) to enable HTTPS support." ON)
|
||||
option(HTTPLIB_USE_ZLIB_IF_AVAILABLE "Uses ZLIB (if available) to enable Zlib compression support." ON)
|
||||
# Lets you compile the program as a regular library instead of header-only
|
||||
option(HTTPLIB_COMPILE "If ON, uses a Python script to split the header into a compilable header & source file (requires Python v3)." OFF)
|
||||
# Just setting this variable here for people building in-tree
|
||||
if(HTTPLIB_COMPILE)
|
||||
set(HTTPLIB_IS_COMPILED TRUE)
|
||||
endif()
|
||||
|
||||
# Lets you disable the installation (useful when fetched from another CMake project)
|
||||
option(HTTPLIB_INSTALL "Enables the installation target" ON)
|
||||
option(HTTPLIB_TEST "Enables testing and builds tests" OFF)
|
||||
option(HTTPLIB_REQUIRE_BROTLI "Requires Brotli to be found & linked, or fails build." OFF)
|
||||
option(HTTPLIB_USE_BROTLI_IF_AVAILABLE "Uses Brotli (if available) to enable Brotli decompression support." ON)
|
||||
option(HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN "Enable feature to load system certs from the Apple Keychain." ON)
|
||||
# Defaults to static library
|
||||
option(BUILD_SHARED_LIBS "Build the library as a shared library instead of static. Has no effect if using header-only." OFF)
|
||||
if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
|
||||
@ -115,40 +109,48 @@ if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
endif()
|
||||
|
||||
# Set some variables that are used in-tree and while building based on our options
|
||||
set(HTTPLIB_IS_COMPILED ${HTTPLIB_COMPILE})
|
||||
set(HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN ${HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN})
|
||||
|
||||
# Threads needed for <thread> on some systems, and for <pthread.h> on Linux
|
||||
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||
find_package(Threads REQUIRED)
|
||||
# Since Cmake v3.11, Crypto & SSL became optional when not specified as COMPONENTS.
|
||||
if(HTTPLIB_REQUIRE_OPENSSL)
|
||||
find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} COMPONENTS Crypto SSL REQUIRED)
|
||||
set(HTTPLIB_IS_USING_OPENSSL TRUE)
|
||||
elseif(HTTPLIB_USE_OPENSSL_IF_AVAILABLE)
|
||||
find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} COMPONENTS Crypto SSL QUIET)
|
||||
endif()
|
||||
# Just setting this variable here for people building in-tree
|
||||
if(OPENSSL_FOUND)
|
||||
set(HTTPLIB_IS_USING_OPENSSL TRUE)
|
||||
# Avoid a rare circumstance of not finding all components but the end-user did their
|
||||
# own call for OpenSSL, which might trick us into thinking we'd otherwise have what we wanted
|
||||
if (TARGET OpenSSL::SSL AND TARGET OpenSSL::Crypto)
|
||||
set(HTTPLIB_IS_USING_OPENSSL ${OPENSSL_FOUND})
|
||||
else()
|
||||
set(HTTPLIB_IS_USING_OPENSSL FALSE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(HTTPLIB_REQUIRE_ZLIB)
|
||||
find_package(ZLIB REQUIRED)
|
||||
set(HTTPLIB_IS_USING_ZLIB TRUE)
|
||||
elseif(HTTPLIB_USE_ZLIB_IF_AVAILABLE)
|
||||
find_package(ZLIB QUIET)
|
||||
endif()
|
||||
# Just setting this variable here for people building in-tree
|
||||
# FindZLIB doesn't have a ZLIB_FOUND variable, so check the target.
|
||||
if(TARGET ZLIB::ZLIB)
|
||||
set(HTTPLIB_IS_USING_ZLIB TRUE)
|
||||
# FindZLIB doesn't have a ZLIB_FOUND variable, so check the target.
|
||||
if(TARGET ZLIB::ZLIB)
|
||||
set(HTTPLIB_IS_USING_ZLIB TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Adds our cmake folder to the search path for find_package
|
||||
# This is so we can use our custom FindBrotli.cmake
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
if(HTTPLIB_REQUIRE_BROTLI)
|
||||
find_package(Brotli COMPONENTS encoder decoder common REQUIRED)
|
||||
set(HTTPLIB_IS_USING_BROTLI TRUE)
|
||||
elseif(HTTPLIB_USE_BROTLI_IF_AVAILABLE)
|
||||
find_package(Brotli COMPONENTS encoder decoder common QUIET)
|
||||
endif()
|
||||
# Just setting this variable here for people building in-tree
|
||||
if(Brotli_FOUND)
|
||||
set(HTTPLIB_IS_USING_BROTLI TRUE)
|
||||
set(HTTPLIB_IS_USING_BROTLI ${Brotli_FOUND})
|
||||
endif()
|
||||
|
||||
# Used for default, common dirs that the end-user can change (if needed)
|
||||
@ -176,7 +178,7 @@ if(HTTPLIB_COMPILE)
|
||||
ERROR_VARIABLE _httplib_split_error
|
||||
)
|
||||
if(_httplib_split_error)
|
||||
message(FATAL_ERROR "Failed when trying to split Cpp-httplib with the Python script.\n${_httplib_split_error}")
|
||||
message(FATAL_ERROR "Failed when trying to split cpp-httplib with the Python script.\n${_httplib_split_error}")
|
||||
endif()
|
||||
|
||||
# split.py puts output in "out"
|
||||
@ -188,6 +190,12 @@ if(HTTPLIB_COMPILE)
|
||||
$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
|
||||
)
|
||||
set_target_properties(${PROJECT_NAME}
|
||||
PROPERTIES
|
||||
VERSION ${${PROJECT_NAME}_VERSION}
|
||||
SOVERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}"
|
||||
OUTPUT_NAME cpp-httplib
|
||||
)
|
||||
else()
|
||||
# This is for header-only.
|
||||
set(_INTERFACE_OR_PUBLIC INTERFACE)
|
||||
@ -198,22 +206,10 @@ endif()
|
||||
# Only useful if building in-tree, versus using it from an installation.
|
||||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||
|
||||
# Might be missing some, but this list is somewhat comprehensive
|
||||
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||
cxx_std_11
|
||||
cxx_nullptr
|
||||
cxx_lambdas
|
||||
cxx_override
|
||||
cxx_defaulted_functions
|
||||
cxx_attribute_deprecated
|
||||
cxx_auto_type
|
||||
cxx_decltype
|
||||
cxx_deleted_functions
|
||||
cxx_range_for
|
||||
cxx_sizeof_member
|
||||
)
|
||||
# Require C++11
|
||||
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} cxx_std_11)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||
target_include_directories(${PROJECT_NAME} SYSTEM ${_INTERFACE_OR_PUBLIC}
|
||||
$<BUILD_INTERFACE:${_httplib_build_includedir}>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
@ -224,7 +220,8 @@ target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||
# Needed for Windows libs on Mingw, as the pragma comment(lib, "xyz") aren't triggered.
|
||||
$<$<PLATFORM_ID:Windows>:ws2_32>
|
||||
$<$<PLATFORM_ID:Windows>:crypt32>
|
||||
$<$<PLATFORM_ID:Windows>:cryptui>
|
||||
# Needed for API from MacOS Security framework
|
||||
"$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN}>>:-framework CoreFoundation -framework Security>"
|
||||
# Can't put multiple targets in a single generator expression or it bugs out.
|
||||
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::common>
|
||||
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::encoder>
|
||||
@ -236,74 +233,77 @@ target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||
|
||||
# Set the definitions to enable optional features
|
||||
target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||
$<$<BOOL:${HTTPLIB_NO_EXCEPTIONS}>:CPPHTTPLIB_NO_EXCEPTIONS>
|
||||
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:CPPHTTPLIB_BROTLI_SUPPORT>
|
||||
$<$<BOOL:${HTTPLIB_IS_USING_ZLIB}>:CPPHTTPLIB_ZLIB_SUPPORT>
|
||||
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:CPPHTTPLIB_OPENSSL_SUPPORT>
|
||||
$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN}>>:CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN>
|
||||
)
|
||||
|
||||
# Cmake's find_package search path is different based on the system
|
||||
# See https://cmake.org/cmake/help/latest/command/find_package.html for the list
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_PREFIX}/cmake/${PROJECT_NAME}")
|
||||
else()
|
||||
# On Non-Windows, it should be /usr/lib/cmake/<name>/<name>Config.cmake
|
||||
# NOTE: This may or may not work for macOS...
|
||||
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
endif()
|
||||
# CMake configuration files installation directory
|
||||
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# Configures the meta-file httplibConfig.cmake.in to replace variables with paths/values/etc.
|
||||
configure_package_config_file("${PROJECT_NAME}Config.cmake.in"
|
||||
configure_package_config_file("cmake/${PROJECT_NAME}Config.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
INSTALL_DESTINATION "${_TARGET_INSTALL_CMAKEDIR}"
|
||||
# Passes the includedir install path
|
||||
PATH_VARS CMAKE_INSTALL_FULL_INCLUDEDIR
|
||||
# There aren't any components, so don't use the macro
|
||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||
)
|
||||
|
||||
if(HTTPLIB_COMPILE)
|
||||
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
|
||||
# Example: if you find_package(httplib 0.5.4)
|
||||
# then anything >= 0.5 and <= 1.0 is accepted
|
||||
COMPATIBILITY SameMajorVersion
|
||||
# then anything >= 0.5.4 and < 0.6 is accepted
|
||||
COMPATIBILITY SameMinorVersion
|
||||
)
|
||||
else()
|
||||
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
|
||||
# Example: if you find_package(httplib 0.5.4)
|
||||
# then anything >= 0.5 and <= 1.0 is accepted
|
||||
COMPATIBILITY SameMajorVersion
|
||||
# then anything >= 0.5.4 and < 0.6 is accepted
|
||||
COMPATIBILITY SameMinorVersion
|
||||
# Tells Cmake that it's a header-only lib
|
||||
# Mildly useful for end-users :)
|
||||
ARCH_INDEPENDENT
|
||||
)
|
||||
endif()
|
||||
|
||||
# Creates the export httplibTargets.cmake
|
||||
# This is strictly what holds compilation requirements
|
||||
# and linkage information (doesn't find deps though).
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
EXPORT httplibTargets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
if(HTTPLIB_INSTALL)
|
||||
# Creates the export httplibTargets.cmake
|
||||
# This is strictly what holds compilation requirements
|
||||
# and linkage information (doesn't find deps though).
|
||||
install(TARGETS ${PROJECT_NAME} EXPORT httplibTargets)
|
||||
|
||||
install(FILES "${_httplib_build_includedir}/httplib.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
install(FILES "${_httplib_build_includedir}/httplib.h" TYPE INCLUDE)
|
||||
|
||||
install(FILES
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
# Install it so it can be used later by the httplibConfig.cmake file.
|
||||
# Put it in the same dir as our config file instead of a global path so we don't potentially stomp on other packages.
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindBrotli.cmake"
|
||||
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
||||
)
|
||||
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
||||
)
|
||||
|
||||
# NOTE: This path changes depending on if it's on Windows or Linux
|
||||
install(EXPORT httplibTargets
|
||||
# Puts the targets into the httplib namespace
|
||||
# So this makes httplib::httplib linkable after doing find_package(httplib)
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
||||
)
|
||||
# NOTE: This path changes depending on if it's on Windows or Linux
|
||||
install(EXPORT httplibTargets
|
||||
# Puts the targets into the httplib namespace
|
||||
# So this makes httplib::httplib linkable after doing find_package(httplib)
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
||||
)
|
||||
|
||||
# Install documentation & license
|
||||
# ex: /usr/share/doc/httplib/README.md and /usr/share/licenses/httplib/LICENSE
|
||||
install(FILES "README.md" DESTINATION "${CMAKE_INSTALL_DOCDIR}")
|
||||
install(FILES "LICENSE" DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/${PROJECT_NAME}")
|
||||
|
||||
include(CPack)
|
||||
endif()
|
||||
|
||||
if(HTTPLIB_TEST)
|
||||
include(CTest)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
11
Dockerfile
Normal file
11
Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM yhirose4dockerhub/ubuntu-builder AS builder
|
||||
WORKDIR /build
|
||||
COPY httplib.h .
|
||||
COPY docker/main.cc .
|
||||
RUN g++ -std=c++23 -static -o server -O2 -I. main.cc && strip server
|
||||
|
||||
FROM scratch
|
||||
COPY --from=builder /build/server /server
|
||||
COPY docker/html/index.html /html/index.html
|
||||
EXPOSE 80
|
||||
CMD ["/server"]
|
||||
306
README.md
306
README.md
@ -7,12 +7,13 @@ A C++11 single-file header-only cross platform HTTP/HTTPS library.
|
||||
|
||||
It's extremely easy to setup. Just include the **httplib.h** file in your code!
|
||||
|
||||
NOTE: This is a 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.
|
||||
> [!IMPORTANT]
|
||||
> This library uses 'blocking' socket I/O. If you are looking for a library with 'non-blocking' socket I/O, this is not the one that you want.
|
||||
|
||||
Simple examples
|
||||
---------------
|
||||
|
||||
#### Server
|
||||
#### Server (Multi-threaded)
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@ -38,20 +39,51 @@ svr.listen("0.0.0.0", 8080);
|
||||
#include "path/to/httplib.h"
|
||||
|
||||
// HTTP
|
||||
httplib::Client cli("http://cpp-httplib-server.yhirose.repl.co");
|
||||
httplib::Client cli("http://yhirose.github.io");
|
||||
|
||||
// HTTPS
|
||||
httplib::Client cli("https://cpp-httplib-server.yhirose.repl.co");
|
||||
httplib::Client cli("https://yhirose.github.io");
|
||||
|
||||
auto res = cli.Get("/hi");
|
||||
res->status;
|
||||
res->body;
|
||||
```
|
||||
|
||||
### Try out the examples on Repl.it!
|
||||
SSL Support
|
||||
-----------
|
||||
|
||||
1. Run server at https://repl.it/@yhirose/cpp-httplib-server
|
||||
2. Run client at https://repl.it/@yhirose/cpp-httplib-client
|
||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||
|
||||
> [!NOTE]
|
||||
> cpp-httplib currently supports only version 3.0 or later. Please see [this page](https://www.openssl.org/policies/releasestrat.html) to get more information.
|
||||
|
||||
> [!TIP]
|
||||
> For macOS: cpp-httplib now can use system certs with `CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN`. `CoreFoundation` and `Security` should be linked with `-framework`.
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include "path/to/httplib.h"
|
||||
|
||||
// Server
|
||||
httplib::SSLServer svr("./cert.pem", "./key.pem");
|
||||
|
||||
// Client
|
||||
httplib::Client cli("https://localhost:1234"); // scheme + host
|
||||
httplib::SSLClient cli("localhost:1234"); // host
|
||||
httplib::SSLClient cli("localhost", 1234); // host, port
|
||||
|
||||
// Use your CA bundle
|
||||
cli.set_ca_cert_path("./ca-bundle.crt");
|
||||
|
||||
// Disable cert verification
|
||||
cli.enable_server_certificate_verification(false);
|
||||
|
||||
// Disable host verification
|
||||
cli.enable_server_hostname_verification(false);
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.
|
||||
|
||||
Server
|
||||
------
|
||||
@ -69,11 +101,20 @@ int main(void)
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
// Match the request path against a regular expression
|
||||
// and extract its captures
|
||||
svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) {
|
||||
auto numbers = req.matches[1];
|
||||
res.set_content(numbers, "text/plain");
|
||||
});
|
||||
|
||||
// Capture the second segment of the request path as "id" path param
|
||||
svr.Get("/users/:id", [&](const Request& req, Response& res) {
|
||||
auto user_id = req.path_params.at("id");
|
||||
res.set_content(user_id, "text/plain");
|
||||
});
|
||||
|
||||
// Extract values from HTTP headers and URL query params
|
||||
svr.Get("/body-header-param", [](const Request& req, Response& res) {
|
||||
if (req.has_header("Content-Length")) {
|
||||
auto val = req.get_header_value("Content-Length");
|
||||
@ -84,6 +125,21 @@ int main(void)
|
||||
res.set_content(req.body, "text/plain");
|
||||
});
|
||||
|
||||
// If the handler takes time to finish, you can also poll the connection state
|
||||
svr.Get("/task", [&](const Request& req, Response& res) {
|
||||
const char * result = nullptr;
|
||||
process.run(); // for example, starting an external process
|
||||
while (result == nullptr) {
|
||||
sleep(1);
|
||||
if (req.is_connection_closed()) {
|
||||
process.kill(); // kill the process
|
||||
return;
|
||||
}
|
||||
result = process.stdout(); // != nullptr if the process finishes
|
||||
}
|
||||
res.set_content(result, "text/plain");
|
||||
});
|
||||
|
||||
svr.Get("/stop", [&](const Request& req, Response& res) {
|
||||
svr.stop();
|
||||
});
|
||||
@ -156,7 +212,17 @@ The followings are built-in mappings:
|
||||
| webm | video/webm | zip | application/zip |
|
||||
| mp3 | audio/mp3 | wasm | application/wasm |
|
||||
|
||||
NOTE: These static file server methods are not thread-safe.
|
||||
> [!WARNING]
|
||||
> These static file server methods are not thread-safe.
|
||||
|
||||
### File request handler
|
||||
|
||||
```cpp
|
||||
// The handler is called right before the response is sent to a client
|
||||
svr.set_file_request_handler([](const Request &req, Response &res) {
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
### Logging
|
||||
|
||||
@ -177,15 +243,37 @@ svr.set_error_handler([](const auto& req, auto& res) {
|
||||
});
|
||||
```
|
||||
|
||||
### Exception handler
|
||||
The exception handler gets called if a user routing handler throws an error.
|
||||
|
||||
```cpp
|
||||
svr.set_exception_handler([](const auto& req, auto& res, std::exception_ptr ep) {
|
||||
auto fmt = "<h1>Error 500</h1><p>%s</p>";
|
||||
char buf[BUFSIZ];
|
||||
try {
|
||||
std::rethrow_exception(ep);
|
||||
} catch (std::exception &e) {
|
||||
snprintf(buf, sizeof(buf), fmt, e.what());
|
||||
} catch (...) { // See the following NOTE
|
||||
snprintf(buf, sizeof(buf), fmt, "Unknown Exception");
|
||||
}
|
||||
res.set_content(buf, "text/html");
|
||||
res.status = StatusCode::InternalServerError_500;
|
||||
});
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> if you don't provide the `catch (...)` block for a rethrown exception pointer, an uncaught exception will end up causing the server crash. Be careful!
|
||||
|
||||
### Pre routing handler
|
||||
|
||||
```cpp
|
||||
svr.set_pre_routing_handler([](const auto& req, auto& res) -> bool {
|
||||
svr.set_pre_routing_handler([](const auto& req, auto& res) {
|
||||
if (req.path == "/hello") {
|
||||
res.set_content("world", "text/html");
|
||||
return true; // This request is handled
|
||||
return Server::HandlerResponse::Handled;
|
||||
}
|
||||
return false; // Let the router handle this request
|
||||
return Server::HandlerResponse::Unhandled;
|
||||
});
|
||||
```
|
||||
|
||||
@ -216,6 +304,7 @@ svr.Post("/multipart", [&](const auto& req, auto& res) {
|
||||
svr.Post("/content_receiver",
|
||||
[&](const Request &req, Response &res, const ContentReader &content_reader) {
|
||||
if (req.is_multipart_form_data()) {
|
||||
// NOTE: `content_reader` is blocking until every form data field is read
|
||||
MultipartFormDataItems files;
|
||||
content_reader(
|
||||
[&](const MultipartFormData &file) {
|
||||
@ -232,7 +321,6 @@ svr.Post("/content_receiver",
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
res.set_content(body, "text/plain");
|
||||
}
|
||||
});
|
||||
```
|
||||
@ -248,12 +336,12 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||
res.set_content_provider(
|
||||
data->size(), // Content length
|
||||
"text/plain", // Content type
|
||||
[data](size_t offset, size_t length, DataSink &sink) {
|
||||
[&, data](size_t offset, size_t length, DataSink &sink) {
|
||||
const auto &d = *data;
|
||||
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
|
||||
return true; // return 'false' if you want to cancel the process.
|
||||
},
|
||||
[data] { delete data; });
|
||||
[data](bool success) { delete data; });
|
||||
});
|
||||
```
|
||||
|
||||
@ -263,7 +351,7 @@ Without content length:
|
||||
svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||
res.set_content_provider(
|
||||
"text/plain", // Content type
|
||||
[&](size_t offset, size_t length, DataSink &sink) {
|
||||
[&](size_t offset, DataSink &sink) {
|
||||
if (/* there is still data */) {
|
||||
std::vector<char> data;
|
||||
// prepare data...
|
||||
@ -293,6 +381,39 @@ svr.Get("/chunked", [&](const Request& req, Response& res) {
|
||||
});
|
||||
```
|
||||
|
||||
With trailer:
|
||||
|
||||
```cpp
|
||||
svr.Get("/chunked", [&](const Request& req, Response& res) {
|
||||
res.set_header("Trailer", "Dummy1, Dummy2");
|
||||
res.set_chunked_content_provider(
|
||||
"text/plain",
|
||||
[](size_t offset, DataSink &sink) {
|
||||
sink.write("123", 3);
|
||||
sink.write("345", 3);
|
||||
sink.write("789", 3);
|
||||
sink.done_with_trailer({
|
||||
{"Dummy1", "DummyVal1"},
|
||||
{"Dummy2", "DummyVal2"}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### Send file content
|
||||
|
||||
```cpp
|
||||
svr.Get("/content", [&](const Request &req, Response &res) {
|
||||
res.set_file_content("./path/to/content.html");
|
||||
});
|
||||
|
||||
svr.Get("/content", [&](const Request &req, Response &res) {
|
||||
res.set_file_content("./path/to/content", "text/html");
|
||||
});
|
||||
```
|
||||
|
||||
### 'Expect: 100-continue' handler
|
||||
|
||||
By default, the server sends a `100 Continue` response for an `Expect: 100-continue` header.
|
||||
@ -300,14 +421,14 @@ By default, the server sends a `100 Continue` response for an `Expect: 100-conti
|
||||
```cpp
|
||||
// Send a '417 Expectation Failed' response.
|
||||
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
||||
return 417;
|
||||
return StatusCode::ExpectationFailed_417;
|
||||
});
|
||||
```
|
||||
|
||||
```cpp
|
||||
// Send a final status without reading the message body.
|
||||
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
||||
return res.status = 401;
|
||||
return res.status = StatusCode::Unauthorized_401;
|
||||
});
|
||||
```
|
||||
|
||||
@ -332,13 +453,16 @@ svr.set_idle_interval(0, 100000); // 100 milliseconds
|
||||
svr.set_payload_max_length(1024 * 1024 * 512); // 512MB
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> When the request body content type is 'www-form-urlencoded', the actual payload length shouldn't exceed `CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH`.
|
||||
|
||||
### Server-Sent Events
|
||||
|
||||
Please see [Server example](https://github.com/yhirose/cpp-httplib/blob/master/example/ssesvr.cc) and [Client example](https://github.com/yhirose/cpp-httplib/blob/master/example/ssecli.cc).
|
||||
|
||||
### Default thread pool support
|
||||
|
||||
`ThreadPool` is used as a **default** task queue, and the default thread count is 8, or `std::thread::hardware_concurrency()`. You can change it with `CPPHTTPLIB_THREAD_POOL_COUNT`.
|
||||
`ThreadPool` is used as the **default** task queue, with a default thread count of 8 or `std::thread::hardware_concurrency() - 1`, whichever is greater. You can change it with `CPPHTTPLIB_THREAD_POOL_COUNT`.
|
||||
|
||||
If you want to set the thread count at runtime, there is no convenient way... But here is how.
|
||||
|
||||
@ -346,6 +470,17 @@ If you want to set the thread count at runtime, there is no convenient way... Bu
|
||||
svr.new_task_queue = [] { return new ThreadPool(12); };
|
||||
```
|
||||
|
||||
You can also provide an optional parameter to limit the maximum number
|
||||
of pending requests, i.e. requests `accept()`ed by the listener but
|
||||
still waiting to be serviced by worker threads.
|
||||
|
||||
```cpp
|
||||
svr.new_task_queue = [] { return new ThreadPool(/*num_threads=*/12, /*max_queued_requests=*/18); };
|
||||
```
|
||||
|
||||
Default limit is 0 (unlimited). Once the limit is reached, the listener
|
||||
will shutdown the client connection.
|
||||
|
||||
### Override the default thread pool with yours
|
||||
|
||||
You can supply your own thread pool implementation according to your need.
|
||||
@ -357,8 +492,10 @@ public:
|
||||
pool_.start_with_thread_count(n);
|
||||
}
|
||||
|
||||
virtual void enqueue(std::function<void()> fn) override {
|
||||
pool_.enqueue(fn);
|
||||
virtual bool enqueue(std::function<void()> fn) override {
|
||||
/* Return true if the task was actually enqueued, or false
|
||||
* if the caller must drop the corresponding connection. */
|
||||
return pool_.enqueue(fn);
|
||||
}
|
||||
|
||||
virtual void shutdown() override {
|
||||
@ -386,17 +523,18 @@ int main(void)
|
||||
httplib::Client cli("localhost", 1234);
|
||||
|
||||
if (auto res = cli.Get("/hi")) {
|
||||
if (res->status == 200) {
|
||||
if (res->status == StatusCode::OK_200) {
|
||||
std::cout << res->body << std::endl;
|
||||
}
|
||||
} else {
|
||||
auto err = res.error();
|
||||
...
|
||||
std::cout << "HTTP error: " << httplib::to_string(err) << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
NOTE: Constructor with scheme-host-port string is now supported!
|
||||
> [!TIP]
|
||||
> Constructor with scheme-host-port string is now supported!
|
||||
|
||||
```c++
|
||||
httplib::Client cli("localhost");
|
||||
@ -424,7 +562,9 @@ enum Error {
|
||||
SSLConnection,
|
||||
SSLLoadingCerts,
|
||||
SSLServerVerification,
|
||||
UnsupportedMultipartBoundaryChars
|
||||
UnsupportedMultipartBoundaryChars,
|
||||
Compression,
|
||||
ConnectionTimeout,
|
||||
};
|
||||
```
|
||||
|
||||
@ -432,14 +572,18 @@ enum Error {
|
||||
|
||||
```c++
|
||||
httplib::Headers headers = {
|
||||
{ "Accept-Encoding", "gzip, deflate" }
|
||||
{ "Hello", "World!" }
|
||||
};
|
||||
auto res = cli.Get("/hi", headers);
|
||||
```
|
||||
or
|
||||
```c++
|
||||
auto res = cli.Get("/hi", {{"Hello", "World!"}});
|
||||
```
|
||||
or
|
||||
```c++
|
||||
cli.set_default_headers({
|
||||
{ "Accept-Encoding", "gzip, deflate" }
|
||||
{ "Hello", "World!" }
|
||||
});
|
||||
auto res = cli.Get("/hi");
|
||||
```
|
||||
@ -510,6 +654,9 @@ res = cli.Options("/resource/foo");
|
||||
cli.set_connection_timeout(0, 300000); // 300 milliseconds
|
||||
cli.set_read_timeout(5, 0); // 5 seconds
|
||||
cli.set_write_timeout(5, 0); // 5 seconds
|
||||
|
||||
// This method works the same as curl's `--max-timeout` option
|
||||
svr.set_max_timeout(5000); // 5 seconds
|
||||
```
|
||||
|
||||
### Receive content with a content receiver
|
||||
@ -530,7 +677,7 @@ std::string body;
|
||||
auto res = cli.Get(
|
||||
"/stream", Headers(),
|
||||
[&](const Response &response) {
|
||||
EXPECT_EQ(200, response.status);
|
||||
EXPECT_EQ(StatusCode::OK_200, response.status);
|
||||
return true; // return 'false' if you want to cancel the request.
|
||||
},
|
||||
[&](const char *data, size_t data_length) {
|
||||
@ -571,7 +718,7 @@ auto res = cli.Post(
|
||||
### With Progress Callback
|
||||
|
||||
```cpp
|
||||
httplib::Client client(url, port);
|
||||
httplib::Client cli(url, port);
|
||||
|
||||
// prints: 0 / 000 bytes => 50% complete
|
||||
auto res = cli.Get("/", [](uint64_t len, uint64_t total) {
|
||||
@ -598,7 +745,8 @@ cli.set_digest_auth("user", "pass");
|
||||
cli.set_bearer_token_auth("token");
|
||||
```
|
||||
|
||||
NOTE: OpenSSL is required for Digest Authentication.
|
||||
> [!NOTE]
|
||||
> OpenSSL is required for Digest Authentication.
|
||||
|
||||
### Proxy server support
|
||||
|
||||
@ -615,7 +763,8 @@ cli.set_proxy_digest_auth("user", "pass");
|
||||
cli.set_proxy_bearer_token_auth("pass");
|
||||
```
|
||||
|
||||
NOTE: OpenSSL is required for Digest Authentication.
|
||||
> [!NOTE]
|
||||
> OpenSSL is required for Digest Authentication.
|
||||
|
||||
### Range
|
||||
|
||||
@ -664,7 +813,8 @@ res->status; // 200
|
||||
|
||||
### Use a specific network interface
|
||||
|
||||
NOTE: This feature is not available on Windows, yet.
|
||||
> [!NOTE]
|
||||
> This feature is not available on Windows, yet.
|
||||
|
||||
```cpp
|
||||
cli.set_interface("eth0"); // Interface name, IP address or host name
|
||||
@ -691,6 +841,21 @@ The server can apply compression to the following MIME type contents:
|
||||
Brotli compression is available with `CPPHTTPLIB_BROTLI_SUPPORT`. Necessary libraries should be linked.
|
||||
Please see https://github.com/google/brotli for more detail.
|
||||
|
||||
### Default `Accept-Encoding` value
|
||||
|
||||
The default `Accept-Encoding` value contains all possible compression types. So, the following two examples are same.
|
||||
|
||||
```c++
|
||||
res = cli.Get("/resource/foo");
|
||||
res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
|
||||
```
|
||||
|
||||
If we don't want a response without compression, we have to set `Accept-Encoding` to an empty string. This behavior is similar to curl.
|
||||
|
||||
```c++
|
||||
res = cli.Get("/resource/foo", {{"Accept-Encoding", ""}});
|
||||
```
|
||||
|
||||
### Compress request body on client
|
||||
|
||||
```c++
|
||||
@ -702,43 +867,72 @@ res = cli.Post("/resource/foo", "...", "text/plain");
|
||||
|
||||
```c++
|
||||
cli.set_decompress(false);
|
||||
res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
|
||||
res = cli.Get("/resource/foo");
|
||||
res->body; // Compressed data
|
||||
|
||||
```
|
||||
|
||||
SSL Support
|
||||
-----------
|
||||
Unix Domain Socket Support
|
||||
--------------------------
|
||||
|
||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||
|
||||
NOTE: cpp-httplib currently supports only version 1.1.1.
|
||||
Unix Domain Socket support is available on Linux and macOS.
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include "path/to/httplib.h"
|
||||
|
||||
// Server
|
||||
httplib::SSLServer svr("./cert.pem", "./key.pem");
|
||||
httplib::Server svr;
|
||||
svr.set_address_family(AF_UNIX).listen("./my-socket.sock", 80);
|
||||
|
||||
// Client
|
||||
httplib::Client cli("https://localhost:1234");
|
||||
|
||||
// Use your CA bundle
|
||||
cli.set_ca_cert_path("./ca-bundle.crt");
|
||||
|
||||
// Disable cert verification
|
||||
cli.enable_server_certificate_verification(false);
|
||||
httplib::Client cli("./my-socket.sock");
|
||||
cli.set_address_family(AF_UNIX);
|
||||
```
|
||||
|
||||
Note: When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.
|
||||
"my-socket.sock" can be a relative path or an absolute path. You application must have the appropriate permissions for the path. You can also use an abstract socket address on Linux. To use an abstract socket address, prepend a null byte ('\x00') to the path.
|
||||
|
||||
|
||||
Split httplib.h into .h and .cc
|
||||
-------------------------------
|
||||
|
||||
```console
|
||||
$ ./split.py -h
|
||||
usage: split.py [-h] [-e EXTENSION] [-o OUT]
|
||||
|
||||
This script splits httplib.h into .h and .cc parts.
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-e EXTENSION, --extension EXTENSION
|
||||
extension of the implementation file (default: cc)
|
||||
-o OUT, --out OUT where to write the files (default: out)
|
||||
|
||||
$ ./split.py
|
||||
Wrote out/httplib.h and out/httplib.cc
|
||||
```
|
||||
|
||||
Dockerfile for Static HTTP Server
|
||||
---------------------------------
|
||||
|
||||
Dockerfile for static HTTP server is available. Port number of this HTTP server is 80, and it serves static files from `/html` directory in the container.
|
||||
|
||||
```bash
|
||||
> python3 split.py
|
||||
> ls out
|
||||
httplib.h httplib.cc
|
||||
> docker build -t cpp-httplib-server .
|
||||
...
|
||||
|
||||
> docker run --rm -it -p 8080:80 -v ./docker/html:/html cpp-httplib-server
|
||||
Serving HTTP on 0.0.0.0 port 80 ...
|
||||
192.168.65.1 - - [31/Aug/2024:21:33:56 +0000] "GET / HTTP/1.1" 200 599 "-" "curl/8.7.1"
|
||||
192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "GET / HTTP/1.1" 200 599 "-" "Mozilla/5.0 ..."
|
||||
192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "GET /favicon.ico HTTP/1.1" 404 152 "-" "Mozilla/5.0 ..."
|
||||
```
|
||||
|
||||
From Docker Hub
|
||||
|
||||
```bash
|
||||
> docker run --rm -it -p 8080:80 -v ./docker/html:/html yhirose4dockerhub/cpp-httplib-server
|
||||
Serving HTTP on 0.0.0.0 port 80 ...
|
||||
192.168.65.1 - - [31/Aug/2024:21:33:56 +0000] "GET / HTTP/1.1" 200 599 "-" "curl/8.7.1"
|
||||
192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "GET / HTTP/1.1" 200 599 "-" "Mozilla/5.0 ..."
|
||||
192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "GET /favicon.ico HTTP/1.1" 404 152 "-" "Mozilla/5.0 ..."
|
||||
```
|
||||
|
||||
NOTE
|
||||
@ -763,12 +957,16 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
|
||||
#include <httplib.h>
|
||||
```
|
||||
|
||||
Note: Windows 8 or lower and Cygwin on Windows are not supported.
|
||||
> [!NOTE]
|
||||
> cpp-httplib officially supports only the latest Visual Studio. It might work with former versions of Visual Studio, but I can no longer verify it. Pull requests are always welcome for the older versions of Visual Studio unless they break the C++11 conformance.
|
||||
|
||||
> [!NOTE]
|
||||
> Windows 8 or lower, Visual Studio 2015 or lower, and Cygwin and MSYS2 including MinGW are neither supported nor tested.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
MIT license (© 2020 Yuji Hirose)
|
||||
MIT license (© 2025 Yuji Hirose)
|
||||
|
||||
Special Thanks To
|
||||
-----------------
|
||||
|
||||
62
benchmark/Makefile
Normal file
62
benchmark/Makefile
Normal file
@ -0,0 +1,62 @@
|
||||
CXXFLAGS = -std=c++11 -O2 -I..
|
||||
|
||||
CPPHTTPLIB_FLAGS = -DCPPHTTPLIB_THREAD_POOL_COUNT=16
|
||||
|
||||
BENCH = bombardier -c 10 -d 5s localhost:8080
|
||||
MONITOR = ali http://localhost:8080
|
||||
|
||||
# cpp-httplib
|
||||
bench: server
|
||||
@echo "--------------------\n cpp-httplib latest\n--------------------\n"
|
||||
@./server & export PID=$$!; $(BENCH); kill $${PID}
|
||||
@echo ""
|
||||
|
||||
monitor: server
|
||||
@./server & export PID=$$!; $(MONITOR); kill $${PID}
|
||||
|
||||
run : server
|
||||
@./server
|
||||
|
||||
server : cpp-httplib/main.cpp ../httplib.h
|
||||
g++ -o $@ $(CXXFLAGS) $(CPPHTTPLIB_FLAGS) cpp-httplib/main.cpp
|
||||
|
||||
# cpp-httplib
|
||||
bench-base: server-base
|
||||
@echo "---------------------\n cpp-httplib v0.18.0\n---------------------\n"
|
||||
@./server-base & export PID=$$!; $(BENCH); kill $${PID}
|
||||
@echo ""
|
||||
|
||||
monitor-base: server-base
|
||||
@./server-base & export PID=$$!; $(MONITOR); kill $${PID}
|
||||
|
||||
run-base : server-base
|
||||
@./server-base
|
||||
|
||||
server-base : cpp-httplib-base/main.cpp cpp-httplib-base/httplib.h
|
||||
g++ -o $@ $(CXXFLAGS) $(CPPHTTPLIB_FLAGS) cpp-httplib-base/main.cpp
|
||||
|
||||
# crow
|
||||
bench-crow: server-crow
|
||||
@echo "-------------\n Crow v1.2.0\n-------------\n"
|
||||
@./server-crow & export PID=$$!; $(BENCH); kill $${PID}
|
||||
@echo ""
|
||||
|
||||
monitor-crow: server-crow
|
||||
@./server-crow & export PID=$$!; $(MONITOR); kill $${PID}
|
||||
|
||||
run-crow : server-crow
|
||||
@./server-crow
|
||||
|
||||
server-crow : crow/main.cpp
|
||||
g++ -o $@ $(CXXFLAGS) crow/main.cpp
|
||||
|
||||
# misc
|
||||
build: server server-base server-crow
|
||||
|
||||
bench-all: bench-crow bench bench-base
|
||||
|
||||
issue:
|
||||
bombardier -c 10 -d 30s localhost:8080
|
||||
|
||||
clean:
|
||||
rm -rf server*
|
||||
10154
benchmark/cpp-httplib-base/httplib.h
Normal file
10154
benchmark/cpp-httplib-base/httplib.h
Normal file
File diff suppressed because it is too large
Load Diff
12
benchmark/cpp-httplib-base/main.cpp
Normal file
12
benchmark/cpp-httplib-base/main.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "./httplib.h"
|
||||
using namespace httplib;
|
||||
|
||||
int main() {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/", [](const Request &, Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
svr.listen("0.0.0.0", 8080);
|
||||
}
|
||||
12
benchmark/cpp-httplib/main.cpp
Normal file
12
benchmark/cpp-httplib/main.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "httplib.h"
|
||||
using namespace httplib;
|
||||
|
||||
int main() {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/", [](const Request &, Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
svr.listen("0.0.0.0", 8080);
|
||||
}
|
||||
14316
benchmark/crow/crow_all.h
Normal file
14316
benchmark/crow/crow_all.h
Normal file
File diff suppressed because it is too large
Load Diff
17
benchmark/crow/main.cpp
Normal file
17
benchmark/crow/main.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "crow_all.h"
|
||||
|
||||
class CustomLogger : public crow::ILogHandler {
|
||||
public:
|
||||
void log(std::string, crow::LogLevel) {}
|
||||
};
|
||||
|
||||
int main() {
|
||||
CustomLogger logger;
|
||||
crow::logger::setHandler(&logger);
|
||||
|
||||
crow::SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/")([]() { return "Hello world!"; });
|
||||
|
||||
app.port(8080).multithreaded().run();
|
||||
}
|
||||
2
benchmark/download.sh
Executable file
2
benchmark/download.sh
Executable file
@ -0,0 +1,2 @@
|
||||
rm -f httplib.h
|
||||
wget https://raw.githubusercontent.com/yhirose/cpp-httplib/v$1/httplib.h
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
# If they asked for a specific version, warn/fail since we don't support it.
|
||||
# TODO: if they start distributing the version somewhere, implement finding it.
|
||||
# But currently there's a version header that doesn't seem to get installed.
|
||||
# See https://github.com/google/brotli/issues/773#issuecomment-579133187
|
||||
if(Brotli_FIND_VERSION)
|
||||
set(_brotli_version_error_msg "FindBrotli.cmake doesn't have version support!")
|
||||
# If the package is required, throw a fatal error
|
||||
@ -68,29 +68,24 @@ if(BROTLI_USE_STATIC_LIBS)
|
||||
set(_brotli_stat_str "_STATIC")
|
||||
endif()
|
||||
|
||||
# Lets us know we are using the PkgConfig libraries
|
||||
# Will be set false if any non-pkgconf vars are used
|
||||
set(_brotli_using_pkgconf TRUE)
|
||||
|
||||
# Each string here is "ComponentName;LiteralName" (the semi-colon is a delimiter)
|
||||
foreach(_listvar "common;common" "decoder;dec" "encoder;enc")
|
||||
# Split the component name and literal library name from the listvar
|
||||
list(GET _listvar 0 _component_name)
|
||||
list(GET _listvar 1 _libname)
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
# NOTE: We can't rely on PkgConf for static libs since the upstream static lib support is broken
|
||||
# See https://github.com/google/brotli/issues/795
|
||||
# TODO: whenever their issue is fixed upstream, remove this "AND NOT BROTLI_USE_STATIC_LIBS" check
|
||||
if(PKG_CONFIG_FOUND AND NOT BROTLI_USE_STATIC_LIBS)
|
||||
# These need to be GLOBAL for MinGW when making ALIAS libraries against them.
|
||||
if(BROTLI_USE_STATIC_LIBS)
|
||||
# Have to use _STATIC to tell PkgConfig to find the static libs.
|
||||
pkg_check_modules(Brotli_${_component_name}_STATIC QUIET GLOBAL IMPORTED_TARGET libbrotli${_libname})
|
||||
else()
|
||||
pkg_check_modules(Brotli_${_component_name} QUIET GLOBAL IMPORTED_TARGET libbrotli${_libname})
|
||||
endif()
|
||||
# Have to postfix _STATIC on the name to tell PkgConfig to find the static libs.
|
||||
pkg_check_modules(Brotli_${_component_name}${_brotli_stat_str} QUIET GLOBAL IMPORTED_TARGET libbrotli${_libname})
|
||||
endif()
|
||||
|
||||
# Check if the target was already found by Pkgconf
|
||||
if(TARGET PkgConfig::Brotli_${_component_name} OR TARGET PkgConfig::Brotli_${_component_name}_STATIC)
|
||||
# Can't use generators for ALIAS targets, so you get this jank
|
||||
if(TARGET PkgConfig::Brotli_${_component_name}${_brotli_stat_str})
|
||||
# ALIAS since we don't want the PkgConfig namespace on the Cmake library (for end-users)
|
||||
add_library(Brotli::${_component_name} ALIAS PkgConfig::Brotli_${_component_name}${_brotli_stat_str})
|
||||
|
||||
# Tells HANDLE_COMPONENTS we found the component
|
||||
@ -109,23 +104,18 @@ foreach(_listvar "common;common" "decoder;dec" "encoder;enc")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
# Lets us know we aren't using the PkgConfig libraries
|
||||
set(_brotli_using_pkgconf FALSE)
|
||||
if(Brotli_FIND_REQUIRED_${_component_name})
|
||||
# If it's required, we can set the name used in find_library as a required var for FindPackageHandleStandardArgs
|
||||
list(APPEND _brotli_req_vars "Brotli_${_component_name}")
|
||||
endif()
|
||||
|
||||
list(APPEND _brotli_lib_names
|
||||
"brotli${_libname}"
|
||||
"libbrotli${_libname}"
|
||||
)
|
||||
if(BROTLI_USE_STATIC_LIBS)
|
||||
list(APPEND _brotli_lib_names
|
||||
"brotli${_libname}-static"
|
||||
"libbrotli${_libname}-static"
|
||||
)
|
||||
else()
|
||||
list(APPEND _brotli_lib_names
|
||||
"brotli${_libname}"
|
||||
"libbrotli${_libname}"
|
||||
)
|
||||
# Postfix "-static" to the libnames since we're looking for static libs
|
||||
list(TRANSFORM _brotli_lib_names APPEND "-static")
|
||||
endif()
|
||||
|
||||
find_library(Brotli_${_component_name}
|
||||
@ -168,7 +158,7 @@ find_package_handle_standard_args(Brotli
|
||||
Brotli_FOUND
|
||||
REQUIRED_VARS
|
||||
Brotli_INCLUDE_DIR
|
||||
${_brotli_required_targets}
|
||||
${_brotli_req_vars}
|
||||
HANDLE_COMPONENTS
|
||||
)
|
||||
|
||||
|
||||
@ -12,19 +12,19 @@ set(HTTPLIB_VERSION @PROJECT_VERSION@)
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
# We add find_dependency calls here to not make the end-user have to call them.
|
||||
find_dependency(Threads REQUIRED)
|
||||
find_dependency(Threads)
|
||||
if(@HTTPLIB_IS_USING_OPENSSL@)
|
||||
# OpenSSL COMPONENTS were added in Cmake v3.11
|
||||
if(CMAKE_VERSION VERSION_LESS "3.11")
|
||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ REQUIRED)
|
||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@)
|
||||
else()
|
||||
# Once the COMPONENTS were added, they were made optional when not specified.
|
||||
# Since we use both, we need to search for both.
|
||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ COMPONENTS Crypto SSL REQUIRED)
|
||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ COMPONENTS Crypto SSL)
|
||||
endif()
|
||||
endif()
|
||||
if(@HTTPLIB_IS_USING_ZLIB@)
|
||||
find_dependency(ZLIB REQUIRED)
|
||||
find_dependency(ZLIB)
|
||||
endif()
|
||||
|
||||
if(@HTTPLIB_IS_USING_BROTLI@)
|
||||
@ -32,7 +32,7 @@ if(@HTTPLIB_IS_USING_BROTLI@)
|
||||
# Note that the FindBrotli.cmake file is installed in the same dir as this file.
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
|
||||
set(BROTLI_USE_STATIC_LIBS @BROTLI_USE_STATIC_LIBS@)
|
||||
find_dependency(Brotli COMPONENTS common encoder decoder REQUIRED)
|
||||
find_dependency(Brotli COMPONENTS common encoder decoder)
|
||||
endif()
|
||||
|
||||
# Mildly useful for end-users
|
||||
@ -42,10 +42,19 @@ set_and_check(HTTPLIB_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@")
|
||||
# This is helpful if you're using Cmake's pre-compiled header feature
|
||||
set_and_check(HTTPLIB_HEADER_PATH "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@/httplib.h")
|
||||
|
||||
# Brings in the target library
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/httplibTargets.cmake")
|
||||
# Consider each library support as a "component"
|
||||
set(httplib_OpenSSL_FOUND @HTTPLIB_IS_USING_OPENSSL@)
|
||||
set(httplib_ZLIB_FOUND @HTTPLIB_IS_USING_ZLIB@)
|
||||
set(httplib_Brotli_FOUND @HTTPLIB_IS_USING_BROTLI@)
|
||||
|
||||
# Ouputs a "found httplib /usr/include/httplib.h" message when using find_package(httplib)
|
||||
check_required_components(httplib)
|
||||
|
||||
# Brings in the target library, but only if all required components are found
|
||||
if(NOT DEFINED httplib_FOUND OR httplib_FOUND)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/httplibTargets.cmake")
|
||||
endif()
|
||||
|
||||
# Outputs a "found httplib /usr/include/httplib.h" message when using find_package(httplib)
|
||||
include(FindPackageMessage)
|
||||
if(TARGET httplib::httplib)
|
||||
set(HTTPLIB_FOUND TRUE)
|
||||
7
docker-compose.yml
Normal file
7
docker-compose.yml
Normal file
@ -0,0 +1,7 @@
|
||||
services:
|
||||
http:
|
||||
build: .
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- ./docker/html:/html
|
||||
21
docker/html/index.html
Normal file
21
docker/html/index.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to cpp-httplib!</title>
|
||||
<style>
|
||||
html { color-scheme: light dark; }
|
||||
body { width: 35em; margin: 0 auto;
|
||||
font-family: Tahoma, Verdana, Arial, sans-serif; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Welcome to cpp-httplib!</h1>
|
||||
<p>If you see this page, the cpp-httplib web server is successfully installed and
|
||||
working. Further configuration is required.</p>
|
||||
|
||||
<p>For online documentation and support please refer to
|
||||
<a href="https://github.com/yhirose/cpp-httplib">github.com/yhirose/cpp-httplib</a>.<br/>
|
||||
|
||||
<p><em>Thank you for using cpp-httplib.</em></p>
|
||||
</body>
|
||||
</html>
|
||||
81
docker/main.cc
Normal file
81
docker/main.cc
Normal file
@ -0,0 +1,81 @@
|
||||
//
|
||||
// main.cc
|
||||
//
|
||||
// Copyright (c) 2025 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <format>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <httplib.h>
|
||||
|
||||
constexpr auto error_html = R"(<html>
|
||||
<head><title>{} {}</title></head>
|
||||
<body>
|
||||
<center><h1>404 Not Found</h1></center>
|
||||
<hr><center>cpp-httplib/{}</center>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
void sigint_handler(int s) { exit(1); }
|
||||
|
||||
std::string time_local() {
|
||||
auto p = std::chrono::system_clock::now();
|
||||
auto t = std::chrono::system_clock::to_time_t(p);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::put_time(std::localtime(&t), "%d/%b/%Y:%H:%M:%S %z");
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string log(auto &req, auto &res) {
|
||||
auto remote_user = "-"; // TODO:
|
||||
auto request = std::format("{} {} {}", req.method, req.path, req.version);
|
||||
auto body_bytes_sent = res.get_header_value("Content-Length");
|
||||
auto http_referer = "-"; // TODO:
|
||||
auto http_user_agent = req.get_header_value("User-Agent", "-");
|
||||
|
||||
// NOTE: From NGINX default access log format
|
||||
// log_format combined '$remote_addr - $remote_user [$time_local] '
|
||||
// '"$request" $status $body_bytes_sent '
|
||||
// '"$http_referer" "$http_user_agent"';
|
||||
return std::format(R"({} - {} [{}] "{}" {} {} "{}" "{}")", req.remote_addr,
|
||||
remote_user, time_local(), request, res.status,
|
||||
body_bytes_sent, http_referer, http_user_agent);
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
auto base_dir = "./html";
|
||||
auto host = "0.0.0.0";
|
||||
auto port = 80;
|
||||
|
||||
httplib::Server svr;
|
||||
|
||||
svr.set_error_handler([](auto & /*req*/, auto &res) {
|
||||
auto body =
|
||||
std::format(error_html, res.status, httplib::status_message(res.status),
|
||||
CPPHTTPLIB_VERSION);
|
||||
|
||||
res.set_content(body, "text/html");
|
||||
});
|
||||
|
||||
svr.set_logger(
|
||||
[](auto &req, auto &res) { std::cout << log(req, res) << std::endl; });
|
||||
|
||||
svr.set_mount_point("/", base_dir);
|
||||
|
||||
std::cout << std::format("Serving HTTP on {0} port {1} ...", host, port)
|
||||
<< std::endl;
|
||||
|
||||
auto ret = svr.listen(host, port);
|
||||
|
||||
return ret ? 0 : 1;
|
||||
}
|
||||
12
example/Dockerfile.hello
Normal file
12
example/Dockerfile.hello
Normal file
@ -0,0 +1,12 @@
|
||||
FROM alpine as builder
|
||||
WORKDIR /src/example
|
||||
RUN apk add g++ make openssl-dev zlib-dev brotli-dev
|
||||
COPY ./httplib.h /src
|
||||
COPY ./example/hello.cc /src/example
|
||||
COPY ./example/Makefile /src/example
|
||||
RUN make hello
|
||||
|
||||
FROM alpine
|
||||
RUN apk --no-cache add brotli libstdc++
|
||||
COPY --from=builder /src/example/hello /bin/hello
|
||||
CMD ["/bin/hello"]
|
||||
@ -1,16 +1,24 @@
|
||||
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -std=c++11 -I.. -Wall -Wextra -pthread
|
||||
CXXFLAGS = -O2 -std=c++11 -I.. -Wall -Wextra -pthread
|
||||
|
||||
OPENSSL_DIR = /usr/local/opt/openssl
|
||||
PREFIX ?= $(shell brew --prefix)
|
||||
|
||||
OPENSSL_DIR = $(PREFIX)/opt/openssl@3
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
|
||||
ifneq ($(OS), Windows_NT)
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S), Darwin)
|
||||
OPENSSL_SUPPORT += -framework CoreFoundation -framework Security
|
||||
endif
|
||||
endif
|
||||
|
||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||
|
||||
BROTLI_DIR = /usr/local/opt/brotli
|
||||
# BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon-static -lbrotlienc-static -lbrotlidec-static
|
||||
BROTLI_DIR = $(PREFIX)/opt/brotli
|
||||
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
||||
|
||||
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark
|
||||
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark one_time_request server_and_client
|
||||
|
||||
server : server.cc ../httplib.h Makefile
|
||||
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
|
||||
@ -42,9 +50,15 @@ ssecli : ssecli.cc ../httplib.h Makefile
|
||||
benchmark : benchmark.cc ../httplib.h Makefile
|
||||
$(CXX) -o benchmark $(CXXFLAGS) benchmark.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
|
||||
|
||||
one_time_request : one_time_request.cc ../httplib.h Makefile
|
||||
$(CXX) -o one_time_request $(CXXFLAGS) one_time_request.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
|
||||
|
||||
server_and_client : server_and_client.cc ../httplib.h Makefile
|
||||
$(CXX) -o server_and_client $(CXXFLAGS) server_and_client.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
|
||||
|
||||
pem:
|
||||
openssl genrsa 2048 > key.pem
|
||||
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||
|
||||
clean:
|
||||
rm server client hello simplecli simplesvr upload redirect ssesvr sselci benchmark *.pem
|
||||
rm server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark one_time_request server_and_client *.pem
|
||||
|
||||
@ -26,7 +26,7 @@ int main(void) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
StopWatch sw(to_string(i).c_str());
|
||||
auto res = cli.Post("/post", body, "application/octet-stream");
|
||||
assert(res->status == 200);
|
||||
assert(res->status == httplib::StatusCode::OK_200);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@ -22,34 +22,34 @@
|
||||
<ProjectGuid>{6DB1FC63-B153-4279-92B7-D8A11AF285D6}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>client</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
|
||||
@ -15,5 +15,5 @@ int main(void) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
svr.listen("localhost", 8080);
|
||||
svr.listen("0.0.0.0", 8080);
|
||||
}
|
||||
|
||||
56
example/one_time_request.cc
Normal file
56
example/one_time_request.cc
Normal file
@ -0,0 +1,56 @@
|
||||
#include <httplib.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace httplib;
|
||||
|
||||
const char *HOST = "localhost";
|
||||
const int PORT = 1234;
|
||||
|
||||
void one_time_request_server(const char *label) {
|
||||
std::thread th;
|
||||
Server svr;
|
||||
|
||||
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content(std::string("Hello from ") + label, "text/plain");
|
||||
|
||||
// Stop server
|
||||
th = std::thread([&]() { svr.stop(); });
|
||||
});
|
||||
|
||||
svr.listen(HOST, PORT);
|
||||
th.join();
|
||||
|
||||
std::cout << label << " ended..." << std::endl;
|
||||
}
|
||||
|
||||
void send_request(const char *label) {
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
std::cout << "Send " << label << " request" << std::endl;
|
||||
auto res = cli.Get("/hi");
|
||||
|
||||
if (res) {
|
||||
std::cout << res->body << std::endl;
|
||||
} else {
|
||||
std::cout << "Request error: " + to_string(res.error()) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
auto th1 = std::thread([&]() { one_time_request_server("Server #1"); });
|
||||
auto th2 = std::thread([&]() { one_time_request_server("Server #2"); });
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
send_request("1st");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
send_request("2nd");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
send_request("3rd");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
th1.join();
|
||||
th2.join();
|
||||
}
|
||||
@ -18,38 +18,41 @@
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="simplesvr.cc" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{864CD288-050A-4C8B-9BEF-3048BD876C5B}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>sample</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
@ -151,9 +154,6 @@
|
||||
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="server.cc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
||||
90
example/server_and_client.cc
Normal file
90
example/server_and_client.cc
Normal file
@ -0,0 +1,90 @@
|
||||
//
|
||||
// server_and_client.cc
|
||||
//
|
||||
// Copyright (c) 2025 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <httplib.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace httplib;
|
||||
|
||||
std::string dump_headers(const Headers &headers) {
|
||||
std::string s;
|
||||
char buf[BUFSIZ];
|
||||
|
||||
for (auto it = headers.begin(); it != headers.end(); ++it) {
|
||||
const auto &x = *it;
|
||||
snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str());
|
||||
s += buf;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void logger(const Request &req, const Response &res) {
|
||||
std::string s;
|
||||
char buf[BUFSIZ];
|
||||
|
||||
s += "================================\n";
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s %s %s", req.method.c_str(),
|
||||
req.version.c_str(), req.path.c_str());
|
||||
s += buf;
|
||||
|
||||
std::string query;
|
||||
for (auto it = req.params.begin(); it != req.params.end(); ++it) {
|
||||
const auto &x = *it;
|
||||
snprintf(buf, sizeof(buf), "%c%s=%s",
|
||||
(it == req.params.begin()) ? '?' : '&', x.first.c_str(),
|
||||
x.second.c_str());
|
||||
query += buf;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "%s\n", query.c_str());
|
||||
s += buf;
|
||||
|
||||
s += dump_headers(req.headers);
|
||||
|
||||
s += "--------------------------------\n";
|
||||
|
||||
snprintf(buf, sizeof(buf), "%d %s\n", res.status, res.version.c_str());
|
||||
s += buf;
|
||||
s += dump_headers(res.headers);
|
||||
s += "\n";
|
||||
|
||||
if (!res.body.empty()) { s += res.body; }
|
||||
|
||||
s += "\n";
|
||||
|
||||
std::cout << s;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
// Server
|
||||
Server svr;
|
||||
svr.set_logger(logger);
|
||||
|
||||
svr.Post("/post", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("POST", "text/plain");
|
||||
});
|
||||
|
||||
auto th = std::thread([&]() { svr.listen("localhost", 8080); });
|
||||
|
||||
auto se = detail::scope_exit([&] {
|
||||
svr.stop();
|
||||
th.join();
|
||||
});
|
||||
|
||||
svr.wait_until_ready();
|
||||
|
||||
// Client
|
||||
Client cli{"localhost", 8080};
|
||||
|
||||
std::string body = R"({"hello": "world"})";
|
||||
|
||||
auto res = cli.Post("/post", body, "application/json");
|
||||
std::cout << "--------------------------------" << std::endl;
|
||||
std::cout << to_string(res.error()) << std::endl;
|
||||
}
|
||||
@ -1,10 +1,3 @@
|
||||
//
|
||||
// sse.cc
|
||||
//
|
||||
// Copyright (c) 2020 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
@ -19,16 +12,13 @@ using namespace std;
|
||||
|
||||
class EventDispatcher {
|
||||
public:
|
||||
EventDispatcher() {
|
||||
id_ = 0;
|
||||
cid_ = -1;
|
||||
}
|
||||
EventDispatcher() {}
|
||||
|
||||
void wait_event(DataSink *sink) {
|
||||
unique_lock<mutex> lk(m_);
|
||||
int id = id_;
|
||||
cv_.wait(lk, [&] { return cid_ == id; });
|
||||
if (sink->is_writable()) { sink->write(message_.data(), message_.size()); }
|
||||
sink->write(message_.data(), message_.size());
|
||||
}
|
||||
|
||||
void send_event(const string &message) {
|
||||
@ -41,8 +31,8 @@ public:
|
||||
private:
|
||||
mutex m_;
|
||||
condition_variable cv_;
|
||||
atomic_int id_;
|
||||
atomic_int cid_;
|
||||
atomic_int id_{0};
|
||||
atomic_int cid_{-1};
|
||||
string message_;
|
||||
};
|
||||
|
||||
|
||||
6
example/uploader.sh
Executable file
6
example/uploader.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#/usr/bin/env bash
|
||||
for i in {1..1000000}
|
||||
do
|
||||
echo "#### $i ####"
|
||||
curl -X POST -F image_file=@$1 http://localhost:1234/post > /dev/null
|
||||
done
|
||||
116
meson.build
Normal file
116
meson.build
Normal file
@ -0,0 +1,116 @@
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
project(
|
||||
'cpp-httplib',
|
||||
'cpp',
|
||||
license: 'MIT',
|
||||
default_options: [
|
||||
'cpp_std=c++11',
|
||||
'buildtype=release',
|
||||
'b_ndebug=if-release',
|
||||
'b_lto=true',
|
||||
'warning_level=3'
|
||||
],
|
||||
meson_version: '>=0.62.0'
|
||||
)
|
||||
|
||||
# Check just in case downstream decides to edit the source
|
||||
# and add a project version
|
||||
version = meson.project_version()
|
||||
if version == 'undefined'
|
||||
cxx = meson.get_compiler('cpp')
|
||||
version = cxx.get_define('CPPHTTPLIB_VERSION',
|
||||
prefix: '#include <httplib.h>',
|
||||
include_directories: include_directories('.')).strip('"')
|
||||
assert(version != '', 'failed to get version from httplib.h')
|
||||
endif
|
||||
|
||||
deps = [dependency('threads')]
|
||||
args = []
|
||||
|
||||
openssl_dep = dependency('openssl', version: '>=3.0.0', required: get_option('cpp-httplib_openssl'))
|
||||
if openssl_dep.found()
|
||||
deps += openssl_dep
|
||||
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
|
||||
if host_machine.system() == 'darwin'
|
||||
macosx_keychain_dep = dependency('appleframeworks', modules: ['CoreFoundation', 'Security'], required: get_option('cpp-httplib_macosx_keychain'))
|
||||
if macosx_keychain_dep.found()
|
||||
deps += macosx_keychain_dep
|
||||
args += '-DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN'
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
zlib_dep = dependency('zlib', required: get_option('cpp-httplib_zlib'))
|
||||
if zlib_dep.found()
|
||||
deps += zlib_dep
|
||||
args += '-DCPPHTTPLIB_ZLIB_SUPPORT'
|
||||
endif
|
||||
|
||||
brotli_deps = [dependency('libbrotlicommon', required: get_option('cpp-httplib_brotli'))]
|
||||
brotli_deps += dependency('libbrotlidec', required: get_option('cpp-httplib_brotli'))
|
||||
brotli_deps += dependency('libbrotlienc', required: get_option('cpp-httplib_brotli'))
|
||||
|
||||
brotli_found_all = true
|
||||
foreach brotli_dep : brotli_deps
|
||||
if not brotli_dep.found()
|
||||
brotli_found_all = false
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if brotli_found_all
|
||||
deps += brotli_deps
|
||||
args += '-DCPPHTTPLIB_BROTLI_SUPPORT'
|
||||
endif
|
||||
|
||||
cpp_httplib_dep = dependency('', required: false)
|
||||
|
||||
if get_option('cpp-httplib_compile')
|
||||
python3 = find_program('python3')
|
||||
|
||||
httplib_ch = custom_target(
|
||||
'split',
|
||||
input: 'httplib.h',
|
||||
output: ['httplib.cc', 'httplib.h'],
|
||||
command: [python3, files('split.py'), '--out', meson.current_build_dir()],
|
||||
install: true,
|
||||
install_dir: [false, get_option('includedir')]
|
||||
)
|
||||
lib = library(
|
||||
'cpp-httplib',
|
||||
sources: httplib_ch,
|
||||
dependencies: deps,
|
||||
cpp_args: args,
|
||||
version: version,
|
||||
soversion: version.split('.')[0] + '.' + version.split('.')[1],
|
||||
install: true
|
||||
)
|
||||
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, link_with: lib, sources: httplib_ch[1])
|
||||
|
||||
import('pkgconfig').generate(
|
||||
lib,
|
||||
description: 'A C++ HTTP/HTTPS server and client library',
|
||||
extra_cflags: args,
|
||||
url: 'https://github.com/yhirose/cpp-httplib',
|
||||
version: version
|
||||
)
|
||||
else
|
||||
install_headers('httplib.h')
|
||||
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, include_directories: '.')
|
||||
|
||||
import('pkgconfig').generate(
|
||||
name: 'cpp-httplib',
|
||||
description: 'A C++ HTTP/HTTPS server and client library',
|
||||
install_dir: get_option('datadir')/'pkgconfig',
|
||||
url: 'https://github.com/yhirose/cpp-httplib',
|
||||
version: version
|
||||
)
|
||||
endif
|
||||
|
||||
meson.override_dependency('cpp-httplib', cpp_httplib_dep)
|
||||
|
||||
if get_option('cpp-httplib_test')
|
||||
subdir('test')
|
||||
endif
|
||||
10
meson_options.txt
Normal file
10
meson_options.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
option('cpp-httplib_openssl', type: 'feature', value: 'auto', description: 'Enable OpenSSL support')
|
||||
option('cpp-httplib_zlib', type: 'feature', value: 'auto', description: 'Enable zlib support')
|
||||
option('cpp-httplib_brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support')
|
||||
option('cpp-httplib_macosx_keychain', type: 'feature', value: 'auto', description: 'Enable loading certs from the Keychain on Apple devices')
|
||||
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires python3)')
|
||||
option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests')
|
||||
81
split.py
Normal file → Executable file
81
split.py
Normal file → Executable file
@ -1,32 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""This script splits httplib.h into .h and .cc parts."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
border = '// ----------------------------------------------------------------------------'
|
||||
|
||||
PythonVersion = sys.version_info[0];
|
||||
args_parser = argparse.ArgumentParser(description=__doc__)
|
||||
args_parser.add_argument(
|
||||
"-e", "--extension", help="extension of the implementation file (default: cc)",
|
||||
default="cc"
|
||||
)
|
||||
args_parser.add_argument(
|
||||
"-o", "--out", help="where to write the files (default: out)", default="out"
|
||||
)
|
||||
args = args_parser.parse_args()
|
||||
|
||||
with open('httplib.h') as f:
|
||||
lines = f.readlines()
|
||||
inImplementation = False
|
||||
cur_dir = os.path.dirname(sys.argv[0])
|
||||
lib_name = 'httplib'
|
||||
header_name = '/' + lib_name + '.h'
|
||||
source_name = '/' + lib_name + '.' + args.extension
|
||||
# get the input file
|
||||
in_file = cur_dir + header_name
|
||||
# get the output file
|
||||
h_out = args.out + header_name
|
||||
cc_out = args.out + source_name
|
||||
|
||||
if PythonVersion < 3:
|
||||
os.makedirs('out')
|
||||
# if the modification time of the out file is after the in file,
|
||||
# don't split (as it is already finished)
|
||||
do_split = True
|
||||
|
||||
if os.path.exists(h_out):
|
||||
in_time = os.path.getmtime(in_file)
|
||||
out_time = os.path.getmtime(h_out)
|
||||
do_split = in_time > out_time
|
||||
|
||||
if do_split:
|
||||
with open(in_file) as f:
|
||||
lines = f.readlines()
|
||||
|
||||
python_version = sys.version_info[0]
|
||||
if python_version < 3:
|
||||
os.makedirs(args.out)
|
||||
else:
|
||||
os.makedirs('out', exist_ok=True)
|
||||
os.makedirs(args.out, exist_ok=True)
|
||||
|
||||
with open('out/httplib.h', 'w') as fh:
|
||||
with open('out/httplib.cc', 'w') as fc:
|
||||
fc.write('#include "httplib.h"\n')
|
||||
fc.write('namespace httplib {\n')
|
||||
for line in lines:
|
||||
isBorderLine = border in line
|
||||
if isBorderLine:
|
||||
inImplementation = not inImplementation
|
||||
else:
|
||||
if inImplementation:
|
||||
fc.write(line.replace('inline ', ''))
|
||||
pass
|
||||
else:
|
||||
fh.write(line)
|
||||
pass
|
||||
fc.write('} // namespace httplib\n')
|
||||
in_implementation = False
|
||||
cc_out = args.out + source_name
|
||||
with open(h_out, 'w') as fh, open(cc_out, 'w') as fc:
|
||||
fc.write('#include "httplib.h"\n')
|
||||
fc.write('namespace httplib {\n')
|
||||
for line in lines:
|
||||
is_border_line = border in line
|
||||
if is_border_line:
|
||||
in_implementation = not in_implementation
|
||||
elif in_implementation:
|
||||
fc.write(line.replace('inline ', ''))
|
||||
else:
|
||||
fh.write(line)
|
||||
fc.write('} // namespace httplib\n')
|
||||
|
||||
print("Wrote {} and {}".format(h_out, cc_out))
|
||||
else:
|
||||
print("{} and {} are up to date".format(h_out, cc_out))
|
||||
|
||||
121
test/CMakeLists.txt
Normal file
121
test/CMakeLists.txt
Normal file
@ -0,0 +1,121 @@
|
||||
find_package(GTest)
|
||||
|
||||
if(GTest_FOUND)
|
||||
if(NOT TARGET GTest::gtest_main AND TARGET GTest::Main)
|
||||
# CMake <3.20
|
||||
add_library(GTest::gtest_main INTERFACE IMPORTED)
|
||||
target_link_libraries(GTest::gtest_main INTERFACE GTest::Main)
|
||||
endif()
|
||||
else()
|
||||
if(POLICY CMP0135)
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
endif()
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
set(BUILD_GMOCK OFF)
|
||||
set(INSTALL_GTEST OFF)
|
||||
set(gtest_force_shared_crt ON)
|
||||
|
||||
FetchContent_Declare(
|
||||
gtest
|
||||
URL https://github.com/google/googletest/archive/main.tar.gz
|
||||
)
|
||||
FetchContent_MakeAvailable(gtest)
|
||||
endif()
|
||||
|
||||
find_package(CURL REQUIRED)
|
||||
|
||||
add_executable(httplib-test test.cc include_httplib.cc $<$<BOOL:${WIN32}>:include_windows_h.cc>)
|
||||
target_compile_options(httplib-test PRIVATE "$<$<CXX_COMPILER_ID:MSVC>:/utf-8;/bigobj>")
|
||||
target_link_libraries(httplib-test PRIVATE httplib GTest::gtest_main CURL::libcurl)
|
||||
gtest_discover_tests(httplib-test)
|
||||
|
||||
file(
|
||||
COPY www www2 www3 ca-bundle.crt image.jpg
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
if(HTTPLIB_IS_USING_OPENSSL)
|
||||
if (OPENSSL_VERSION VERSION_LESS "3.2.0")
|
||||
set(OPENSSL_X509_FLAG "-x509")
|
||||
else()
|
||||
set(OPENSSL_X509_FLAG "-x509v1")
|
||||
endif()
|
||||
find_program(OPENSSL_COMMAND
|
||||
NAMES openssl
|
||||
PATHS ${OPENSSL_INCLUDE_DIR}/../bin
|
||||
REQUIRED
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} genrsa 2048
|
||||
OUTPUT_FILE key.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key.pem
|
||||
COMMAND ${OPENSSL_COMMAND} x509 -days 3650 -req -signkey key.pem
|
||||
OUTPUT_FILE cert.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} req ${OPENSSL_X509_FLAG} -new -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key.pem -sha256 -days 3650 -nodes -out cert2.pem -extensions SAN
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} genrsa 2048
|
||||
OUTPUT_FILE rootCA.key.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} req ${OPENSSL_X509_FLAG} -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.rootCA.conf -key rootCA.key.pem -days 1024
|
||||
OUTPUT_FILE rootCA.cert.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} genrsa 2048
|
||||
OUTPUT_FILE client.key.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key client.key.pem
|
||||
COMMAND ${OPENSSL_COMMAND} x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial
|
||||
OUTPUT_FILE client.cert.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} genrsa -passout pass:test123! 2048
|
||||
OUTPUT_FILE key_encrypted.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key_encrypted.pem
|
||||
COMMAND ${OPENSSL_COMMAND} x509 -days 3650 -req -signkey key_encrypted.pem
|
||||
OUTPUT_FILE cert_encrypted.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} genrsa -aes256 -passout pass:test012! 2048
|
||||
OUTPUT_FILE client_encrypted.key.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key client_encrypted.key.pem -passin pass:test012!
|
||||
COMMAND ${OPENSSL_COMMAND} x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial
|
||||
OUTPUT_FILE client_encrypted.cert.pem
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
endif()
|
||||
|
||||
add_subdirectory(fuzzing)
|
||||
@ -1,35 +1,100 @@
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -g -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
||||
CXX = clang++
|
||||
CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion -Wshadow # -fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS -fsanitize=address
|
||||
|
||||
OPENSSL_DIR = /usr/local/opt/openssl@1.1
|
||||
PREFIX ?= $(shell brew --prefix)
|
||||
|
||||
OPENSSL_DIR = $(PREFIX)/opt/openssl@3
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
|
||||
ifneq ($(OS), Windows_NT)
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S), Darwin)
|
||||
OPENSSL_SUPPORT += -DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN -framework CoreFoundation -framework Security
|
||||
endif
|
||||
endif
|
||||
|
||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||
|
||||
BROTLI_DIR = /usr/local/opt/brotli
|
||||
BROTLI_DIR = $(PREFIX)/opt/brotli
|
||||
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
||||
|
||||
all : test
|
||||
TEST_ARGS = gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread -lcurl
|
||||
|
||||
# By default, use standalone_fuzz_target_runner.
|
||||
# This runner does no fuzzing, but simply executes the inputs
|
||||
# provided via parameters.
|
||||
# Run e.g. "make all LIB_FUZZING_ENGINE=/path/to/libFuzzer.a"
|
||||
# to link the fuzzer(s) against a real fuzzing engine.
|
||||
# OSS-Fuzz will define its own value for LIB_FUZZING_ENGINE.
|
||||
LIB_FUZZING_ENGINE ?= standalone_fuzz_target_runner.o
|
||||
|
||||
CLANG_FORMAT = clang-format
|
||||
REALPATH = $(shell which grealpath 2>/dev/null || which realpath 2>/dev/null)
|
||||
STYLE_CHECK_FILES = $(filter-out httplib.h httplib.cc, \
|
||||
$(wildcard example/*.h example/*.cc fuzzing/*.h fuzzing/*.cc *.h *.cc ../httplib.h))
|
||||
|
||||
all : test test_split
|
||||
./test
|
||||
|
||||
proxy : test_proxy
|
||||
./test_proxy
|
||||
|
||||
test : test.cc ../httplib.h Makefile cert.pem
|
||||
$(CXX) -o test $(CXXFLAGS) test.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
|
||||
test : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
|
||||
$(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS)
|
||||
|
||||
# Note: The intention of test_split is to verify that it works to compile and
|
||||
# link the split httplib.h, so there is normally no need to execute it.
|
||||
test_split : test.cc ../httplib.h httplib.cc Makefile cert.pem
|
||||
$(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS)
|
||||
|
||||
check_abi:
|
||||
@./check-shared-library-abi-compatibility.sh
|
||||
|
||||
.PHONY: style_check
|
||||
style_check: $(STYLE_CHECK_FILES)
|
||||
@for file in $(STYLE_CHECK_FILES); do \
|
||||
$(CLANG_FORMAT) $$file > $$file.formatted; \
|
||||
if ! diff -u $$file $$file.formatted; then \
|
||||
file2=$$($(REALPATH) --relative-to=.. $$file); \
|
||||
printf "\n%*s\n" 80 | tr ' ' '#'; \
|
||||
printf "##%*s##\n" 76; \
|
||||
printf "## %-70s ##\n" "$$file2 not properly formatted. Please run clang-format."; \
|
||||
printf "##%*s##\n" 76; \
|
||||
printf "%*s\n\n" 80 | tr ' ' '#'; \
|
||||
failed=1; \
|
||||
fi; \
|
||||
rm -f $$file.formatted; \
|
||||
done; \
|
||||
if [ -n "$$failed" ]; then \
|
||||
echo "Style check failed for one or more files. See above for details."; \
|
||||
false; \
|
||||
else \
|
||||
echo "All files are properly formatted."; \
|
||||
fi
|
||||
|
||||
test_proxy : test_proxy.cc ../httplib.h Makefile cert.pem
|
||||
$(CXX) -o test_proxy $(CXXFLAGS) test_proxy.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
|
||||
$(CXX) -o $@ -I.. $(CXXFLAGS) test_proxy.cc $(TEST_ARGS)
|
||||
|
||||
# Runs server_fuzzer.cc based on value of $(LIB_FUZZING_ENGINE).
|
||||
# Usage: make fuzz_test LIB_FUZZING_ENGINE=/path/to/libFuzzer
|
||||
fuzz_test: server_fuzzer
|
||||
./server_fuzzer fuzzing/corpus/*
|
||||
|
||||
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
|
||||
server_fuzzer : fuzzing/server_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o
|
||||
$(CXX) -o $@ -I.. $(CXXFLAGS) $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||
|
||||
# Standalone fuzz runner, which just reads inputs from fuzzing/corpus/ dir and
|
||||
# feeds it to server_fuzzer.
|
||||
standalone_fuzz_target_runner.o : fuzzing/standalone_fuzz_target_runner.cpp
|
||||
$(CXX) -o $@ -I.. $(CXXFLAGS) -c $<
|
||||
|
||||
httplib.cc : ../httplib.h
|
||||
python3 ../split.py -o .
|
||||
|
||||
cert.pem:
|
||||
openssl genrsa 2048 > key.pem
|
||||
openssl req -new -batch -config test.conf -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||
openssl req -x509 -config test.conf -key key.pem -sha256 -days 3650 -nodes -out cert2.pem -extensions SAN
|
||||
openssl genrsa 2048 > rootCA.key.pem
|
||||
openssl req -x509 -new -batch -config test.rootCA.conf -key rootCA.key.pem -days 1024 > rootCA.cert.pem
|
||||
openssl genrsa 2048 > client.key.pem
|
||||
openssl req -new -batch -config test.conf -key client.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client.cert.pem
|
||||
#c_rehash .
|
||||
./gen-certs.sh
|
||||
|
||||
clean:
|
||||
rm -f test test_proxy pem *.0 *.1 *.srl
|
||||
rm -rf test test_split test_proxy server_fuzzer *.pem *.0 *.o *.1 *.srl httplib.h httplib.cc _build* *.dSYM
|
||||
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
|
||||
#CXX = clang++
|
||||
CXXFLAGS += -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion
|
||||
|
||||
OPENSSL_DIR = /usr/local/opt/openssl@1.1
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
|
||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||
|
||||
BROTLI_DIR = /usr/local/opt/brotli
|
||||
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
||||
|
||||
# By default, use standalone_fuzz_target_runner.
|
||||
# This runner does no fuzzing, but simply executes the inputs
|
||||
# provided via parameters.
|
||||
# Run e.g. "make all LIB_FUZZING_ENGINE=/path/to/libFuzzer.a"
|
||||
# to link the fuzzer(s) against a real fuzzing engine.
|
||||
# OSS-Fuzz will define its own value for LIB_FUZZING_ENGINE.
|
||||
LIB_FUZZING_ENGINE ?= standalone_fuzz_target_runner.o
|
||||
|
||||
# Runs server_fuzzer.cc based on value of $(LIB_FUZZING_ENGINE).
|
||||
# Usage: make fuzz_test LIB_FUZZING_ENGINE=/path/to/libFuzzer
|
||||
all fuzz_test: server_fuzzer
|
||||
./server_fuzzer fuzzing/corpus/*
|
||||
|
||||
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
|
||||
server_fuzzer : fuzzing/server_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o
|
||||
$(CXX) $(CXXFLAGS) -o $@ $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||
|
||||
# Standalone fuzz runner, which just reads inputs from fuzzing/corpus/ dir and
|
||||
# feeds it to server_fuzzer.
|
||||
standalone_fuzz_target_runner.o : fuzzing/standalone_fuzz_target_runner.cpp
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f server_fuzzer pem *.0 *.o *.1 *.srl *.zip
|
||||
10
test/fuzzing/CMakeLists.txt
Normal file
10
test/fuzzing/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
file(GLOB HTTPLIB_CORPUS corpus/*)
|
||||
add_executable(httplib-test-fuzz
|
||||
server_fuzzer.cc
|
||||
standalone_fuzz_target_runner.cpp
|
||||
)
|
||||
target_link_libraries(httplib-test-fuzz PRIVATE httplib)
|
||||
add_test(
|
||||
NAME httplib-test-fuzz
|
||||
COMMAND httplib-test-fuzz ${HTTPLIB_CORPUS}
|
||||
)
|
||||
BIN
test/fuzzing/corpus/3
Normal file
BIN
test/fuzzing/corpus/3
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
19
test/fuzzing/corpus/issue1264
Normal file
19
test/fuzzing/corpus/issue1264
Normal file
File diff suppressed because one or more lines are too long
@ -1,5 +1,6 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include <httplib.h>
|
||||
#include <memory>
|
||||
|
||||
class FuzzedStream : public httplib::Stream {
|
||||
public:
|
||||
@ -22,19 +23,26 @@ public:
|
||||
|
||||
ssize_t write(const std::string &s) { return write(s.data(), s.size()); }
|
||||
|
||||
std::string get_remote_addr() const { return ""; }
|
||||
|
||||
bool is_readable() const override { return true; }
|
||||
|
||||
bool is_writable() const override { return true; }
|
||||
bool wait_readable() const override { return true; }
|
||||
|
||||
bool wait_writable() const override { return true; }
|
||||
|
||||
void get_remote_ip_and_port(std::string &ip, int &port) const override {
|
||||
ip = "127.0.0.1";
|
||||
port = 8080;
|
||||
}
|
||||
|
||||
void get_local_ip_and_port(std::string &ip, int &port) const override {
|
||||
ip = "127.0.0.1";
|
||||
port = 8080;
|
||||
}
|
||||
|
||||
socket_t socket() const override { return 0; }
|
||||
|
||||
time_t duration() const override { return 0; };
|
||||
|
||||
private:
|
||||
const uint8_t *data_;
|
||||
size_t size_;
|
||||
@ -46,8 +54,12 @@ class FuzzableServer : public httplib::Server {
|
||||
public:
|
||||
void ProcessFuzzedRequest(FuzzedStream &stream) {
|
||||
bool connection_close = false;
|
||||
process_request(stream, /*last_connection=*/false, connection_close,
|
||||
nullptr);
|
||||
process_request(stream,
|
||||
/*remote_addr=*/"",
|
||||
/*remote_port =*/0,
|
||||
/*local_addr=*/"",
|
||||
/*local_port =*/0,
|
||||
/*last_connection=*/false, connection_close, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -5,13 +5,13 @@
|
||||
// on the test corpus or on a single file,
|
||||
// e.g. the one that comes from a bug report.
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
// Forward declare the "fuzz target" interface.
|
||||
// We deliberately keep this inteface simple and header-free.
|
||||
// We deliberately keep this interface simple and header-free.
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
||||
|
||||
// It reads all files passed as parameters and feeds their contents
|
||||
@ -20,12 +20,12 @@ int main(int argc, char **argv) {
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::ifstream in(argv[i]);
|
||||
in.seekg(0, in.end);
|
||||
size_t length = in.tellg();
|
||||
in.seekg (0, in.beg);
|
||||
size_t length = static_cast<size_t>(in.tellg());
|
||||
in.seekg(0, in.beg);
|
||||
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
|
||||
// Allocate exactly length bytes so that we reliably catch buffer overflows.
|
||||
std::vector<char> bytes(length);
|
||||
in.read(bytes.data(), bytes.size());
|
||||
in.read(bytes.data(), static_cast<std::streamsize>(bytes.size()));
|
||||
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
|
||||
bytes.size());
|
||||
std::cout << "Execution successful" << std::endl;
|
||||
|
||||
18
test/gen-certs.sh
Executable file
18
test/gen-certs.sh
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
if [[ $(openssl version) =~ 3\.[2-9]\.[0-9]+ ]]; then
|
||||
OPENSSL_X509_FLAG='-x509v1'
|
||||
else
|
||||
OPENSSL_X509_FLAG='-x509'
|
||||
fi
|
||||
|
||||
openssl genrsa 2048 > key.pem
|
||||
openssl req -new -batch -config test.conf -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||
openssl req -x509 -config test.conf -key key.pem -sha256 -days 3650 -nodes -out cert2.pem -extensions SAN
|
||||
openssl genrsa 2048 > rootCA.key.pem
|
||||
openssl req $OPENSSL_X509_FLAG -new -batch -config test.rootCA.conf -key rootCA.key.pem -days 1024 > rootCA.cert.pem
|
||||
openssl genrsa 2048 > client.key.pem
|
||||
openssl req -new -batch -config test.conf -key client.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client.cert.pem
|
||||
openssl genrsa -passout pass:test123! 2048 > key_encrypted.pem
|
||||
openssl req -new -batch -config test.conf -key key_encrypted.pem | openssl x509 -days 3650 -req -signkey key_encrypted.pem > cert_encrypted.pem
|
||||
openssl genrsa -aes256 -passout pass:test012! 2048 > client_encrypted.key.pem
|
||||
openssl req -new -batch -config test.conf -key client_encrypted.key.pem -passin pass:test012! | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client_encrypted.cert.pem
|
||||
File diff suppressed because it is too large
Load Diff
20884
test/gtest/gtest.h
20884
test/gtest/gtest.h
File diff suppressed because it is too large
Load Diff
@ -27,13 +27,28 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <cstdio>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
GTEST_API_ int main(int argc, char **argv) {
|
||||
std::cout << "Running main() from gtest_main.cc\n";
|
||||
#if GTEST_OS_ESP8266 || GTEST_OS_ESP32
|
||||
#if GTEST_OS_ESP8266
|
||||
extern "C" {
|
||||
#endif
|
||||
void setup() {
|
||||
testing::InitGoogleTest();
|
||||
}
|
||||
|
||||
void loop() { RUN_ALL_TESTS(); }
|
||||
|
||||
#if GTEST_OS_ESP8266
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
GTEST_API_ int main(int argc, char **argv) {
|
||||
printf("Running main() from %s\n", __FILE__);
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
#endif
|
||||
|
||||
5
test/include_httplib.cc
Normal file
5
test/include_httplib.cc
Normal file
@ -0,0 +1,5 @@
|
||||
// The sole purpose of this file is to include httplib.h in a separate
|
||||
// compilation unit, thus verifying that inline keywords have not been forgotten
|
||||
// when linked together with test.cc.
|
||||
|
||||
#include <httplib.h>
|
||||
6
test/include_windows_h.cc
Normal file
6
test/include_windows_h.cc
Normal file
@ -0,0 +1,6 @@
|
||||
// Test if including windows.h conflicts with httplib.h
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <httplib.h>
|
||||
28
test/make-shared-library.sh
Executable file
28
test/make-shared-library.sh
Executable file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 build_dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUILD_DIR=$1
|
||||
|
||||
# Make the build directory
|
||||
rm -rf $BUILD_DIR
|
||||
mkdir -p $BUILD_DIR/out
|
||||
|
||||
cd $BUILD_DIR
|
||||
|
||||
# Build the version
|
||||
git checkout $BUILD_DIR -q
|
||||
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_CXX_FLAGS="-g -Og" \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DHTTPLIB_COMPILE=ON \
|
||||
-DCMAKE_INSTALL_PREFIX=./out \
|
||||
../..
|
||||
|
||||
cmake --build . --target install
|
||||
cmake --build . --target clean
|
||||
|
||||
144
test/meson.build
Normal file
144
test/meson.build
Normal file
@ -0,0 +1,144 @@
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
gtest_dep = dependency('gtest', main: true)
|
||||
libcurl_dep = dependency('libcurl')
|
||||
openssl = find_program('openssl')
|
||||
test_conf = files('test.conf')
|
||||
req_x509_flag = openssl.version().version_compare('>=3.2.0') ? '-x509v1' : '-x509'
|
||||
|
||||
key_pem = custom_target(
|
||||
'key_pem',
|
||||
output: 'key.pem',
|
||||
command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048']
|
||||
)
|
||||
|
||||
temp_req = custom_target(
|
||||
'temp_req',
|
||||
input: key_pem,
|
||||
output: 'temp_req',
|
||||
command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-out', '@OUTPUT@']
|
||||
)
|
||||
|
||||
cert_pem = custom_target(
|
||||
'cert_pem',
|
||||
input: [temp_req, key_pem],
|
||||
output: 'cert.pem',
|
||||
command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '3650', '-req', '-signkey', '@INPUT1@', '-out', '@OUTPUT@']
|
||||
)
|
||||
|
||||
cert2_pem = custom_target(
|
||||
'cert2_pem',
|
||||
input: key_pem,
|
||||
output: 'cert2.pem',
|
||||
command: [openssl, 'req', req_x509_flag, '-config', test_conf, '-key', '@INPUT@', '-sha256', '-days', '3650', '-nodes', '-out', '@OUTPUT@', '-extensions', 'SAN']
|
||||
)
|
||||
|
||||
key_encrypted_pem = custom_target(
|
||||
'key_encrypted_pem',
|
||||
output: 'key_encrypted.pem',
|
||||
command: [openssl, 'genrsa', '-passout', 'pass:test123!', '-out', '@OUTPUT@', '2048']
|
||||
)
|
||||
|
||||
cert_encrypted_pem = custom_target(
|
||||
'cert_encrypted_pem',
|
||||
input: key_encrypted_pem,
|
||||
output: 'cert_encrypted.pem',
|
||||
command: [openssl, 'req', req_x509_flag, '-config', test_conf, '-key', '@INPUT@', '-sha256', '-days', '3650', '-nodes', '-out', '@OUTPUT@', '-extensions', 'SAN']
|
||||
)
|
||||
|
||||
rootca_key_pem = custom_target(
|
||||
'rootca_key_pem',
|
||||
output: 'rootCA.key.pem',
|
||||
command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048']
|
||||
)
|
||||
|
||||
rootca_cert_pem = custom_target(
|
||||
'rootca_cert_pem',
|
||||
input: rootca_key_pem,
|
||||
output: 'rootCA.cert.pem',
|
||||
command: [openssl, 'req', req_x509_flag, '-new', '-batch', '-config', files('test.rootCA.conf'), '-key', '@INPUT@', '-days', '1024', '-out', '@OUTPUT@']
|
||||
)
|
||||
|
||||
client_key_pem = custom_target(
|
||||
'client_key_pem',
|
||||
output: 'client.key.pem',
|
||||
command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048']
|
||||
)
|
||||
|
||||
client_temp_req = custom_target(
|
||||
'client_temp_req',
|
||||
input: client_key_pem,
|
||||
output: 'client_temp_req',
|
||||
command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-out', '@OUTPUT@']
|
||||
)
|
||||
|
||||
client_cert_pem = custom_target(
|
||||
'client_cert_pem',
|
||||
input: [client_temp_req, rootca_cert_pem, rootca_key_pem],
|
||||
output: 'client.cert.pem',
|
||||
command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '370', '-req', '-CA', '@INPUT1@', '-CAkey', '@INPUT2@', '-CAcreateserial', '-out', '@OUTPUT@']
|
||||
)
|
||||
|
||||
client_encrypted_key_pem = custom_target(
|
||||
'client_encrypted_key_pem',
|
||||
output: 'client_encrypted.key.pem',
|
||||
command: [openssl, 'genrsa', '-aes256', '-passout', 'pass:test012!', '-out', '@OUTPUT@', '2048']
|
||||
)
|
||||
|
||||
client_encrypted_temp_req = custom_target(
|
||||
'client_encrypted_temp_req',
|
||||
input: client_encrypted_key_pem,
|
||||
output: 'client_encrypted_temp_req',
|
||||
command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-passin', 'pass:test012!', '-out', '@OUTPUT@']
|
||||
)
|
||||
|
||||
client_encrypted_cert_pem = custom_target(
|
||||
'client_encrypted_cert_pem',
|
||||
input: [client_encrypted_temp_req, rootca_cert_pem, rootca_key_pem],
|
||||
output: 'client_encrypted.cert.pem',
|
||||
command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '370', '-req', '-CA', '@INPUT1@', '-CAkey', '@INPUT2@', '-CAcreateserial', '-out', '@OUTPUT@']
|
||||
)
|
||||
|
||||
# Copy test files to the build directory
|
||||
configure_file(input: 'ca-bundle.crt', output: 'ca-bundle.crt', copy: true)
|
||||
configure_file(input: 'image.jpg', output: 'image.jpg', copy: true)
|
||||
subdir('www')
|
||||
subdir('www2'/'dir')
|
||||
subdir('www3'/'dir')
|
||||
|
||||
# GoogleTest 1.13.0 requires C++14
|
||||
test_options = []
|
||||
if gtest_dep.version().version_compare('>=1.13.0')
|
||||
test_options += 'cpp_std=c++14'
|
||||
endif
|
||||
|
||||
test(
|
||||
'main',
|
||||
executable(
|
||||
'main',
|
||||
'test.cc',
|
||||
dependencies: [
|
||||
cpp_httplib_dep,
|
||||
gtest_dep,
|
||||
libcurl_dep
|
||||
],
|
||||
override_options: test_options
|
||||
),
|
||||
depends: [
|
||||
key_pem,
|
||||
cert_pem,
|
||||
cert2_pem,
|
||||
key_encrypted_pem,
|
||||
cert_encrypted_pem,
|
||||
rootca_key_pem,
|
||||
rootca_cert_pem,
|
||||
client_key_pem,
|
||||
client_cert_pem,
|
||||
client_encrypted_key_pem,
|
||||
client_encrypted_cert_pem
|
||||
],
|
||||
workdir: meson.current_build_dir(),
|
||||
timeout: 300
|
||||
)
|
||||
@ -1 +0,0 @@
|
||||
docker-compose down --rmi all
|
||||
@ -1 +0,0 @@
|
||||
docker-compose up -d
|
||||
5682
test/test.cc
5682
test/test.cc
File diff suppressed because it is too large
Load Diff
@ -28,26 +28,26 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
@ -116,6 +116,7 @@
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@ -158,6 +159,7 @@
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
|
||||
@ -5,12 +5,11 @@
|
||||
using namespace std;
|
||||
using namespace httplib;
|
||||
|
||||
template <typename T>
|
||||
void ProxyTest(T& cli, bool basic) {
|
||||
template <typename T> void ProxyTest(T &cli, bool basic) {
|
||||
cli.set_proxy("localhost", basic ? 3128 : 3129);
|
||||
auto res = cli.Get("/httpbin/get");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(407, res->status);
|
||||
EXPECT_EQ(StatusCode::ProxyAuthenticationRequired_407, res->status);
|
||||
}
|
||||
|
||||
TEST(ProxyTest, NoSSLBasic) {
|
||||
@ -38,7 +37,7 @@ TEST(ProxyTest, SSLDigest) {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
void RedirectProxyText(T& cli, const char *path, bool basic) {
|
||||
void RedirectProxyText(T &cli, const char *path, bool basic) {
|
||||
cli.set_proxy("localhost", basic ? 3128 : 3129);
|
||||
if (basic) {
|
||||
cli.set_proxy_basic_auth("hello", "world");
|
||||
@ -51,7 +50,7 @@ void RedirectProxyText(T& cli, const char *path, bool basic) {
|
||||
|
||||
auto res = cli.Get(path);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, HTTPBinNoSSLBasic) {
|
||||
@ -82,7 +81,7 @@ TEST(RedirectTest, YouTubeNoSSLBasic) {
|
||||
RedirectProxyText(cli, "/", true);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, YouTubeNoSSLDigest) {
|
||||
TEST(RedirectTest, DISABLED_YouTubeNoSSLDigest) {
|
||||
Client cli("youtube.com");
|
||||
RedirectProxyText(cli, "/", false);
|
||||
}
|
||||
@ -100,46 +99,46 @@ TEST(RedirectTest, YouTubeSSLDigest) {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
void BaseAuthTestFromHTTPWatch(T& cli) {
|
||||
template <typename T> void BaseAuthTestFromHTTPWatch(T &cli) {
|
||||
cli.set_proxy("localhost", 3128);
|
||||
cli.set_proxy_basic_auth("hello", "world");
|
||||
|
||||
{
|
||||
auto res = cli.Get("/basic-auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(401, res->status);
|
||||
EXPECT_EQ(StatusCode::Unauthorized_401, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
auto res =
|
||||
cli.Get("/basic-auth/hello/world",
|
||||
{make_basic_authentication_header("hello", "world")});
|
||||
auto res = cli.Get("/basic-auth/hello/world",
|
||||
{make_basic_authentication_header("hello", "world")});
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n",
|
||||
res->body);
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
cli.set_basic_auth("hello", "world");
|
||||
auto res = cli.Get("/basic-auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n",
|
||||
res->body);
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
cli.set_basic_auth("hello", "bad");
|
||||
auto res = cli.Get("/basic-auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(401, res->status);
|
||||
EXPECT_EQ(StatusCode::Unauthorized_401, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
cli.set_basic_auth("bad", "world");
|
||||
auto res = cli.Get("/basic-auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(401, res->status);
|
||||
EXPECT_EQ(StatusCode::Unauthorized_401, res->status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,15 +157,14 @@ TEST(BaseAuthTest, SSL) {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
template <typename T>
|
||||
void DigestAuthTestFromHTTPWatch(T& cli) {
|
||||
template <typename T> void DigestAuthTestFromHTTPWatch(T &cli) {
|
||||
cli.set_proxy("localhost", 3129);
|
||||
cli.set_proxy_digest_auth("hello", "world");
|
||||
|
||||
{
|
||||
auto res = cli.Get("/digest-auth/auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(401, res->status);
|
||||
EXPECT_EQ(StatusCode::Unauthorized_401, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
@ -181,24 +179,25 @@ void DigestAuthTestFromHTTPWatch(T& cli) {
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n",
|
||||
res->body);
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
}
|
||||
|
||||
cli.set_digest_auth("hello", "bad");
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(401, res->status);
|
||||
EXPECT_EQ(StatusCode::Unauthorized_401, res->status);
|
||||
}
|
||||
|
||||
// NOTE: Until httpbin.org fixes issue #46, the following test is commented
|
||||
// out. Plese see https://httpbin.org/digest-auth/auth/hello/world
|
||||
// out. Please see https://httpbin.org/digest-auth/auth/hello/world
|
||||
// cli.set_digest_auth("bad", "world");
|
||||
// for (auto path : paths) {
|
||||
// auto res = cli.Get(path.c_str());
|
||||
// ASSERT_TRUE(res != nullptr);
|
||||
// EXPECT_EQ(401, res->status);
|
||||
// EXPECT_EQ(StatusCode::Unauthorized_401, res->status);
|
||||
// }
|
||||
}
|
||||
}
|
||||
@ -216,8 +215,7 @@ TEST(DigestAuthTest, NoSSL) {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
void KeepAliveTest(T& cli, bool basic) {
|
||||
template <typename T> void KeepAliveTest(T &cli, bool basic) {
|
||||
cli.set_proxy("localhost", basic ? 3128 : 3129);
|
||||
if (basic) {
|
||||
cli.set_proxy_basic_auth("hello", "world");
|
||||
@ -234,11 +232,11 @@ void KeepAliveTest(T& cli, bool basic) {
|
||||
|
||||
{
|
||||
auto res = cli.Get("/httpbin/get");
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
}
|
||||
{
|
||||
auto res = cli.Get("/httpbin/redirect/2");
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
@ -249,10 +247,11 @@ void KeepAliveTest(T& cli, bool basic) {
|
||||
"/httpbin/digest-auth/auth-int/hello/world/MD5",
|
||||
};
|
||||
|
||||
for (auto path: paths) {
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n",
|
||||
res->body);
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,7 +259,7 @@ void KeepAliveTest(T& cli, bool basic) {
|
||||
int count = 10;
|
||||
while (count--) {
|
||||
auto res = cli.Get("/httpbin/get");
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8192
test/www/dir/1MB.txt
Normal file
8192
test/www/dir/1MB.txt
Normal file
File diff suppressed because it is too large
Load Diff
8
test/www/dir/meson.build
Normal file
8
test/www/dir/meson.build
Normal file
@ -0,0 +1,8 @@
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
configure_file(input: 'index.html', output: 'index.html', copy: true)
|
||||
configure_file(input: 'test.abcde', output: 'test.abcde', copy: true)
|
||||
configure_file(input: 'test.html', output: 'test.html', copy: true)
|
||||
configure_file(input: '1MB.txt', output: '1MB.txt', copy: true)
|
||||
0
test/www/empty_file
Normal file
0
test/www/empty_file
Normal file
1
test/www/file
Normal file
1
test/www/file
Normal file
@ -0,0 +1 @@
|
||||
file
|
||||
7
test/www/meson.build
Normal file
7
test/www/meson.build
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-FileCopyrightText: 2024 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
configure_file(input: 'empty_file', output: 'empty_file', copy: true)
|
||||
configure_file(input: 'file', output: 'file', copy: true)
|
||||
subdir('dir')
|
||||
1
test/www/日本語Dir/日本語File.txt
Normal file
1
test/www/日本語Dir/日本語File.txt
Normal file
@ -0,0 +1 @@
|
||||
日本語コンテンツ
|
||||
6
test/www2/dir/meson.build
Normal file
6
test/www2/dir/meson.build
Normal file
@ -0,0 +1,6 @@
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
configure_file(input: 'index.html', output: 'index.html', copy: true)
|
||||
configure_file(input: 'test.html', output: 'test.html', copy: true)
|
||||
6
test/www3/dir/meson.build
Normal file
6
test/www3/dir/meson.build
Normal file
@ -0,0 +1,6 @@
|
||||
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
configure_file(input: 'index.html', output: 'index.html', copy: true)
|
||||
configure_file(input: 'test.html', output: 'test.html', copy: true)
|
||||
Loading…
Reference in New Issue
Block a user