mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a66ff5278 | ||
|
|
e1dd3f06fe | ||
|
|
32ab35f61f | ||
|
|
8ab96ec74b | ||
|
|
0730590e35 | ||
|
|
f7e0b1331c | ||
|
|
17aa2a7092 | ||
|
|
7d38d05ca7 | ||
|
|
33bad047f0 | ||
|
|
5234b73c29 | ||
|
|
c566e2eba5 | ||
|
|
e565977481 | ||
|
|
8927c9d22b | ||
|
|
28eb251ccd | ||
|
|
b99dc95b1d | ||
|
|
75aadb3e79 | ||
|
|
b6ecd5fb4c | ||
|
|
1e7f55ed84 | ||
|
|
eeadc5ecff | ||
|
|
568ca42988 | ||
|
|
7b758bfc9d | ||
|
|
8ef454b75f | ||
|
|
0a2f2fda5c | ||
|
|
6b440918ec | ||
|
|
a73fce945c | ||
|
|
d349bb3cc7 | ||
|
|
7c4119a287 | ||
|
|
8a71ad99e6 | ||
|
|
41b405d7bf | ||
|
|
30b80dc5ee | ||
|
|
524aa2e586 | ||
|
|
990a1b9ae0 | ||
|
|
4cb0c6864e | ||
|
|
5183693951 | ||
|
|
e47506f957 | ||
|
|
824e4dc0e4 | ||
|
|
d033497714 | ||
|
|
d8028df29d | ||
|
|
564683d25a | ||
|
|
30c0a3f201 | ||
|
|
08fe9ac518 | ||
|
|
14d6f71f82 | ||
|
|
29454e2296 | ||
|
|
d6cbf9f77d | ||
|
|
b9fead4d6e | ||
|
|
c6a04e1719 | ||
|
|
c839a61fea | ||
|
|
c06abbc5f7 | ||
|
|
c8e88e1710 | ||
|
|
bf7a115bf2 | ||
|
|
b8dfb69cf5 | ||
|
|
1cd9715095 | ||
|
|
5c7aeac148 | ||
|
|
09a80628e6 | ||
|
|
2ff2c369f7 | ||
|
|
eef70aaf74 | ||
|
|
89ac97caba | ||
|
|
c00b236c8d | ||
|
|
eacd3f35e0 | ||
|
|
8a2aa72571 | ||
|
|
7da8f273d2 | ||
|
|
a90a5a2b6a | ||
|
|
b5b819b4a3 | ||
|
|
821e2b3a75 | ||
|
|
773efb0745 | ||
|
|
ed1904a75d | ||
|
|
a66663f0f6 | ||
|
|
1104688b02 | ||
|
|
36d95e230e | ||
|
|
8a27a95a2a | ||
|
|
2e63b5a5e5 | ||
|
|
4cbd113d01 | ||
|
|
ba5ae74144 | ||
|
|
f2b40e5fae | ||
|
|
8ed682930b | ||
|
|
86ad7922b0 | ||
|
|
f7476f53c7 | ||
|
|
5d6f7ac8ef | ||
|
|
f16fa5ed55 | ||
|
|
c0ba32f7a6 | ||
|
|
409c12a851 | ||
|
|
bcdcbf20ee | ||
|
|
8fc5b5a55f | ||
|
|
7cf28a5623 | ||
|
|
6668c456ac | ||
|
|
98f2476751 | ||
|
|
faeedb2930 | ||
|
|
c8e8f94ffe | ||
|
|
22aeb6111b | ||
|
|
2d1d2e6534 | ||
|
|
05c88a98fc | ||
|
|
5224afb946 | ||
|
|
3bd79c7cd5 | ||
|
|
c52e0744cb | ||
|
|
6c09c303d0 | ||
|
|
b6fde06990 | ||
|
|
af892c9726 | ||
|
|
df7ad13680 | ||
|
|
057c2d4b42 | ||
|
|
6c9b43bcf5 | ||
|
|
a91438acae | ||
|
|
a6fa51dbc2 | ||
|
|
6ceac6c84c | ||
|
|
288efe3c23 | ||
|
|
5207545a8b | ||
|
|
716d524f9b | ||
|
|
a434bab767 | ||
|
|
e9633a8d2d | ||
|
|
c7b6a6f0ea | ||
|
|
6cdb9f7ed3 | ||
|
|
4502dd0c86 | ||
|
|
95055f6048 | ||
|
|
2c36532ec4 | ||
|
|
f2b0b9c737 | ||
|
|
b3866439a0 | ||
|
|
4f2205a667 | ||
|
|
6cec1d36a8 | ||
|
|
4238370ae1 | ||
|
|
b4924b715e | ||
|
|
7a0385dcac | ||
|
|
44a146faf6 | ||
|
|
53a55da7bc | ||
|
|
bf44277764 | ||
|
|
a2e1da6e30 | ||
|
|
bef3f85ba8 | ||
|
|
eb72a63cca | ||
|
|
f19bbc73cc | ||
|
|
25326b3b12 | ||
|
|
10ee430bfd | ||
|
|
10d0bfdab4 | ||
|
|
6b6637c337 | ||
|
|
878acca213 | ||
|
|
ace9285966 | ||
|
|
cdc167b0cc | ||
|
|
a0dd396b95 | ||
|
|
8082501b9c | ||
|
|
e4ad3010be | ||
|
|
03a82ef848 | ||
|
|
9459d69dcc | ||
|
|
b28662362d | ||
|
|
66450bf3d1 | ||
|
|
478ca05e7a | ||
|
|
8cad0a72f9 | ||
|
|
31178f4d48 | ||
|
|
7a5485493e | ||
|
|
d3cbb160f5 | ||
|
|
45ff2c2c19 | ||
|
|
c3db7b141b | ||
|
|
0a0ea36ace | ||
|
|
72ed954721 | ||
|
|
33e035273a | ||
|
|
223f36c130 | ||
|
|
4c0bc5bd03 | ||
|
|
808a5f6f53 | ||
|
|
404e635665 | ||
|
|
9d7a60a63f | ||
|
|
ba9c74187b | ||
|
|
e9d4847cbb | ||
|
|
ea76de07ee | ||
|
|
5a8c468ff7 | ||
|
|
5b143a6237 | ||
|
|
d244a7fdfd | ||
|
|
300a27de99 | ||
|
|
04a15a0c10 | ||
|
|
f0f6ae05aa | ||
|
|
8a7d535d92 | ||
|
|
e7ac6024de | ||
|
|
9981b99ac8 | ||
|
|
87a36c0c30 | ||
|
|
f2b13761c0 | ||
|
|
9786478a31 | ||
|
|
7d236d7f33 | ||
|
|
e098a7292d | ||
|
|
632b3a38a9 | ||
|
|
23141c2fef | ||
|
|
c8ba652f1c | ||
|
|
6ac2fa2ae6 | ||
|
|
0322ec5647 | ||
|
|
a5223a9850 | ||
|
|
016968ada1 | ||
|
|
710b888122 | ||
|
|
cf53959f34 | ||
|
|
30d29fb77f | ||
|
|
c05b51bbfc | ||
|
|
1ab704ac92 | ||
|
|
2bc77e065c | ||
|
|
790daabf7d | ||
|
|
a6598a1278 | ||
|
|
0af5502b74 | ||
|
|
f059e41cac | ||
|
|
2cce91de9a | ||
|
|
0ff5b5b044 | ||
|
|
0c5d9a98c2 | ||
|
|
c9fbdfc1b5 | ||
|
|
6193b5171c | ||
|
|
911115ccc0 | ||
|
|
2f3ddeccc5 | ||
|
|
ca3f5a3257 | ||
|
|
df206bc05a | ||
|
|
531fec34fd | ||
|
|
4c10d1f356 | ||
|
|
94dccdd6f4 | ||
|
|
544352b7d3 | ||
|
|
29c2ae4886 | ||
|
|
1a2e2ec2fd | ||
|
|
3e780d21fc | ||
|
|
63fcea0159 | ||
|
|
e5d21225be | ||
|
|
0c9caf3120 | ||
|
|
54ba36c1c9 | ||
|
|
5077350589 | ||
|
|
2d880c4f5c | ||
|
|
4ca4c862a5 | ||
|
|
0918df1566 | ||
|
|
aa2583cc26 | ||
|
|
e1b1d39635 | ||
|
|
3bf82dd541 | ||
|
|
2896965c97 | ||
|
|
53b784b143 | ||
|
|
8e91e3a305 | ||
|
|
dbc07c8b00 | ||
|
|
ce953575e2 | ||
|
|
80db048578 | ||
|
|
382d165574 | ||
|
|
0fb2f38cb7 | ||
|
|
aa007f1285 | ||
|
|
8752910330 | ||
|
|
2a5a8eec49 | ||
|
|
ceca52de30 | ||
|
|
8a3691055c | ||
|
|
56d12c89da | ||
|
|
2cfce4d8b2 | ||
|
|
f372faf614 | ||
|
|
882f3b3ac3 | ||
|
|
fac0e9b0ac | ||
|
|
a3cf67c380 | ||
|
|
2987cbdb5c | ||
|
|
6083b9c5c5 | ||
|
|
83237a02d9 | ||
|
|
965188bac9 | ||
|
|
66da41d401 | ||
|
|
cfd334ac6c | ||
|
|
b5f6254c1e | ||
|
|
dcbd1cbbd1 | ||
|
|
a3b568f460 | ||
|
|
75770aec80 | ||
|
|
3a8ae7ac17 | ||
|
|
b116b2fa82 | ||
|
|
7806609773 | ||
|
|
72f45aa7e2 | ||
|
|
5e3e6a6dce | ||
|
|
1388bb1db4 | ||
|
|
3cc0d16f65 | ||
|
|
b50095fa61 | ||
|
|
7ad85219d8 | ||
|
|
347a249d10 | ||
|
|
07d982129a | ||
|
|
73f2602ba0 | ||
|
|
44561d163a | ||
|
|
c941a1a39a | ||
|
|
bee22312bc | ||
|
|
bdb16c624c | ||
|
|
e389e70d05 | ||
|
|
695559f691 | ||
|
|
13be74b4c6 | ||
|
|
02a1032fd4 | ||
|
|
788f35b19c | ||
|
|
3721efe1e9 | ||
|
|
e0822ffb78 | ||
|
|
ae7d5a8f94 | ||
|
|
ced7a2e0b5 | ||
|
|
5ed6708468 | ||
|
|
9fe53f307b | ||
|
|
c64d30e1f6 | ||
|
|
b4c25fc7ba | ||
|
|
b67b618811 | ||
|
|
5a19438f70 | ||
|
|
860addf1e3 | ||
|
|
03d571a119 | ||
|
|
0d9ecffc53 | ||
|
|
d4c8384b85 | ||
|
|
bd40145301 | ||
|
|
9b42494316 | ||
|
|
2daff6959f | ||
|
|
e9c434a6a1 | ||
|
|
d6fba7e45d | ||
|
|
0a0130b90c | ||
|
|
facf2af66e | ||
|
|
cc54195f59 | ||
|
|
e2fb707d9f | ||
|
|
7dfff935d6 | ||
|
|
5727a5c0ed | ||
|
|
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-2015 Cesanta Software Limited
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
This software is dual-licensed: you can redistribute it and/or modify
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
it under the terms of the GNU General Public License version 2 as
|
||||||
in the Software without restriction, including without limitation the rights
|
published by the Free Software Foundation. For the terms of this
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
license, see <http://www.gnu.org/licenses/>.
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
You are free to use this software under the terms of the GNU General
|
||||||
all copies or substantial portions of the Software.
|
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU General Public License for more details.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
Alternatively, you can license this software under a commercial
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
license, as set out in <https://www.cesanta.com/license>.
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
@@ -1,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,101 @@
|
|||||||
Overview
|
# <img src="http://www.cesanta.com/hubfs/www.cesanta.com/Images/mongoose_library.png" width="64" height="64"> Mongoose - Embedded 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.
|
[](https://gitter.im/cesanta/mongoose?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
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.
|

|
||||||
|
|
||||||
|
|
||||||
Features
|
Mongoose is ideal for embedded environments, it has been designed as an open
|
||||||
--------
|
source platform for connecting devices and bringing them online.
|
||||||
|
[Mongoose](https://www.cesanta.com/products)
|
||||||
|
has been on the market since 2004 and attracted
|
||||||
|
over 1 million cumulative downloads.
|
||||||
|
|
||||||
- Crossplatform - works on Windows, MacOS and most flavors of UNIX
|
It provides easy to use event-driven interface that allows to implement
|
||||||
- CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV support
|
network protocols or scalable network applications with little effort.
|
||||||
- Lua server pages (like PHP, but using Lua as a language instead of PHP)
|
Mongoose helps developers to manage the complexity of network programming
|
||||||
- Resumed download, URL rewrite, IP-based ACL, Windows service
|
and let them concentrate on the logic, saving time and money.
|
||||||
- Excluding files from serving by URI pattern (file blacklist)
|
|
||||||
- Download speed limit based on client subnet or URI pattern
|
[Download Mongoose Source Code here](http://hubs.ly/H0150FK0)
|
||||||
- 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
|
> Nothing overdone. Nothing less. So unbelievably easy to use.
|
||||||
- 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.
|
> Just how good software should be!
|
||||||
- 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)
|
> - Pritin Tyagaraj, SAP
|
||||||
- Super-liberal MIT license, great for commercial users!
|
|
||||||
|
|
||||||
|
|
||||||
Mailing list
|
# Features
|
||||||
------------
|
|
||||||
|
|
||||||
You can read it online, subscribe to, or send a message at [mongoose-users](http://groups.google.com/group/mongoose-users).
|
* Cross-platform: works on Linux/UNIX, QNX, eCos, Windows, Android, iPhone, etc
|
||||||
|
* Single-threaded, asynchronous, non-blocking core with simple event-based API
|
||||||
|
* Builtin protocols:
|
||||||
|
- plain TCP, plain UDP, SSL/TLS (over TCP, one-way or two-way)
|
||||||
|
- HTTP client, HTTP server
|
||||||
|
- Websocket client, Websocket server
|
||||||
|
- JSON-RPC client, JSON-RPC server
|
||||||
|
- MQTT client, MQTT broker
|
||||||
|
- CoAP client, CoAP server
|
||||||
|
- DNS client, DNS server, async DNS resolver
|
||||||
|
* Tiny static and run-time footprint
|
||||||
|
* Source code is both ISO C and ISO C++ compliant
|
||||||
|
* Very easy to integrate: just copy
|
||||||
|
[mongoose.c](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.c) and
|
||||||
|
[mongoose.h](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.h)
|
||||||
|
files to your build tree
|
||||||
|
* Extensively tested and production-ready, trusted by many blue chip businesses
|
||||||
|
|
||||||
|
# Examples & Documentation
|
||||||
|
|
||||||
|
- [User Guide](https://docs.cesanta.com/mongoose) - Detailed User Guide and API reference
|
||||||
|
- [examples](examples) - Collection of well-commented examples. To build any example,
|
||||||
|
go into respective directory and type `make`
|
||||||
|
|
||||||
|
# Dashboard Example
|
||||||
|
|
||||||
|
Mongoose is often used to implement device dashboards and real-time
|
||||||
|
data exchange over Websocket. Here is a dashboard example that illustrates
|
||||||
|
the functionality:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
[Developing a new product? Contact us today to discuss how Mongoose can help
|
||||||
|
](https://www.cesanta.com/contact)
|
||||||
|
|
||||||
|
|
||||||
Keep Sergey happy
|
# Licensing
|
||||||
-----------------
|
|
||||||
|
|
||||||
I have a [books wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority) on Amazon. If you feel brave, you can buy me a book!
|
You are using Mongoose under
|
||||||
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!
|
[GPLv2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||||
|
and until you commercialize your
|
||||||
|
application, can continue to use Mongoose free of charge.
|
||||||
|
|
||||||
|
Once you commercialize your product, in order to comply with GPLv2
|
||||||
|
you need to either open the source code of your product fully or
|
||||||
|
alternatively purchase a commercial license.
|
||||||
|
We offer [three levels of commercial licensing](https://www.cesanta.com/products):
|
||||||
|
|
||||||
|
- Mongoose Lite: cross platform integration, SSL/TLS, HTTP support
|
||||||
|
- Mongoose: Mongoose Lite + HTTP CGI, HTTP SSI, HTTP Digest Auth,
|
||||||
|
WebSocket support
|
||||||
|
- Mongoose Premium: Mongoose + Plain TCP, UDP, JSON-RPC, MQTT, DNS,
|
||||||
|
CoAP support
|
||||||
|
|
||||||
|
|
||||||
|
[Enquire about commercial licensing here](https://www.cesanta.com/contact)
|
||||||
|
|
||||||
|
# Mongoose Binary
|
||||||
|
|
||||||
|
This is our easy to use web server for web developers (PHP, Ruby, Python, etc)
|
||||||
|
and web designers. Available in three editions to suit your needs: free, pro
|
||||||
|
(USD 5) and dev edition (from USD 8). To install, simply download, double-click
|
||||||
|
to start and run browser - that's all!
|
||||||
|
|
||||||
|
[Download Mongoose Binary here](https://www.cesanta.com/mongoose)
|
||||||
|
|
||||||
|
# Other products by Cesanta
|
||||||
|
|
||||||
|
- [Smart.js](https://github.com/cesanta/smart.js) -
|
||||||
|
Generic, hardware independent, full-stack IoT software platform
|
||||||
|
- [V7](https://github.com/cesanta/v7) - Embedded JavaScript engine
|
||||||
|
- [Frozen](https://github.com/cesanta/frozen) - JSON parser and generator
|
||||||
|
- [SLRE](https://github.com/cesanta/slre) - Super Light Regular Expression
|
||||||
|
library
|
||||||
@@ -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)
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
*.exe
|
||||||
+9
-22
@@ -1,28 +1,15 @@
|
|||||||
CFLAGS= -W -Wall -I.. -pthread -g
|
# Copyright (c) 2014 Cesanta Software
|
||||||
|
# All rights reserved
|
||||||
|
|
||||||
all:
|
SUBDIRS = $(sort $(dir $(wildcard */)))
|
||||||
OS=`uname`; \
|
X = $(SUBDIRS)
|
||||||
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
|
|
||||||
|
|
||||||
|
.PHONY: $(SUBDIRS)
|
||||||
|
|
||||||
MSVC = e:/vc6
|
all: $(SUBDIRS)
|
||||||
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
|
|
||||||
|
|
||||||
windows:
|
$(SUBDIRS):
|
||||||
$(CL) upload.c ../mongoose.c $(CLFLAGS)
|
@$(MAKE) -C $@
|
||||||
$(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)
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf hello hello.exe upload upload.exe post post.exe websocket websocket.exe chat chat.exe *.dSYM *.obj
|
for d in $(SUBDIRS) ; do $(MAKE) -C $$d clean ; done
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
PROG = api_server
|
||||||
|
SOURCES = $(PROG).c sqlite3.c db_plugin_sqlite.c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -pthread $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
ifeq ($(OS), Windows_NT)
|
||||||
|
else
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
ifeq ($(UNAME_S), Linux)
|
||||||
|
CFLAGS += -ldl -lm
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I.. /MD /Fe$@
|
||||||
|
|
||||||
|
test: $(PROG)
|
||||||
|
sh unit_test.sh $$(pwd)/$(PROG)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "db_plugin.h"
|
||||||
|
|
||||||
|
static const char *s_http_port = "8000";
|
||||||
|
static struct mg_serve_http_opts s_http_server_opts;
|
||||||
|
static int s_sig_num = 0;
|
||||||
|
static void *s_db_handle = NULL;
|
||||||
|
static const char *s_db_path = "api_server.db";
|
||||||
|
static const struct mg_str s_get_method = MG_STR("GET");
|
||||||
|
static const struct mg_str s_put_method = MG_STR("PUT");
|
||||||
|
static const struct mg_str s_delele_method = MG_STR("DELETE");
|
||||||
|
|
||||||
|
static void signal_handler(int sig_num) {
|
||||||
|
signal(sig_num, signal_handler);
|
||||||
|
s_sig_num = sig_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int has_prefix(const struct mg_str *uri, const struct mg_str *prefix) {
|
||||||
|
return uri->len > prefix->len && memcmp(uri->p, prefix->p, prefix->len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_equal(const struct mg_str *s1, const struct mg_str *s2) {
|
||||||
|
return s1->len == s2->len && memcmp(s1->p, s2->p, s2->len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
static const struct mg_str api_prefix = MG_STR("/api/v1");
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
struct mg_str key;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_HTTP_REQUEST:
|
||||||
|
if (has_prefix(&hm->uri, &api_prefix)) {
|
||||||
|
key.p = hm->uri.p + api_prefix.len;
|
||||||
|
key.len = hm->uri.len - api_prefix.len;
|
||||||
|
if (is_equal(&hm->method, &s_get_method)) {
|
||||||
|
db_op(nc, hm, &key, s_db_handle, API_OP_GET);
|
||||||
|
} else if (is_equal(&hm->method, &s_put_method)) {
|
||||||
|
db_op(nc, hm, &key, s_db_handle, API_OP_SET);
|
||||||
|
} else if (is_equal(&hm->method, &s_delele_method)) {
|
||||||
|
db_op(nc, hm, &key, s_db_handle, API_OP_DEL);
|
||||||
|
} else {
|
||||||
|
mg_printf(nc, "%s",
|
||||||
|
"HTTP/1.0 501 Not Implemented\r\n"
|
||||||
|
"Content-Length: 0\r\n\r\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mg_serve_http(nc, hm, s_http_server_opts); /* Serve static content */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Open listening socket */
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
s_http_server_opts.document_root = "web_root";
|
||||||
|
|
||||||
|
/* Parse command line arguments */
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-D") == 0) {
|
||||||
|
mgr.hexdump_file = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-f") == 0) {
|
||||||
|
s_db_path = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-r") == 0) {
|
||||||
|
s_http_server_opts.document_root = argv[++i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
|
||||||
|
/* Open database */
|
||||||
|
if ((s_db_handle = db_open(s_db_path)) == NULL) {
|
||||||
|
fprintf(stderr, "Cannot open DB [%s]\n", s_db_path);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run event loop until signal is received */
|
||||||
|
printf("Starting RESTful server on port %s\n", s_http_port);
|
||||||
|
while (s_sig_num == 0) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
db_close(&s_db_handle);
|
||||||
|
|
||||||
|
printf("Exiting on signal %d\n", s_sig_num);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DB_PLUGIN_HEADER_DEFINED
|
||||||
|
#define DB_PLUGIN_HEADER_DEFINED
|
||||||
|
|
||||||
|
#include "../../mongoose.h"
|
||||||
|
|
||||||
|
void *db_open(const char *db_path);
|
||||||
|
void db_close(void **db_handle);
|
||||||
|
|
||||||
|
enum { API_OP_GET, API_OP_SET, API_OP_DEL };
|
||||||
|
|
||||||
|
void db_op(struct mg_connection *nc, const struct http_message *hm,
|
||||||
|
const struct mg_str *key, void *db, int op);
|
||||||
|
|
||||||
|
#endif /* DB_PLUGIN_HEADER_DEFINED */
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "db_plugin.h"
|
||||||
|
#include "sqlite3.h"
|
||||||
|
|
||||||
|
void *db_open(const char *db_path) {
|
||||||
|
sqlite3 *db = NULL;
|
||||||
|
if (sqlite3_open_v2(db_path, &db, SQLITE_OPEN_READWRITE |
|
||||||
|
SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) == SQLITE_OK) {
|
||||||
|
sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS kv(key PRIMARY KEY, val);",
|
||||||
|
0, 0, 0);
|
||||||
|
}
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
void db_close(void **db_handle) {
|
||||||
|
if (db_handle != NULL && *db_handle != NULL) {
|
||||||
|
sqlite3_close(*db_handle);
|
||||||
|
*db_handle = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void op_set(struct mg_connection *nc, const struct http_message *hm,
|
||||||
|
const struct mg_str *key, void *db) {
|
||||||
|
sqlite3_stmt *stmt = NULL;
|
||||||
|
char value[200];
|
||||||
|
const struct mg_str *body = hm->query_string.len > 0 ?
|
||||||
|
&hm->query_string : &hm->body;
|
||||||
|
|
||||||
|
mg_get_http_var(body, "value", value, sizeof(value));
|
||||||
|
if (sqlite3_prepare_v2(db, "INSERT OR REPLACE INTO kv VALUES (?, ?);",
|
||||||
|
-1, &stmt, NULL) == SQLITE_OK) {
|
||||||
|
sqlite3_bind_text(stmt, 1, key->p, key->len, SQLITE_STATIC);
|
||||||
|
sqlite3_bind_text(stmt, 2, value, strlen(value), SQLITE_STATIC);
|
||||||
|
sqlite3_step(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
}
|
||||||
|
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void op_get(struct mg_connection *nc, const struct http_message *hm,
|
||||||
|
const struct mg_str *key, void *db) {
|
||||||
|
sqlite3_stmt *stmt = NULL;
|
||||||
|
const char *data = NULL;
|
||||||
|
int result;
|
||||||
|
(void) hm;
|
||||||
|
|
||||||
|
if (sqlite3_prepare_v2(db, "SELECT val FROM kv WHERE key = ?;",
|
||||||
|
-1, &stmt, NULL) == SQLITE_OK) {
|
||||||
|
sqlite3_bind_text(stmt, 1, key->p, key->len, SQLITE_STATIC);
|
||||||
|
result = sqlite3_step(stmt);
|
||||||
|
data = (char *) sqlite3_column_text(stmt, 0);
|
||||||
|
if ((result == SQLITE_OK || result == SQLITE_ROW) && data != NULL) {
|
||||||
|
mg_printf(nc, "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Content-Type: text/plain\r\n"
|
||||||
|
"Content-Length: %d\r\n\r\n%s",
|
||||||
|
(int) strlen(data), data);
|
||||||
|
} else {
|
||||||
|
mg_printf(nc, "%s", "HTTP/1.1 404 Not Found\r\n"
|
||||||
|
"Content-Length: 0\r\n\r\n");
|
||||||
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
} else {
|
||||||
|
mg_printf(nc, "%s", "HTTP/1.1 500 Server Error\r\n"
|
||||||
|
"Content-Length: 0\r\n\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void op_del(struct mg_connection *nc, const struct http_message *hm,
|
||||||
|
const struct mg_str *key, void *db) {
|
||||||
|
sqlite3_stmt *stmt = NULL;
|
||||||
|
int result;
|
||||||
|
(void) hm;
|
||||||
|
|
||||||
|
if (sqlite3_prepare_v2(db, "DELETE FROM kv WHERE key = ?;",
|
||||||
|
-1, &stmt, NULL) == SQLITE_OK) {
|
||||||
|
sqlite3_bind_text(stmt, 1, key->p, key->len, SQLITE_STATIC);
|
||||||
|
result = sqlite3_step(stmt);
|
||||||
|
if (result == SQLITE_OK || result == SQLITE_ROW) {
|
||||||
|
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
|
||||||
|
} else {
|
||||||
|
mg_printf(nc, "%s", "HTTP/1.1 404 Not Found\r\n"
|
||||||
|
"Content-Length: 0\r\n\r\n");
|
||||||
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
} else {
|
||||||
|
mg_printf(nc, "%s", "HTTP/1.1 500 Server Error\r\n"
|
||||||
|
"Content-Length: 0\r\n\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void db_op(struct mg_connection *nc, const struct http_message *hm,
|
||||||
|
const struct mg_str *key, void *db, int op) {
|
||||||
|
switch (op) {
|
||||||
|
case API_OP_GET: op_get(nc, hm, key, db); break;
|
||||||
|
case API_OP_SET: op_set(nc, hm, key, db); break;
|
||||||
|
case API_OP_DEL: op_del(nc, hm, key, db); break;
|
||||||
|
default:
|
||||||
|
mg_printf(nc, "%s", "HTTP/1.0 501 Not Implemented\r\n"
|
||||||
|
"Content-Length: 0\r\n\r\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PROG=$1
|
||||||
|
PORT=${2:-8000} # If second param is given, this is load balancer port
|
||||||
|
DB_FILE=/tmp/_$$.db
|
||||||
|
URL=http://127.0.0.1:$PORT/api/v1
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -rf $DB_FILE
|
||||||
|
kill -9 $PID >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
#set -x
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
$PROG -f $DB_FILE &
|
||||||
|
PID=$!
|
||||||
|
|
||||||
|
#sleep 1
|
||||||
|
curl -s -X PUT -d 'value=123' $URL/foo
|
||||||
|
curl -s -X PUT -d 'value=success' $URL/bar/baz
|
||||||
|
|
||||||
|
# Fetch existing key
|
||||||
|
RESULT=$(curl -s $URL/bar/baz)
|
||||||
|
test "$RESULT" = "success" || exit 1
|
||||||
|
|
||||||
|
# Delete it
|
||||||
|
curl -s -X DELETE $URL/bar/baz
|
||||||
|
|
||||||
|
# Make sure it's deleted - GET must result in 404
|
||||||
|
RESULT=$(curl -s -i $URL/bar/baz | head -1 | tr -d '\r')
|
||||||
|
test "$RESULT" = "HTTP/1.1 404 Not Found" || exit 1
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>RESTful API demo</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style type="text/css">
|
||||||
|
* { outline: none; }
|
||||||
|
body {
|
||||||
|
background-color: #789; margin: 0;
|
||||||
|
padding: 0; font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||||
|
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
div.content {
|
||||||
|
width: 800px; margin: 2em auto; padding: 20px 50px;
|
||||||
|
background-color: #fff; border-radius: 1em;
|
||||||
|
}
|
||||||
|
label { display: inline-block; min-width: 7em; }
|
||||||
|
input { border: 1px solid #ccc; padding: 0.2em; }
|
||||||
|
a:link, a:visited { color: #69c; text-decoration: none; }
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
body { background-color: #fff; }
|
||||||
|
div.content { width: auto; margin: 0 auto; padding: 1em; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||||
|
<script language="javascript" type="text/javascript">
|
||||||
|
jQuery(function() {
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<h1>API server</h1>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# This "makefile" is only intended to prevent errors during main makefile execution
|
||||||
|
all:
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Arduino Restful Client
|
||||||
|
|
||||||
|
This example demonstrates how to use [Mongoose](https://www.cesanta.com/mongoose) to send HTTP commands from Arduino.
|
||||||
|
|
||||||
|
Example sends free memory size and current board uptime, but it can be modified to send any user-specific data.
|
||||||
|
|
||||||
|
At the moment this example supports [Arduino Mega 2560](http://arduino.cc/en/Main/ArduinoBoardMega2560) board (and compatible) with either W5100-based
|
||||||
|
network shield (like [Arduino Ethernet Shield](http://arduino.cc/en/Main/ArduinoEthernetShield)) or [CC3000](http://www.ti.com/product/cc3000)-based WIFI Shield.
|
||||||
|
|
||||||
|
## Build and run instructions:
|
||||||
|
|
||||||
|
###To run with Arduino Ethernet (W5100) shield:
|
||||||
|
1. Add (Sketch->Add file...) the following files to sketch:
|
||||||
|
- /mongoose/mongoose.h
|
||||||
|
- /mongoose/mongoose.c
|
||||||
|
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
|
||||||
|
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
|
||||||
|
3. Buils and start (in console) /Users/alex/Projects/mongoose/examples/restful_server example
|
||||||
|
4. Make `board_ip` and `board_mac` variables suitable for your network and board
|
||||||
|
5. Change IP address in `s_target_address` variable to IP address of host running restful_server
|
||||||
|
6. Uncomment line `#include <Ethernet.h>`
|
||||||
|
7. Compile and flash sketch
|
||||||
|
8. restful_server will start to show current uptime and free memory size (with 5 seconds interval)
|
||||||
|
|
||||||
|
###To run with Adafruit WiFi (CC3000) shield:
|
||||||
|
1. Add (Sketch->Add files...) the following files to sketch:
|
||||||
|
- /mongoose/mongoose.h
|
||||||
|
- /mongoose/mongoose.c
|
||||||
|
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
|
||||||
|
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
|
||||||
|
2. Import Adafruit CC3000 library for mongoose
|
||||||
|
(select Sketch->Import Library...->Add library... and point
|
||||||
|
/mongoose/platforms/arduino_wifi_CC3000/adafruit_CC3000_lib_mongoose folder)
|
||||||
|
3. Buils and start (in console) /Users/alex/Projects/mongoose/examples/restful_server example
|
||||||
|
4. Make the following variables suitable for your network
|
||||||
|
- `board_ip`
|
||||||
|
- `subnet_mask`
|
||||||
|
- `gateway`
|
||||||
|
- `dns`
|
||||||
|
- `wlan_ssid`
|
||||||
|
- `wlan_pwd`
|
||||||
|
- `wlan_security`
|
||||||
|
5. Change IP address in `s_target_address` variable to IP address of host running restful_server
|
||||||
|
6. Uncomment line `#include <Adafruit_CC3000.h>`
|
||||||
|
8. Compile and flash sketch
|
||||||
|
9. restful_server will start to show current uptime and free memory size (with 5 seconds interval)
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*
|
||||||
|
* Build and run instructions:
|
||||||
|
* To run with Arduino Ethernet (W5100) shield:
|
||||||
|
* -----------------------------------------------------------
|
||||||
|
* 1. Add (Sketch->Add file...) the following files to sketch:
|
||||||
|
* - /mongoose/mongoose.h
|
||||||
|
* - /mongoose/mongoose.c
|
||||||
|
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
|
||||||
|
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
|
||||||
|
* 2. Buils and run in console /Users/alex/Projects/mongoose/examples/restful_server example
|
||||||
|
* 3. Make board_ip and board_mac variables suitable for your network and board
|
||||||
|
* 4. Uncomment line #include <Ethernet.h>
|
||||||
|
* 5. Change IP address in s_target_address variable to IP address of host running restful_server
|
||||||
|
* 6. Compile & flash sketch
|
||||||
|
* 7. restful_server server will start to show current uptime and free memory size (with 5 second interval)
|
||||||
|
*
|
||||||
|
* To run with Adafruit WiFi (CC3000) shield:
|
||||||
|
* -----------------------------------------------------------
|
||||||
|
* 1. Add (Sketch->Add files...) the following files to sketch:
|
||||||
|
* - /mongoose/mongoose.h
|
||||||
|
* - /mongoose/mongoose.c
|
||||||
|
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
|
||||||
|
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
|
||||||
|
* 2. Import Adafruit CC3000 library for mongoose (select Sketch->Import Library...->Add library... and point
|
||||||
|
* /mongoose/platforms/arduino_wifi_CC3000/adafruit_CC3000_lib_mongoose folder
|
||||||
|
* 3. Buils and run in console /Users/alex/Projects/mongoose/examples/restful_server example
|
||||||
|
* 4. Make the following variables suitable for your network
|
||||||
|
* - board_ip
|
||||||
|
* - subnet_mask
|
||||||
|
* - gateway
|
||||||
|
* - dns
|
||||||
|
* - wlan_ssid
|
||||||
|
* - wlan_pwd
|
||||||
|
* - wlan_security
|
||||||
|
* 5. Uncomment line #include <Adafruit_CC3000.h>
|
||||||
|
* 6. Compile & flash sketch
|
||||||
|
* 7. restful_server server will start to show current uptime and free memory size (with 5 second interval) *
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
//#include <Ethernet.h>
|
||||||
|
//#include <Adafruit_CC3000.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
// CHANGE THESE VARIABLES
|
||||||
|
// NB: Devices with the same address must not end up on the same network.
|
||||||
|
// Use MAC address provided by device manufacturer (e.g. on a sticker).
|
||||||
|
// If there isn't one, use a random one from the locally administered range.
|
||||||
|
// See http://en.wikipedia.org/wiki/MAC_address for details.
|
||||||
|
static uint8_t board_mac[] = {
|
||||||
|
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
|
||||||
|
};
|
||||||
|
static uint8_t board_ip[] = {192, 168, 10, 177};
|
||||||
|
|
||||||
|
#ifdef WIFI_CC3000
|
||||||
|
static uint8_t subnet_mask[] = {255, 255, 255, 0};
|
||||||
|
static uint8_t gateway[] = {192, 168, 10, 254};
|
||||||
|
static uint8_t dmg_ip[] = {192, 168, 10, 254};
|
||||||
|
|
||||||
|
static const char *wlan_ssid = "mynetwork";
|
||||||
|
static const char *wlan_pwd = "mypassword";
|
||||||
|
static int wlan_security = WLAN_SEC_WPA2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *s_target_address = "192.168.10.3:8000";
|
||||||
|
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
|
||||||
|
static const char *s_request = "/printcontent";
|
||||||
|
|
||||||
|
static uint32_t IP2U32(uint8_t* iparr) {
|
||||||
|
return ((uint32_t)iparr[0] << 24) | ((uint32_t)iparr[1] << 16) | (iparr[2] << 8) | (iparr[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_data_to_send(char* buf, int buf_size) {
|
||||||
|
// Adding data to send
|
||||||
|
// It could be any sensor data, now just put uptime & free memory size here
|
||||||
|
return snprintf(buf, buf_size, "Uptime: %lus Free memory: %db",
|
||||||
|
millis()/1000, get_freememsize());
|
||||||
|
}
|
||||||
|
static void rfc_ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
int connect_status;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case NS_CONNECT:
|
||||||
|
connect_status = * (int *) ev_data;
|
||||||
|
|
||||||
|
if (connect_status == 0) {
|
||||||
|
char buf[100];
|
||||||
|
int len = get_data_to_send(buf, sizeof(buf));
|
||||||
|
mg_printf(nc, "POST %s HTTP/1.0\r\nHost: %s\r\nContent-Lenght: %d"
|
||||||
|
"\r\n\r\n%s", s_request, s_target_address, len, buf);
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
} else {
|
||||||
|
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mg_mgr mgr;
|
||||||
|
static struct mg_connection *nc;
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(9600);
|
||||||
|
Serial.println("Initialization...");
|
||||||
|
#if defined(ETHERNET_W5100)
|
||||||
|
avr_netinit(board_mac, board_ip);
|
||||||
|
#elif defined(WIFI_CC3000)
|
||||||
|
if (avr_netinit(wlan_ssid, wlan_pwd, wlan_security, IP2U32(board_ip),
|
||||||
|
IP2U32(subnet_mask), IP2U32(gateway), IP2U32(dmg_ip)) != 0) {
|
||||||
|
Serial.println("Initialization error, check network settings");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
Serial.println("Initialization done");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
nc = mg_connect(&mgr, s_target_address, rfc_ev_handler);
|
||||||
|
if (nc != NULL) {
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t time_to_finish = millis() + 5000;
|
||||||
|
while (millis() < time_to_finish) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# This "makefile" is only intended to prevent errors during main makefile execution
|
||||||
|
all:
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Arduino Restful Server
|
||||||
|
|
||||||
|
This example demonstrates how to use [Mongoose](https://www.cesanta.com/mongoose) to control Arduino
|
||||||
|
using HTTP requests.
|
||||||
|
Example just blinks by LED when Mongoose receives HTTP command, but it can be modified to execute any user-specific code.
|
||||||
|
|
||||||
|
At the moment this example supports [Arduino Mega 2560](http://arduino.cc/en/Main/ArduinoBoardMega2560) board (and compatible) with either W5100-based
|
||||||
|
network shield (like [Arduino Ethernet Shield](http://arduino.cc/en/Main/ArduinoEthernetShield)) or [CC3000](http://www.ti.com/product/cc3000)-based WIFI Shield.
|
||||||
|
|
||||||
|
## Build and run instructions:
|
||||||
|
|
||||||
|
###To run with Arduino Ethernet (W5100) shield:
|
||||||
|
1. Add (Sketch->Add file...) the following files to sketch:
|
||||||
|
- /mongoose/mongoose.h
|
||||||
|
- /mongoose/mongoose.c
|
||||||
|
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
|
||||||
|
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
|
||||||
|
2. Make `board_ip` and `board_mac` variables suitable for your network and board
|
||||||
|
3. Uncomment line `#include <Ethernet.h>`
|
||||||
|
4. Compile and flash sketch
|
||||||
|
5. Run `curl http://<board_ip/blink`
|
||||||
|
LED attached to PIN 13 will blink and board free memory size and board uptime will be displayed.
|
||||||
|
|
||||||
|
|
||||||
|
###To run with Adafruit WiFi (CC3000) shield:
|
||||||
|
1. Add (Sketch->Add files...) the following files to sketch:
|
||||||
|
- /mongoose/mongoose.h
|
||||||
|
- /mongoose/mongoose.c
|
||||||
|
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
|
||||||
|
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
|
||||||
|
2. Import Adafruit CC3000 library for mongoose
|
||||||
|
(select Sketch->Import Library...->Add library... and point
|
||||||
|
/mongoose/platforms/arduino_wifi_CC3000/adafruit_CC3000_lib_mongoose folder)
|
||||||
|
3. Make the following variables suitable for your network
|
||||||
|
- `board_ip`
|
||||||
|
- `subnet_mask`
|
||||||
|
- `gateway`
|
||||||
|
- `dns`
|
||||||
|
- `wlan_ssid`
|
||||||
|
- `wlan_pwd`
|
||||||
|
- `wlan_security`
|
||||||
|
5. Uncomment line `#include <Adafruit_CC3000.h>`
|
||||||
|
4. Compile and flash sketch
|
||||||
|
5. Run curl `http://<board_ip/blink`
|
||||||
|
LED attached to PIN 13 will blink and board free memory size and board uptime will be displayed.
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*
|
||||||
|
* Build and run instructions:
|
||||||
|
* To run with Arduino Ethernet (W5100) shield:
|
||||||
|
* -----------------------------------------------------------
|
||||||
|
* 1. Add (Sketch->Add file...) the following files to sketch:
|
||||||
|
* - /mongoose/mongoose.h
|
||||||
|
* - /mongoose/mongoose.c
|
||||||
|
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
|
||||||
|
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
|
||||||
|
* 2. Make board_ip and board_mac variables suitable for your network and board
|
||||||
|
* 3. Uncomment line #include <Ethernet.h>
|
||||||
|
* 4. Compile & flash sketch
|
||||||
|
* 5. Run curl http://<board_ip/blink
|
||||||
|
* LED attached to PIN 13 will blink and board free memory size and uptime will responsed
|
||||||
|
*
|
||||||
|
* To run with Adafruit WiFi (CC3000) shield:
|
||||||
|
* -----------------------------------------------------------
|
||||||
|
* 1. Add (Sketch->Add files...) the following files to sketch:
|
||||||
|
* - /mongoose/mongoose.h
|
||||||
|
* - /mongoose/mongoose.c
|
||||||
|
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
|
||||||
|
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
|
||||||
|
* 2. Import Adafruit CC3000 library for mongoose (select Sketch->Import Library...->Add library... and point
|
||||||
|
* /mongoose/platforms/arduino_wifi_CC3000/adafruit_CC3000_lib_mongoose folder
|
||||||
|
* 3. Make the following variables suitable for your network
|
||||||
|
* - board_ip
|
||||||
|
* - subnet_mask
|
||||||
|
* - gateway
|
||||||
|
* - dns
|
||||||
|
* - wlan_ssid
|
||||||
|
* - wlan_pwd
|
||||||
|
* - wlan_security
|
||||||
|
* 5. Uncomment line #include <Adafruit_CC3000.h>
|
||||||
|
* 4. Compile & flash sketch
|
||||||
|
* 5. Run curl http://<board_ip/blink
|
||||||
|
* LED attached to PIN 13 will blink and board free memory size and uptime will responsed
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#include <Ethernet.h>
|
||||||
|
//#include <Adafruit_CC3000.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
// CHANGE THESE VARIABLES
|
||||||
|
// NB: Devices with the same address must not end up on the same network.
|
||||||
|
// Use MAC address provided by device manufacturer (e.g. on a sticker).
|
||||||
|
// If there isn't one, use a random one from the locally administered range.
|
||||||
|
// See http://en.wikipedia.org/wiki/MAC_address for details.
|
||||||
|
static uint8_t board_mac[] = {
|
||||||
|
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t board_ip[] = {192, 168, 10, 8};
|
||||||
|
|
||||||
|
#ifdef WIFI_CC3000
|
||||||
|
static uint8_t subnet_mask[] = {255, 255, 255, 0};
|
||||||
|
static uint8_t gateway[] = {192, 168, 10, 254};
|
||||||
|
static uint8_t dmg_ip[] = {192, 168, 10, 254};
|
||||||
|
|
||||||
|
static const char *wlan_ssid = "mynetwork";
|
||||||
|
static const char *wlan_pwd = "mypassword";
|
||||||
|
static int wlan_security = WLAN_SEC_WPA2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////
|
||||||
|
|
||||||
|
static const char *s_http_port = "60000";
|
||||||
|
|
||||||
|
static uint32_t IP2U32(uint8_t* iparr) {
|
||||||
|
return ((uint32_t)iparr[0] << 24) | ((uint32_t)iparr[1] << 16) | (iparr[2] << 8) | (iparr[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rfs_ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
char buf[100];
|
||||||
|
int clen;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case NS_HTTP_REQUEST:
|
||||||
|
if (mg_vcmp(&hm->uri, "/blink") == 0) {
|
||||||
|
blink(1, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
clen = snprintf(buf, sizeof(buf),
|
||||||
|
"Free memory size: %d Uptime: %d",
|
||||||
|
(int)get_freememsize(), (int)time(NULL));
|
||||||
|
|
||||||
|
mg_printf_http_chunk(nc, "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Content-Length: %d\r\n"
|
||||||
|
"Transfer-Encoding: chunked\r\n\r\n"
|
||||||
|
"%s",
|
||||||
|
clen, buf);
|
||||||
|
|
||||||
|
mg_send_http_chunk(nc, "", 0);
|
||||||
|
break;
|
||||||
|
case NS_SEND:
|
||||||
|
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mg_connection *nc;
|
||||||
|
static struct mg_mgr mgr;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
Serial.println("Initialization...");
|
||||||
|
#if defined(ETHERNET_W5100)
|
||||||
|
avr_netinit(board_mac, board_ip);
|
||||||
|
#elif defined(WIFI_CC3000)
|
||||||
|
if (avr_netinit(wlan_ssid, wlan_pwd, wlan_security, IP2U32(board_ip),
|
||||||
|
IP2U32(subnet_mask), IP2U32(gateway), IP2U32(dmg_ip)) != 0) {
|
||||||
|
Serial.println("Initialization error, check network settings");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
nc = mg_bind(&mgr, s_http_port, rfs_ev_handler);
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
Serial.println("Initialization done");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
PROG = captive_dns_server
|
||||||
|
MODULE_CFLAGS=-DMG_ENABLE_DNS_SERVER
|
||||||
|
|
||||||
|
include ../rules.mk
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try it out with:
|
||||||
|
* $ dig -t A www.google.com -4 @localhost -p 5533
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../../mongoose.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static int s_exit_flag = 0;
|
||||||
|
static in_addr_t s_our_ip_addr;
|
||||||
|
static const char *s_listening_addr = "udp://:5533";
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct mg_dmg_message *msg;
|
||||||
|
struct mg_dmg_resource_record *rr;
|
||||||
|
struct mg_dmg_reply reply;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_DNS_MESSAGE:
|
||||||
|
msg = (struct mg_dmg_message *) ev_data;
|
||||||
|
reply = mg_dmg_create_reply(&nc->send_mbuf, msg);
|
||||||
|
|
||||||
|
for (i = 0; i < msg->num_questions; i++) {
|
||||||
|
rr = &msg->questions[i];
|
||||||
|
if (rr->rtype == MG_DNS_A_RECORD) {
|
||||||
|
mg_dmg_reply_record(&reply, rr, NULL, rr->rtype, 3600,
|
||||||
|
&s_our_ip_addr, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't set the error flag even if there were no answers
|
||||||
|
* maching the MG_DNS_A_RECORD query type.
|
||||||
|
* This indicates that we have (syntetic) answers for MG_DNS_A_RECORD.
|
||||||
|
* See http://goo.gl/QWvufr for a distinction between NXDOMAIN and NODATA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mg_dmg_send_reply(nc, &reply);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
s_our_ip_addr = inet_addr("127.0.0.1");
|
||||||
|
|
||||||
|
/* Parse command line arguments */
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-D") == 0) {
|
||||||
|
mgr.hexdump_file = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) {
|
||||||
|
s_listening_addr = argv[++i];
|
||||||
|
} else {
|
||||||
|
s_our_ip_addr = inet_addr(argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Listening on '%s'\n", s_listening_addr);
|
||||||
|
if ((nc = mg_bind(&mgr, s_listening_addr, ev_handler)) == NULL) {
|
||||||
|
fprintf(stderr, "cannot bind to socket\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
mg_set_protocol_dns(nc);
|
||||||
|
|
||||||
|
while (s_exit_flag == 0) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
-390
@@ -1,390 +0,0 @@
|
|||||||
// 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;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until enter is pressed, then exit
|
|
||||||
printf("Chat server started on ports %s, press enter to quit.\n",
|
|
||||||
mg_get_option(ctx, "listening_ports"));
|
|
||||||
getchar();
|
|
||||||
mg_stop(ctx);
|
|
||||||
printf("%s\n", "Chat server stopped.");
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// vim:ts=2:sw=2:et
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
PROG = coap_client
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. $(CFLAGS_EXTRA) -DMG_ENABLE_COAP
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*
|
||||||
|
* This program sends CoAP CON-message to server (coap.me by default)
|
||||||
|
* and waits for answer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int s_time_to_exit = 0;
|
||||||
|
static char* s_default_address = "udp://coap.me:5683";
|
||||||
|
|
||||||
|
static void coap_handler(struct mg_connection *nc, int ev, void *p) {
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_CONNECT: {
|
||||||
|
struct mg_coap_message cm;
|
||||||
|
uint32_t res;
|
||||||
|
|
||||||
|
memset(&cm, 0, sizeof(cm));
|
||||||
|
cm.msg_id = 1;
|
||||||
|
cm.msg_type = MG_COAP_MSG_CON;
|
||||||
|
printf("Sending CON...\n");
|
||||||
|
res = mg_coap_send_message(nc, &cm);
|
||||||
|
if (res == 0) {
|
||||||
|
printf("Sent CON with msg_id = %d\n", cm.msg_id);
|
||||||
|
} else {
|
||||||
|
printf("Error: %d\n", res);
|
||||||
|
s_time_to_exit = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MG_EV_COAP_ACK:
|
||||||
|
case MG_EV_COAP_RST: {
|
||||||
|
struct mg_coap_message *cm = (struct mg_coap_message *)p;
|
||||||
|
printf("ACK/RST for message with msg_id = %d received\n",
|
||||||
|
cm->msg_id);
|
||||||
|
s_time_to_exit = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
char *address = s_default_address;
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
address = argv[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Using %s as CoAP server\n", address);
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, 0);
|
||||||
|
|
||||||
|
nc = mg_connect(&mgr, address, coap_handler);
|
||||||
|
if (nc == NULL) {
|
||||||
|
printf("Unable to connect to %s\n", address);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mg_set_protocol_coap(nc);
|
||||||
|
|
||||||
|
while (!s_time_to_exit) {
|
||||||
|
mg_mgr_poll(&mgr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
PROG = coap_server
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. $(CFLAGS_EXTRA) -DMG_ENABLE_COAP
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*
|
||||||
|
* This program listens on 5683 for CoAP messages,
|
||||||
|
* sends ACK is nessesary and dump everything received.
|
||||||
|
* It is possible to use ../coap_client to send message.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static char* s_default_address = "udp://:5683";
|
||||||
|
static int s_sig_received = 0;
|
||||||
|
|
||||||
|
static void signal_handler(int sig_num) {
|
||||||
|
signal(sig_num, signal_handler);
|
||||||
|
s_sig_received = sig_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void coap_handler(struct mg_connection *nc, int ev, void *p) {
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_COAP_CON: {
|
||||||
|
uint32_t res;
|
||||||
|
struct mg_coap_message *cm = (struct mg_coap_message *)p;
|
||||||
|
printf("CON with msg_id = %d received\n", cm->msg_id);
|
||||||
|
res = mg_coap_send_ack(nc, cm->msg_id);
|
||||||
|
if (res == 0) {
|
||||||
|
printf("Successfully sent ACK for message with msg_id = %d\n",
|
||||||
|
cm->msg_id);
|
||||||
|
} else {
|
||||||
|
printf("Error: %d\n", res);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MG_EV_COAP_NOC:
|
||||||
|
case MG_EV_COAP_ACK:
|
||||||
|
case MG_EV_COAP_RST: {
|
||||||
|
struct mg_coap_message *cm = (struct mg_coap_message *)p;
|
||||||
|
printf("ACK/RST/NOC with msg_id = %d received\n",
|
||||||
|
cm->msg_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, 0);
|
||||||
|
|
||||||
|
nc = mg_bind(&mgr, s_default_address, coap_handler);
|
||||||
|
if (nc == NULL) {
|
||||||
|
printf("Unable to start listener at %s\n", s_default_address);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Listening for CoAP messages at %s\n", s_default_address);
|
||||||
|
|
||||||
|
mg_set_protocol_coap(nc);
|
||||||
|
|
||||||
|
while (!s_sig_received) {
|
||||||
|
mg_mgr_poll(&mgr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Exiting on signal %d\n", s_sig_received);
|
||||||
|
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
static void *callback(enum mg_event event,
|
|
||||||
struct mg_connection *conn) {
|
|
||||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
|
||||||
|
|
||||||
if (event == MG_NEW_REQUEST) {
|
|
||||||
char content[1024];
|
|
||||||
int content_length = snprintf(content, sizeof(content),
|
|
||||||
"Hello from mongoose! Remote port: %d",
|
|
||||||
request_info->remote_port);
|
|
||||||
mg_printf(conn,
|
|
||||||
"HTTP/1.1 200 OK\r\n"
|
|
||||||
"Content-Type: text/plain\r\n"
|
|
||||||
"Content-Length: %d\r\n" // Always set Content-Length
|
|
||||||
"\r\n"
|
|
||||||
"%s",
|
|
||||||
content_length, content);
|
|
||||||
// Mark as processed
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_context *ctx;
|
|
||||||
const char *options[] = {"listening_ports", "8080", NULL};
|
|
||||||
|
|
||||||
ctx = mg_start(&callback, NULL, options);
|
|
||||||
getchar(); // Wait until user hits "enter"
|
|
||||||
mg_stop(ctx);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,73 +0,0 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" dir="ltr">
|
|
||||||
<!-- This file is part of the Mongoose project,
|
|
||||||
http://code.google.com/p/mongoose -->
|
|
||||||
<head>
|
|
||||||
<title>Mongoose chat server</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
|
||||||
<link type="text/css" rel="stylesheet" href="style.css"/>
|
|
||||||
<script src="jquery.js"></script>
|
|
||||||
<script src="main.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="header">
|
|
||||||
<div id="logo"></div>
|
|
||||||
<div class="rounded infobox help-message" id="motd">
|
|
||||||
Chat room implemented using
|
|
||||||
<a href="http://code.google.com/p/mongoose" target="_blank">Mongoose</a>
|
|
||||||
embeddable web server.
|
|
||||||
This application was written for educational purposes demonstrating
|
|
||||||
how web interface could be decoupled from the business logic. Not a
|
|
||||||
single line of HTML is generated by the server, instead, server
|
|
||||||
gives data to the client in JSON format.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div id="middle">
|
|
||||||
<div><center><span id="error" class="rounded"></span><center></div>
|
|
||||||
|
|
||||||
<div id="menu">
|
|
||||||
<div class="menu-item left-rounded menu-item-selected"
|
|
||||||
name="chat">Chat</div>
|
|
||||||
<div class="menu-item left-rounded" name="settings">Settings</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="content" class="rounded">
|
|
||||||
|
|
||||||
<div id="chat" class="main">
|
|
||||||
<div class="chat-window">
|
|
||||||
<span class="top-rounded chat-title">Main room</span>
|
|
||||||
<div class="bottom-rounded chat-content">
|
|
||||||
<div class="message-list" id="mml">
|
|
||||||
</div>
|
|
||||||
<input type="text" size="40" class="message-input"></input>
|
|
||||||
<span class="help-message">
|
|
||||||
Type your message here and press enter</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="settings" class="hidden main">
|
|
||||||
<div>
|
|
||||||
<span class="top-rounded chat-title">Settings</span>
|
|
||||||
<div class="bottom-rounded chat-content">
|
|
||||||
<table>
|
|
||||||
<tr><td>Max messages to display:</td><td>blah blah</td></tr>
|
|
||||||
<tr><td>Text color:</td><td>blah blah</td></tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="footer">
|
|
||||||
Copyright © 2004-2010 by Sergey Lyubka
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Vendored
-154
@@ -1,154 +0,0 @@
|
|||||||
/*!
|
|
||||||
* jQuery JavaScript Library v1.4.2
|
|
||||||
* http://jquery.com/
|
|
||||||
*
|
|
||||||
* Copyright 2010, John Resig
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* Includes Sizzle.js
|
|
||||||
* http://sizzlejs.com/
|
|
||||||
* Copyright 2010, The Dojo Foundation
|
|
||||||
* Released under the MIT, BSD, and GPL Licenses.
|
|
||||||
*
|
|
||||||
* Date: Sat Feb 13 22:33:48 2010 -0500
|
|
||||||
*/
|
|
||||||
(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
|
|
||||||
e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
|
|
||||||
j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
|
|
||||||
"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
|
|
||||||
true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
|
|
||||||
Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
|
|
||||||
(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
|
|
||||||
a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
|
|
||||||
"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
|
|
||||||
function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
|
|
||||||
c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
|
|
||||||
L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
|
|
||||||
"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
|
|
||||||
a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
|
|
||||||
d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
|
|
||||||
a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
|
|
||||||
!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
|
|
||||||
true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
|
|
||||||
var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
|
|
||||||
parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
|
|
||||||
false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
|
|
||||||
s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
|
|
||||||
applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
|
|
||||||
else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
|
|
||||||
a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
|
|
||||||
w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
|
|
||||||
cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
|
|
||||||
i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
|
|
||||||
" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
|
|
||||||
this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
|
|
||||||
e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
|
|
||||||
c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
|
|
||||||
a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
|
|
||||||
function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
|
|
||||||
k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
|
|
||||||
C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
|
|
||||||
null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
|
|
||||||
e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
|
|
||||||
f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
|
|
||||||
if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
|
|
||||||
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
|
|
||||||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
|
|
||||||
"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
|
|
||||||
a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
|
|
||||||
isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
|
|
||||||
{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
|
|
||||||
if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
|
|
||||||
e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
|
|
||||||
"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
|
|
||||||
d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
|
|
||||||
!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
|
|
||||||
toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
|
|
||||||
u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
|
|
||||||
function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
|
|
||||||
if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
|
|
||||||
e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
|
|
||||||
t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
|
|
||||||
g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
|
|
||||||
for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
|
|
||||||
1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
|
|
||||||
CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
|
|
||||||
relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
|
|
||||||
l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
|
|
||||||
h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
|
|
||||||
CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
|
|
||||||
g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
|
|
||||||
text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
|
|
||||||
setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
|
|
||||||
h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
|
|
||||||
m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
|
|
||||||
"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
|
|
||||||
h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
|
|
||||||
!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
|
|
||||||
h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
|
|
||||||
q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
|
|
||||||
if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
|
|
||||||
(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
|
|
||||||
function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
|
|
||||||
gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
|
|
||||||
c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
|
|
||||||
{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
|
|
||||||
"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
|
|
||||||
d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
|
|
||||||
a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
|
|
||||||
1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
|
|
||||||
a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
|
|
||||||
c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
|
|
||||||
wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
|
|
||||||
prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
|
|
||||||
this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
|
|
||||||
return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
|
|
||||||
""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
|
|
||||||
this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
|
|
||||||
u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
|
|
||||||
1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
|
|
||||||
return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
|
|
||||||
""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
|
|
||||||
c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
|
|
||||||
c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
|
|
||||||
function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
|
|
||||||
Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
|
|
||||||
"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
|
|
||||||
a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
|
|
||||||
a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
|
|
||||||
"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
|
|
||||||
serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
|
|
||||||
function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
|
|
||||||
global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
|
|
||||||
e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
|
|
||||||
"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
|
|
||||||
false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
|
|
||||||
false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
|
|
||||||
c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
|
|
||||||
d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
|
|
||||||
g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
|
|
||||||
1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
|
|
||||||
"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
|
|
||||||
if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
|
|
||||||
this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
|
|
||||||
"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
|
|
||||||
animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
|
|
||||||
j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
|
|
||||||
this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
|
|
||||||
"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
|
|
||||||
c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
|
|
||||||
this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
|
|
||||||
this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
|
|
||||||
e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
|
|
||||||
c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
|
|
||||||
function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
|
|
||||||
this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
|
|
||||||
k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
|
|
||||||
f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
|
|
||||||
a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
|
|
||||||
c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
|
|
||||||
d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
|
|
||||||
f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
|
|
||||||
"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
|
|
||||||
e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" dir="ltr">
|
|
||||||
<!-- This file is part of the Mongoose project,
|
|
||||||
http://code.google.com/p/mongoose -->
|
|
||||||
<head>
|
|
||||||
<title>Mongoose chat: login</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
|
||||||
<!--
|
|
||||||
Note that this page is self-sufficient, it does not load any other
|
|
||||||
CSS or Javascript file. This is done so because only this page is
|
|
||||||
allowed for non-authorized users. If we want to load other files
|
|
||||||
from the frontend, we need to change backend code to allow those
|
|
||||||
for non-authorized users. See chat.c :: must_authorize() function.
|
|
||||||
-->
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
window.onload = function() {
|
|
||||||
// Set correct action for the login form. We assume that the SSL port
|
|
||||||
// is the next one to insecure one.
|
|
||||||
var httpsPort = location.protocol.match(/https/) ? location.port :
|
|
||||||
parseInt(location.port) + 1;
|
|
||||||
document.forms[0].action = 'https://' + location.hostname + ':' +
|
|
||||||
httpsPort + '/authorize';
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<center>
|
|
||||||
<h2>Mongoose chat server login</h2>
|
|
||||||
<div style="max-width: 30em;">
|
|
||||||
Username and password can be any non-empty strings.
|
|
||||||
</div>
|
|
||||||
<br/>
|
|
||||||
<form>
|
|
||||||
<input type="text" name="user"></input><br/>
|
|
||||||
<input type="text" name="password"></input><br/>
|
|
||||||
<input type="submit" value="Login"></input>
|
|
||||||
</form>
|
|
||||||
</center>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,106 +0,0 @@
|
|||||||
// This file is part of Mongoose project, http://code.google.com/p/mongoose
|
|
||||||
|
|
||||||
var chat = {
|
|
||||||
// Backend URL, string.
|
|
||||||
// 'http://backend.address.com' or '' if backend is the same as frontend
|
|
||||||
backendUrl: '',
|
|
||||||
maxVisibleMessages: 10,
|
|
||||||
errorMessageFadeOutTimeoutMs: 2000,
|
|
||||||
errorMessageFadeOutTimer: null,
|
|
||||||
lastMessageId: 0,
|
|
||||||
getMessagesIntervalMs: 1000,
|
|
||||||
};
|
|
||||||
|
|
||||||
chat.normalizeText = function(text) {
|
|
||||||
return text.replace('<', '<').replace('>', '>');
|
|
||||||
};
|
|
||||||
|
|
||||||
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();
|
|
||||||
$('<span>')
|
|
||||||
.addClass('message-timestamp')
|
|
||||||
.html('[' + timestamp + ']')
|
|
||||||
.prependTo(row);
|
|
||||||
$('<span>')
|
|
||||||
.addClass('message-user')
|
|
||||||
.addClass(entry.user ? '' : 'message-user-server')
|
|
||||||
.html(chat.normalizeText((entry.user || '[server]') + ':'))
|
|
||||||
.appendTo(row);
|
|
||||||
$('<span>')
|
|
||||||
.addClass('message-text')
|
|
||||||
.addClass(entry.user ? '' : 'message-text-server')
|
|
||||||
.html(chat.normalizeText(entry.text))
|
|
||||||
.appendTo(row);
|
|
||||||
chat.lastMessageId = Math.max(chat.lastMessageId, entry.id) + 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Keep only chat.maxVisibleMessages, delete older ones.
|
|
||||||
while ($('#mml').children().length > chat.maxVisibleMessages) {
|
|
||||||
$('#mml div:first-child').remove();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
chat.getMessages = function(enter_loop) {
|
|
||||||
$.ajax({
|
|
||||||
dataType: 'jsonp',
|
|
||||||
url: chat.backendUrl + '/ajax/get_messages',
|
|
||||||
data: {last_id: chat.lastMessageId},
|
|
||||||
success: chat.refresh,
|
|
||||||
error: function() {
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (enter_loop) {
|
|
||||||
window.setTimeout('chat.getMessages(true)', chat.getMessagesIntervalMs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
chat.handleMenuItemClick = function(ev) {
|
|
||||||
$('.menu-item').removeClass('menu-item-selected'); // Deselect menu buttons
|
|
||||||
$(this).addClass('menu-item-selected'); // Select clicked button
|
|
||||||
$('.main').addClass('hidden'); // Hide all main windows
|
|
||||||
$('#' + $(this).attr('name')).removeClass('hidden'); // Show main window
|
|
||||||
};
|
|
||||||
|
|
||||||
chat.showError = function(message) {
|
|
||||||
$('#error').html(message).fadeIn('fast');
|
|
||||||
window.clearTimeout(chat.errorMessageFadeOutTimer);
|
|
||||||
chat.errorMessageFadeOutTimer = window.setTimeout(function() {
|
|
||||||
$('#error').fadeOut('slow');
|
|
||||||
}, chat.errorMessageFadeOutTimeoutMs);
|
|
||||||
};
|
|
||||||
|
|
||||||
chat.handleMessageInput = function(ev) {
|
|
||||||
var input = ev.target;
|
|
||||||
if (ev.keyCode != 13 || !input.value)
|
|
||||||
return;
|
|
||||||
//input.disabled = true;
|
|
||||||
$.ajax({
|
|
||||||
dataType: 'jsonp',
|
|
||||||
url: chat.backendUrl + '/ajax/send_message',
|
|
||||||
data: {text: input.value},
|
|
||||||
success: function(ev) {
|
|
||||||
input.value = '';
|
|
||||||
input.disabled = false;
|
|
||||||
chat.getMessages(false);
|
|
||||||
},
|
|
||||||
error: function(ev) {
|
|
||||||
chat.showError('Error sending message');
|
|
||||||
input.disabled = false;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
$('.menu-item').click(chat.handleMenuItemClick);
|
|
||||||
$('.message-input').keypress(chat.handleMessageInput);
|
|
||||||
chat.getMessages(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// vim:ts=2:sw=2:et
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
/*
|
|
||||||
* vim:ts=2:sw=2:et:ai
|
|
||||||
*/
|
|
||||||
|
|
||||||
body {
|
|
||||||
font: 13px Arial; margin: 0.5em 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logo {
|
|
||||||
background: url('logo.png') no-repeat ;
|
|
||||||
width: 160px;
|
|
||||||
height: 40px;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
#motd {
|
|
||||||
margin-left: 170px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.infobox {
|
|
||||||
background: #eed;
|
|
||||||
padding: 1px 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-message {
|
|
||||||
color: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
#middle {
|
|
||||||
margin: 0.5em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#error {
|
|
||||||
background: #c44;
|
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content, .menu-item-selected, .chat-title, .chat-content {
|
|
||||||
background: #c3d9ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
overflow: hidden;
|
|
||||||
min-height: 7em;
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-title {
|
|
||||||
padding: 1px 1ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-content {
|
|
||||||
padding: 1ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-window {
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-row {
|
|
||||||
margin: 2px;
|
|
||||||
border-bottom: 1px solid #bbb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-timestamp {
|
|
||||||
color: #484;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-user {
|
|
||||||
margin-left: 0.5em;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-text {
|
|
||||||
margin-left: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-user-server {
|
|
||||||
color: purple;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-text-server {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
padding: 0.5em;
|
|
||||||
background: #f0fcff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu {
|
|
||||||
margin-top: 1em;
|
|
||||||
min-width: 7em;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
color: #ccc;
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-item {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0.1em 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-item-selected {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-list {
|
|
||||||
min-height: 1em;
|
|
||||||
background: white;
|
|
||||||
margin: 0.5em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rounded {
|
|
||||||
border-radius: 6px;
|
|
||||||
-moz-border-radius: 6px;
|
|
||||||
-webkit-border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left-rounded {
|
|
||||||
border-radius: 6px 0 0 6px;
|
|
||||||
-moz-border-radius: 6px 0 0 6px;
|
|
||||||
-webkit-border-radius: 6px 0 0 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-rounded {
|
|
||||||
border-radius: 0 0 6px 6px;
|
|
||||||
-moz-border-radius: 0 0 6px 6px;
|
|
||||||
-webkit-border-radius: 0 0 6px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-rounded {
|
|
||||||
border-radius: 6px 6px 0 0;
|
|
||||||
-moz-border-radius: 6px 6px 0 0;
|
|
||||||
-webkit-border-radius: 6px 6px 0 0;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
PROG = http_client
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. -DMG_ENABLE_SSL -lssl -lcrypto -pthread $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*
|
||||||
|
* This program fetches HTTP URLs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int s_exit_flag = 0;
|
||||||
|
static int s_show_headers = 0;
|
||||||
|
static const char *s_show_headers_opt = "--show-headers";
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_CONNECT:
|
||||||
|
if (* (int *) ev_data != 0) {
|
||||||
|
fprintf(stderr, "connect() failed: %s\n", strerror(* (int *) ev_data));
|
||||||
|
s_exit_flag = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_EV_HTTP_REPLY:
|
||||||
|
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
if (s_show_headers) {
|
||||||
|
fwrite(hm->message.p, 1, hm->message.len, stdout);
|
||||||
|
} else {
|
||||||
|
fwrite(hm->body.p, 1, hm->body.len, stdout);
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
s_exit_flag = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
|
||||||
|
/* Process command line arguments */
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], s_show_headers_opt) == 0) {
|
||||||
|
s_show_headers = 1;
|
||||||
|
} else if (strcmp(argv[i], "--hexdump") == 0 && i + 1 < argc) {
|
||||||
|
mgr.hexdump_file = argv[++i];
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + 1 != argc) {
|
||||||
|
fprintf(stderr, "Usage: %s [%s] [--hexdump <file>] <URL>\n",
|
||||||
|
argv[0], s_show_headers_opt);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
mg_connect_http(&mgr, ev_handler, argv[i], NULL, NULL);
|
||||||
|
|
||||||
|
while (s_exit_flag == 0) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
PROG = json_rpc_server
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*
|
||||||
|
* To test this server, do
|
||||||
|
* $ curl -d '{"id":1,method:"sum",params:[22,33]}' 127.0.0.1:8000
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static const char *s_http_port = "8000";
|
||||||
|
|
||||||
|
static int rpc_sum(char *buf, int len, struct mg_rpc_request *req) {
|
||||||
|
double sum = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (req->params[0].type != JSON_TYPE_ARRAY) {
|
||||||
|
return mg_rpc_create_std_error(buf, len, req,
|
||||||
|
JSON_RPC_INVALID_PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < req->params[0].num_desc; i++) {
|
||||||
|
if (req->params[i + 1].type != JSON_TYPE_NUMBER) {
|
||||||
|
return mg_rpc_create_std_error(buf, len, req,
|
||||||
|
JSON_RPC_INVALID_PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
sum += strtod(req->params[i + 1].ptr, NULL);
|
||||||
|
}
|
||||||
|
return mg_rpc_create_reply(buf, len, req, "f", sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
static const char *methods[] = { "sum", NULL };
|
||||||
|
static mg_rpc_handler_t handlers[] = { rpc_sum, NULL };
|
||||||
|
char buf[100];
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_HTTP_REQUEST:
|
||||||
|
mg_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf),
|
||||||
|
methods, handlers);
|
||||||
|
mg_printf(nc, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
|
||||||
|
"Content-Type: application/json\r\n\r\n%s",
|
||||||
|
(int) strlen(buf), buf);
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
|
||||||
|
printf("Starting JSON-RPC server on port %s\n", s_http_port);
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
FROM cesanta/mongoose
|
||||||
|
|
||||||
|
COPY load_balancer.c /mongoose/
|
||||||
|
WORKDIR /mongoose
|
||||||
|
RUN mkdir /mongoose/certs; \
|
||||||
|
sed -i 's:#include "../../mongoose.h":#include "mongoose.h":' load_balancer.c; \
|
||||||
|
cc load_balancer.c mongoose.c -o load_balancer -W -Wall -pthread -DMG_ENABLE_SSL -lssl -lcrypto
|
||||||
|
EXPOSE 8000
|
||||||
|
VOLUME ["/mongoose/certs"]
|
||||||
|
ENTRYPOINT ["/mongoose/load_balancer"]
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# To build with SSL under windows, do:
|
||||||
|
# wine make load_balancer.exe SSL=openssl # OpenSSL build
|
||||||
|
# wine make load_balancer.exe SSL=krypton # Krypton build
|
||||||
|
|
||||||
|
PROG = load_balancer
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -pthread $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
ifeq ($(SSL), openssl)
|
||||||
|
OPENSSL_PATH = ./openssl-0.9.8
|
||||||
|
CFLAGS_EXTRA += -DMG_ENABLE_SSL -I$(OPENSSL_PATH)/include
|
||||||
|
CFLAGS_EXTRA += /link /libpath:$(OPENSSL_PATH)/lib ssleay32.lib libeay32.lib
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(SSL), krypton)
|
||||||
|
KRYPTON_PATH = ../../../krypton
|
||||||
|
CFLAGS_EXTRA += -DMG_ENABLE_SSL $(KRYPTON_PATH)/krypton.c -I$(KRYPTON_PATH)
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I.. /MD /Fe$@ /DMG_ENABLE_THREADS advapi32.lib $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
test: $(PROG)
|
||||||
|
$(MAKE) -C ../api_server
|
||||||
|
sh unit_test.sh $$(pwd)/$(PROG)
|
||||||
|
|
||||||
|
docker-build:
|
||||||
|
docker build -t cesanta/load_balancer .
|
||||||
|
|
||||||
|
docker-push:
|
||||||
|
docker push cesanta/load_balancer
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Mongoose-based HTTP load balancer
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Load balancer is configured with command-line flags.
|
||||||
|
|
||||||
|
### Global flags
|
||||||
|
|
||||||
|
* `-p port` – TCP port to listen on. Default: 8000.
|
||||||
|
* `-l log_file` – path to the log file. Default: none.
|
||||||
|
* `-s ssl_cert` – path to SSL certificate. Default: none.
|
||||||
|
|
||||||
|
### Backend configuration
|
||||||
|
|
||||||
|
Main flag is `-b uri_prefix host_port` – it adds a new backend for a given
|
||||||
|
URI prefix. Example: `-b /stuff/ 127.0.0.1:8080` will route all requests that
|
||||||
|
start with '/stuff/' to a backend at port 8080 on localhost. There is a special
|
||||||
|
syntax for `uri_prefix` that allows you to change the URIs that get passed to
|
||||||
|
backends:
|
||||||
|
|
||||||
|
* `-b /stuff/=/ 127.0.0.1:8080` – for '/stuff/thing' backend will see '/thing'.
|
||||||
|
* `-b /stuff/=/other/ 127.0.0.1:8080` – '/stuff/thing' => '/other/thing'.
|
||||||
|
|
||||||
|
Also there are few per-backend flags that can be placed before `-b` and apply
|
||||||
|
only to the next backend:
|
||||||
|
|
||||||
|
* `-r` – instead of proxying requests load balancer will reply with 302
|
||||||
|
redirect.
|
||||||
|
* `-v vhost` – match not only URI prefix but 'Host:' header as well.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```
|
||||||
|
load_balancer -s path/to/cert.pem \
|
||||||
|
-v example.com -b /site/=/ 127.0.0.1:8080 \
|
||||||
|
-b /static/ 127.0.0.1:8081 \
|
||||||
|
-b /static/ 127.0.0.1:8082
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example requests to 'example.com/site/' will be forwarded to the
|
||||||
|
backend on port 8080 with '/site' prefix stripped off and requests to
|
||||||
|
'/static/' on any virtual host will be balanced in round-robin fashion between
|
||||||
|
backends on ports 8081 and 8082.
|
||||||
|
|
||||||
@@ -0,0 +1,637 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../../mongoose.h"
|
||||||
|
#include <sys/queue.h>
|
||||||
|
|
||||||
|
#define MAX_IDLE_CONNS 5
|
||||||
|
#define CONN_IDLE_TIMEOUT 30
|
||||||
|
|
||||||
|
struct http_backend;
|
||||||
|
|
||||||
|
struct be_conn {
|
||||||
|
struct http_backend *be;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
time_t idle_deadline;
|
||||||
|
|
||||||
|
STAILQ_ENTRY(be_conn) conns;
|
||||||
|
};
|
||||||
|
|
||||||
|
STAILQ_HEAD(be_conn_list_head, be_conn);
|
||||||
|
struct http_backend {
|
||||||
|
const char *vhost; /* NULL if any host */
|
||||||
|
const char *uri_prefix; /* URI prefix, e.g. "/api/v1/", "/static/" */
|
||||||
|
const char *uri_prefix_replacement; /* if not NULL, will replace uri_prefix in
|
||||||
|
requests to backends */
|
||||||
|
const char *host_port; /* Backend address */
|
||||||
|
int redirect; /* if true redirect instead of proxy */
|
||||||
|
int usage_counter; /* Number of times this backend was chosen */
|
||||||
|
|
||||||
|
struct be_conn_list_head conns;
|
||||||
|
int num_conns;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct peer {
|
||||||
|
struct mg_connection *nc;
|
||||||
|
int64_t body_len; /* Size of the HTTP body to forward */
|
||||||
|
int64_t body_sent; /* Number of bytes already forwarded */
|
||||||
|
struct {
|
||||||
|
/* Headers have been sent, no more headers. */
|
||||||
|
unsigned int headers_sent : 1;
|
||||||
|
unsigned int keep_alive : 1;
|
||||||
|
} flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct conn_data {
|
||||||
|
struct be_conn *be_conn; /* Chosen backend */
|
||||||
|
struct peer client; /* Client peer */
|
||||||
|
struct peer backend; /* Backend peer */
|
||||||
|
time_t last_activity;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *s_error_500 = "HTTP/1.1 500 Failed\r\n";
|
||||||
|
static const char *s_content_len_0 = "Content-Length: 0\r\n";
|
||||||
|
static const char *s_connection_close = "Connection: close\r\n";
|
||||||
|
static const char *s_http_port = "8000";
|
||||||
|
static struct http_backend s_vhost_backends[100], s_default_backends[100];
|
||||||
|
static int s_num_vhost_backends = 0, s_num_default_backends = 0;
|
||||||
|
static int s_sig_num = 0;
|
||||||
|
static int s_backend_keepalive = 0;
|
||||||
|
static FILE *s_log_file = NULL;
|
||||||
|
#ifdef MG_ENABLE_SSL
|
||||||
|
const char *s_ssl_cert = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data);
|
||||||
|
static void write_log(const char *fmt, ...);
|
||||||
|
|
||||||
|
static void signal_handler(int sig_num) {
|
||||||
|
signal(sig_num, signal_handler);
|
||||||
|
s_sig_num = sig_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_http_err(struct mg_connection *nc, const char *err_line) {
|
||||||
|
mg_printf(nc, "%s%s%s\r\n", err_line, s_content_len_0, s_connection_close);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void respond_with_error(struct conn_data *conn, const char *err_line) {
|
||||||
|
struct mg_connection *nc = conn->client.nc;
|
||||||
|
int headers_sent = conn->client.flags.headers_sent;
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("conn=%p nc=%p respond_with_error %d\n", conn, nc, headers_sent);
|
||||||
|
#endif
|
||||||
|
if (nc == NULL) return;
|
||||||
|
if (!headers_sent) {
|
||||||
|
send_http_err(nc, err_line);
|
||||||
|
conn->client.flags.headers_sent = 1;
|
||||||
|
}
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int has_prefix(const struct mg_str *uri, const char *prefix) {
|
||||||
|
size_t prefix_len = strlen(prefix);
|
||||||
|
return uri->len >= prefix_len && memcmp(uri->p, prefix, prefix_len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int matches_vhost(const struct mg_str *host, const char *vhost) {
|
||||||
|
size_t vhost_len;
|
||||||
|
if (vhost == NULL) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
vhost_len = strlen(vhost);
|
||||||
|
return host->len == vhost_len && memcmp(host->p, vhost, vhost_len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_log(const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
if (s_log_file != NULL) {
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(s_log_file, fmt, ap);
|
||||||
|
fflush(s_log_file);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct http_backend *choose_backend_from_list(
|
||||||
|
struct http_message *hm, struct http_backend *backends, int num_backends) {
|
||||||
|
int i;
|
||||||
|
struct mg_str vhost = {"", 0};
|
||||||
|
const struct mg_str *host = mg_get_http_header(hm, "host");
|
||||||
|
if (host != NULL) vhost = *host;
|
||||||
|
|
||||||
|
const char *vhost_end = vhost.p;
|
||||||
|
|
||||||
|
while (vhost_end < vhost.p + vhost.len && *vhost_end != ':') {
|
||||||
|
vhost_end++;
|
||||||
|
}
|
||||||
|
vhost.len = vhost_end - vhost.p;
|
||||||
|
|
||||||
|
struct http_backend *chosen = NULL;
|
||||||
|
for (i = 0; i < num_backends; i++) {
|
||||||
|
struct http_backend *be = &backends[i];
|
||||||
|
if (has_prefix(&hm->uri, be->uri_prefix) &&
|
||||||
|
matches_vhost(&vhost, be->vhost) &&
|
||||||
|
(chosen == NULL ||
|
||||||
|
/* Prefer most specific URI prefixes */
|
||||||
|
strlen(be->uri_prefix) > strlen(chosen->uri_prefix) ||
|
||||||
|
/* Among prefixes of the same length chose the least used. */
|
||||||
|
(strlen(be->uri_prefix) == strlen(chosen->uri_prefix) &&
|
||||||
|
be->usage_counter < chosen->usage_counter))) {
|
||||||
|
chosen = be;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chosen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct http_backend *choose_backend(struct http_message *hm) {
|
||||||
|
struct http_backend *chosen =
|
||||||
|
choose_backend_from_list(hm, s_vhost_backends, s_num_vhost_backends);
|
||||||
|
|
||||||
|
/* Nothing was chosen for this vhost, look for vhost == NULL backends. */
|
||||||
|
if (chosen == NULL) {
|
||||||
|
chosen = choose_backend_from_list(hm, s_default_backends,
|
||||||
|
s_num_default_backends);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chosen != NULL) chosen->usage_counter++;
|
||||||
|
|
||||||
|
return chosen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void forward_body(struct peer *src, struct peer *dst) {
|
||||||
|
struct mbuf *src_io = &src->nc->recv_mbuf;
|
||||||
|
if (src->body_sent < src->body_len) {
|
||||||
|
size_t to_send = src->body_len - src->body_sent;
|
||||||
|
if (src_io->len < to_send) {
|
||||||
|
to_send = src_io->len;
|
||||||
|
}
|
||||||
|
mg_send(dst->nc, src_io->buf, to_send);
|
||||||
|
src->body_sent += to_send;
|
||||||
|
mbuf_remove(src_io, to_send);
|
||||||
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("forward_body %p (ka=%d) -> %p sent %d of %d\n", src->nc,
|
||||||
|
src->flags.keep_alive, dst->nc, src->body_sent, src->body_len);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void forward(struct conn_data *conn, struct http_message *hm,
|
||||||
|
struct peer *src_peer, struct peer *dst_peer) {
|
||||||
|
struct mg_connection *src = src_peer->nc;
|
||||||
|
struct mg_connection *dst = dst_peer->nc;
|
||||||
|
struct mbuf *io = &src->recv_mbuf;
|
||||||
|
int i;
|
||||||
|
int is_request = (src_peer == &conn->client);
|
||||||
|
src_peer->body_len = hm->body.len;
|
||||||
|
struct http_backend *be = conn->be_conn->be;
|
||||||
|
|
||||||
|
if (is_request) {
|
||||||
|
/* Write rewritten request line. */
|
||||||
|
size_t trim_len = strlen(be->uri_prefix);
|
||||||
|
mg_printf(dst, "%.*s%s%.*s\r\n", (int) (hm->uri.p - io->buf), io->buf,
|
||||||
|
be->uri_prefix_replacement,
|
||||||
|
(int) (hm->proto.p + hm->proto.len - (hm->uri.p + trim_len)),
|
||||||
|
hm->uri.p + trim_len);
|
||||||
|
} else {
|
||||||
|
/* Reply line goes without modification */
|
||||||
|
mg_printf(dst, "%.*s %d %.*s\r\n", (int) hm->proto.len, hm->proto.p,
|
||||||
|
(int) hm->resp_code, (int) hm->resp_status_msg.len,
|
||||||
|
hm->resp_status_msg.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Headers. */
|
||||||
|
for (i = 0; i < MG_MAX_HTTP_HEADERS && hm->header_names[i].len > 0; i++) {
|
||||||
|
struct mg_str hn = hm->header_names[i];
|
||||||
|
struct mg_str hv = hm->header_values[i];
|
||||||
|
#ifdef MG_ENABLE_SSL
|
||||||
|
/*
|
||||||
|
* If we terminate SSL and backend redirects to local HTTP port,
|
||||||
|
* strip protocol to let client use HTTPS.
|
||||||
|
* TODO(lsm): web page content may also contain local HTTP references,
|
||||||
|
* they need to be rewritten too.
|
||||||
|
*/
|
||||||
|
if (mg_vcasecmp(&hn, "Location") == 0 && s_ssl_cert != NULL) {
|
||||||
|
size_t hlen = strlen(be->host_port);
|
||||||
|
const char *hp = be->host_port, *p = memchr(hp, ':', hlen);
|
||||||
|
|
||||||
|
if (p == NULL) {
|
||||||
|
p = hp + hlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mg_ncasecmp(hv.p, "http://", 7) == 0 &&
|
||||||
|
mg_ncasecmp(hv.p + 7, hp, (p - hp)) == 0) {
|
||||||
|
mg_printf(dst, "Location: %.*s\r\n", (int) (hv.len - (7 + (p - hp))),
|
||||||
|
hv.p + 7 + (p - hp));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* We always rewrite the connection header depending on the settings. */
|
||||||
|
if (mg_vcasecmp(&hn, "Connection") == 0) continue;
|
||||||
|
|
||||||
|
mg_printf(dst, "%.*s: %.*s\r\n", (int) hn.len, hn.p, (int) hv.len, hv.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit the connection header. */
|
||||||
|
const char *connection_mode = "close";
|
||||||
|
if (dst_peer == &conn->backend) {
|
||||||
|
if (s_backend_keepalive) connection_mode = "keep-alive";
|
||||||
|
} else {
|
||||||
|
if (conn->client.flags.keep_alive) connection_mode = "keep-alive";
|
||||||
|
}
|
||||||
|
mg_printf(dst, "Connection: %s\r\n", connection_mode);
|
||||||
|
|
||||||
|
mg_printf(dst, "%s", "\r\n");
|
||||||
|
|
||||||
|
mbuf_remove(io, hm->body.p - hm->message.p); /* We've forwarded headers */
|
||||||
|
dst_peer->flags.headers_sent = 1;
|
||||||
|
|
||||||
|
forward_body(src_peer, dst_peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct be_conn *get_conn(struct http_backend *be) {
|
||||||
|
if (STAILQ_EMPTY(&be->conns)) return NULL;
|
||||||
|
struct be_conn *result = STAILQ_FIRST(&be->conns);
|
||||||
|
STAILQ_REMOVE_HEAD(&be->conns, conns);
|
||||||
|
be->num_conns--;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* choose_backend parses incoming HTTP request and routes it to the appropriate
|
||||||
|
* backend. It assumes that clients don't do HTTP pipelining, handling only
|
||||||
|
* one request request for each connection. To give a hint to backend about
|
||||||
|
* this it inserts "Connection: close" header into each forwarded request.
|
||||||
|
*/
|
||||||
|
static int connect_backend(struct conn_data *conn, struct http_message *hm) {
|
||||||
|
struct mg_connection *nc = conn->client.nc;
|
||||||
|
struct http_backend *be = choose_backend(hm);
|
||||||
|
|
||||||
|
write_log("%.*s %.*s backend=%s\n", (int) hm->method.len, hm->method.p,
|
||||||
|
(int) hm->uri.len, hm->uri.p, be->host_port);
|
||||||
|
|
||||||
|
if (be == NULL) return 0;
|
||||||
|
if (be->redirect != 0) {
|
||||||
|
mg_printf(nc, "HTTP/1.1 302 Found\r\nLocation: %s\r\n\r\n", be->host_port);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
struct be_conn *bec = get_conn(be);
|
||||||
|
if (bec != NULL) {
|
||||||
|
bec->nc->handler = ev_handler;
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("conn=%p to %p (%s) reusing bec=%p\n", conn, be, be->host_port,
|
||||||
|
bec);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
bec = malloc(sizeof(*conn->be_conn));
|
||||||
|
memset(bec, 0, sizeof(*bec));
|
||||||
|
bec->nc = mg_connect(nc->mgr, be->host_port, ev_handler);
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("conn=%p new conn to %p (%s) bec=%p\n", conn, be, be->host_port,
|
||||||
|
bec);
|
||||||
|
#endif
|
||||||
|
if (bec->nc == NULL) {
|
||||||
|
free(bec);
|
||||||
|
write_log("Connection to [%s] failed\n", be->host_port);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bec->be = be;
|
||||||
|
conn->be_conn = bec;
|
||||||
|
conn->backend.nc = bec->nc;
|
||||||
|
conn->backend.nc->user_data = conn;
|
||||||
|
mg_set_protocol_http_websocket(conn->backend.nc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_keep_alive(struct http_message *hm) {
|
||||||
|
const struct mg_str *connection_header = mg_get_http_header(hm, "Connection");
|
||||||
|
if (connection_header == NULL) {
|
||||||
|
/* HTTP/1.1 connections are keep-alive by default. */
|
||||||
|
if (mg_vcasecmp(&hm->proto, "HTTP/1.1") != 0) return 0;
|
||||||
|
} else if (mg_vcasecmp(connection_header, "keep-alive") != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// We must also have Content-Length.
|
||||||
|
return mg_get_http_header(hm, "Content-Length") != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void idle_backend_handler(struct mg_connection *nc, int ev,
|
||||||
|
void *ev_data) {
|
||||||
|
(void) ev_data; /* Unused. */
|
||||||
|
struct be_conn *bec = nc->user_data;
|
||||||
|
const time_t now = time(NULL);
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("%d idle bec=%p nc=%p ev=%d deadline=%d\n", now, bec, nc, ev,
|
||||||
|
bec->idle_deadline);
|
||||||
|
#endif
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_POLL: {
|
||||||
|
if (bec->idle_deadline > 0 && now > bec->idle_deadline) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("bec=%p nc=%p closing due to idleness\n", bec, bec->nc);
|
||||||
|
#endif
|
||||||
|
bec->nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MG_EV_CLOSE: {
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("bec=%p closed\n", bec);
|
||||||
|
#endif
|
||||||
|
if (bec->idle_deadline > 0) {
|
||||||
|
STAILQ_REMOVE(&bec->be->conns, bec, be_conn, conns);
|
||||||
|
}
|
||||||
|
free(bec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void release_backend(struct conn_data *conn) {
|
||||||
|
/* Disassociate the backend, put back on the pool. */
|
||||||
|
struct be_conn *bec = conn->be_conn;
|
||||||
|
conn->be_conn = NULL;
|
||||||
|
if (bec->nc == NULL) {
|
||||||
|
free(bec);
|
||||||
|
memset(&conn->backend, 0, sizeof(conn->backend));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct http_backend *be = bec->be;
|
||||||
|
bec->nc->user_data = bec;
|
||||||
|
bec->nc->handler = idle_backend_handler;
|
||||||
|
if (conn->backend.flags.keep_alive) {
|
||||||
|
bec->idle_deadline = time(NULL) + CONN_IDLE_TIMEOUT;
|
||||||
|
STAILQ_INSERT_TAIL(&be->conns, bec, conns);
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("bec=%p becoming idle\n", bec);
|
||||||
|
#endif
|
||||||
|
be->num_conns++;
|
||||||
|
while (be->num_conns > MAX_IDLE_CONNS) {
|
||||||
|
bec = STAILQ_FIRST(&be->conns);
|
||||||
|
STAILQ_REMOVE_HEAD(&be->conns, conns);
|
||||||
|
be->num_conns--;
|
||||||
|
bec->idle_deadline = 0;
|
||||||
|
bec->nc->flags = MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("bec=%p evicted\n", bec);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bec->idle_deadline = 0;
|
||||||
|
bec->nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
}
|
||||||
|
memset(&conn->backend, 0, sizeof(conn->backend));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct conn_data *conn = (struct conn_data *) nc->user_data;
|
||||||
|
const time_t now = time(NULL);
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("%d conn=%p nc=%p ev=%d ev_data=%p bec=%p bec_nc=%p\n", now, conn,
|
||||||
|
nc, ev, ev_data, conn != NULL ? conn->be_conn : NULL,
|
||||||
|
conn != NULL && conn->be_conn != NULL ? conn->be_conn->nc : NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (conn == NULL) {
|
||||||
|
if (ev == MG_EV_ACCEPT) {
|
||||||
|
conn = calloc(1, sizeof(*conn));
|
||||||
|
if (conn == NULL) {
|
||||||
|
send_http_err(nc, s_error_500);
|
||||||
|
} else {
|
||||||
|
memset(conn, 0, sizeof(*conn));
|
||||||
|
nc->user_data = conn;
|
||||||
|
conn->client.nc = nc;
|
||||||
|
conn->client.body_len = -1;
|
||||||
|
conn->backend.body_len = -1;
|
||||||
|
conn->last_activity = now;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev != MG_EV_POLL) conn->last_activity = now;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_HTTP_REQUEST: { /* From client */
|
||||||
|
assert(conn != NULL);
|
||||||
|
assert(conn->be_conn == NULL);
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
conn->client.flags.keep_alive = is_keep_alive(hm);
|
||||||
|
|
||||||
|
if (!connect_backend(conn, hm)) {
|
||||||
|
respond_with_error(conn, s_error_500);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->backend.nc == NULL) {
|
||||||
|
/* This is a redirect, we're done. */
|
||||||
|
conn->client.nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
forward(conn, hm, &conn->client, &conn->backend);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MG_EV_CONNECT: { /* To backend */
|
||||||
|
assert(conn != NULL);
|
||||||
|
assert(conn->be_conn != NULL);
|
||||||
|
int status = *(int *) ev_data;
|
||||||
|
if (status != 0) {
|
||||||
|
write_log("Error connecting to %s: %d (%s)\n",
|
||||||
|
conn->be_conn->be->host_port, status, strerror(status));
|
||||||
|
/* TODO(lsm): mark backend as defunct, try it later on */
|
||||||
|
respond_with_error(conn, s_error_500);
|
||||||
|
conn->be_conn->nc = NULL;
|
||||||
|
release_backend(conn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MG_EV_HTTP_REPLY: { /* From backend */
|
||||||
|
assert(conn != NULL);
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
conn->backend.flags.keep_alive = s_backend_keepalive && is_keep_alive(hm);
|
||||||
|
forward(conn, hm, &conn->backend, &conn->client);
|
||||||
|
release_backend(conn);
|
||||||
|
if (!conn->client.flags.keep_alive) {
|
||||||
|
conn->client.nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
} else {
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("conn=%p remains open\n", conn);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MG_EV_POLL: {
|
||||||
|
assert(conn != NULL);
|
||||||
|
if (now - conn->last_activity > CONN_IDLE_TIMEOUT &&
|
||||||
|
conn->backend.nc == NULL /* not waiting for backend */) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("conn=%p has been idle for too long\n", conn);
|
||||||
|
conn->client.nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MG_EV_CLOSE: {
|
||||||
|
assert(conn != NULL);
|
||||||
|
if (nc == conn->client.nc) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("conn=%p nc=%p client closed, body_sent=%d\n", conn, nc,
|
||||||
|
conn->backend.body_sent);
|
||||||
|
#endif
|
||||||
|
conn->client.nc = NULL;
|
||||||
|
if (conn->backend.nc != NULL) {
|
||||||
|
conn->backend.nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
}
|
||||||
|
} else if (nc == conn->backend.nc) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
write_log("conn=%p nc=%p backend closed\n", conn, nc);
|
||||||
|
#endif
|
||||||
|
conn->backend.nc = NULL;
|
||||||
|
if (conn->client.nc != NULL &&
|
||||||
|
(conn->backend.body_len < 0 ||
|
||||||
|
conn->backend.body_sent < conn->backend.body_len)) {
|
||||||
|
write_log("Backend %s disconnected.\n", conn->be_conn->be->host_port);
|
||||||
|
respond_with_error(conn, s_error_500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (conn->client.nc == NULL && conn->backend.nc == NULL) {
|
||||||
|
free(conn);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_usage_and_exit(const char *prog_name) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: %s [-D debug_dump_file] [-p http_port] [-l log] [-k]"
|
||||||
|
#if MG_ENABLE_SSL
|
||||||
|
"[-s ssl_cert] "
|
||||||
|
#endif
|
||||||
|
"<[-r] [-v vhost] -b uri_prefix[=replacement] host_port> ... \n",
|
||||||
|
prog_name);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
int i, redirect = 0;
|
||||||
|
const char *vhost = NULL;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
|
||||||
|
/* Parse command line arguments */
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-D") == 0) {
|
||||||
|
mgr.hexdump_file = argv[i + 1];
|
||||||
|
i++;
|
||||||
|
} else if (strcmp(argv[i], "-k") == 0) {
|
||||||
|
s_backend_keepalive = 1;
|
||||||
|
} else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) {
|
||||||
|
if (strcmp(argv[i + 1], "-") == 0) {
|
||||||
|
s_log_file = stdout;
|
||||||
|
} else {
|
||||||
|
s_log_file = fopen(argv[i + 1], "a");
|
||||||
|
if (s_log_file == NULL) {
|
||||||
|
perror("fopen");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
} else if (strcmp(argv[i], "-p") == 0) {
|
||||||
|
s_http_port = argv[i + 1];
|
||||||
|
i++;
|
||||||
|
} else if (strcmp(argv[i], "-r") == 0 && i + 1 < argc) {
|
||||||
|
redirect = 1;
|
||||||
|
} else if (strcmp(argv[i], "-v") == 0 && i + 1 < argc) {
|
||||||
|
if (strcmp(argv[i + 1], "") == 0) {
|
||||||
|
vhost = NULL;
|
||||||
|
} else {
|
||||||
|
vhost = argv[i + 1];
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
} else if (strcmp(argv[i], "-b") == 0 && i + 2 < argc) {
|
||||||
|
struct http_backend *be =
|
||||||
|
vhost != NULL ? &s_vhost_backends[s_num_vhost_backends++]
|
||||||
|
: &s_default_backends[s_num_default_backends++];
|
||||||
|
STAILQ_INIT(&be->conns);
|
||||||
|
char *r = NULL;
|
||||||
|
be->vhost = vhost;
|
||||||
|
be->uri_prefix = argv[i + 1];
|
||||||
|
be->host_port = argv[i + 2];
|
||||||
|
be->redirect = redirect;
|
||||||
|
be->uri_prefix_replacement = be->uri_prefix;
|
||||||
|
if ((r = strchr(be->uri_prefix, '=')) != NULL) {
|
||||||
|
*r = '\0';
|
||||||
|
be->uri_prefix_replacement = r + 1;
|
||||||
|
}
|
||||||
|
printf(
|
||||||
|
"Adding backend for %s%s : %s "
|
||||||
|
"[redirect=%d,prefix_replacement=%s]\n",
|
||||||
|
be->vhost == NULL ? "" : be->vhost, be->uri_prefix, be->host_port,
|
||||||
|
be->redirect, be->uri_prefix_replacement);
|
||||||
|
vhost = NULL;
|
||||||
|
redirect = 0;
|
||||||
|
i += 2;
|
||||||
|
#ifdef MG_ENABLE_SSL
|
||||||
|
} else if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) {
|
||||||
|
s_ssl_cert = argv[++i];
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
print_usage_and_exit(argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open listening socket */
|
||||||
|
if ((nc = mg_bind(&mgr, s_http_port, ev_handler)) == NULL) {
|
||||||
|
fprintf(stderr, "mg_bind(%s) failed\n", s_http_port);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if MG_ENABLE_SSL
|
||||||
|
if (s_ssl_cert != NULL) {
|
||||||
|
const char *err_str = mg_set_ssl(nc, s_ssl_cert, NULL);
|
||||||
|
if (err_str != NULL) {
|
||||||
|
fprintf(stderr, "Error loading SSL cert: %s\n", err_str);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
|
||||||
|
if (s_num_vhost_backends + s_num_default_backends == 0) {
|
||||||
|
print_usage_and_exit(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
|
||||||
|
/* Run event loop until signal is received */
|
||||||
|
printf("Starting LB on port %s\n", s_http_port);
|
||||||
|
while (s_sig_num == 0) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
printf("Exiting on signal %d\n", s_sig_num);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PROG=$1
|
||||||
|
PORT=8002
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
kill -9 $PID >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
#set -x
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
$PROG -p $PORT -b /api/ 127.0.0.1:8000 &
|
||||||
|
PID=$!
|
||||||
|
|
||||||
|
# Perform api_server unit test through the load balancer by passing
|
||||||
|
# load balancer port to the unit test script
|
||||||
|
(cd ../api_server && make && sh unit_test.sh ./api_server $PORT)
|
||||||
|
|
||||||
|
exit $?
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
PROG = mqtt_broker
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. -pthread -DMG_ENABLE_SSL -DMG_ENABLE_MQTT_BROKER -lssl -lcrypto $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /DMG_ENABLE_SSL /DMG_ENABLE_MQTT_BROKER /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
* This software 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/>.
|
||||||
|
*
|
||||||
|
* You are free to use this software 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.
|
||||||
|
*
|
||||||
|
* Alternatively, you can license this software under a commercial
|
||||||
|
* license, as set out in <https://www.cesanta.com/license>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../../mongoose.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
const char *address = "0.0.0.0:1883";
|
||||||
|
struct mg_connection *nc;
|
||||||
|
struct mg_mqtt_broker brk;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
mg_mqtt_broker_init(&brk, NULL);
|
||||||
|
|
||||||
|
if ((nc = mg_bind(&mgr, address, mg_mqtt_broker)) == NULL) {
|
||||||
|
fprintf(stderr, "mg_bind(%s) failed\n", address);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
nc->user_data = &brk;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: Add a HTTP status page that shows current sessions
|
||||||
|
* and subscriptions
|
||||||
|
*/
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
PROG = mqtt_client
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. -pthread -DMG_ENABLE_SSL -lssl -lcrypto $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /DMG_ENABLE_SSL /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
* This software 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/>.
|
||||||
|
*
|
||||||
|
* You are free to use this software 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.
|
||||||
|
*
|
||||||
|
* Alternatively, you can license this software under a commercial
|
||||||
|
* license, as set out in <https://www.cesanta.com/license>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
struct mg_mqtt_topic_expression topic_expressions[] = {
|
||||||
|
{"/stuff", 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *p) {
|
||||||
|
struct mg_mqtt_message *msg = (struct mg_mqtt_message *)p;
|
||||||
|
(void) nc;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (ev != MG_EV_POLL)
|
||||||
|
printf("USER HANDLER GOT %d\n", ev);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_CONNECT:
|
||||||
|
mg_set_protocol_mqtt(nc);
|
||||||
|
mg_send_mqtt_handshake(nc, "dummy");
|
||||||
|
break;
|
||||||
|
case MG_EV_MQTT_CONNACK:
|
||||||
|
if (msg->connack_ret_code != MG_EV_MQTT_CONNACK_ACCEPTED) {
|
||||||
|
printf("Got mqtt connection error: %d\n", msg->connack_ret_code);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
printf("Subscribing to '/stuff'\n");
|
||||||
|
mg_mqtt_subscribe(nc, topic_expressions, sizeof(topic_expressions)/sizeof(*topic_expressions), 42);
|
||||||
|
break;
|
||||||
|
case MG_EV_MQTT_PUBACK:
|
||||||
|
printf("Message publishing acknowledged (msg_id: %d)\n", msg->message_id);
|
||||||
|
break;
|
||||||
|
case MG_EV_MQTT_SUBACK:
|
||||||
|
printf("Subscription acknowledged, forwarding to '/test'\n");
|
||||||
|
break;
|
||||||
|
case MG_EV_MQTT_PUBLISH:
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
char hex[1024] = {0};
|
||||||
|
mg_hexdump(nc->recv_mbuf.buf, msg->payload.len, hex, sizeof(hex));
|
||||||
|
printf("Got incoming message %s:\n%s", msg->topic, hex);
|
||||||
|
#else
|
||||||
|
printf("Got incoming message %s: %.*s\n", msg->topic, (int)msg->payload.len, msg->payload.p);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printf("Forwarding to /test\n");
|
||||||
|
mg_mqtt_publish(nc, "/test", 65, MG_MQTT_QOS(0), msg->payload.p, msg->payload.len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_EV_CLOSE:
|
||||||
|
printf("Connection closed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
const char *address = "localhost:1883";
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
|
||||||
|
if (mg_connect(&mgr, address, ev_handler) == NULL) {
|
||||||
|
fprintf(stderr, "mg_connect(%s) failed\n", address);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
PROG = multithreaded_restful_server
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
APP_FLAGS = -DMG_ENABLE_THREADS $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
ifeq ($(OS), Windows_NT)
|
||||||
|
APP_FLAGS += advapi32.lib
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ -W -Wall -I../.. -pthread $(APP_FLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /MD /Fe$@ $(APP_FLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) 2015 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
// This example shows how to handle long, blocking requests by
|
||||||
|
// handing off computation to different threads. Here, each
|
||||||
|
// request spawns a new thread. In a production scenario, a thread
|
||||||
|
// pools can be used for efficiency, if required.
|
||||||
|
// Long computation is simulated by sleeping for a random interval.
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static const char *s_http_port = "8000";
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *c, int ev, void *p) {
|
||||||
|
if (ev == MG_EV_HTTP_REQUEST) {
|
||||||
|
struct http_message *hm = (struct http_message *) p;
|
||||||
|
char reply[100];
|
||||||
|
|
||||||
|
/* Simulate long calculation */
|
||||||
|
sleep(3);
|
||||||
|
|
||||||
|
/* Send the reply */
|
||||||
|
snprintf(reply, sizeof(reply), "{ \"uri\": \"%.*s\" }\n",
|
||||||
|
(int) hm->uri.len, hm->uri.p);
|
||||||
|
mg_printf(c, "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Content-Type: application/json\r\n"
|
||||||
|
"Content-Length: %d\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"%s",
|
||||||
|
(int) strlen(reply), reply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
|
||||||
|
/* For each new connection, execute ev_handler in a separate thread */
|
||||||
|
mg_enable_multithreading(nc);
|
||||||
|
|
||||||
|
printf("Starting multi-threaded server on port %s\n", s_http_port);
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 3000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
PROG = nc
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. -pthread -DMG_ENABLE_SSL -DMG_ENABLE_THREADS -lssl -lcrypto $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /DMG_ENABLE_SSL /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
// Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// This software 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/>.
|
||||||
|
//
|
||||||
|
// You are free to use this software 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.
|
||||||
|
//
|
||||||
|
// Alternatively, you can license this software under a commercial
|
||||||
|
// license, as set out in <https://www.cesanta.com/license>.
|
||||||
|
//
|
||||||
|
// $Date: 2014-09-28 05:04:41 UTC $
|
||||||
|
|
||||||
|
// This file implements "netcat" utility with SSL and traffic hexdump.
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static sig_atomic_t s_received_signal = 0;
|
||||||
|
|
||||||
|
static void signal_handler(int sig_num) {
|
||||||
|
signal(sig_num, signal_handler);
|
||||||
|
s_received_signal = sig_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_usage_and_exit(const char *prog_name) {
|
||||||
|
fprintf(stderr, "%s\n", "Copyright (c) 2014 CESANTA SOFTWARE");
|
||||||
|
fprintf(stderr, "%s\n", "Usage:");
|
||||||
|
fprintf(stderr, " %s\n [-d debug_file] [-l] [tcp|ssl]://[ip:]port[:cert][:ca_cert]",
|
||||||
|
prog_name);
|
||||||
|
fprintf(stderr, "%s\n", "Examples:");
|
||||||
|
fprintf(stderr, " %s\n -d hexdump.txt ssl://google.com:443", prog_name);
|
||||||
|
fprintf(stderr, " %s\n -l ssl://443:ssl_cert.pem", prog_name);
|
||||||
|
fprintf(stderr, " %s\n -l tcp://8080", prog_name);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_stdin_read(struct mg_connection *nc, int ev, void *p) {
|
||||||
|
int ch = * (int *) p;
|
||||||
|
|
||||||
|
(void) ev;
|
||||||
|
|
||||||
|
if (ch < 0) {
|
||||||
|
// EOF is received from stdin. Schedule the connection to close
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
if (nc->send_mbuf.len <= 0) {
|
||||||
|
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// A character is received from stdin. Send it to the connection.
|
||||||
|
unsigned char c = (unsigned char) ch;
|
||||||
|
mg_send(nc, &c, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *stdio_thread_func(void *param) {
|
||||||
|
struct mg_mgr *mgr = (struct mg_mgr *) param;
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
// Read stdin until EOF character by character, sending them to the mgr
|
||||||
|
while ((ch = getchar()) != EOF) {
|
||||||
|
mg_broadcast(mgr, on_stdin_read, &ch, sizeof(ch));
|
||||||
|
}
|
||||||
|
s_received_signal = 1;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *p) {
|
||||||
|
(void) p;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_ACCEPT:
|
||||||
|
case MG_EV_CONNECT:
|
||||||
|
mg_start_thread(stdio_thread_func, nc->mgr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MG_EV_CLOSE:
|
||||||
|
s_received_signal = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MG_EV_RECV:
|
||||||
|
fwrite(nc->recv_mbuf.buf, 1, nc->recv_mbuf.len, stdout);
|
||||||
|
mbuf_remove(&nc->recv_mbuf, nc->recv_mbuf.len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
int i, is_listening = 0;
|
||||||
|
const char *address = NULL;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
|
||||||
|
// Parse command line options
|
||||||
|
for (i = 1; i < argc && argv[i][0] == '-'; i++) {
|
||||||
|
if (strcmp(argv[i], "-l") == 0) {
|
||||||
|
is_listening = 1;
|
||||||
|
} else if (strcmp(argv[i], "-d") == 0 && i + 1 < argc) {
|
||||||
|
mgr.hexdump_file = argv[++i];
|
||||||
|
} else {
|
||||||
|
show_usage_and_exit(argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + 1 == argc) {
|
||||||
|
address = argv[i];
|
||||||
|
} else {
|
||||||
|
show_usage_and_exit(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
|
if (is_listening) {
|
||||||
|
if (mg_bind(&mgr, address, ev_handler) == NULL) {
|
||||||
|
fprintf(stderr, "mg_bind(%s) failed\n", address);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
} else if (mg_connect(&mgr, address, ev_handler) == NULL) {
|
||||||
|
fprintf(stderr, "mg_connect(%s) failed\n", address);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (s_received_signal == 0) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
static const char *html_form =
|
|
||||||
"<html><body>POST example."
|
|
||||||
"<form method=\"POST\" action=\"/handle_post_request\">"
|
|
||||||
"Input 1: <input type=\"text\" name=\"input_1\" /> <br/>"
|
|
||||||
"Input 2: <input type=\"text\" name=\"input_2\" /> <br/>"
|
|
||||||
"<input type=\"submit\" />"
|
|
||||||
"</form></body></html>";
|
|
||||||
|
|
||||||
static void *callback(enum mg_event event,
|
|
||||||
struct mg_connection *conn) {
|
|
||||||
const struct mg_request_info *ri = mg_get_request_info(conn);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 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 "";
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_context *ctx;
|
|
||||||
const char *options[] = {"listening_ports", "8080", NULL};
|
|
||||||
|
|
||||||
ctx = mg_start(&callback, NULL, options);
|
|
||||||
getchar(); // Wait until user hits "enter"
|
|
||||||
mg_stop(ctx);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
PROG = publish_subscribe
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. -DMG_ENABLE_THREADS -pthread $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
// Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// This software 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/>.
|
||||||
|
//
|
||||||
|
// You are free to use this software 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.
|
||||||
|
//
|
||||||
|
// Alternatively, you can license this software under a commercial
|
||||||
|
// license, as set out in <https://www.cesanta.com/license>.
|
||||||
|
//
|
||||||
|
// $Date: 2014-09-28 05:04:41 UTC $
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static void *stdin_thread(void *param) {
|
||||||
|
int ch, sock = * (int *) param;
|
||||||
|
while ((ch = getchar()) != EOF) {
|
||||||
|
unsigned char c = (unsigned char) ch;
|
||||||
|
send(sock, &c, 1, 0); // Forward all types characters to the socketpair
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void server_handler(struct mg_connection *nc, int ev, void *p) {
|
||||||
|
(void) p;
|
||||||
|
if (ev == MG_EV_RECV) {
|
||||||
|
// Push received message to all ncections
|
||||||
|
struct mbuf *io = &nc->recv_mbuf;
|
||||||
|
struct mg_connection *c;
|
||||||
|
|
||||||
|
for (c = mg_next(nc->mgr, NULL); c != NULL; c = mg_next(nc->mgr, c)) {
|
||||||
|
mg_send(c, io->buf, io->len);
|
||||||
|
}
|
||||||
|
mbuf_remove(io, io->len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void client_handler(struct mg_connection *conn, int ev, void *p) {
|
||||||
|
struct mbuf *io = &conn->recv_mbuf;
|
||||||
|
(void) p;
|
||||||
|
|
||||||
|
if (ev == MG_EV_CONNECT) {
|
||||||
|
if (conn->flags & MG_F_CLOSE_IMMEDIATELY) {
|
||||||
|
printf("%s\n", "Error connecting to server!");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
printf("%s\n", "Connected to server. Type a message and press enter.");
|
||||||
|
} else if (ev == MG_EV_RECV) {
|
||||||
|
if (conn->flags & MG_F_USER_1) {
|
||||||
|
// Received data from the stdin, forward it to the server
|
||||||
|
struct mg_connection *c = (struct mg_connection *) conn->user_data;
|
||||||
|
mg_send(c, io->buf, io->len);
|
||||||
|
mbuf_remove(io, io->len);
|
||||||
|
} else {
|
||||||
|
// Received data from server connection, print it
|
||||||
|
fwrite(io->buf, io->len, 1, stdout);
|
||||||
|
mbuf_remove(io, io->len);
|
||||||
|
}
|
||||||
|
} else if (ev == MG_EV_CLOSE) {
|
||||||
|
// Connection has closed, most probably cause server has stopped
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
fprintf(stderr, "Usage: %s <port> <client|server>\n", argv[0]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else if (strcmp(argv[2], "client") == 0) {
|
||||||
|
int fds[2];
|
||||||
|
struct mg_connection *ioconn, *server_conn;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
|
||||||
|
// Connect to the pubsub server
|
||||||
|
server_conn = mg_connect(&mgr, argv[1], client_handler);
|
||||||
|
if (server_conn == NULL) {
|
||||||
|
fprintf(stderr, "Cannot connect to port %s\n", argv[1]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a socketpair and give one end to the thread that reads stdin
|
||||||
|
mg_socketpair(fds, SOCK_STREAM);
|
||||||
|
mg_start_thread(stdin_thread, &fds[1]);
|
||||||
|
|
||||||
|
// The other end of a pair goes inside the server
|
||||||
|
ioconn = mg_add_sock(&mgr, fds[0], client_handler);
|
||||||
|
ioconn->flags |= MG_F_USER_1; // Mark this so we know this is a stdin
|
||||||
|
ioconn->user_data = server_conn;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Server code path
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
mg_bind(&mgr, argv[1], server_handler);
|
||||||
|
printf("Starting pubsub server on port %s\n", argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
NS=../../mongoose.c
|
||||||
|
FLAGS = ../../mongoose.c -I../..
|
||||||
|
CFLAGS=-W -Wall -DMG_ENABLE_THREADS -pthread $(CFLAGS_EXTRA)
|
||||||
|
PROGS = device_side cloud_side
|
||||||
|
|
||||||
|
all: $(PROGS)
|
||||||
|
|
||||||
|
device_side: Makefile device_side.c $(NS)
|
||||||
|
$(CC) device_side.c $(FLAGS) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
cloud_side: Makefile cloud_side.c $(NS)
|
||||||
|
$(CC) cloud_side.c $(FLAGS) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
device_side.exe: Makefile device_side.c $(NS)
|
||||||
|
cl device_side.c $(FLAGS) /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROGS)
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
= Raspberry Pi camera/LED demo
|
||||||
|
|
||||||
|
== Overview
|
||||||
|
|
||||||
|
The link:/[demo] consists of web app providing access to a webcam and a LED attached to a RaspberryPi.
|
||||||
|
The device is assumed to have a limited bandwidth towards the server hosting the web app.
|
||||||
|
|
||||||
|
== Objective
|
||||||
|
|
||||||
|
The demo shows how to use websockets to communicate bidirectionally with an embedded device using standard protocols.
|
||||||
|
|
||||||
|
It also shows that it's possible to use Smart.c to develop also the cloud endpoint and expose WebSocket and RESTful APIs
|
||||||
|
easy to integreate with modern web stacks.
|
||||||
|
|
||||||
|
== How it works
|
||||||
|
|
||||||
|
image::docs/arch.png[]
|
||||||
|
|
||||||
|
There are two components, once with runs on the device (`device_side`) and one that runs on a stronger machine
|
||||||
|
and with more bandwidth (`cloud_side`).
|
||||||
|
|
||||||
|
The device app connects to the cloud app via websocket and sends a new jpeg frame as fast as the underlying `raspistill` camera
|
||||||
|
grabbing application can handle. The device automatically attempts reconnecting.
|
||||||
|
|
||||||
|
The cloud side serves the webapp static pages and serves an MPJEG image on `/mpjg`.
|
||||||
|
The MPJEG image handler blocks all the clients until a JPEG frame arrives via websocket
|
||||||
|
and then every client will recieve a copy of the frame.
|
||||||
|
|
||||||
|
The web app can turn on and off the LED via a RESTful api accessible via the `/api` handler.
|
||||||
|
|
||||||
|
== Installation
|
||||||
|
|
||||||
|
=== Server side
|
||||||
|
|
||||||
|
----
|
||||||
|
git clone https://github.com/cesanta/mongoose
|
||||||
|
cd mongoose/examples/web_demo
|
||||||
|
make cloud_side && ./cloud_side 0.0.0.0:8080
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Raspberry Pi
|
||||||
|
|
||||||
|
The instructions provided here are tailored for the Raspbian distribution.
|
||||||
|
|
||||||
|
==== Dependencies
|
||||||
|
|
||||||
|
jpegoptim::
|
||||||
|
apt-get install jpegoptim
|
||||||
|
|
||||||
|
camera::
|
||||||
|
run raspi-config and enable camera
|
||||||
|
|
||||||
|
==== LED
|
||||||
|
|
||||||
|
In order to access the led on your link:http://www.qdh.org.uk/wordpress/?page_id=15[HotPi]
|
||||||
|
board you need to export the gpio pins:
|
||||||
|
|
||||||
|
----
|
||||||
|
for i in 22 23 24; do
|
||||||
|
echo $i >/sys/class/gpio/export
|
||||||
|
echo out >/sys/class/gpio/gpio$i/direction
|
||||||
|
chgrp pi /sys/class/gpio/gpio$i/value
|
||||||
|
done
|
||||||
|
----
|
||||||
|
|
||||||
|
==== Build and run
|
||||||
|
|
||||||
|
----
|
||||||
|
git clone https://github.com/cesanta/mongoose
|
||||||
|
cd mongoose/examples/web_demo
|
||||||
|
make device_side && ./device_side yourserver:8080
|
||||||
|
----
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the cloud endpoint of the Raspberry Pi camera/LED example
|
||||||
|
* of the Mongoose networking library.
|
||||||
|
* It is a simple web server, serving both static files, a REST API handler,
|
||||||
|
* and a WebSocket handler.
|
||||||
|
*/
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static struct mg_serve_http_opts web_root_opts;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Forwards the jpeg frame data to all open mjpeg connections.
|
||||||
|
*
|
||||||
|
* Incoming messages follow a very simple binary frame format:
|
||||||
|
* 4 bytes: timestamp (in network byte order)
|
||||||
|
* n bytes: jpeg payload
|
||||||
|
*
|
||||||
|
* The timestamp is used to compute a lag.
|
||||||
|
* It's done in a quite stupid way as it requires the device clock
|
||||||
|
* to be synchronized with the cloud endpoint.
|
||||||
|
*/
|
||||||
|
static void push_frame_to_clients(struct mg_mgr *mgr,
|
||||||
|
const struct websocket_message *wm) {
|
||||||
|
struct mg_connection *nc;
|
||||||
|
/*
|
||||||
|
* mjpeg connections are tagged with the MG_F_USER_2 flag so we can find them
|
||||||
|
* my scanning the connection list provided by the mongoose manager.
|
||||||
|
*/
|
||||||
|
for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
|
||||||
|
if (!(nc->flags & MG_F_USER_2)) continue; // Ignore un-marked requests
|
||||||
|
|
||||||
|
mg_printf(nc, "--w00t\r\nContent-Type: image/jpeg\r\n"
|
||||||
|
"Content-Length: %lu\r\n\r\n", (unsigned long) wm->size);
|
||||||
|
mg_send(nc, wm->data, wm->size);
|
||||||
|
mg_send(nc, "\r\n", 2);
|
||||||
|
printf("Image pushed to %p\n", nc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Forwards API payload to the device, by scanning through
|
||||||
|
* all the connections to find those that are tagged as WebSocket.
|
||||||
|
*/
|
||||||
|
static void send_command_to_the_device(struct mg_mgr *mgr,
|
||||||
|
const struct mg_str *cmd) {
|
||||||
|
struct mg_connection *nc;
|
||||||
|
for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
|
||||||
|
if (!(nc->flags & MG_F_IS_WEBSOCKET)) continue; // Ignore non-websocket requests
|
||||||
|
|
||||||
|
mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, cmd->p, cmd->len);
|
||||||
|
printf("Sent API command [%.*s] to %p\n", (int) cmd->len, cmd->p, nc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Main event handler. Receives data events and dispatches to
|
||||||
|
* the appropriate handler function.
|
||||||
|
*
|
||||||
|
* 1. RESTful API requests are handled by send_command_to_the_device.
|
||||||
|
* 2. requests to /mpeg are established and left open waiting for data to arrive
|
||||||
|
* from WebSocket.
|
||||||
|
* 3. WebSocket frames are handled by push_frame_to_clients.
|
||||||
|
* 4. All other connections are passed to the mg_serve_http handler
|
||||||
|
* which serves static files.
|
||||||
|
*/
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_HTTP_REQUEST:
|
||||||
|
if (mg_vcmp(&hm->uri, "/mjpg") == 0) {
|
||||||
|
nc->flags |= MG_F_USER_2; /* Set a mark on image requests */
|
||||||
|
mg_printf(nc, "%s",
|
||||||
|
"HTTP/1.0 200 OK\r\n"
|
||||||
|
"Cache-Control: no-cache\r\n"
|
||||||
|
"Pragma: no-cache\r\n"
|
||||||
|
"Expires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-Type: multipart/x-mixed-replace; "
|
||||||
|
"boundary=--w00t\r\n\r\n");
|
||||||
|
} else if (mg_vcmp(&hm->uri, "/api") == 0 && hm->body.len > 0) {
|
||||||
|
/*
|
||||||
|
* RESTful API call. HTTP message body should be a JSON message.
|
||||||
|
* We should parse it and take appropriate action.
|
||||||
|
* In our case, simply forward that call to the device.
|
||||||
|
*/
|
||||||
|
printf("API CALL: [%.*s] [%.*s]\n", (int) hm->method.len, hm->method.p,
|
||||||
|
(int) hm->body.len, hm->body.p);
|
||||||
|
send_command_to_the_device(nc->mgr, &hm->body);
|
||||||
|
mg_printf(nc, "HTTP/1.0 200 OK\nContent-Length: 0\n\n");
|
||||||
|
} else {
|
||||||
|
/* Delegate to the static web server handler for all other paths. */
|
||||||
|
mg_serve_http(nc, hm, web_root_opts);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_EV_WEBSOCKET_FRAME:
|
||||||
|
printf("Got websocket frame, size %lu\n", (unsigned long) wm->size);
|
||||||
|
push_frame_to_clients(nc->mgr, wm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <listening_addr>\n", argv[0]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Listening on: [%s]\n", argv[1]);
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mg_bind() creates a listening connection on a given ip:port and
|
||||||
|
* with an attached event handler.
|
||||||
|
* The event handler will only trigger TCP events until the http
|
||||||
|
* protocol handler is installed.
|
||||||
|
*/
|
||||||
|
if ((nc = mg_bind(&mgr, argv[1], ev_handler)) == NULL) {
|
||||||
|
fprintf(stderr, "Error binding to %s\n", argv[1]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
web_root_opts.document_root = "./web_root";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We explicitly hand over control to the Mongoose manager
|
||||||
|
* in this event loop and we can easily multiplex other activities.
|
||||||
|
*/
|
||||||
|
for(;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the device endpoint of the Raspberry Pi camera/LED example
|
||||||
|
* of the Mongoose networking library.
|
||||||
|
* It is a simple websocket client, sending jpeg frames obtained from the
|
||||||
|
* RPi camera and receiving JSON commands through the same WebSocket channel
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int s_poll_interval_ms = 100;
|
||||||
|
static int s_still_period = 100;
|
||||||
|
static int s_vertical_flip = 0;
|
||||||
|
static int s_width = 320;
|
||||||
|
static int s_height = 180;
|
||||||
|
static const char *s_mjpg_file = "/var/run/shm/cam.jpg";
|
||||||
|
|
||||||
|
static struct mg_connection *client;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if there is a new image available and
|
||||||
|
* send it to the cloud endpoint if the send buffer is not too full.
|
||||||
|
* The image is moved in a new file by the jpeg optimizer function;
|
||||||
|
* this ensures that we will detect a new frame when raspistill writes
|
||||||
|
* it's output file.
|
||||||
|
*/
|
||||||
|
static void send_mjpg_frame(struct mg_connection *nc, const char *file_path) {
|
||||||
|
static int skipped_frames = 0;
|
||||||
|
struct stat st;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
/* Check file modification time. */
|
||||||
|
if (stat(file_path, &st) == 0) {
|
||||||
|
/* Skip the frame if there is too much unsent data. */
|
||||||
|
if (nc->send_mbuf.len > 256) skipped_frames++;
|
||||||
|
|
||||||
|
/* Read new mjpg frame into a buffer */
|
||||||
|
fp = fopen(file_path, "rb");
|
||||||
|
char buf[st.st_size];
|
||||||
|
fread(buf, 1, sizeof(buf), fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delete the file so we can detect when raspistill creates a new one.
|
||||||
|
* mtime granularity is only 1s.
|
||||||
|
*/
|
||||||
|
unlink(file_path);
|
||||||
|
|
||||||
|
/* Send those buffer through the websocket connection */
|
||||||
|
mg_send_websocket_frame(nc, WEBSOCKET_OP_BINARY, buf, sizeof(buf));
|
||||||
|
printf("Sent mjpg frame, %lu bytes after skippping %d frames\n", (unsigned long) sizeof(buf), skipped_frames);
|
||||||
|
skipped_frames = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Turn on or off the LED.
|
||||||
|
* The LED in this example is an RGB led, so all the colors have to be set.
|
||||||
|
*/
|
||||||
|
static void set_led(int v) {
|
||||||
|
char cmd[512];
|
||||||
|
snprintf(cmd, sizeof(cmd), "for i in 22 23 24; do"
|
||||||
|
" echo %d >/sys/class/gpio/gpio$i/value; done", v);
|
||||||
|
system(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse control JSON and perform command:
|
||||||
|
* for now only LED on/off is supported.
|
||||||
|
*/
|
||||||
|
static void perform_control_command(const char* data, size_t len) {
|
||||||
|
struct json_token toks[200], *onoff;
|
||||||
|
parse_json(data, len, toks, sizeof(toks));
|
||||||
|
onoff = find_json_token(toks, "onoff");
|
||||||
|
set_led(strncmp("[\"on\"]", onoff->ptr, onoff->len) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main event handler. Sends websocket frames and receives control commands */
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_CONNECT:
|
||||||
|
printf("Reconnect: %s\n", * (int *) ev_data == 0 ? "ok" : "failed");
|
||||||
|
if (* (int *) ev_data == 0) {
|
||||||
|
/*
|
||||||
|
* Tune the tcp send buffer size, so that we can skip frames
|
||||||
|
* when the connection is congested. This helps maintaining a
|
||||||
|
* reasonable latency.
|
||||||
|
*/
|
||||||
|
int sndbuf_size = 512;
|
||||||
|
if(setsockopt(nc->sock, SOL_SOCKET, SO_SNDBUF,
|
||||||
|
(void *) &sndbuf_size, sizeof(int)) == -1) {
|
||||||
|
perror("failed to tune TCP send buffer size\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
mg_send_websocket_handshake(nc, "/stream", NULL);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_EV_CLOSE:
|
||||||
|
printf("Connection %p closed\n", nc);
|
||||||
|
client = NULL;
|
||||||
|
break;
|
||||||
|
case MG_EV_POLL:
|
||||||
|
send_mjpg_frame(nc, s_mjpg_file);
|
||||||
|
break;
|
||||||
|
case MG_EV_WEBSOCKET_FRAME:
|
||||||
|
printf("Got control command: [%.*s]\n", (int) wm->size, wm->data);
|
||||||
|
perform_control_command((const char*)wm->data, wm->size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This thread regenerates s_mjpg_file every s_poll_interval_ms milliseconds.
|
||||||
|
* It is Raspberry PI specific, change this function on other systems.
|
||||||
|
*/
|
||||||
|
static void *generate_mjpg_data_thread_func(void *param) {
|
||||||
|
char cmd[400];
|
||||||
|
(void) param;
|
||||||
|
|
||||||
|
snprintf(cmd, sizeof(cmd), "raspistill -w %d -h %d -n -q 100 -tl %d "
|
||||||
|
"-t 999999999 -v %s -o %s >/dev/null 2>&1", s_width, s_height,
|
||||||
|
s_still_period, s_vertical_flip ? "-vf" : "", s_mjpg_file);
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
int ret = system(cmd);
|
||||||
|
if (WIFSIGNALED(ret)) exit(1);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
char *addr = argv[1];
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <server_address>\n", argv[0]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start separate thread that generates MJPG data */
|
||||||
|
mg_start_thread(generate_mjpg_data_thread_func, NULL);
|
||||||
|
|
||||||
|
printf("Streaming [%s] to [%s]\n", s_mjpg_file, addr);
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
mg_mgr_poll(&mgr, s_poll_interval_ms);
|
||||||
|
|
||||||
|
/* Reconnect if disconnected */
|
||||||
|
if (!client) {
|
||||||
|
sleep(1); /* limit reconnections frequency */
|
||||||
|
printf("Reconnecting to %s...\n", addr);
|
||||||
|
client = mg_connect(&mgr, addr, ev_handler);
|
||||||
|
if (client) mg_set_protocol_http_websocket(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
@@ -0,0 +1,533 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta name="generator" content="Asciidoctor 1.5.1">
|
||||||
|
<title>Raspberry Pi camera/LED demo</title>
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic|Noto+Serif:400,400italic,700,700italic|Droid+Sans+Mono:400">
|
||||||
|
<style>
|
||||||
|
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
|
||||||
|
/* Remove the comments around the @import statement below when using this as a custom stylesheet */
|
||||||
|
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic|Noto+Serif:400,400italic,700,700italic|Droid+Sans+Mono:400";*/
|
||||||
|
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
|
||||||
|
audio,canvas,video{display:inline-block}
|
||||||
|
audio:not([controls]){display:none;height:0}
|
||||||
|
[hidden],template{display:none}
|
||||||
|
script{display:none!important}
|
||||||
|
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
|
||||||
|
body{margin:0}
|
||||||
|
a{background:transparent}
|
||||||
|
a:focus{outline:thin dotted}
|
||||||
|
a:active,a:hover{outline:0}
|
||||||
|
h1{font-size:2em;margin:.67em 0}
|
||||||
|
abbr[title]{border-bottom:1px dotted}
|
||||||
|
b,strong{font-weight:bold}
|
||||||
|
dfn{font-style:italic}
|
||||||
|
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
|
||||||
|
mark{background:#ff0;color:#000}
|
||||||
|
code,kbd,pre,samp{font-family:monospace;font-size:1em}
|
||||||
|
pre{white-space:pre-wrap}
|
||||||
|
q{quotes:"\201C" "\201D" "\2018" "\2019"}
|
||||||
|
small{font-size:80%}
|
||||||
|
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
|
||||||
|
sup{top:-.5em}
|
||||||
|
sub{bottom:-.25em}
|
||||||
|
img{border:0}
|
||||||
|
svg:not(:root){overflow:hidden}
|
||||||
|
figure{margin:0}
|
||||||
|
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
|
||||||
|
legend{border:0;padding:0}
|
||||||
|
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
|
||||||
|
button,input{line-height:normal}
|
||||||
|
button,select{text-transform:none}
|
||||||
|
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
|
||||||
|
button[disabled],html input[disabled]{cursor:default}
|
||||||
|
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
|
||||||
|
input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}
|
||||||
|
input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}
|
||||||
|
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
|
||||||
|
textarea{overflow:auto;vertical-align:top}
|
||||||
|
table{border-collapse:collapse;border-spacing:0}
|
||||||
|
*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
|
||||||
|
html,body{font-size:100%}
|
||||||
|
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto}
|
||||||
|
a:hover{cursor:pointer}
|
||||||
|
img,object,embed{max-width:100%;height:auto}
|
||||||
|
object,embed{height:100%}
|
||||||
|
img{-ms-interpolation-mode:bicubic}
|
||||||
|
#map_canvas img,#map_canvas embed,#map_canvas object,.map_canvas img,.map_canvas embed,.map_canvas object{max-width:none!important}
|
||||||
|
.left{float:left!important}
|
||||||
|
.right{float:right!important}
|
||||||
|
.text-left{text-align:left!important}
|
||||||
|
.text-right{text-align:right!important}
|
||||||
|
.text-center{text-align:center!important}
|
||||||
|
.text-justify{text-align:justify!important}
|
||||||
|
.hide{display:none}
|
||||||
|
.antialiased,body{-webkit-font-smoothing:antialiased}
|
||||||
|
img{display:inline-block;vertical-align:middle}
|
||||||
|
textarea{height:auto;min-height:50px}
|
||||||
|
select{width:100%}
|
||||||
|
p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}
|
||||||
|
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
|
||||||
|
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
|
||||||
|
a{color:#2156a5;text-decoration:underline;line-height:inherit}
|
||||||
|
a:hover,a:focus{color:#1d4b8f}
|
||||||
|
a img{border:none}
|
||||||
|
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
|
||||||
|
p aside{font-size:.875em;line-height:1.35;font-style:italic}
|
||||||
|
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
|
||||||
|
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
|
||||||
|
h1{font-size:2.125em}
|
||||||
|
h2{font-size:1.6875em}
|
||||||
|
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
|
||||||
|
h4,h5{font-size:1.125em}
|
||||||
|
h6{font-size:1em}
|
||||||
|
hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
|
||||||
|
em,i{font-style:italic;line-height:inherit}
|
||||||
|
strong,b{font-weight:bold;line-height:inherit}
|
||||||
|
small{font-size:60%;line-height:inherit}
|
||||||
|
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
|
||||||
|
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
|
||||||
|
ul,ol,ul.no-bullet,ol.no-bullet{margin-left:1.5em}
|
||||||
|
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
|
||||||
|
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
|
||||||
|
ul.square{list-style-type:square}
|
||||||
|
ul.circle{list-style-type:circle}
|
||||||
|
ul.disc{list-style-type:disc}
|
||||||
|
ul.no-bullet{list-style:none}
|
||||||
|
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
|
||||||
|
dl dt{margin-bottom:.3125em;font-weight:bold}
|
||||||
|
dl dd{margin-bottom:1.25em}
|
||||||
|
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
|
||||||
|
abbr{text-transform:none}
|
||||||
|
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
|
||||||
|
blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
|
||||||
|
blockquote cite:before{content:"\2014 \0020"}
|
||||||
|
blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
|
||||||
|
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
|
||||||
|
@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
|
||||||
|
h1{font-size:2.75em}
|
||||||
|
h2{font-size:2.3125em}
|
||||||
|
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
|
||||||
|
h4{font-size:1.4375em}}table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
|
||||||
|
table thead,table tfoot{background:#f7f8f7;font-weight:bold}
|
||||||
|
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
|
||||||
|
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
|
||||||
|
table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}
|
||||||
|
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
|
||||||
|
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
|
||||||
|
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
|
||||||
|
.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}
|
||||||
|
.clearfix:after,.float-group:after{clear:both}
|
||||||
|
*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
|
||||||
|
pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}
|
||||||
|
.keyseq{color:rgba(51,51,51,.8)}
|
||||||
|
kbd{display:inline-block;color:rgba(0,0,0,.8);font-size:.75em;line-height:1.4;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:-.15em .15em 0 .15em;padding:.2em .6em .2em .5em;vertical-align:middle;white-space:nowrap}
|
||||||
|
.keyseq kbd:first-child{margin-left:0}
|
||||||
|
.keyseq kbd:last-child{margin-right:0}
|
||||||
|
.menuseq,.menu{color:rgba(0,0,0,.8)}
|
||||||
|
b.button:before,b.button:after{position:relative;top:-1px;font-weight:400}
|
||||||
|
b.button:before{content:"[";padding:0 3px 0 2px}
|
||||||
|
b.button:after{content:"]";padding:0 2px 0 3px}
|
||||||
|
p a>code:hover{color:rgba(0,0,0,.9)}
|
||||||
|
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
|
||||||
|
#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table}
|
||||||
|
#header:after,#content:after,#footnotes:after,#footer:after{clear:both}
|
||||||
|
#content{margin-top:1.25em}
|
||||||
|
#content:before{content:none}
|
||||||
|
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
|
||||||
|
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}
|
||||||
|
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}
|
||||||
|
#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
|
||||||
|
#header .details span:first-child{margin-left:-.125em}
|
||||||
|
#header .details span.email a{color:rgba(0,0,0,.85)}
|
||||||
|
#header .details br{display:none}
|
||||||
|
#header .details br+span:before{content:"\00a0\2013\00a0"}
|
||||||
|
#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
|
||||||
|
#header .details br+span#revremark:before{content:"\00a0|\00a0"}
|
||||||
|
#header #revnumber{text-transform:capitalize}
|
||||||
|
#header #revnumber:after{content:"\00a0"}
|
||||||
|
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
|
||||||
|
#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}
|
||||||
|
#toc>ul{margin-left:.125em}
|
||||||
|
#toc ul.sectlevel0>li>a{font-style:italic}
|
||||||
|
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
|
||||||
|
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
|
||||||
|
#toc a{text-decoration:none}
|
||||||
|
#toc a:active{text-decoration:underline}
|
||||||
|
#toctitle{color:#7a2518;font-size:1.2em}
|
||||||
|
@media only screen and (min-width:768px){#toctitle{font-size:1.375em}
|
||||||
|
body.toc2{padding-left:15em;padding-right:0}
|
||||||
|
#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
|
||||||
|
#toc.toc2 #toctitle{margin-top:0;font-size:1.2em}
|
||||||
|
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
|
||||||
|
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
|
||||||
|
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
|
||||||
|
body.toc2.toc-right{padding-left:0;padding-right:15em}
|
||||||
|
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
|
||||||
|
#toc.toc2{width:20em}
|
||||||
|
#toc.toc2 #toctitle{font-size:1.375em}
|
||||||
|
#toc.toc2>ul{font-size:.95em}
|
||||||
|
#toc.toc2 ul ul{padding-left:1.25em}
|
||||||
|
body.toc2.toc-right{padding-left:0;padding-right:20em}}#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
|
||||||
|
#content #toc>:first-child{margin-top:0}
|
||||||
|
#content #toc>:last-child{margin-bottom:0}
|
||||||
|
#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}
|
||||||
|
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
|
||||||
|
.sect1{padding-bottom:.625em}
|
||||||
|
@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}.sect1+.sect1{border-top:1px solid #efefed}
|
||||||
|
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
|
||||||
|
#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
|
||||||
|
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
|
||||||
|
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
|
||||||
|
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
|
||||||
|
.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
|
||||||
|
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
|
||||||
|
table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}
|
||||||
|
.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}
|
||||||
|
table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}
|
||||||
|
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
|
||||||
|
.admonitionblock>table td.icon{text-align:center;width:80px}
|
||||||
|
.admonitionblock>table td.icon img{max-width:none}
|
||||||
|
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
|
||||||
|
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}
|
||||||
|
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
|
||||||
|
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
|
||||||
|
.exampleblock>.content>:first-child{margin-top:0}
|
||||||
|
.exampleblock>.content>:last-child{margin-bottom:0}
|
||||||
|
.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
|
||||||
|
.sidebarblock>:first-child{margin-top:0}
|
||||||
|
.sidebarblock>:last-child{margin-bottom:0}
|
||||||
|
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
|
||||||
|
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
|
||||||
|
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}
|
||||||
|
.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}
|
||||||
|
.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}
|
||||||
|
.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}
|
||||||
|
@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
|
||||||
|
.listingblock pre.highlightjs{padding:0}
|
||||||
|
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
|
||||||
|
.listingblock pre.prettyprint{border-width:0}
|
||||||
|
.listingblock>.content{position:relative}
|
||||||
|
.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}
|
||||||
|
.listingblock:hover code[data-lang]:before{display:block}
|
||||||
|
.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}
|
||||||
|
.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}
|
||||||
|
table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}
|
||||||
|
table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0}
|
||||||
|
table.pyhltable td.code{padding-left:.75em;padding-right:0}
|
||||||
|
pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}
|
||||||
|
pre.pygments .lineno{display:inline-block;margin-right:.25em}
|
||||||
|
table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
|
||||||
|
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
|
||||||
|
.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}
|
||||||
|
.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
|
||||||
|
.quoteblock blockquote{margin:0;padding:0;border:0}
|
||||||
|
.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
|
||||||
|
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
|
||||||
|
.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}
|
||||||
|
.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}
|
||||||
|
.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}
|
||||||
|
.quoteblock .quoteblock blockquote:before{display:none}
|
||||||
|
.verseblock{margin:0 1em 1.25em 1em}
|
||||||
|
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
|
||||||
|
.verseblock pre strong{font-weight:400}
|
||||||
|
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
|
||||||
|
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
|
||||||
|
.quoteblock .attribution br,.verseblock .attribution br{display:none}
|
||||||
|
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.05em;color:rgba(0,0,0,.6)}
|
||||||
|
.quoteblock.abstract{margin:0 0 1.25em 0;display:block}
|
||||||
|
.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}
|
||||||
|
.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}
|
||||||
|
table.tableblock{max-width:100%;border-collapse:separate}
|
||||||
|
table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}
|
||||||
|
table.spread{width:100%}
|
||||||
|
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
|
||||||
|
table.grid-all th.tableblock,table.grid-all td.tableblock{border-width:0 1px 1px 0}
|
||||||
|
table.grid-all tfoot>tr>th.tableblock,table.grid-all tfoot>tr>td.tableblock{border-width:1px 1px 0 0}
|
||||||
|
table.grid-cols th.tableblock,table.grid-cols td.tableblock{border-width:0 1px 0 0}
|
||||||
|
table.grid-all *>tr>.tableblock:last-child,table.grid-cols *>tr>.tableblock:last-child{border-right-width:0}
|
||||||
|
table.grid-rows th.tableblock,table.grid-rows td.tableblock{border-width:0 0 1px 0}
|
||||||
|
table.grid-all tbody>tr:last-child>th.tableblock,table.grid-all tbody>tr:last-child>td.tableblock,table.grid-all thead:last-child>tr>th.tableblock,table.grid-rows tbody>tr:last-child>th.tableblock,table.grid-rows tbody>tr:last-child>td.tableblock,table.grid-rows thead:last-child>tr>th.tableblock{border-bottom-width:0}
|
||||||
|
table.grid-rows tfoot>tr>th.tableblock,table.grid-rows tfoot>tr>td.tableblock{border-width:1px 0 0 0}
|
||||||
|
table.frame-all{border-width:1px}
|
||||||
|
table.frame-sides{border-width:0 1px}
|
||||||
|
table.frame-topbot{border-width:1px 0}
|
||||||
|
th.halign-left,td.halign-left{text-align:left}
|
||||||
|
th.halign-right,td.halign-right{text-align:right}
|
||||||
|
th.halign-center,td.halign-center{text-align:center}
|
||||||
|
th.valign-top,td.valign-top{vertical-align:top}
|
||||||
|
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
|
||||||
|
th.valign-middle,td.valign-middle{vertical-align:middle}
|
||||||
|
table thead th,table tfoot th{font-weight:bold}
|
||||||
|
tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
|
||||||
|
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
|
||||||
|
p.tableblock>code:only-child{background:none;padding:0}
|
||||||
|
p.tableblock{font-size:1em}
|
||||||
|
td>div.verse{white-space:pre}
|
||||||
|
ol{margin-left:1.75em}
|
||||||
|
ul li ol{margin-left:1.5em}
|
||||||
|
dl dd{margin-left:1.125em}
|
||||||
|
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
|
||||||
|
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
|
||||||
|
ul.unstyled,ol.unnumbered,ul.checklist,ul.none{list-style-type:none}
|
||||||
|
ul.unstyled,ol.unnumbered,ul.checklist{margin-left:.625em}
|
||||||
|
ul.checklist li>p:first-child>.fa-check-square-o:first-child,ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
|
||||||
|
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{position:relative;top:1px}
|
||||||
|
ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}
|
||||||
|
ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}
|
||||||
|
ul.inline>li>*{display:block}
|
||||||
|
.unstyled dl dt{font-weight:400;font-style:normal}
|
||||||
|
ol.arabic{list-style-type:decimal}
|
||||||
|
ol.decimal{list-style-type:decimal-leading-zero}
|
||||||
|
ol.loweralpha{list-style-type:lower-alpha}
|
||||||
|
ol.upperalpha{list-style-type:upper-alpha}
|
||||||
|
ol.lowerroman{list-style-type:lower-roman}
|
||||||
|
ol.upperroman{list-style-type:upper-roman}
|
||||||
|
ol.lowergreek{list-style-type:lower-greek}
|
||||||
|
.hdlist>table,.colist>table{border:0;background:none}
|
||||||
|
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
|
||||||
|
td.hdlist1{padding-right:.75em;font-weight:bold}
|
||||||
|
td.hdlist1,td.hdlist2{vertical-align:top}
|
||||||
|
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
|
||||||
|
.colist>table tr>td:first-of-type{padding:0 .75em;line-height:1}
|
||||||
|
.colist>table tr>td:last-of-type{padding:.25em 0}
|
||||||
|
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
|
||||||
|
.imageblock.left,.imageblock[style*="float: left"]{margin:.25em .625em 1.25em 0}
|
||||||
|
.imageblock.right,.imageblock[style*="float: right"]{margin:.25em 0 1.25em .625em}
|
||||||
|
.imageblock>.title{margin-bottom:0}
|
||||||
|
.imageblock.thumb,.imageblock.th{border-width:6px}
|
||||||
|
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
|
||||||
|
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
|
||||||
|
.image.left{margin-right:.625em}
|
||||||
|
.image.right{margin-left:.625em}
|
||||||
|
a.image{text-decoration:none}
|
||||||
|
span.footnote,span.footnoteref{vertical-align:super;font-size:.875em}
|
||||||
|
span.footnote a,span.footnoteref a{text-decoration:none}
|
||||||
|
span.footnote a:active,span.footnoteref a:active{text-decoration:underline}
|
||||||
|
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
|
||||||
|
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}
|
||||||
|
#footnotes .footnote{padding:0 .375em;line-height:1.3;font-size:.875em;margin-left:1.2em;text-indent:-1.2em;margin-bottom:.2em}
|
||||||
|
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}
|
||||||
|
#footnotes .footnote:last-of-type{margin-bottom:0}
|
||||||
|
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
|
||||||
|
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
|
||||||
|
.gist .file-data>table td.line-data{width:99%}
|
||||||
|
div.unbreakable{page-break-inside:avoid}
|
||||||
|
.big{font-size:larger}
|
||||||
|
.small{font-size:smaller}
|
||||||
|
.underline{text-decoration:underline}
|
||||||
|
.overline{text-decoration:overline}
|
||||||
|
.line-through{text-decoration:line-through}
|
||||||
|
.aqua{color:#00bfbf}
|
||||||
|
.aqua-background{background-color:#00fafa}
|
||||||
|
.black{color:#000}
|
||||||
|
.black-background{background-color:#000}
|
||||||
|
.blue{color:#0000bf}
|
||||||
|
.blue-background{background-color:#0000fa}
|
||||||
|
.fuchsia{color:#bf00bf}
|
||||||
|
.fuchsia-background{background-color:#fa00fa}
|
||||||
|
.gray{color:#606060}
|
||||||
|
.gray-background{background-color:#7d7d7d}
|
||||||
|
.green{color:#006000}
|
||||||
|
.green-background{background-color:#007d00}
|
||||||
|
.lime{color:#00bf00}
|
||||||
|
.lime-background{background-color:#00fa00}
|
||||||
|
.maroon{color:#600000}
|
||||||
|
.maroon-background{background-color:#7d0000}
|
||||||
|
.navy{color:#000060}
|
||||||
|
.navy-background{background-color:#00007d}
|
||||||
|
.olive{color:#606000}
|
||||||
|
.olive-background{background-color:#7d7d00}
|
||||||
|
.purple{color:#600060}
|
||||||
|
.purple-background{background-color:#7d007d}
|
||||||
|
.red{color:#bf0000}
|
||||||
|
.red-background{background-color:#fa0000}
|
||||||
|
.silver{color:#909090}
|
||||||
|
.silver-background{background-color:#bcbcbc}
|
||||||
|
.teal{color:#006060}
|
||||||
|
.teal-background{background-color:#007d7d}
|
||||||
|
.white{color:#bfbfbf}
|
||||||
|
.white-background{background-color:#fafafa}
|
||||||
|
.yellow{color:#bfbf00}
|
||||||
|
.yellow-background{background-color:#fafa00}
|
||||||
|
span.icon>.fa{cursor:default}
|
||||||
|
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
|
||||||
|
.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}
|
||||||
|
.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
|
||||||
|
.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}
|
||||||
|
.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}
|
||||||
|
.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}
|
||||||
|
.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
|
||||||
|
.conum[data-value] *{color:#fff!important}
|
||||||
|
.conum[data-value]+b{display:none}
|
||||||
|
.conum[data-value]:after{content:attr(data-value)}
|
||||||
|
pre .conum[data-value]{position:relative;top:-.125em}
|
||||||
|
b.conum *{color:inherit!important}
|
||||||
|
.conum:not([data-value]):empty{display:none}
|
||||||
|
h1,h2{letter-spacing:-.01em}
|
||||||
|
dt,th.tableblock,td.content{text-rendering:optimizeLegibility}
|
||||||
|
p,td.content{letter-spacing:-.01em}
|
||||||
|
p strong,td.content strong{letter-spacing:-.005em}
|
||||||
|
p,blockquote,dt,td.content{font-size:1.0625rem}
|
||||||
|
p{margin-bottom:1.25rem}
|
||||||
|
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
|
||||||
|
.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
|
||||||
|
.print-only{display:none!important}
|
||||||
|
@media print{@page{margin:1.25cm .75cm}
|
||||||
|
*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
|
||||||
|
a{color:inherit!important;text-decoration:underline!important}
|
||||||
|
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
|
||||||
|
a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
|
||||||
|
abbr[title]:after{content:" (" attr(title) ")"}
|
||||||
|
pre,blockquote,tr,img{page-break-inside:avoid}
|
||||||
|
thead{display:table-header-group}
|
||||||
|
img{max-width:100%!important}
|
||||||
|
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
|
||||||
|
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
|
||||||
|
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
|
||||||
|
#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}
|
||||||
|
.sect1{padding-bottom:0!important}
|
||||||
|
.sect1+.sect1{border:0!important}
|
||||||
|
#header>h1:first-child{margin-top:1.25rem}
|
||||||
|
body.book #header{text-align:center}
|
||||||
|
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}
|
||||||
|
body.book #header .details{border:0!important;display:block;padding:0!important}
|
||||||
|
body.book #header .details span:first-child{margin-left:0!important}
|
||||||
|
body.book #header .details br{display:block}
|
||||||
|
body.book #header .details br+span:before{content:none!important}
|
||||||
|
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
|
||||||
|
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
|
||||||
|
.listingblock code[data-lang]:before{display:block}
|
||||||
|
#footer{background:none!important;padding:0 .9375em}
|
||||||
|
#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}
|
||||||
|
.hide-on-print{display:none!important}
|
||||||
|
.print-only{display:block!important}
|
||||||
|
.hide-for-print{display:none!important}
|
||||||
|
.show-for-print{display:inherit!important}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="article">
|
||||||
|
<div id="header">
|
||||||
|
<h1>Raspberry Pi camera/LED demo</h1>
|
||||||
|
</div>
|
||||||
|
<div id="content">
|
||||||
|
<div class="sect1">
|
||||||
|
<h2 id="_overview">Overview</h2>
|
||||||
|
<div class="sectionbody">
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>The <a href="/">demo</a> consists of web app providing access to a webcam and a LED attached to a RaspberryPi.
|
||||||
|
The device is assumed to have a limited bandwidth towards the server hosting the web app.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect1">
|
||||||
|
<h2 id="_objective">Objective</h2>
|
||||||
|
<div class="sectionbody">
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>The demo shows how to use websockets to communicate bidirectionally with an embedded device using standard protocols.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>It also shows that it’s possible to use Smart.c to develop also the cloud endpoint and expose WebSocket and RESTful APIs
|
||||||
|
easy to integreate with modern web stacks.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect1">
|
||||||
|
<h2 id="_how_it_works">How it works</h2>
|
||||||
|
<div class="sectionbody">
|
||||||
|
<div class="imageblock">
|
||||||
|
<div class="content">
|
||||||
|
<img src="docs/arch.png" alt="arch">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>There are two components, once with runs on the device (<code>device_side</code>) and one that runs on a stronger machine
|
||||||
|
and with more bandwidth (<code>cloud_side</code>).</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>The device app connects to the cloud app via websocket and sends a new jpeg frame as fast as the underlying <code>raspistill</code> camera
|
||||||
|
grabbing application can handle. The device automatically attempts reconnecting.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>The cloud side serves the webapp static pages and serves an MPJEG image on <code>/mpjg</code>.
|
||||||
|
The MPJEG image handler blocks all the clients until a JPEG frame arrives via websocket
|
||||||
|
and then every client will recieve a copy of the frame.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>The web app can turn on and off the LED via a RESTful api accessible via the <code>/api</code> handler.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect1">
|
||||||
|
<h2 id="_installation">Installation</h2>
|
||||||
|
<div class="sectionbody">
|
||||||
|
<div class="sect2">
|
||||||
|
<h3 id="_server_side">Server side</h3>
|
||||||
|
<div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre>git clone https://github.com/cesanta/mongoose
|
||||||
|
cd mongoose/examples/web_demo
|
||||||
|
make cloud_side && ./cloud_side 0.0.0.0:8080</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect2">
|
||||||
|
<h3 id="_raspberry_pi">Raspberry Pi</h3>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>The instructions provided here are tailored for the Raspbian distribution.</p>
|
||||||
|
</div>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_dependencies">Dependencies</h4>
|
||||||
|
<div class="dlist">
|
||||||
|
<dl>
|
||||||
|
<dt class="hdlist1">jpegoptim</dt>
|
||||||
|
<dd>
|
||||||
|
<p>apt-get install jpegoptim</p>
|
||||||
|
</dd>
|
||||||
|
<dt class="hdlist1">camera</dt>
|
||||||
|
<dd>
|
||||||
|
<p>run raspi-config and enable camera</p>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_led">LED</h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>In order to access the led on your <a href="http://www.qdh.org.uk/wordpress/?page_id=15">HotPi</a>
|
||||||
|
board you need to export the gpio pins:</p>
|
||||||
|
</div>
|
||||||
|
<div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre>for i in 22 23 24; do
|
||||||
|
echo $i >/sys/class/gpio/export
|
||||||
|
echo out >/sys/class/gpio/gpio$i/direction
|
||||||
|
chgrp pi /sys/class/gpio/gpio$i/value
|
||||||
|
done</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_build_and_run">Build and run</h4>
|
||||||
|
<div class="listingblock">
|
||||||
|
<div class="content">
|
||||||
|
<pre>git clone https://github.com/cesanta/mongoose
|
||||||
|
cd mongoose/examples/web_demo
|
||||||
|
make device_side && ./device_side yourserver:8080</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="footer">
|
||||||
|
<div id="footer-text">
|
||||||
|
Last updated 2014-11-03 21:30:06 GMT
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../docs/arch.png
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Framework7 0.9.6
|
||||||
|
* Full Featured HTML Framework For Building iOS 7 Apps
|
||||||
|
*
|
||||||
|
* http://www.idangero.us/framework7
|
||||||
|
*
|
||||||
|
* Copyright 2014, Vladimir Kharlampidi
|
||||||
|
* The iDangero.us
|
||||||
|
* http://www.idangero.us/
|
||||||
|
*
|
||||||
|
* Licensed under MIT
|
||||||
|
*
|
||||||
|
* Released on: September 13, 2014
|
||||||
|
*/
|
||||||
|
html,body{position:relative;height:100%;width:100%;overflow-x:hidden}body{font-family:Helvetica Neue,Helvetica,Arial,sans-serif;margin:0;padding:0;color:#000;font-size:14px;line-height:1.4;width:100%;-webkit-text-size-adjust:100%;background:#fff;overflow:hidden}@media all and (width:1024px) and (height:691px) and (orientation:landscape){html,body{height:671px}}@media all and (width:1024px) and (height:692px) and (orientation:landscape){html,body{height:672px}}*{-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-touch-callout:none}a,input,textarea,select{outline:0}a{text-decoration:none;color:#007aff}p{margin:1em 0}.row{display:-webkit-box;display:-ms-flexbox;display:-webkit-flex;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;-webkit-box-lines:multiple;-moz-box-lines:multiple;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:start;-ms-flex-align:start;-webkit-align-items:flex-start;align-items:flex-start}.row>[class*=col-]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row .col-100{width:100%;width:-webkit-calc((100% - 15px*0) / 1);width:calc((100% - 15px*0) / 1)}.row.no-gutter .col-100{width:100%}.row .col-95{width:95%;width:-webkit-calc((100% - 15px*0.05263157894736836) / 1.0526315789473684);width:calc((100% - 15px*0.05263157894736836) / 1.0526315789473684)}.row.no-gutter .col-95{width:95%}.row .col-90{width:90%;width:-webkit-calc((100% - 15px*0.11111111111111116) / 1.1111111111111112);width:calc((100% - 15px*0.11111111111111116) / 1.1111111111111112)}.row.no-gutter .col-90{width:90%}.row .col-85{width:85%;width:-webkit-calc((100% - 15px*0.17647058823529416) / 1.1764705882352942);width:calc((100% - 15px*0.17647058823529416) / 1.1764705882352942)}.row.no-gutter .col-85{width:85%}.row .col-80{width:80%;width:-webkit-calc((100% - 15px*0.25) / 1.25);width:calc((100% - 15px*0.25) / 1.25)}.row.no-gutter .col-80{width:80%}.row .col-75{width:75%;width:-webkit-calc((100% - 15px*0.33333333333333326) / 1.3333333333333333);width:calc((100% - 15px*0.33333333333333326) / 1.3333333333333333)}.row.no-gutter .col-75{width:75%}.row .col-66{width:66.66666666666666%;width:-webkit-calc((100% - 15px*0.5000000000000002) / 1.5000000000000002);width:calc((100% - 15px*0.5000000000000002) / 1.5000000000000002)}.row.no-gutter .col-66{width:66.66666666666666%}.row .col-60{width:60%;width:-webkit-calc((100% - 15px*0.6666666666666667) / 1.6666666666666667);width:calc((100% - 15px*0.6666666666666667) / 1.6666666666666667)}.row.no-gutter .col-60{width:60%}.row .col-50{width:50%;width:-webkit-calc((100% - 15px*1) / 2);width:calc((100% - 15px*1) / 2)}.row.no-gutter .col-50{width:50%}.row .col-40{width:40%;width:-webkit-calc((100% - 15px*1.5) / 2.5);width:calc((100% - 15px*1.5) / 2.5)}.row.no-gutter .col-40{width:40%}.row .col-33{width:33.333333333333336%;width:-webkit-calc((100% - 15px*2) / 3);width:calc((100% - 15px*2) / 3)}.row.no-gutter .col-33{width:33.333333333333336%}.row .col-25{width:25%;width:-webkit-calc((100% - 15px*3) / 4);width:calc((100% - 15px*3) / 4)}.row.no-gutter .col-25{width:25%}.row .col-20{width:20%;width:-webkit-calc((100% - 15px*4) / 5);width:calc((100% - 15px*4) / 5)}.row.no-gutter .col-20{width:20%}.row .col-15{width:15%;width:-webkit-calc((100% - 15px*5.666666666666667) / 6.666666666666667);width:calc((100% - 15px*5.666666666666667) / 6.666666666666667)}.row.no-gutter .col-15{width:15%}.row .col-10{width:10%;width:-webkit-calc((100% - 15px*9) / 10);width:calc((100% - 15px*9) / 10)}.row.no-gutter .col-10{width:10%}.row .col-5{width:5%;width:-webkit-calc((100% - 15px*19) / 20);width:calc((100% - 15px*19) / 20)}.row.no-gutter .col-5{width:5%}@media all and (min-width:768px){.row .tablet-100{width:100%;width:-webkit-calc((100% - 15px*0) / 1);width:calc((100% - 15px*0) / 1)}.row.no-gutter .tablet-100{width:100%}.row .tablet-95{width:95%;width:-webkit-calc((100% - 15px*0.05263157894736836) / 1.0526315789473684);width:calc((100% - 15px*0.05263157894736836) / 1.0526315789473684)}.row.no-gutter .tablet-95{width:95%}.row .tablet-90{width:90%;width:-webkit-calc((100% - 15px*0.11111111111111116) / 1.1111111111111112);width:calc((100% - 15px*0.11111111111111116) / 1.1111111111111112)}.row.no-gutter .tablet-90{width:90%}.row .tablet-85{width:85%;width:-webkit-calc((100% - 15px*0.17647058823529416) / 1.1764705882352942);width:calc((100% - 15px*0.17647058823529416) / 1.1764705882352942)}.row.no-gutter .tablet-85{width:85%}.row .tablet-80{width:80%;width:-webkit-calc((100% - 15px*0.25) / 1.25);width:calc((100% - 15px*0.25) / 1.25)}.row.no-gutter .tablet-80{width:80%}.row .tablet-75{width:75%;width:-webkit-calc((100% - 15px*0.33333333333333326) / 1.3333333333333333);width:calc((100% - 15px*0.33333333333333326) / 1.3333333333333333)}.row.no-gutter .tablet-75{width:75%}.row .tablet-66{width:66.66666666666666%;width:-webkit-calc((100% - 15px*0.5000000000000002) / 1.5000000000000002);width:calc((100Line truncated
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Framework7 0.9.6
|
||||||
|
* Full Featured HTML Framework For Building iOS 7 Apps
|
||||||
|
*
|
||||||
|
* http://www.idangero.us/framework7
|
||||||
|
*
|
||||||
|
* Copyright 2014, Vladimir Kharlampidi
|
||||||
|
* The iDangero.us
|
||||||
|
* http://www.idangero.us/
|
||||||
|
*
|
||||||
|
* Licensed under MIT
|
||||||
|
*
|
||||||
|
* Released on: September 13, 2014
|
||||||
|
*/
|
||||||
|
!function(){"use strict";window.Framework7=function(b){function c(a){var b=a.replace(/^./,function(a){return a.toUpperCase()});l["onPage"+b]=function(b,c){return l.onPage(a,b,c)}}function d(a,b,c){b=n(b);var d=b.find(a);return d.length>1&&("string"==typeof c.selector&&(d=b.find(c.selector+" "+a)),d.length>1&&(d=b.find("."+l.params.viewMainClass+" "+a))),1===d.length?d:void 0}function e(a,b,c){"to-left"===c&&(a.removeClass("page-on-center").addClass("page-from-center-to-left"),b.addClass("page-from-right-to-center")),"to-right"===c&&(a.removeClass("page-on-left").addClass("page-from-left-to-center"),b.removeClass("page-on-center").addClass("page-from-center-to-right"))}function f(a,b,c){"to-left"===c&&(b.removeClass("navbar-on-right").addClass("navbar-from-right-to-center"),b.find(".sliding").each(function(){var a=n(this);a.transform("translate3d(0px,0,0)"),l.params.animateNavBackIcon&&a.hasClass("left")&&a.find(".back .icon").length>0&&a.find(".back .icon").transform("translate3d(0px,0,0)")}),a.removeClass("navbar-on-center").addClass("navbar-from-center-to-left"),a.find(".sliding").each(function(){var a=n(this);l.params.animateNavBackIcon&&(a.hasClass("center")&&b.find(".sliding.left .back .icon").length>0&&(this.f7NavbarLeftOffset+=b.find(".sliding.left .back span")[0].offsetLeft),a.hasClass("left")&&a.find(".back .icon").length>0&&a.find(".back .icon").transform("translate3d("+-this.f7NavbarLeftOffset+"px,0,0)")),a.transform("translate3d("+this.f7NavbarLeftOffset+"px,0,0)")})),"to-right"===c&&(a.removeClass("navbar-on-left").addClass("navbar-from-left-to-center"),a.find(".sliding").each(function(){var a=n(this);a.transform("translate3d(0px,0,0)"),l.params.animateNavBackIcon&&a.hasClass("left")&&a.find(".back .icon").length>0&&a.find(".back .icon").transform("translate3d(0px,0,0)")}),b.removeClass("navbar-on-center").addClass("navbar-from-center-to-right"),b.find(".sliding").each(function(){var a=n(this);l.params.animateNavBackIcon&&a.hasClass("left")&&a.find(".back .icon").length>0&&a.find(".back .icon").transform("translate3d("+-this.f7NavbarRightOffset+"px,0,0)"),a.transform("translate3d("+this.f7NavbarRightOffset+"px,0,0)")}))}function g(a,b,c,e){var f,g,h,i,j,k,m,o,p=n(a.container),q=n(a.pagesContainer);if(l.pluginHook("loadPage",a,b,c),l._tempDomElement.innerHTML="",b||"string"==typeof c)l._tempDomElement.innerHTML=c;else if("length"in c&&c.length>1)for(var r=0;r<c.length;r++)n(l._tempDomElement).append(c[r]);else n(l._tempDomElement).append(c);if(o=e.reloadPrevious?"left":"center",f=d(".page",l._tempDomElement,a),!f)return void(a.allowPageChange=!0);if(f.addClass("page-on-"+o),h=q.children(".page:not(.cached)"),e.reloadPrevious&&1===h.length)return void(a.allowPageChange=!0);g=h.eq(h.length-1),a.params.dynamicNavbar&&(m=!0,j=d(".navbar-inner",l._tempDomElement,a),j||(m=!1),k=p.find(".navbar"),i=k.find(".navbar-inner:last-child")),m&&j.addClass("navbar-on-"+o),b||(b="#content-"+a.history.length,a.params.domCache||(a.contentCache[b]=c)),a.url=b;var s=a.history[a.history.length-(e.reloadPrevious?2:1)];0===s.indexOf("#content")&&s in a.contentCache&&(a.contentCache[s]=null,delete a.contentCache[s]),a.history[a.history.length-(e.reloadPrevious?2:1)]=b,e.reloadPrevious?(g=g.prev(".page"),f.insertBefore(g),m&&(i=i.prev(".navbar-inner"),j.insertAfter(i))):(q.append(f[0]),m&&k.append(j[0])),l.pageRemoveCallback(a,g[0],o),g.remove(),m&&i.remove(),l.pageInitCallback(a,f[0],b,o,m?j[0]:void 0),m&&l.navbarInitCallback(a,f[0],k[0],j[0],b,o),a.allowPageChange=!0}function h(a,b,c,g){function h(){a.allowPageChange=!0,i.removeClass("page-from-right-to-center page-on-right").addClass("page-on-center"),j.removeClass("page-from-center-to-left page-on-center").addClass("page-on-left"),r&&(p.removeClass("navbar-from-right-to-center navbar-on-right").addClass("navbar-on-center"),o.removeClass("navbar-from-center-to-left navbar-on-center").addClass("navbar-on-left")),l.pageAnimCallbacks("after",a,{pageContainer:i[0],url:b,position:"right",oldPage:j,newPage:i}),l.params.pushState&&l.pushStateClearQueue(),a.params.swipeBackPage||a.params.preloadPreviousPage||(a.params.domCache?(j.addClass("cached"),o.addClass("cached")):(j.remove(),o.remove())),a.params.uniqueHistory&&w&&a.refreshPreviousPage()}var i,j,k,m,o,p,q,r,s=n(a.container),t=n(a.pagesContainer),u=g.animatePages;if("undefined"==typeof u&&(u=a.params.animatePages),l.pluginHook("loadPage",a,b,c),l._tempDomElement.innerHTML="",b||"string"==typeof c)l._tempDomElement.innerHTML=c;else if("length"in c&&c.length>1)for(var v=0;v<c.length;v++)n(l._tempDomElement).append(c[v]);else n(l._tempDomElement).append(c);if(i=d(".page",l._tempDomElement,a),!i)return void(a.allowPageChange=!0);if(i.addClass("page-on-right"),k=t.children(".page:not(.cached)"),k.length>1){for(m=0;m<k.length-2;m++)a.params.domCache?n(k[m]).addClass("cached"):(l.pageRemoveCallback(a,k[m],"left"),n(k[m]).remove());a.params.domCache?n(k[m]).addClass("cached"):(l.pageRemoveCallback(a,k[m],"left"),n(k[m]).removeLine truncated
|
||||||
|
var e=a.title?'<div class="modal-title">'+a.title+"</div>":"",f=a.title?'<div class="modal-text">'+a.text+"</div>":"",g=a.afterText?a.afterText:"",h=a.buttons&&0!==a.buttons.length?"":"modal-no-buttons";b='<div class="modal '+h+'"><div class="modal-inner">'+(e+f+g)+'</div><div class="modal-buttons">'+c+"</div></div>"}s.innerHTML=b;var i=n(s).children();return n("body").append(i[0]),i.find(".modal-button").each(function(b,c){n(c).on("click",function(c){a.buttons[b].close!==!1&&l.closeModal(i),a.buttons[b].onClick&&a.buttons[b].onClick(i,c),a.onClick&&a.onClick(i,b)})}),l.openModal(i),i[0]},l.alert=function(a,b,c){return"function"==typeof b&&(c=arguments[1],b=void 0),l.modal({text:a||"",title:"undefined"==typeof b?l.params.modalTitle:b,buttons:[{text:l.params.modalButtonOk,bold:!0,onClick:c}]})},l.confirm=function(a,b,c,d){return"function"==typeof b&&(d=arguments[2],c=arguments[1],b=void 0),l.modal({text:a||"",title:"undefined"==typeof b?l.params.modalTitle:b,buttons:[{text:l.params.modalButtonCancel,onClick:d},{text:l.params.modalButtonOk,bold:!0,onClick:c}]})},l.prompt=function(a,b,c,d){return"function"==typeof b&&(d=arguments[2],c=arguments[1],b=void 0),l.modal({text:a||"",title:"undefined"==typeof b?l.params.modalTitle:b,afterText:'<input type="text" class="modal-text-input">',buttons:[{text:l.params.modalButtonCancel},{text:l.params.modalButtonOk,bold:!0}],onClick:function(a,b){0===b&&d&&d(n(a).find(".modal-text-input").val()),1===b&&c&&c(n(a).find(".modal-text-input").val())}})},l.modalLogin=function(a,b,c,d){return"function"==typeof b&&(d=arguments[2],c=arguments[1],b=void 0),l.modal({text:a||"",title:"undefined"==typeof b?l.params.modalTitle:b,afterText:'<input type="text" name="modal-username" placeholder="'+l.params.modalUsernamePlaceholder+'" class="modal-text-input modal-text-input-double"><input type="password" name="modal-password" placeholder="'+l.params.modalPasswordPlaceholder+'" class="modal-text-input modal-text-input-double">',buttons:[{text:l.params.modalButtonCancel},{text:l.params.modalButtonOk,bold:!0}],onClick:function(a,b){var e=n(a).find('.modal-text-input[name="modal-username"]').val(),f=n(a).find('.modal-text-input[name="modal-password"]').val();0===b&&d&&d(e,f),1===b&&c&&c(e,f)}})},l.modalPassword=function(a,b,c,d){return"function"==typeof b&&(d=arguments[2],c=arguments[1],b=void 0),l.modal({text:a||"",title:"undefined"==typeof b?l.params.modalTitle:b,afterText:'<input type="password" name="modal-password" placeholder="'+l.params.modalPasswordPlaceholder+'" class="modal-text-input">',buttons:[{text:l.params.modalButtonCancel},{text:l.params.modalButtonOk,bold:!0}],onClick:function(a,b){var e=n(a).find('.modal-text-input[name="modal-password"]').val();0===b&&d&&d(e),1===b&&c&&c(e)}})},l.showPreloader=function(a){return l.modal({title:a||l.params.modalPreloaderTitle,text:'<div class="preloader"></div>'})},l.hidePreloader=function(){l.closeModal(".modal.modal-in")},l.showIndicator=function(){n("body").append('<div class="preloader-indicator-overlay"></div><div class="preloader-indicator-modal"><span class="preloader preloader-white"></span></div>')},l.hideIndicator=function(){n(".preloader-indicator-overlay, .preloader-indicator-modal").remove()},l.actions=function(a,b){var c,d,e,f=!1;1===arguments.length?b=a:l.device.ios?l.device.ipad&&(f=!0):n(window).width()>=768&&(f=!0),b=b||[],b.length>0&&!n.isArray(b[0])&&(b=[b]);var g;if(f){var h='<div class="popover actions-popover"><div class="popover-inner">{{#each this}}<div class="list-block"><ul>{{#each this}}{{#if label}}<li class="actions-popover-label {{#if color}}color-{{color}}{{/if}} {{#if bold}}actions-popover-bold{{/if}}">{{text}}</li>{{else}}<li><a href="#" class="item-link list-button {{#if color}}color-{{color}}{{/if}} {{#if bold}}actions-popover-bold{{/if}}">{{text}}</a></li>{{/if}}{{/each}}</ul></div>{{/each}}</div></div>';l._compiledTemplates.actionsPopover||(l._compiledTemplates.actionsPopover=o.compile(h));var i=l._compiledTemplates.actionsPopover(b);c=n(l.popover(i,a,!0)),d=".list-block ul",e=".list-button"}else{if(l.params.modalActionsTemplate)l._compiledTemplates.actions||(l._compiledTemplates.actions=o.compile(l.params.modalActionsTemplate)),g=l._compiledTemplates.actions(b);else{for(var j="",k=0;k<b.length;k++)for(var m=0;m<b[k].length;m++){0===m&&(j+='<div class="actions-modal-group">');var p=b[k][m],q=p.label?"actions-modal-label":"actions-modal-button";p.bold&&(q+=" actions-modal-button-bold"),p.color&&(q+=" color-"+p.color),j+='<span class="'+q+'">'+p.text+"</span>",m===b[k].length-1&&(j+="</div>")}g='<div class="actions-modal">'+j+"</div>"}s.innerHTML=g,c=n(s).children(),n("body").append(c[0]),d=".actions-modal-group",e=".actions-modal-button"}var r=c.find(d);return r.each(function(a,d){var g=a;n(d).children().each(function(a,d){var h,i=a,j=b[g][i];!f&&n(d).is(e)&&(h=n(d)),f&&n(d).find(e).length>0&&(h=n(d).find(e)),h&&h.on("click",function(a){j.close!==!1&&l.closeModal(c),j.onClick&&j.onClick(c,a)})})}),f||l.oLine truncated
|
||||||
|
}function b(){var a=B.parents(".page-content, .panel");return 0===a.length?!1:("yes"!==a.prop("scrollHandlerSet")&&(a.on("scroll",function(){clearTimeout(C)}),a.prop("scrollHandlerSet","yes")),!0)}function c(){B.addClass("active-state")}function d(){B.removeClass("active-state")}function e(a){var b="button checkbox file image radio submit input textarea".split(" ");return document.activeElement&&a!==document.activeElement&&document.activeElement!==document.body?b.indexOf(a.nodeName.toLowerCase())>=0?!1:!0:!1}function f(a){return"input"===a.nodeName.toLowerCase()&&"file"===a.type?!1:a.className.indexOf("no-fastclick")>=0?!1:!0}function g(a){if(document.activeElement===a)return!1;var b=a.nodeName.toLowerCase(),c="button checkbox file image radio submit".split(" ");return a.disabled||a.readOnly?!1:"textarea"===b?!0:"select"===b?"android"===l.device.os?!1:!0:"input"===b&&c.indexOf(a.type)<0?!0:void 0}function h(a){if(a=n(a),a.is("label")||a.parents("label").length>0){if("android"===l.device.os){var b=l.device.osVersion.split(".");return 1*b[0]>4||1*b[0]===4&&1*b[1]>=4?!1:!0}return!1}return!0}function i(b){a(b).addClass("active-state"),"which"in b&&3===b.which&&setTimeout(function(){n(".active-state").removeClass("active-state")},0)}function j(){n(".active-state").removeClass("active-state")}function k(){n(".active-state").removeClass("active-state")}function m(d){if(A=!1,d.targetTouches.length>1)return!0;if(D=f(d.target),!D)return w=!1,!0;if("ios"===l.device.os){var g=window.getSelection();if(g.rangeCount&&(!g.isCollapsed||document.activeElement===g.focusNode))return x=!0,!0;x=!1}"android"===l.device.os&&e(d.target)&&document.activeElement.blur(),w=!0,v=d.target,u=(new Date).getTime(),s=d.targetTouches[0].pageX,t=d.targetTouches[0].pageY,"ios"===l.device.os&&(y=void 0,n(v).parents().each(function(){var a=this;a.scrollHeight>a.offsetHeight&&!y&&(y=a,y.f7ScrollTop=y.scrollTop)})),d.timeStamp-z<200&&d.preventDefault(),l.params.activeState&&(B=a(d),b(d)?C=setTimeout(c,80):c())}function o(a){if(w){var b=!1,c=l.params.fastClicksDistanceThreshold;if(c){var e=a.targetTouches[0].pageX,f=a.targetTouches[0].pageY;(Math.abs(e-s)>c||Math.abs(f-t)>c)&&(b=!0)}else b=!0;b&&(w=!1,v=null,A=!0),l.params.activeState&&(clearTimeout(C),d())}}function p(a){if(clearTimeout(C),!w)return!x&&D&&a.preventDefault(),!0;if(document.activeElement===a.target)return!0;if(x||a.preventDefault(),a.timeStamp-z<200)return!0;if(z=a.timeStamp,u=0,w=!1,"ios"===l.device.os&&y&&y.scrollTop!==y.f7ScrollTop)return!1;l.params.activeState&&(c(),setTimeout(d,0)),g(v)&&v.focus(),a.preventDefault();var b=a.changedTouches[0],e=document.createEvent("MouseEvents"),f="click";return"android"===l.device.os&&"select"===v.nodeName.toLowerCase()&&(f="mousedown"),e.initMouseEvent(f,!0,!0,window,1,b.screenX,b.screenY,b.clientX,b.clientY,!1,!1,!1,!1,0,null),e.forwardedTouchEvent=!0,v.dispatchEvent(e),!1}function q(){w=!1,v=null}function r(a){var b=!1;return w?(v=null,w=!1,!0):"submit"===a.target.type&&0===a.detail?!0:(v||(b=!0),document.activeElement===v&&(b=!0),a.forwardedTouchEvent&&(b=!0),a.cancelable||(b=!0),b||(a.stopImmediatePropagation(),a.stopPropagation(),v?(h(v)||A)&&a.preventDefault():a.preventDefault(),v=null),b)}l.params.activeState&&n("html").addClass("watch-active-state");var s,t,u,v,w,x,y,z,A,B,C,D;l.support.touch?(document.addEventListener("click",r,!0),l.addGlobalEventListener("touchstart",m),l.addGlobalEventListener("touchmove",o),l.addGlobalEventListener("touchend",p),l.addGlobalEventListener("touchcancel",q)):l.params.activeState&&(l.addGlobalEventListener("mousedown",i),l.addGlobalEventListener("mousemove",j),l.addGlobalEventListener("mouseup",k))},l.initClickEvents=function(){function a(a){function b(a){return"false"===a?!1:"true"===a?!0:void 0}var c=n(this),d=c.attr("href"),e="a"===c[0].nodeName.toLowerCase();if(e)for(var f=0;f<l.params.externalLinks.length;f++){if(c.hasClass(l.params.externalLinks[f]))return;if(c[0].rel===l.params.externalLinks[f])return}if(c.hasClass("smart-select")&&l.smartSelectOpen&&l.smartSelectOpen(c),c.hasClass("open-panel")&&l.openPanel(1===n(".panel").length?n(".panel").hasClass("panel-left")?"left":"right":"right"===c.attr("data-panel")?"right":"left"),c.hasClass("close-panel")&&l.closePanel(),c.hasClass("panel-overlay")&&l.params.panelsCloseByOutside&&l.closePanel(),c.hasClass("open-popover")){var g;g=c.attr("data-popover")?c.attr("data-popover"):".popover",l.popover(g,c)}c.hasClass("close-popover")&&l.closeModal(".popover.modal-in");var h;c.hasClass("open-popup")&&(h=c.attr("data-popup")?c.attr("data-popup"):".popup",l.popup(h)),c.hasClass("close-popup")&&(h=c.attr("data-popup")?c.attr("data-popup"):".popup.modal-in",l.closeModal(h));var i;if(c.hasClass("open-login-screen")&&(i=c.attr("data-login-screen")?c.attr("data-login-screen"):".login-screen",l.loginScreen(i)),c.hasClass("close-login-screen")&&l.closeModal(".login-screen.modal-in"),c.hasClass("modal-overlay")&&(n(".modal.modal-in").length>0&&l.params.modalCloseByLine truncated
|
||||||
|
l.addNotification=function(a){if(a){"undefined"==typeof a.media&&(a.media=l.params.notificationMedia),"undefined"==typeof a.title&&(a.title=l.params.notificationTitle),"undefined"==typeof a.subtitle&&(a.subtitle=l.params.notificationSubtitle),"undefined"==typeof a.closeIcon&&(a.closeIcon=l.params.notificationCloseIcon),"undefined"==typeof a.hold&&(a.hold=l.params.notificationHold),"undefined"==typeof a.closeOnClick&&(a.closeOnClick=l.params.notificationCloseOnClick),v||(v=document.createElement("div"));var b=n(".notifications");0===b.length&&(n("body").append('<div class="notifications list-block media-list"><ul></ul></div>'),b=n(".notifications"));var c,d=b.children("ul");c=a.custom?"<li>"+a.custom+"</li>":'<li class="notification-item notification-hidden"><div class="item-content">'+(a.media?'<div class="item-media">'+a.media+"</div>":"")+'<div class="item-inner"><div class="item-title-row">'+(a.title?'<div class="item-title">'+a.title+"</div>":"")+(a.closeIcon?'<div class="item-after"><a href="#" class="close-notification"><span></span></a></div>':"")+"</div>"+(a.subtitle?'<div class="item-subtitle">'+a.subtitle+"</div>":"")+(a.message?'<div class="item-text">'+a.message+"</div>":"")+"</div></div></li>",v.innerHTML=c;var e=n(v).children();e.on("click",function(b){var c=!1;n(b.target).is(".close-notification")||n(b.target).parents(".close-notification").length>0?c=!0:(a.onClick&&a.onClick(b,e[0]),a.closeOnClick&&(c=!0)),c&&l.closeNotification(e[0])}),a.onClose&&e.data("f7NotificationOnClose",function(){a.onClose(e[0])}),a.additionalClass&&e.addClass(a.additionalClass),a.hold&&setTimeout(function(){e.length>0&&l.closeNotification(e[0])},a.hold),d.prepend(e[0]),b.show();var f=e.height();e.css("marginTop",-f+"px"),e.transition(0);{e[0].clientLeft}return e.transition(""),e.css("marginTop","0px"),b.transform("translate3d(0, 0,0)"),e.removeClass("notification-hidden"),e[0]}},l.closeNotification=function(a){if(a=n(a),0!==a.length&&!a.hasClass("notification-item-removing")){var b=n(".notifications"),c=a.height();a.css("height",c+"px").transition(0);{a[0].clientLeft}a.css("height","0px").transition("").addClass("notification-item-removing"),a.data("f7NotificationOnClose")&&a.data("f7NotificationOnClose")(),0===b.find(".notification-item:not(.notification-item-removing)").length&&b.transform(""),a.addClass("notification-hidden").transitionEnd(function(){a.remove(),0===b.find(".notification-item").length&&b.hide()})}};var w=[];return l.initPlugins=function(){for(var a in l.plugins){var b=l.plugins[a](l,l.params[a]);b&&w.push(b)}},l.pluginHook=function(a){for(var b=0;b<w.length;b++)w[b].hooks&&a in w[b].hooks&&w[b].hooks[a](arguments[1],arguments[2],arguments[3],arguments[4],arguments[5])},l.pluginPrevent=function(a){for(var b=!1,c=0;c<w.length;c++)w[c].prevents&&a in w[c].prevents&&w[c].prevents[a](arguments[1],arguments[2],arguments[3],arguments[4],arguments[5])&&(b=!0);return b},l.pluginProcess=function(a,b){for(var c=b,d=0;d<w.length;d++)w[d].preprocess&&process in w[d].preprocess&&(c=w[d].preprocess[process](b,arguments[2],arguments[3],arguments[4],arguments[5],arguments[6]));return c},l.init=function(){l.initPlugins&&l.initPlugins(),l.getDeviceInfo&&l.getDeviceInfo(),l.initFastClicks&&l.params.fastClicks&&l.initFastClicks(),l.initClickEvents&&l.initClickEvents(),n(".page").each(function(){var a=n(this),b=a.parents("."+l.params.viewClass);if(0!==b.length){var c=b[0].f7View||!1,d=c&&c.url?c.url:!1;b&&b.attr("data-page",a.attr("data-page")||void 0),l.pageInitCallback(c,this,d,"center")}}),l.initResize&&l.initResize(),l.initPushState&&l.params.pushState&&l.initPushState(),l.initSwipeout&&l.params.swipeout&&l.initSwipeout(),l.initSortable&&l.params.sortable&&l.initSortable(),l.initSwipePanels&&l.params.swipePanel&&l.initSwipePanels(),l.params.onAppInit&&l.params.onAppInit(),l.pluginHook("appInit")},l.params.init&&l.init(),l};var a=function(){var a=function(a){var b=this,c=0;for(c=0;c<a.length;c++)b[c]=a[c];return b.length=a.length,this},b=function(b,c){var d=[],e=0;if(b&&!c&&b instanceof a)return b;if(b)if("string"==typeof b){var f=(c||document).querySelectorAll(b);for(e=0;e<f.length;e++)d.push(f[e])}else if(b.nodeType||b===window||b===document)d.push(b);else if(b.length>0&&b[0].nodeType)for(e=0;e<b.length;e++)d.push(b[e]);return new a(d)};a.prototype={addClass:function(a){if("undefined"==typeof a)return this;for(var b=a.split(" "),c=0;c<b.length;c++)for(var d=0;d<this.length;d++)this[d].classList.add(b[c]);return this},removeClass:function(a){for(var b=a.split(" "),c=0;c<b.length;c++)for(var d=0;d<this.length;d++)this[d].classList.remove(b[c]);return this},hasClass:function(a){return this[0]?this[0].classList.contains(a):!1},toggleClass:function(a){for(var b=a.split(" "),c=0;c<b.length;c++)for(var d=0;d<this.length;d++)this[d].classList.toggle(b[c]);return this},attr:function(a,b){if("undefined"==typeof b)return this[0]?this[0].getAttribute(a):void 0;for(var c=0;c<this.length;c++)this[c].setAttribute(a,b);return tLine truncated
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Required meta tags-->
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="framework7.min.css">
|
||||||
|
|
||||||
|
<title>Smart.c mjpg example</title>
|
||||||
|
<style type="text/css">
|
||||||
|
.image img {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- Status bar overlay for full screen mode (PhoneGap) -->
|
||||||
|
<div class="statusbar-overlay"></div>
|
||||||
|
|
||||||
|
<!-- Views -->
|
||||||
|
<div class="views">
|
||||||
|
<!-- Your main view, should have "view-main" class -->
|
||||||
|
<div class="view view-main">
|
||||||
|
<!-- Top Navbar-->
|
||||||
|
<div class="navbar">
|
||||||
|
<div class="navbar-inner">
|
||||||
|
<!-- We need cool sliding animation on title element, so we have additional "sliding" class -->
|
||||||
|
<div class="left">
|
||||||
|
<a href="docs/docs/doc.html" class="link" onclick="location='docs/docs/doc.html'">
|
||||||
|
<span>About</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="center sliding">Remote Camera</div>
|
||||||
|
<div class="right sliding">
|
||||||
|
<a href="https://github.com/cesanta/mongoose/tree/master/examples/raspberry_pi_mjpeg_led" class="link" onclick="location='https://github.com/cesanta/mongoose/tree/master/examples/raspberry_pi_mjpeg_led'">
|
||||||
|
<span>Github</span>
|
||||||
|
<i class="icon icon-next"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Pages container, because we use fixed-through navbar and toolbar, it has additional appropriate classes-->
|
||||||
|
<div class="pages navbar-through toolbar-through">
|
||||||
|
<!-- Page, "data-page" contains page name -->
|
||||||
|
<div data-page="index" class="page">
|
||||||
|
<!-- Scrollable page content -->
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="content-block-title">Camera View</div>
|
||||||
|
<div class="content-block image">
|
||||||
|
<img src="/mjpg">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content-block-title">Device Control</div>
|
||||||
|
<div class="list-block">
|
||||||
|
|
||||||
|
<form action="/api" method="GET" enctype="application/json"
|
||||||
|
id="form-control">
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<!-- Switch (Checkbox) -->
|
||||||
|
<li>
|
||||||
|
<div class="item-content">
|
||||||
|
<div class="item-inner">
|
||||||
|
<div class="item-title label">LED on/off</div>
|
||||||
|
<div class="item-input">
|
||||||
|
<label class="label-switch">
|
||||||
|
<input type="checkbox" name="onoff">
|
||||||
|
<div class="checkbox"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="framework7.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var myApp = new Framework7({
|
||||||
|
pushState: true,
|
||||||
|
swipePanel: 'left',
|
||||||
|
// ... other parameters
|
||||||
|
});
|
||||||
|
Dom7(document).on('change', '#form-control', function(ev) {
|
||||||
|
var data = myApp.formToJSON('#form-control');
|
||||||
|
var json = JSON.stringify(data);
|
||||||
|
Dom7.ajax({
|
||||||
|
url: '/api',
|
||||||
|
method: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: json
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
PROG = restful_client
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
/* RESTful server host and request URI */
|
||||||
|
static const char *s_target_address = "ajax.googleapis.com:80";
|
||||||
|
static const char *s_request = "/ajax/services/search/web?v=1.0&q=cesanta";
|
||||||
|
|
||||||
|
static int s_exit_flag = 0;
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
int connect_status;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_CONNECT:
|
||||||
|
connect_status = * (int *) ev_data;
|
||||||
|
if (connect_status == 0) {
|
||||||
|
printf("Connected to %s, sending request...\n", s_target_address);
|
||||||
|
mg_printf(nc, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
|
||||||
|
s_request, s_target_address);
|
||||||
|
} else {
|
||||||
|
printf("Error connecting to %s: %s\n",
|
||||||
|
s_target_address, strerror(connect_status));
|
||||||
|
s_exit_flag = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_EV_HTTP_REPLY:
|
||||||
|
printf("Got reply:\n%.*s\n", (int) hm->body.len, hm->body.p);
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
s_exit_flag = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
nc = mg_connect(&mgr, s_target_address, ev_handler);
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
|
||||||
|
printf("Starting RESTful client against %s\n", s_target_address);
|
||||||
|
while (s_exit_flag == 0) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
PROG = restful_server
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
ifeq ($(SSL), openssl)
|
||||||
|
CFLAGS += -DMG_ENABLE_SSL -lssl -lcrypto -lcrypto
|
||||||
|
else ifeq ($(SSL), krypton)
|
||||||
|
CFLAGS += -DMG_ENABLE_SSL ../../../krypton/krypton.c
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /MD /DMG_ENABLE_THREADS /Fe$@ advapi32.lib
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>RESTful API demo</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style type="text/css">
|
||||||
|
* { outline: none; }
|
||||||
|
body {
|
||||||
|
background-color: #789; margin: 0;
|
||||||
|
padding: 0; font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||||
|
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
div.content {
|
||||||
|
width: 800px; margin: 2em auto; padding: 20px 50px;
|
||||||
|
background-color: #fff; border-radius: 1em;
|
||||||
|
}
|
||||||
|
label { display: inline-block; min-width: 7em; }
|
||||||
|
input { border: 1px solid #ccc; padding: 0.2em; }
|
||||||
|
a:link, a:visited { color: #69c; text-decoration: none; }
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
body { background-color: #fff; }
|
||||||
|
div.content { width: auto; margin: 0 auto; padding: 1em; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||||
|
<script language="javascript" type="text/javascript">
|
||||||
|
jQuery(function() {
|
||||||
|
|
||||||
|
$(document).on('keyup', '#n1, #n2', function() {
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/sum',
|
||||||
|
method: 'POST',
|
||||||
|
dataType: 'json',
|
||||||
|
data: { n1: $('#n1').val(), n2: $('#n2').val() },
|
||||||
|
success: function(json) {
|
||||||
|
$('#result').html(json.result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<h1>RESTful API demo.</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This page demonstrates how Mongoose could be used to implement
|
||||||
|
RESTful APIs. Start typing numbers into the text fields below.
|
||||||
|
Browser sends two numbers to <tt>/api/v1/sum</tt> URI.
|
||||||
|
Web server calclulates the sum and returns the result.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label>Number 1:</label> <input type="text" id="n1" />
|
||||||
|
</div><div>
|
||||||
|
<label>Number 2:</label> <input type="text" id="n2" />
|
||||||
|
</div><div>
|
||||||
|
<label>Result:</label> <span id="result"> </span>
|
||||||
|
</div><div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static const char *s_http_port = "8000";
|
||||||
|
static struct mg_serve_http_opts s_http_server_opts;
|
||||||
|
|
||||||
|
static void handle_sum_call(struct mg_connection *nc, struct http_message *hm) {
|
||||||
|
char n1[100], n2[100];
|
||||||
|
double result;
|
||||||
|
|
||||||
|
/* Get form variables */
|
||||||
|
mg_get_http_var(&hm->body, "n1", n1, sizeof(n1));
|
||||||
|
mg_get_http_var(&hm->body, "n2", n2, sizeof(n2));
|
||||||
|
|
||||||
|
/* Send headers */
|
||||||
|
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
||||||
|
|
||||||
|
/* Compute the result and send it back as a JSON object */
|
||||||
|
result = strtod(n1, NULL) + strtod(n2, NULL);
|
||||||
|
mg_printf_http_chunk(nc, "{ \"result\": %lf }", result);
|
||||||
|
mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_HTTP_REQUEST:
|
||||||
|
if (mg_vcmp(&hm->uri, "/api/v1/sum") == 0) {
|
||||||
|
handle_sum_call(nc, hm); /* Handle RESTful call */
|
||||||
|
} else if (mg_vcmp(&hm->uri, "/printcontent") == 0) {
|
||||||
|
char buf[100] = {0};
|
||||||
|
memcpy(buf, hm->body.p,
|
||||||
|
sizeof(buf) - 1 < hm->body.len? sizeof(buf) - 1 : hm->body.len);
|
||||||
|
printf("%s\n", buf);
|
||||||
|
} else {
|
||||||
|
mg_serve_http(nc, hm, s_http_server_opts); /* Serve static content */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
int i;
|
||||||
|
char *cp;
|
||||||
|
#ifdef MG_ENABLE_SSL
|
||||||
|
const char *ssl_cert = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
|
||||||
|
/* Process command line options to customize HTTP server */
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-D") == 0 && i + 1 < argc) {
|
||||||
|
mgr.hexdump_file = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-d") == 0 && i + 1 < argc) {
|
||||||
|
s_http_server_opts.document_root = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
|
||||||
|
s_http_port = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-a") == 0 && i + 1 < argc) {
|
||||||
|
s_http_server_opts.auth_domain = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc) {
|
||||||
|
s_http_server_opts.global_auth_file = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
|
||||||
|
s_http_server_opts.per_directory_auth_file = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-r") == 0 && i + 1 < argc) {
|
||||||
|
s_http_server_opts.url_rewrites = argv[++i];
|
||||||
|
#ifndef MG_DISABLE_CGI
|
||||||
|
} else if (strcmp(argv[i], "-i") == 0 && i + 1 < argc) {
|
||||||
|
s_http_server_opts.cgi_interpreter = argv[++i];
|
||||||
|
#endif
|
||||||
|
#ifdef MG_ENABLE_SSL
|
||||||
|
} else if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) {
|
||||||
|
ssl_cert = argv[++i];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set HTTP server options */
|
||||||
|
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||||
|
if (nc == NULL) {
|
||||||
|
fprintf(stderr, "Error starting server on port %s\n", s_http_port);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MG_ENABLE_SSL
|
||||||
|
if (ssl_cert != NULL) {
|
||||||
|
const char *err_str = mg_set_ssl(nc, ssl_cert, NULL);
|
||||||
|
if (err_str != NULL) {
|
||||||
|
fprintf(stderr, "Error loading SSL cert: %s\n", err_str);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
s_http_server_opts.document_root = ".";
|
||||||
|
s_http_server_opts.enable_directory_listing = "yes";
|
||||||
|
|
||||||
|
/* Use current binary directory as document root */
|
||||||
|
if (argc > 0 && ((cp = strrchr(argv[0], '/')) != NULL ||
|
||||||
|
(cp = strrchr(argv[0], '/')) != NULL)) {
|
||||||
|
*cp = '\0';
|
||||||
|
s_http_server_opts.document_root = argv[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Starting RESTful server on port %s\n", s_http_port);
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
PROG = restful_server_s3
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
ifeq ($(SSL), openssl)
|
||||||
|
CFLAGS += -DMG_ENABLE_SSL -lssl -lcrypto -lcrypto
|
||||||
|
else ifeq ($(SSL), krypton)
|
||||||
|
CFLAGS += -DMG_ENABLE_SSL ../../../krypton/krypton.c
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /MD /DMG_ENABLE_THREADS /Fe$@ advapi32.lib
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
RESTful server with Amazon S3 upload example
|
||||||
|
============================================
|
||||||
|
|
||||||
|
This example demonstrates how Mongoose could be used to implement a RESTful
|
||||||
|
service that uses another RESTful service to handle it's own API call.
|
||||||
|
This example takes form data and uploads it as a file to Amazon S3.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Amazon S3 account security credentials: Access Key ID and Secret Access
|
||||||
|
Key ID. Get them from the Amazon IAM console.
|
||||||
|
- Amazon S3 bucket.
|
||||||
|
|
||||||
|
## Building and running the example
|
||||||
|
|
||||||
|
$ git clone https://github.com/cesanta/mongoose.git
|
||||||
|
$ cd mongoose/examples/restful_server_s3
|
||||||
|
$ make
|
||||||
|
$ ./restful_server_s3 -a ACCESS_KEY_ID -s SECRET_ACCESS_KEY_ID
|
||||||
|
Starting RESTful server on port 8000
|
||||||
|
|
||||||
|
Then, open a browser on `http://localhost:8000`
|
||||||
|
|
||||||
|
Note: If you're getting a *Temporary Redirect* error, look what is the
|
||||||
|
Endpoint value is. It's likely that you have something like
|
||||||
|
`BUCKET_NAME.S3_ZONE.amazonaws.com`.
|
||||||
|
Change the *Host* field to `S3_ZONE.amazonaws.com and retry`.
|
||||||
|
|
||||||
|
## Screenshot
|
||||||
|
|
||||||
|

|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>RESTful API demo</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style type="text/css">
|
||||||
|
* { outline: none; }
|
||||||
|
body {
|
||||||
|
background-color: #789; margin: 0;
|
||||||
|
padding: 0; font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||||
|
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
div.content {
|
||||||
|
width: 800px; margin: 2em auto; padding: 20px 50px;
|
||||||
|
background-color: #fff; border-radius: 1em;
|
||||||
|
}
|
||||||
|
label { display: inline-block; min-width: 12em; }
|
||||||
|
input { border: 1px solid #ccc; padding: 0.2em; min-width: 20em; }
|
||||||
|
a:link, a:visited { color: #69c; text-decoration: none; }
|
||||||
|
#result {
|
||||||
|
background: #ffc; min-height: 3em; border: 1px solid #ccc;
|
||||||
|
white-space: pre-wrap; font-size: 85%;
|
||||||
|
}
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
body { background-color: #fff; }
|
||||||
|
div.content { width: auto; margin: 0 auto; padding: 1em; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||||
|
<script language="javascript" type="text/javascript">
|
||||||
|
jQuery(function() {
|
||||||
|
|
||||||
|
$(document).on('click', '#upload', function() {
|
||||||
|
var data = {};
|
||||||
|
$('#upload_form [name]').each(function(index, el) {
|
||||||
|
data[$(el).attr('name')] = $(el).val();
|
||||||
|
});
|
||||||
|
$('#result').text('');
|
||||||
|
$.ajax({
|
||||||
|
url: '/upload',
|
||||||
|
method: 'POST',
|
||||||
|
dataType: 'html',
|
||||||
|
data: data,
|
||||||
|
success: function(data) {
|
||||||
|
$('#result').text(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<h1>RESTful server with Amazon S3 integration demo.</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This page demonstrates how Mongoose could be used to implement
|
||||||
|
a RESTful service that uses another RESTful service to handle
|
||||||
|
it's own API call. This example takes form data and uploads it as a file
|
||||||
|
to Amazon S3.
|
||||||
|
Open S3 console and create a bucket for testing. Fill out correct
|
||||||
|
bucket name in the fields below.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>If you're getting a "Temporary Redirect" error, look what is the
|
||||||
|
<b>Endpoint</b> value is. It's likely that you have something like
|
||||||
|
<b>BUCKET_NAME.S3_ZONE.amazonaws.com</b>. Change
|
||||||
|
the <b>Host</b> field to <b>S3_ZONE.amazonaws.com</b> and retry.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div id="upload_form">
|
||||||
|
<div>
|
||||||
|
<label>Bucket name:</label> <input type="text" name="bucket"
|
||||||
|
value="my_uploads"/>
|
||||||
|
</div><div>
|
||||||
|
<label>File name:</label> <input type="text" name="file_name"
|
||||||
|
value="myfile.txt"/>
|
||||||
|
</div><div>
|
||||||
|
<label>File content:</label> <input type="text" name="file_data"
|
||||||
|
value=":-)"/>
|
||||||
|
</div><div>
|
||||||
|
<label>Host:</label> <input type="text" name="host"
|
||||||
|
value="s3.amazonaws.com"/>
|
||||||
|
</div><div>
|
||||||
|
<label></label>
|
||||||
|
<button id="upload">Upload</button>
|
||||||
|
</div><div>
|
||||||
|
<label>Result:</label> <pre id="result"> </pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static const char *s_http_port = "8000";
|
||||||
|
static const char *s_access_key_id = NULL;
|
||||||
|
static const char *s_secret_access_key = NULL;
|
||||||
|
static struct mg_serve_http_opts s_http_server_opts;
|
||||||
|
|
||||||
|
static void send_error_result(struct mg_connection *nc, const char *msg) {
|
||||||
|
mg_printf_http_chunk(nc, "Error: %s", msg);
|
||||||
|
mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void link_conns(struct mg_connection *nc1, struct mg_connection *nc2) {
|
||||||
|
nc1->user_data = nc2;
|
||||||
|
nc2->user_data = nc1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unlink_conns(struct mg_connection *nc1) {
|
||||||
|
struct mg_connection *nc2 = (struct mg_connection *) nc1->user_data;
|
||||||
|
if (nc1->user_data != NULL) {
|
||||||
|
nc1->user_data = NULL;
|
||||||
|
nc2->user_data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* S3 client handler */
|
||||||
|
static void s3_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
struct mg_connection *nc2 = (struct mg_connection *) nc->user_data;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_HTTP_REPLY:
|
||||||
|
if (nc2 != NULL) {
|
||||||
|
mg_printf_http_chunk(nc2, "Error: %.*s", (int) hm->message.len,
|
||||||
|
hm->message.p);
|
||||||
|
mg_send_http_chunk(nc2, "", 0);
|
||||||
|
}
|
||||||
|
unlink_conns(nc);
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
break;
|
||||||
|
case MG_EV_CLOSE:
|
||||||
|
unlink_conns(nc);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_s3_request(struct mg_connection *nc, const char *file_name,
|
||||||
|
const char *file_data, const char *host,
|
||||||
|
const char *bucket) {
|
||||||
|
char host_port[100];
|
||||||
|
struct mg_connection *s3_conn;
|
||||||
|
|
||||||
|
snprintf(host_port, sizeof(host_port), "%s:80", host);
|
||||||
|
s3_conn = mg_connect(nc->mgr, host_port, s3_handler);
|
||||||
|
|
||||||
|
if (s3_conn == NULL) {
|
||||||
|
send_error_result(nc, "s3 connection failed");
|
||||||
|
} else {
|
||||||
|
const char *content_type = "text/plain", *method = "PUT";
|
||||||
|
char date[100], to_sign[500], signature[100], sha1[20], req[1000];
|
||||||
|
time_t now = time(NULL);
|
||||||
|
|
||||||
|
link_conns(nc, s3_conn);
|
||||||
|
strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
|
||||||
|
mg_set_protocol_http_websocket(s3_conn);
|
||||||
|
|
||||||
|
/* Prepare S3 authorization header */
|
||||||
|
snprintf(to_sign, sizeof(to_sign), "%s\n\n%s\n%s\n/%s/%s", method,
|
||||||
|
content_type, date, bucket, file_name);
|
||||||
|
cs_hmac_sha1((unsigned char *) s_secret_access_key,
|
||||||
|
strlen(s_secret_access_key), (unsigned char *) to_sign,
|
||||||
|
strlen(to_sign), (unsigned char *) sha1);
|
||||||
|
mg_base64_encode((unsigned char *) sha1, sizeof(sha1), signature);
|
||||||
|
snprintf(req, sizeof(req),
|
||||||
|
"%s /%s HTTP/1.1\r\n"
|
||||||
|
"Host: %s.%s\r\n"
|
||||||
|
"Date: %s\r\n"
|
||||||
|
"Content-Type: %s\r\n"
|
||||||
|
"Content-Length: %lu\r\n"
|
||||||
|
"Authorization: AWS %s:%s\r\n"
|
||||||
|
"\r\n",
|
||||||
|
method, file_name, bucket, host, date, content_type,
|
||||||
|
(unsigned long) strlen(file_data), s_access_key_id, signature);
|
||||||
|
mg_printf(s3_conn, "%s%s", req, file_data);
|
||||||
|
/* S3 request sent, wait for a reply */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_api_call(struct mg_connection *nc, struct http_message *hm) {
|
||||||
|
char file_name[100], file_data[100], host[100], bucket[100];
|
||||||
|
|
||||||
|
/* Get form variables */
|
||||||
|
mg_get_http_var(&hm->body, "file_name", file_name, sizeof(file_name));
|
||||||
|
mg_get_http_var(&hm->body, "file_data", file_data, sizeof(file_data));
|
||||||
|
mg_get_http_var(&hm->body, "host", host, sizeof(host));
|
||||||
|
mg_get_http_var(&hm->body, "bucket", bucket, sizeof(bucket));
|
||||||
|
|
||||||
|
/* Send headers */
|
||||||
|
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
||||||
|
|
||||||
|
/* Send body */
|
||||||
|
if (file_name[0] == '\0' || file_data[0] == '\0' || bucket[0] == '\0') {
|
||||||
|
send_error_result(nc, "bad input");
|
||||||
|
} else {
|
||||||
|
send_s3_request(nc, file_name, file_data, host, bucket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Server handler */
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_HTTP_REQUEST:
|
||||||
|
if (mg_vcmp(&hm->uri, "/upload") == 0) {
|
||||||
|
handle_api_call(nc, hm); /* Handle RESTful call */
|
||||||
|
} else {
|
||||||
|
mg_serve_http(nc, hm, s_http_server_opts); /* Serve static content */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_EV_CLOSE:
|
||||||
|
unlink_conns(nc);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
int i;
|
||||||
|
char *cp;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
s_http_server_opts.document_root = ".";
|
||||||
|
s_http_server_opts.enable_directory_listing = "yes";
|
||||||
|
|
||||||
|
/* Use current binary directory as document root */
|
||||||
|
if (argc > 0 && ((cp = strrchr(argv[0], '/')) != NULL ||
|
||||||
|
(cp = strrchr(argv[0], '/')) != NULL)) {
|
||||||
|
*cp = '\0';
|
||||||
|
s_http_server_opts.document_root = argv[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process command line options to customize HTTP server */
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-D") == 0 && i + 1 < argc) {
|
||||||
|
mgr.hexdump_file = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
|
||||||
|
s_http_port = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-a") == 0 && i + 1 < argc) {
|
||||||
|
s_access_key_id = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) {
|
||||||
|
s_secret_access_key = argv[++i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_access_key_id == NULL || s_secret_access_key == NULL) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: %s -a access_key_id -s s_secret_access_key "
|
||||||
|
"[-p port] [-D hexdump_file]\n",
|
||||||
|
argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Starting RESTful server on port %s\n", s_http_port);
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. -pthread -DMG_ENABLE_SSL -DMG_ENABLE_IPV6 -DMG_ENABLE_THREADS -lssl -lcrypto $(CFLAGS_EXTRA) $(MODULE_CFLAGS)
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /DMG_ENABLE_SSL /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
PROG = settings_panel
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. -DMG_ENABLE_SSL -lssl -lcrypto $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
struct device_settings {
|
||||||
|
char setting1[100];
|
||||||
|
char setting2[100];
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *s_http_port = "8000";
|
||||||
|
static struct mg_serve_http_opts s_http_server_opts;
|
||||||
|
static struct device_settings s_settings = { "value1", "value2" };
|
||||||
|
|
||||||
|
static void handle_save(struct mg_connection *nc, struct http_message *hm) {
|
||||||
|
/* Get form variables and store settings values */
|
||||||
|
mg_get_http_var(&hm->body, "setting1", s_settings.setting1,
|
||||||
|
sizeof(s_settings.setting1));
|
||||||
|
mg_get_http_var(&hm->body, "setting2", s_settings.setting2,
|
||||||
|
sizeof(s_settings.setting2));
|
||||||
|
|
||||||
|
/* Send response */
|
||||||
|
mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n%.*s",
|
||||||
|
(unsigned long) hm->body.len, (int) hm->body.len, hm->body.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_ssi_call(struct mg_connection *nc, const char *param) {
|
||||||
|
if (strcmp(param, "setting1") == 0) {
|
||||||
|
mg_printf_html_escape(nc, "%s", s_settings.setting1);
|
||||||
|
} else if (strcmp(param, "setting2") == 0) {
|
||||||
|
mg_printf_html_escape(nc, "%s", s_settings.setting2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_HTTP_REQUEST:
|
||||||
|
if (mg_vcmp(&hm->uri, "/save") == 0) {
|
||||||
|
handle_save(nc, hm); /* Handle RESTful call */
|
||||||
|
} else {
|
||||||
|
mg_serve_http(nc, hm, s_http_server_opts); /* Serve static content */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_EV_SSI_CALL:
|
||||||
|
handle_ssi_call(nc, ev_data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
char *p, path[512];
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
s_http_server_opts.document_root = "./web_root";
|
||||||
|
s_http_server_opts.auth_domain = "example.com";
|
||||||
|
//mgr.hexdump_file = "/dev/stdout";
|
||||||
|
|
||||||
|
/* If our current directory */
|
||||||
|
if (argc > 0 && (p = strrchr(argv[0], '/'))) {
|
||||||
|
snprintf(path, sizeof(path), "%.*s/web_root", (int)(p - argv[0]), argv[0]);
|
||||||
|
s_http_server_opts.document_root = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Starting device configurator on port %s\n", s_http_port);
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,104 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>RESTful API demo</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style type="text/css">
|
||||||
|
* { outline: none; }
|
||||||
|
body {
|
||||||
|
background-color: #789; margin: 0;
|
||||||
|
padding: 0; font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||||
|
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
div.content {
|
||||||
|
width: 800px; margin: 2em auto; padding: 20px 50px;
|
||||||
|
background-color: #fff; border-radius: 1em;
|
||||||
|
}
|
||||||
|
code { background: #eee; padding: 0 0.3em; border-radius: 0.2em; }
|
||||||
|
label { display: inline-block; min-width: 5em; }
|
||||||
|
input { border: 1px solid #ccc; padding: 0.2em; margin-right: 2em; }
|
||||||
|
a:link, a:visited { color: #69c; text-decoration: none; }
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
div.content { width: auto; margin: 2em; padding: 1em; }
|
||||||
|
}
|
||||||
|
fieldset { border: 1px solid #ccc; padding: 1em; }
|
||||||
|
#result {
|
||||||
|
background: #cfc; border: 1px solid #ccc; padding: 2px 1em;
|
||||||
|
white-space: pre-wrap; font-size: 85%; display: none; text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script src="jquery-1.11.3.min.js"></script>
|
||||||
|
<script language="javascript" type="text/javascript">
|
||||||
|
jQuery(function() {
|
||||||
|
|
||||||
|
$(document).on('submit', '#settings_form', function() {
|
||||||
|
var data = {};
|
||||||
|
$('#settings_form [name]').each(function(index, el) {
|
||||||
|
data[$(el).attr('name')] = $(el).val();
|
||||||
|
});
|
||||||
|
$('#result').text('');
|
||||||
|
$.ajax({
|
||||||
|
url: '/save',
|
||||||
|
method: 'POST',
|
||||||
|
dataType: 'html',
|
||||||
|
data: data,
|
||||||
|
success: function(data) {
|
||||||
|
$('#result').text('saved').show().fadeOut(2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<img src="mongoose.jpg" style="float:right; height: 50px; border-radius: 3px;">
|
||||||
|
<h1>Device Configurator demo.</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This page demonstrates how Mongoose could be used to implement
|
||||||
|
device settings page.</p>
|
||||||
|
|
||||||
|
<h3>How to show device parameters on the page</h3>
|
||||||
|
<p>This page has embedded
|
||||||
|
<code><!--#call parameter_name --></code> blocks. For each such
|
||||||
|
block, mongoose triggers <code>MG_EV_SSI_CALL</code> event, passing
|
||||||
|
<code>parameter_name</code> string as an event parameter. A callback
|
||||||
|
then can print some content, which will replace the
|
||||||
|
<code><!--#call parameter_name --></code> block.
|
||||||
|
Take a look at <code>handle_ssi_call()</code> to see how that is done.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>How to save updated values</h3>
|
||||||
|
<p>When Save button is clicked, this page makes Ajax call and passes
|
||||||
|
values to the backend in a POST request. Backend extracts values from
|
||||||
|
the POST request and updates the configuration. Take a look at
|
||||||
|
<code>handle_save()</code> to see how that is done.</p>
|
||||||
|
|
||||||
|
|
||||||
|
<p>You can change values, click Save button, refresh this page - make sure
|
||||||
|
settings values stay intact between refreshes. </p>
|
||||||
|
|
||||||
|
<form method="POST" id="settings_form">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Device settings</legend>
|
||||||
|
<label>Setting 1:</label> <input type="text"
|
||||||
|
name="setting1" value="<!--#call setting1 -->" >
|
||||||
|
<label>Setting 2:</label> <input type="text"
|
||||||
|
name="setting2" value="<!--#call setting2 -->" >
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div style="margin: 1em 0;">
|
||||||
|
<button type="submit">Save</button>
|
||||||
|
</div>
|
||||||
|
<div id="result"> </div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
|
||||||
|
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^Line truncated
|
||||||
|
|
||||||
|
return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var
Line truncated
|
||||||
|
return new Za.prototype.init(a,b,c,d,e)}m.Tween=Za,Za.prototype={constructor:Za,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")},cur:function(){var a=Za.propHooks[this.prop];return a&&a.get?a.get(this):Za.propHooks._default.get(this)},run:function(a){var b,c=Za.propHooks[this.prop];return this.options.duration?this.pos=b=m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Za.propHooks._default.set(this),this}},Za.prototype.init.prototype=Za.prototype,Za.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Za.propHooks.scrollTop=Za.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Za.prototype.init,m.fx.step={};var $a,_a,ab=/^(?:toggle|show|hide)$/,bb=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cb=/queueHooks$/,db=[ib],eb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bb.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bb.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fb(){return setTimeout(function(){$a=void 0}),$a=m.now()}function gb(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hb(a,b,c){for(var d,e=(eb[b]||[]).concat(eb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fa(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fa(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ab.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fa(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hb(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=db.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$a||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$a||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);g>f;f++)if(d=db[f].call(j,a,k,j.opts))return d;return m.map(k,hb,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kb,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],eb[cLine truncated
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
PROG = simplest_web_server
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) 2015 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static const char *s_http_port = "8000";
|
||||||
|
static struct mg_serve_http_opts s_http_server_opts;
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *p) {
|
||||||
|
if (ev == MG_EV_HTTP_REQUEST) {
|
||||||
|
mg_serve_http(nc, p, s_http_server_opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||||
|
|
||||||
|
// Set up HTTP server parameters
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
s_http_server_opts.document_root = "."; // Serve current directory
|
||||||
|
s_http_server_opts.enable_directory_listing = "yes";
|
||||||
|
|
||||||
|
printf("Starting web server on port %s\n", s_http_port);
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -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-----
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
PROG = echo_server
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA)
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(PROG).exe: $(SOURCES)
|
||||||
|
cl $(SOURCES) /I../.. /MD /Fe$@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// This software 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/>.
|
||||||
|
//
|
||||||
|
// You are free to use this software 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.
|
||||||
|
//
|
||||||
|
// Alternatively, you can license this software under a commercial
|
||||||
|
// license, as set out in <https://www.cesanta.com/license>.
|
||||||
|
//
|
||||||
|
// $Date: 2014-09-28 05:04:41 UTC $
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *p) {
|
||||||
|
struct mbuf *io = &nc->recv_mbuf;
|
||||||
|
(void) p;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_RECV:
|
||||||
|
mg_send(nc, io->buf, io->len); // Echo message back
|
||||||
|
mbuf_remove(io, io->len); // Discard message from recv buffer
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
const char *port1 = "1234", *port2 = "127.0.0.1:17000";
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
mg_bind(&mgr, port1, ev_handler);
|
||||||
|
mg_bind(&mgr, port2, ev_handler);
|
||||||
|
|
||||||
|
printf("Starting echo mgr on ports %s, %s\n", port1, port2);
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
// 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 <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>";
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_context *ctx;
|
|
||||||
const char *options[] = {"listening_ports", "8080", NULL};
|
|
||||||
|
|
||||||
ctx = mg_start(&callback, NULL, options);
|
|
||||||
getchar(); // Wait until user hits "enter"
|
|
||||||
pause();
|
|
||||||
mg_stop(ctx);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
// Copyright (c) 2004-2012 Sergey Lyubka
|
|
||||||
// This file is a part of mongoose project, http://github.com/valenok/mongoose
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
static 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;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare frame
|
|
||||||
reply[0] = 0x81; // text, FIN set
|
|
||||||
reply[1] = msg_len;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_context *ctx;
|
|
||||||
const char *options[] = {
|
|
||||||
"listening_ports", "8080",
|
|
||||||
"document_root", "websocket_html_root",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx = mg_start(&callback, NULL, options);
|
|
||||||
getchar(); // Wait until user hits "enter"
|
|
||||||
mg_stop(ctx);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Copyright (c) 2014 Cesanta Software
|
||||||
|
# All rights reserved
|
||||||
|
|
||||||
|
PROG = websocket_chat
|
||||||
|
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
|
||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES)
|
||||||
|
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<!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: #789; 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 div = document.createElement('div');
|
||||||
|
div.innerHTML = ev.data;
|
||||||
|
document.getElementById('messages').appendChild(div);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
document.getElementById('send_button').onclick = function(ev) {
|
||||||
|
var msg = document.getElementById('send_input').value;
|
||||||
|
document.getElementById('send_input').value = '';
|
||||||
|
ws.send(msg);
|
||||||
|
};
|
||||||
|
document.getElementById('send_input').onkeypress = function(ev) {
|
||||||
|
if (ev.keyCode == 13 || ev.which == 13) {
|
||||||
|
document.getElementById('send_button').click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<h1>Websocket PubSub Demonstration</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This page demonstrates how Mongoose 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 the server, making each browser window a websocket client.
|
||||||
|
Send messages, and see messages sent by other clients.
|
||||||
|
</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,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static sig_atomic_t s_signal_received = 0;
|
||||||
|
static const char *s_http_port = "8000";
|
||||||
|
static struct mg_serve_http_opts s_http_server_opts;
|
||||||
|
|
||||||
|
static void signal_handler(int sig_num) {
|
||||||
|
signal(sig_num, signal_handler); // Reinstantiate signal handler
|
||||||
|
s_signal_received = sig_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_websocket(const struct mg_connection *nc) {
|
||||||
|
return nc->flags & MG_F_IS_WEBSOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void broadcast(struct mg_connection *nc, const char *msg, size_t len) {
|
||||||
|
struct mg_connection *c;
|
||||||
|
char buf[500];
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%p %.*s", nc, (int) len, msg);
|
||||||
|
for (c = mg_next(nc->mgr, NULL); c != NULL; c = mg_next(nc->mgr, c)) {
|
||||||
|
mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, buf, strlen(buf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_HTTP_REQUEST:
|
||||||
|
/* Usual HTTP request - serve static files */
|
||||||
|
mg_serve_http(nc, hm, s_http_server_opts);
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
break;
|
||||||
|
case MG_EV_WEBSOCKET_HANDSHAKE_DONE:
|
||||||
|
/* New websocket connection. Tell everybody. */
|
||||||
|
broadcast(nc, "joined", 6);
|
||||||
|
break;
|
||||||
|
case MG_EV_WEBSOCKET_FRAME:
|
||||||
|
/* New websocket message. Tell everybody. */
|
||||||
|
broadcast(nc, (char *) wm->data, wm->size);
|
||||||
|
break;
|
||||||
|
case MG_EV_CLOSE:
|
||||||
|
/* Disconnect. Tell everybody. */
|
||||||
|
if (is_websocket(nc)) {
|
||||||
|
broadcast(nc, "left", 4);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
|
||||||
|
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||||
|
s_http_server_opts.document_root = ".";
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
|
||||||
|
printf("Started on port %s\n", s_http_port);
|
||||||
|
while (s_signal_received == 0) {
|
||||||
|
mg_mgr_poll(&mgr, 200);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
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>
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
LOCAL_PATH := $(call my-dir)/..
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := -std=c99 -O2 -W -Wall -pthread -pipe $(COPT)
|
||||||
|
LOCAL_MODULE := mongoose
|
||||||
|
LOCAL_SRC_FILES := examples/hello_world/hello_world.c mongoose.c
|
||||||
|
|
||||||
|
include $(BUILD_EXECUTABLE)
|
||||||
@@ -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 .
|
|
||||||
+8048
-5177
File diff suppressed because it is too large.
Load diff
+2526
-378
File diff suppressed because it is too large.
Load diff
@@ -1 +0,0 @@
|
|||||||
abc123
|
|
||||||
Loaded 100 of 128 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user