mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5e10450dd | ||
|
|
e9d79b130d | ||
|
|
7951dac9d5 | ||
|
|
6c77351c58 | ||
|
|
7c4cda622f | ||
|
|
d8f8c1cb99 | ||
|
|
d2b9ad570f | ||
|
|
5b36f32996 | ||
|
|
3298030b8e | ||
|
|
6cfc696439 | ||
|
|
a93b3a06dd | ||
|
|
79e8d77ea0 | ||
|
|
d68fab9ee5 | ||
|
|
bda470245e | ||
|
|
0c95290f93 | ||
|
|
8989739d32 | ||
|
|
1112981e6e | ||
|
|
0cf82744ce | ||
|
|
5cdf838325 | ||
|
|
6418cad39f | ||
|
|
46529fd51e | ||
|
|
c09ba40a48 | ||
|
|
47d52896a1 | ||
|
|
d60c99b01c | ||
|
|
b8aeba06a2 | ||
|
|
8a0bc2d614 | ||
|
|
e34747d1ad | ||
|
|
1a6bc7c514 | ||
|
|
ffa0ddfdbf | ||
|
|
ca55bff1cf | ||
|
|
cfee5c4fdb | ||
|
|
e46185068b | ||
|
|
6ef2fea45e | ||
|
|
e244dd84a9 | ||
|
|
5034937a15 | ||
|
|
060045be65 | ||
|
|
9adfe8d484 | ||
|
|
799cc369e0 | ||
|
|
b97d3fea4b | ||
|
|
2a4ca9d5c3 | ||
|
|
747e40e74e | ||
|
|
cf279e7885 | ||
|
|
cbf968735a | ||
|
|
57b35e6007 | ||
|
|
1eaabdd8e5 | ||
|
|
dcd4e660a3 | ||
|
|
96409e74e5 | ||
|
|
e28ee097a1 | ||
|
|
2b4e544867 | ||
|
|
ee042a3481 | ||
|
|
63075f6846 | ||
|
|
7f2e42a06d | ||
|
|
3fbfa33b9a | ||
|
|
179aa75047 | ||
|
|
4658194d08 | ||
|
|
b35657814c | ||
|
|
a2322c77c6 | ||
|
|
b50c183c19 | ||
|
|
a941687db0 | ||
|
|
c2eef52b65 | ||
|
|
3e6cbbde04 | ||
|
|
6400536282 | ||
|
|
776103068f | ||
|
|
93beff3b65 | ||
|
|
22f8d86b05 | ||
|
|
3a9a9c7ad0 | ||
|
|
13238b1ad2 | ||
|
|
56f661e9c6 | ||
|
|
5351f46efb | ||
|
|
0cd3335b78 | ||
|
|
0611627448 | ||
|
|
f014918435 | ||
|
|
51e016089e | ||
|
|
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 |
No files matched your search
@@ -1,16 +1,16 @@
|
|||||||
Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||||
Copyright (c) 2013 Cesanta Software Limited
|
Copyright (c) 2013-2015 Cesanta Software Limited
|
||||||
All rights reserved
|
All rights reserved
|
||||||
|
|
||||||
This code is dual-licensed: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License version 2 as
|
||||||
published by the Free Software Foundation. For the terms of this
|
published by the Free Software Foundation. For the terms of this
|
||||||
license, see <http://www.gnu.org/licenses>.
|
license, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
You are free to use this code under the terms of the GNU General
|
You are free to use this software under the terms of the GNU General
|
||||||
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
See the GNU General Public License for more details.
|
See the GNU General Public License for more details.
|
||||||
|
|
||||||
Alternatively, you can license this code under a commercial
|
Alternatively, you can license this software under a commercial
|
||||||
license, as set out in <http://cesanta.com/>.
|
license, as set out in <https://www.cesanta.com/license>.
|
||||||
@@ -1,63 +1,107 @@
|
|||||||
# Mongoose Web Server
|
# <img src="http://www.cesanta.com/hubfs/www.cesanta.com/Images/mongoose_library.png" width="64" height="64"> Mongoose - Embedded Web Server
|
||||||
|
|
||||||
|
[](https://gitter.im/cesanta/mongoose?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
It provides easy to use event-driven interface that allows to implement
|
||||||
|
network protocols or scalable network applications with little effort.
|
||||||
|
Mongoose helps developers to manage the complexity of network programming
|
||||||
|
and let them concentrate on the logic, saving time and money.
|
||||||
|
|
||||||
|
[Download Mongoose Source Code here](http://hubs.ly/H01bWvx0)
|
||||||
|
|
||||||
|
> Nothing overdone. Nothing less. So unbelievably easy to use.
|
||||||
|
> Just how good software should be! -- Pritin Tyagaraj, SAP
|
||||||
|
|
||||||
Mongoose is easy to use, powerful, embeddable web server.
|
|
||||||
* [Mailing list](http://groups.google.com/group/mongoose-users)
|
|
||||||
* [Downloads & Documentation](http://cesanta.com/mongoose.shtml)
|
|
||||||
* [How to use Lua Server Pages](http://cesanta.com/docs/Lua.shtml)
|
|
||||||
* [How to create basic website with Mongoose](http://cesanta.com/docs/BasicWebsite.shtml)
|
|
||||||
* [How to create PHP website with Mongoose](http://cesanta.com/docs/PhpWebsite.shtml)
|
|
||||||
* [Embedding Guide](http://cesanta.com/docs/Embed.shtml)
|
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
- Works on Windows, Mac, UNIX/Linux, iPhone, Android, and many other platforms
|
* Cross-platform: works on Linux/UNIX, QNX, eCos, Windows, Android, iPhone, etc
|
||||||
- CGI, SSI, SSL, Digest auth, Websocket, WEbDAV, Resumed download, URL rewrite, file blacklist, Custom error pages, Virtual hosts, IP-based ACL, Windows service
|
* Single-threaded, asynchronous, non-blocking core with simple event-based API
|
||||||
- Scripting and database support ([Lua](http://lua.org) +
|
* Builtin protocols:
|
||||||
[Sqlite](http://sqlite.org), see
|
- plain TCP, plain UDP, SSL/TLS (over TCP, one-way or two-way)
|
||||||
[dirscan.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/dirscan.lp) ),
|
- HTTP client, HTTP server
|
||||||
which provides ready to go, powerful web development platform in
|
- Websocket client, Websocket server
|
||||||
one single-click executable with no dependencies
|
- JSON-RPC client, JSON-RPC server
|
||||||
- Simple and clean
|
- MQTT client, MQTT broker
|
||||||
[embedding API](https://github.com/cesanta/mongoose/blob/master/mongoose.h).
|
- CoAP client, CoAP server
|
||||||
The source is in single
|
- DNS client, DNS server, async DNS resolver
|
||||||
[mongoose.c](https://github.com/cesanta/mongoose/blob/master/mongoose.c) file
|
* Tiny static and run-time footprint
|
||||||
to make embedding easy
|
* Source code is both ISO C and ISO C++ compliant
|
||||||
- Extremely lightweight, has a core of under 40kB and tiny runtime footprint
|
* Very easy to integrate: just copy
|
||||||
- Asyncronouns, non-blocking core supporting single- or multi-threaded usage
|
[mongoose.c](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.c) and
|
||||||
- On the market since 2004, stable, mature and tested,
|
[mongoose.h](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.h)
|
||||||
has several man-years invested in continuous improvement and refinement
|
files to your build tree
|
||||||
- Professional support and consultancy available from
|
* Extensively tested and production-ready, trusted by many blue chip businesses
|
||||||
[Cesanta Software](http://cesanta.com), developer of Mongoose
|
|
||||||
|
|
||||||
# Acknowledgements
|
# Examples & Documentation
|
||||||
|
|
||||||
Mongoose made better thanks to the contribution of following people:
|
- [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`
|
||||||
|
|
||||||
Arnout Vandecappelle, Benoît Amiaux, Boris Pek, Cody Hanson, Colin Leitner,
|
# Dashboard Example
|
||||||
Daniel Oaks, Eric Bakan, Erik Oomen, Filipp Kovalev, Ger Hobbelt,
|
|
||||||
Hendrik Polczynski, Igor Okulist, Jay, Joe Mucchiello, John Safranek,
|
Mongoose is often used to implement device dashboards and real-time
|
||||||
José Miguel Gonçalves, Shueng Chuan, Katerina Blinova, Konstantin Sorokin,
|
data exchange over Websocket. Here is a dashboard example that illustrates
|
||||||
Marin Atanasov, Matt Healy, Mitch Hendrickson, Nigel Stewart, Pavel Khlebovich,
|
the functionality:
|
||||||
Sebastian Reinhard, Stefan Doehla, abadc0de, nullable.type,
|
|
||||||
T.Barmann, D.Hughes, J.C.Sloan, R.Romeo, L.E.Spencer, S.Kotay, R.M.Shorter,
|

|
||||||
W.Mar, J.Wilander, Santa from Memphis, S.Davies, C.Beck,
|
|
||||||
O.M.Vilhunen, C.Radik, G.Woodcock, M.Szczepkowski,
|
[Developing a new product? Contact us today to discuss how Mongoose can help
|
||||||
Eternal Lands Dev Team, T.Tollet, C.Tangerino, G.Karsai, A.Bourgett,
|
](https://www.cesanta.com/contact)
|
||||||
C.Blakemore, D.Fonaryov, T.Andrle, O.IJsselmuiden, R.Womack, M.Tomlinson,
|
|
||||||
A.Slåttå, L.Farrell, J.D.P.Ballestero, V.Albaev, B.Harker, T.Scheffel, H.Klein,
|
# Contributions
|
||||||
R.Merit, T.Bennett, H.Solis, A.Zincenko, M.S., S.Krul, K.Cooke, S.McCallum,
|
|
||||||
F.Morenius, and 10 others.
|
People who have agreed to the
|
||||||
|
[Cesanta CLA](https://docs.cesanta.com/contributors_la.shtml)
|
||||||
|
can make contributions. Note that the CLA isn't a copyright
|
||||||
|
_assigment_ but rather a copyright _license_.
|
||||||
|
You retain the copyright on your contributions.
|
||||||
|
|
||||||
# Licensing
|
# Licensing
|
||||||
|
|
||||||
Mongoose is released under
|
You are using Mongoose under
|
||||||
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html).
|
[GPLv2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||||
Businesses have an option to get non-restrictive, royalty-free commercial
|
and until you commercialize your
|
||||||
license and professional support from
|
application, can continue to use Mongoose free of charge.
|
||||||
[Cesanta Software](http://cesanta.com).
|
|
||||||
|
|
||||||
[Frozen JSON parser](https://github.com/cesanta/frozen),
|
Once you commercialize your product, in order to comply with GPLv2
|
||||||
[Super Light Regular Expression library](https://github.com/cesanta/slre),
|
you need to either open the source code of your product fully or
|
||||||
[Super Light DNS Resolver](https://github.com/cesanta/sldr)
|
alternatively purchase a commercial license.
|
||||||
are other projects by Cesanta Software, developed with the same philosophy
|
We offer [three levels of commercial licensing](https://www.cesanta.com/products):
|
||||||
of functionality and simplicity.
|
|
||||||
|
- 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/products#binary)
|
||||||
|
|
||||||
|
# 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
|
||||||
-241
@@ -1,241 +0,0 @@
|
|||||||
# Mongoose API Reference
|
|
||||||
|
|
||||||
struct mg_server *mg_create_server(void *server_param);
|
|
||||||
|
|
||||||
Creates web server instance. Returns opaque instance pointer, or NULL if
|
|
||||||
there is not enough memory. `server_param`: Could be any pointer, or NULL.
|
|
||||||
This pointer will be passed
|
|
||||||
to the callback functions as `struct mg_connection::server_param` field.
|
|
||||||
A common use case is to pass `this` pointer of the C++ wrapper class
|
|
||||||
as `user_param`, to let the callback get the pointer to the C++ object.
|
|
||||||
|
|
||||||
Note that this function doesn't make the
|
|
||||||
server instance to serve. Serving is done by `mg_poll_server()` function.
|
|
||||||
Mongoose has single-threaded, event-driven, asynchronous, non-blocking core.
|
|
||||||
When server instance is created, it contains an information about
|
|
||||||
the configuration and the state of each connection.
|
|
||||||
Server instance is capable on listening on only one port. After creation,
|
|
||||||
`struct mg_server` has a list
|
|
||||||
of active connections and configuration parameters.
|
|
||||||
|
|
||||||
Side-effect: on UNIX, `mg_create_server()` ignores SIGPIPE signals. If custom
|
|
||||||
processing is required SIGPIPE, signal handler must be set up after
|
|
||||||
calling `mg_create_server()`.
|
|
||||||
|
|
||||||
Important: Mongoose does not install `SIGCHLD` handler. If CGI is used,
|
|
||||||
`SIGCHLD` handler must be set up to reap CGI zombie processes.
|
|
||||||
|
|
||||||
|
|
||||||
void mg_destroy_server(struct mg_server **server);
|
|
||||||
|
|
||||||
Deallocates web server instance, closes all pending connections, and makes
|
|
||||||
server pointer a NULL pointer.
|
|
||||||
|
|
||||||
const char mg_set_option(struct mg_server *server, const char *name,
|
|
||||||
const char *value);
|
|
||||||
|
|
||||||
Sets a particular server option. Note that at least one option,
|
|
||||||
`listening_port`, must be specified. To serve static files, `document_root`
|
|
||||||
must be specified too. If `document_root` option is left unset, Mongoose
|
|
||||||
will not access filesystem at all. `mg_set_option()` returns NULL if option was
|
|
||||||
set successfully, otherwise it returns human-readable error string. It is
|
|
||||||
allowed to call `mg_set_option()` by the same thread that does
|
|
||||||
`mg_poll_server()` (Mongoose thread) and change server configuration while it
|
|
||||||
is serving, in between `mg_poll_server()` calls.
|
|
||||||
|
|
||||||
void mg_poll_server(struct mg_server *server, int milliseconds);
|
|
||||||
|
|
||||||
Performs one iteration of IO loop by iterating over all
|
|
||||||
active connections, performing `select()` syscall on all sockets with a timeout
|
|
||||||
of `milliseconds`. When `select()` returns, Mongoose
|
|
||||||
does an IO for each socket that has data to be sent or received. Application
|
|
||||||
code must call `mg_poll_server()` in a loop. It is an error to have more then
|
|
||||||
one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
|
|
||||||
that take `struct mg_server *` parameter. Mongoose does not
|
|
||||||
mutex-protect `struct mg_server *`, therefore only single thread
|
|
||||||
(Mongoose thread) should make Mongoose calls.
|
|
||||||
|
|
||||||
`mg_poll_server()` calls user-specified event handler when certain events
|
|
||||||
occur. Sequence of events for the accepted connection is this:
|
|
||||||
|
|
||||||
* `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
|
|
||||||
handler returns `MG_FALSE`, then Mongoose does not serve the request but
|
|
||||||
sends authorization request to the client. If `MG_TRUE` is returned,
|
|
||||||
then Mongoose continues on with the request.
|
|
||||||
* `MG_REQUEST` - Mongoose asks event handler to serve the request. If
|
|
||||||
event handler serves the request by sending a reply,
|
|
||||||
it should return `MG_TRUE`. Otherwise,
|
|
||||||
it should return `MG_FALSE` which tells Mongoose that request is not
|
|
||||||
served and Mongoose should serve it. For example, event handler might
|
|
||||||
choose to serve only RESTful API requests with URIs that start with
|
|
||||||
certain prefix, and let Mongoose serve all static files.
|
|
||||||
If event handler decides to serve the request, but doesn't have
|
|
||||||
all the data at the moment, it should return `MG_MORE`. That tells
|
|
||||||
Mongoose to send `MG_POLL` events on each iteration of `mg_poll_server()`
|
|
||||||
|
|
||||||
`mg_connection::connection_param` pointer is a placeholder to keep
|
|
||||||
user-specific data. For example, handler could decide to open a DB
|
|
||||||
connection and store DB connection handle in `connection_param`.
|
|
||||||
* `MG_POLL` is sent only to those connections which returned `MG_MORE`.
|
|
||||||
Event handler should try to complete the reply. If reply is completed,
|
|
||||||
then event handler should return `MG_TRUE`. Otherwise, it should
|
|
||||||
return `MG_FALSE`, and polling will continue until
|
|
||||||
handler returns `MG_TRUE`.
|
|
||||||
* `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
|
|
||||||
to the client. Event handler can choose to send a reply itself, in which
|
|
||||||
case event handler must return `MG_TRUE`. Otherwise, event handler must
|
|
||||||
return `MG_FALSE`
|
|
||||||
* `MG_CLOSE` is sent when the connection is closed. This event is used
|
|
||||||
to cleanup per-connection state stored in `connection_param`
|
|
||||||
if it was allocated.
|
|
||||||
|
|
||||||
Sequence of events for the client connection is this:
|
|
||||||
|
|
||||||
* `MG_CONNECT` sent when Mongoose has connected to the remote host.
|
|
||||||
This event is sent to the connection initiated by `mg_connect()` call.
|
|
||||||
Connection status is held in `mg_connection::status_code`: if zero,
|
|
||||||
then connection was successful, otherwise connection was not established.
|
|
||||||
User should send a request upon successful connection.
|
|
||||||
* `MG_REPLY` is sent when response has been received from the remote host.
|
|
||||||
* `MG_CLOSE` same as for the accepted connection.
|
|
||||||
|
|
||||||
|
|
||||||
When mongoose buffers in HTTP request and successfully parses it, it sends
|
|
||||||
`MG_REQUEST` event for GET requests immediately. For POST requests,
|
|
||||||
Mongoose delays the call until the whole POST request is buffered in memory.
|
|
||||||
POST data is available to the callback as `struct mg_connection::content`,
|
|
||||||
and POST data length is in `struct mg_connection::content_len`.
|
|
||||||
|
|
||||||
Note that websocket connections are treated the same way. Mongoose buffers
|
|
||||||
websocket frame in memory, and calls event handler when frame is fully
|
|
||||||
buffered. Frame data is available `struct mg_connection::content`, and
|
|
||||||
data length is in `struct mg_connection::content_len`, i.e. very similar to
|
|
||||||
the POST request. `struct mg_connection::is_websocket` flag indicates
|
|
||||||
whether the request is websocket or not. Also, for websocket requests,
|
|
||||||
there is `struct mg_connection::wsbits` field which contains first byte
|
|
||||||
of the websocket frame which URI handler can examine. Note that to
|
|
||||||
reply to the websocket client, `mg_websocket_write()` should be used.
|
|
||||||
To reply to the plain HTTP client, `mg_write_data()` should be used.
|
|
||||||
|
|
||||||
|
|
||||||
const char **mg_get_valid_option_names(void);
|
|
||||||
|
|
||||||
Returns a NULL-terminated array of option names and their default values.
|
|
||||||
There are two entries per option in an array: an option name followed by a
|
|
||||||
default value. A default value could be NULL. A NULL name indicates an end
|
|
||||||
of the array.
|
|
||||||
|
|
||||||
const char *mg_get_option(const struct mg_server *server, const char *name);
|
|
||||||
|
|
||||||
Returns the value of particular configuration parameter. If
|
|
||||||
given parameter name is not valid, NULL is returned. For valid names, return
|
|
||||||
value is guaranteed to be non-NULL. If parameter is not set, zero-length string
|
|
||||||
is returned.
|
|
||||||
|
|
||||||
|
|
||||||
void mg_iterate_over_connections(struct mg_server *, mg_handler_t func);
|
|
||||||
|
|
||||||
This is an interface primarily designed to push arbitrary data to websocket
|
|
||||||
connections at any time. This function could be called from the Mongoose thread
|
|
||||||
only. When it returns, Mongoose thread calls `func()` for each active
|
|
||||||
connection.
|
|
||||||
It is allowed to call `mg_send_data()` or `mg_websocket_write()` within a
|
|
||||||
callback, cause `func` is executed in the context of the Mongoose thread.
|
|
||||||
|
|
||||||
void mg_wakeup_server(struct mg_server *);
|
|
||||||
|
|
||||||
Makes `mg_poll_server()` that could be sleeping in the `select()` syscall
|
|
||||||
to break the call and return. This function can be called from any thread.
|
|
||||||
It is designed to let other threads wake up Mongoose thread from the sleep
|
|
||||||
and let it do a fresh new IO iteration over all connection. Usually it is done
|
|
||||||
when other threads decides there is new data ready to be sent by Mongoose.
|
|
||||||
|
|
||||||
void mg_send_status(struct mg_connection *, int status_code);
|
|
||||||
void mg_send_header(struct mg_connection *, const char *name,
|
|
||||||
const char *value);
|
|
||||||
void mg_send_data(struct mg_connection *, const void *data, int data_len);
|
|
||||||
void mg_printf_data(struct mg_connection *, const char *format, ...);
|
|
||||||
|
|
||||||
These functions are used to construct a response to the client. HTTP response
|
|
||||||
consists of three parts: a status line, zero or more HTTP headers,
|
|
||||||
a response body. Mongoose provides functions for all three parts:
|
|
||||||
* `mg_send_status()` is used to create status line. This function can be
|
|
||||||
called zero or once. If `mg_send_status()` is not called, then Mongoose
|
|
||||||
will send status 200 (success) implicitly.
|
|
||||||
* `mg_send_header()` adds HTTP header to the response. This function could
|
|
||||||
be called zero or more times.
|
|
||||||
* `mg_send_data()` and `mg_printf_data()` are used to send data to the
|
|
||||||
client. Note that Mongoose adds `Transfer-Encoding: chunked` header
|
|
||||||
implicitly, and sends data in chunks. Therefore, it is not necessary to
|
|
||||||
set `Content-Length` header. Note that `mg_send_data()` and
|
|
||||||
`mg_printf_data()` do not send data immediately. Instead, they spool
|
|
||||||
data in memory, and Mongoose sends that data later after URI handler
|
|
||||||
returns. If data to be sent is huge, an URI handler might
|
|
||||||
send data in pieces by saving state in
|
|
||||||
`struct mg_connection::connection_param` variable and returning `0`. Then
|
|
||||||
Mongoose will call a handler repeatedly after each socket write.
|
|
||||||
|
|
||||||
<!-- -->
|
|
||||||
|
|
||||||
int mg_websocket_write(struct mg_connection* conn, int opcode,
|
|
||||||
const char *data, size_t data_len);
|
|
||||||
|
|
||||||
Similar to `mg_write()`, but wraps the data into a websocket frame with a
|
|
||||||
given websocket `opcode`.
|
|
||||||
|
|
||||||
const char *mg_get_header(const struct mg_connection *, const char *name);
|
|
||||||
|
|
||||||
Get the value of particular HTTP header. This is a helper function.
|
|
||||||
It traverses http_headers array, and if the header is present in the array,
|
|
||||||
returns its value. If it is not present, NULL is returned.
|
|
||||||
|
|
||||||
|
|
||||||
int mg_get_var(const struct mg_connection *conn, const char *var_name,
|
|
||||||
char *buf, size_t buf_len);
|
|
||||||
|
|
||||||
Gets HTTP form variable. Both POST buffer and query string are inspected.
|
|
||||||
Form variable is url-decoded and written to the buffer. On success, this
|
|
||||||
function returns the length of decoded variable. On error, -1 is returned if
|
|
||||||
variable not found, and -2 is returned if destination buffer is too small
|
|
||||||
to hold the variable. Destination buffer is guaranteed to be
|
|
||||||
'\0' - terminated if it is not NULL or zero length.
|
|
||||||
|
|
||||||
int mg_parse_header(const char *hdr, const char *var_name, char *buf,
|
|
||||||
size_t buf_size);
|
|
||||||
|
|
||||||
This function parses HTTP header and fetches given variable's value in a buffer.
|
|
||||||
A header should be like `x=123, y=345, z="other value"`. This function is
|
|
||||||
designed to parse Cookie headers, Authorization headers, and similar. Returns
|
|
||||||
the length of the fetched value, or 0 if variable not found.
|
|
||||||
|
|
||||||
int mg_modify_passwords_file(const char *passwords_file_name,
|
|
||||||
const char *domain,
|
|
||||||
const char *user,
|
|
||||||
const char *password);
|
|
||||||
|
|
||||||
Add, edit or delete the entry in the passwords file.
|
|
||||||
This function allows an application to manipulate .htpasswd files on the
|
|
||||||
fly by adding, deleting and changing user records. This is one of the
|
|
||||||
several ways of implementing authentication on the server side. For another,
|
|
||||||
cookie-based way please refer to the examples/chat.c in the source tree.
|
|
||||||
If password is not NULL, entry is added (or modified if already exists).
|
|
||||||
If password is NULL, entry is deleted.
|
|
||||||
Return: 1 on success, 0 on error.
|
|
||||||
|
|
||||||
|
|
||||||
int mg_parse_multipart(const char *buf, int buf_len,
|
|
||||||
char *var_name, int var_name_len,
|
|
||||||
char *file_name, int file_name_len,
|
|
||||||
const char **data, int *data_len);
|
|
||||||
|
|
||||||
Parses a buffer that contains multipart form data. Stores chunk name
|
|
||||||
in a `var_name` buffer. If chunk is an uploaded file, then `file_name`
|
|
||||||
will have a file name. `data` and `data_len` will point to the chunk data.
|
|
||||||
Returns number of bytes to skip to the next chunk.
|
|
||||||
|
|
||||||
struct mg_connection *mg_connect(struct mg_server *server,
|
|
||||||
const char *host, int port, int use_ssl);
|
|
||||||
|
|
||||||
Create connection to the remote host. Returns `NULL` on error, non-null
|
|
||||||
if the connection has been scheduled for connection. Upon a connection,
|
|
||||||
Mongoose will send `MG_CONNECT` event to the event handler.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Mongoose Build on Android
|
|
||||||
|
|
||||||
This is a small guide to help you run mongoose on Android. Currently it is
|
|
||||||
tested on the HTC Wildfire. If you have managed to run it on other devices
|
|
||||||
as well, please comment or drop an email in the mailing list.
|
|
||||||
Note : You dont need root access to run mongoose on Android.
|
|
||||||
|
|
||||||
- Clone Mongoose Git repo
|
|
||||||
- Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html)
|
|
||||||
- Run `/path-to-ndk/ndk-build -C /path-to-mongoose/examples`
|
|
||||||
That should generate mongoose/lib/armeabi/mongoose
|
|
||||||
- Using the adb tool (you need to have Android SDK installed for that),
|
|
||||||
push the generated mongoose binary to `/data/local` folder on device.
|
|
||||||
- From adb shell, navigate to `/data/local` and execute `./mongoose`.
|
|
||||||
- To test if the server is running fine, visit your web-browser and
|
|
||||||
navigate to `http://127.0.0.1:8080` You should see the `Index of /` page.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
- `jni` stands for Java Native Interface. Read up on Android NDK if you want
|
|
||||||
to know how to interact with the native C functions of mongoose in Android
|
|
||||||
Java applications.
|
|
||||||
- TODO: A Java application that interacts with the native binary or a
|
|
||||||
shared library.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
How To Create A PHP Website With Mongoose
|
|
||||||
===========================================
|
|
||||||
|
|
||||||
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
|
|
||||||
|
|
||||||

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

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

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

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

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

|
|
||||||
|
|
||||||
## 7. Click on the mongoose icon and choose "Start browser" menu:
|
|
||||||

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

|
|
||||||
-173
@@ -1,173 +0,0 @@
|
|||||||
# Mongoose Embedding Guide
|
|
||||||
|
|
||||||
Embedding Mongoose is done in two steps:
|
|
||||||
|
|
||||||
1. Copy
|
|
||||||
[mongoose.c](https://raw.github.com/cesanta/mongoose/master/mongoose.c) and
|
|
||||||
[mongoose.h](https://raw.github.com/cesanta/mongoose/master/mongoose.h)
|
|
||||||
to your application's source tree and include them in the build.
|
|
||||||
2. Somewhere in the application code, call `mg_create_server()` to create
|
|
||||||
a server, configure it with `mg_set_option()` and loop with
|
|
||||||
`mg_poll_server()` until done. Call `mg_destroy_server()` to cleanup.
|
|
||||||
|
|
||||||
Here's a minimal application `app.c` that embeds mongoose:
|
|
||||||
|
|
||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_server *server = mg_create_server(NULL, NULL);
|
|
||||||
mg_set_option(server, "document_root", "."); // Serve current directory
|
|
||||||
mg_set_option(server, "listening_port", "8080"); // Open port 8080
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
|
|
||||||
}
|
|
||||||
mg_destroy_server(&server);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
To compile it, put `mongoose.c`, `mongoose.h` and `app.c` into one
|
|
||||||
folder, start terminal on UNIX or Visual Studio command line prompt on Windows,
|
|
||||||
and run the following command:
|
|
||||||
|
|
||||||
cc app.c mongoose.c -pthread -o app # on Unix
|
|
||||||
cl.exe app.c mongoose.c /TC /MD # on Windows
|
|
||||||
|
|
||||||
When run, this simple application opens port 8080 and serves static files,
|
|
||||||
CGI files and lists directory content in the current working directory.
|
|
||||||
|
|
||||||
It is possible to generate HTML page content. Mongoose can call user-defined
|
|
||||||
function when certain events occur.
|
|
||||||
That function is called _an event handler_, and it is the second parameter
|
|
||||||
to `mg_create_server()` function. Here is the example event handler function:
|
|
||||||
|
|
||||||
int event_handler(struct mg_connection *conn, enum mg_event ev) {
|
|
||||||
switch (ev) {
|
|
||||||
case MG_AUTH: return MG_TRUE;
|
|
||||||
default: return MG_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Event handler is called by Mongoose with `struct mg_connection *`
|
|
||||||
pointer and an event number. `struct mg_connection *conn`
|
|
||||||
has all information about the request: HTTP headers, POST or websocket
|
|
||||||
data buffer, etcetera. `enum mg_event ev` tells which exactly event is sent.
|
|
||||||
For each event, an event handler returns a value which tells Mongoose how
|
|
||||||
to behave.
|
|
||||||
|
|
||||||
The sequence of events for every connection is this:
|
|
||||||
|
|
||||||
* `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
|
|
||||||
handler returns `MG_FALSE`, then Mongoose does not serve the request but
|
|
||||||
sends authorization request to the client. If `MG_TRUE` is returned,
|
|
||||||
then Mongoose continues on with the request.
|
|
||||||
* `MG_REQUEST` - Mongoose asks event handler to serve the request. If
|
|
||||||
event handler serves the request by sending a reply,
|
|
||||||
it should return `MG_TRUE`. Otherwise,
|
|
||||||
it should return `MG_FALSE` which tells Mongoose that request is not
|
|
||||||
served and Mongoose should serve it. For example, event handler might
|
|
||||||
choose to serve only RESTful API requests with URIs that start with
|
|
||||||
certain prefix, and let Mongoose serve all static files.
|
|
||||||
If event handler decides to serve the request, but doesn't have
|
|
||||||
all the data at the moment, it should return `MG_MORE`. That tells
|
|
||||||
Mongoose to send `MG_POLL` events on each iteration of `mg_poll_server()`
|
|
||||||
|
|
||||||
`mg_connection::connection_param` pointer is a placeholder to keep
|
|
||||||
user-specific data. For example, handler could decide to open a DB
|
|
||||||
connection and store DB connection handle in `connection_param`.
|
|
||||||
* `MG_POLL` is sent only to those connections which returned `MG_MORE`.
|
|
||||||
Event handler should try to complete the reply. If reply is completed,
|
|
||||||
then event handler should return `MG_TRUE`. Otherwise, it should
|
|
||||||
return `MG_FALSE`, and polling will continue until
|
|
||||||
handler returns `MG_TRUE`.
|
|
||||||
* `MG_CLOSE` is sent when the connection is closed. This event is used
|
|
||||||
to cleanup per-connection state stored in `connection_param`
|
|
||||||
if it was allocated.
|
|
||||||
|
|
||||||
Let's extend our minimal application example and
|
|
||||||
create an URI that will be served by user's C code. The app will handle
|
|
||||||
`/hello` URI by showing a hello message. So, when app is run,
|
|
||||||
http://127.0.0.1:8080/hello will say hello, and here's the code:
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
static int event_handler(struct mg_connection *conn, enum mg_event ev) {
|
|
||||||
if (ev == MG_AUTH) {
|
|
||||||
return MG_TRUE; // Authorize all requests
|
|
||||||
} else if (ev == MG_REQUEST && !strcmp(conn->uri, "/hello")) {
|
|
||||||
mg_printf_data(conn, "%s", "Hello world");
|
|
||||||
return MG_TRUE; // Mark as processed
|
|
||||||
} else {
|
|
||||||
return MG_FALSE; // Rest of the events are not processed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_server *server = mg_create_server(NULL, event_handler);
|
|
||||||
mg_set_option(server, "document_root", ".");
|
|
||||||
mg_set_option(server, "listening_port", "8080");
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
|
|
||||||
}
|
|
||||||
mg_destroy_server(&server);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
## Example code
|
|
||||||
|
|
||||||
Mongoose source code contains a well-commented example code, listed below:
|
|
||||||
|
|
||||||
* [hello.c](https://github.com/cesanta/mongoose/blob/master/examples/hello.c)
|
|
||||||
a minimalistic hello world example
|
|
||||||
* [post.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
|
|
||||||
shows how to handle form input
|
|
||||||
* [upload.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
|
|
||||||
shows how to upload files
|
|
||||||
* [websocket.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates websocket usage
|
|
||||||
* [auth.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates API-controlled Digest authorization
|
|
||||||
* [mjpg.c](https://github.com/cesanta/mongoose/blob/master/examples/mjpg.c) demonstrates MJPEG streaming implementation
|
|
||||||
|
|
||||||
## Compilation flags
|
|
||||||
|
|
||||||
Below is the list of compilation flags that enable or disable certain
|
|
||||||
features. By default, some features are enabled, and could be disabled
|
|
||||||
by setting appropriate `NO_*` flag. Features that are disabled by default
|
|
||||||
could be enabled by setting appropriate `USE_*` flag. Bare bones Mongoose
|
|
||||||
is quite small, about 30 kilobytes of compiled x86 code. Each feature adds
|
|
||||||
a couple of kilobytes to the executable size, and also has some runtime penalty.
|
|
||||||
|
|
||||||
Note that some flags start with `NS_` prefix. This is because Mongoose uses
|
|
||||||
[Net Skeleton](http://github.com/cesanta/net_skeleton) as a low-level
|
|
||||||
networking engine. If user code has `#include <net_skeleton.h>`, then
|
|
||||||
all Net Skeleton functions will be available too.
|
|
||||||
|
|
||||||
|
|
||||||
-DMONGOOSE_NO_AUTH Disable MD5 authorization support
|
|
||||||
-DMONGOOSE_NO_CGI Disable CGI support
|
|
||||||
-DMONGOOSE_NO_DAV Disable WebDAV support
|
|
||||||
(PUT, DELETE, MKCOL, PROPFIND methods)
|
|
||||||
-DMONGOOSE_NO_DIRECTORY_LISTING Disable directory listing
|
|
||||||
-DMONGOOSE_NO_FILESYSTEM Disables all file IO, serving from memory only
|
|
||||||
-DMONGOOSE_NO_LOGGING Disable access/error logging
|
|
||||||
-DMONGOOSE_NO_THREADS
|
|
||||||
-DMONGOOSE_NO_WEBSOCKET Disable WebSocket support
|
|
||||||
|
|
||||||
-DMONGOOSE_USE_IDLE_TIMEOUT_SECONDS=X Idle connection timeout, default is 30
|
|
||||||
-DMONGOOSE_USE_LUA Enable Lua scripting
|
|
||||||
-DMONGOOSE_USE_LUA_SQLITE3 Enable sqlite3 binding for Lua
|
|
||||||
-DMONGOOSE_USE_POST_SIZE_LIMIT=X POST requests larger than X will be
|
|
||||||
rejected, not set by default
|
|
||||||
-DMONGOOSE_USE_EXTRA_HTTP_HEADERS=X Append X to the HTTP headers
|
|
||||||
for static files, empty by default
|
|
||||||
|
|
||||||
-DNS_ENABLE_DEBUG Enables debug messages on stdout, very noisy
|
|
||||||
-DNS_ENABLE_SSL Enable SSL
|
|
||||||
-DNS_ENABLE_IPV6 Enable IPv6 support
|
|
||||||
-DNS_ENABLE_HEXDUMP Enables hexdump of sent and received traffic
|
|
||||||
-DNS_STACK_SIZE=X Sets stack size to X for ns_start_thread()
|
|
||||||
-DNS_DISABLE_THREADS Disable threads support
|
|
||||||
-DNS_DISABLE_SOCKETPAIR For systems without loopback interface
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
# Mongoose FAQ
|
|
||||||
|
|
||||||
### PHP doesn't work: getting empty page, or 'File not found' error
|
|
||||||
|
|
||||||
The reason for that is wrong paths to the interpreter. Remember that with PHP,
|
|
||||||
correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify
|
|
||||||
full path to the PHP interpreter, e.g.:
|
|
||||||
|
|
||||||
mongoose -cgi_interpreter /full/path/to/php-cgi
|
|
||||||
|
|
||||||
### Mongoose fails to start
|
|
||||||
|
|
||||||
If Mongoose exits immediately when run, this
|
|
||||||
usually indicates a syntax error in the configuration file
|
|
||||||
(named `mongoose.conf` by default) or the command-line arguments.
|
|
||||||
Syntax checking is omitted from Mongoose to keep its size low. However,
|
|
||||||
the Manual should be of help. Note: the syntax changes from time to time,
|
|
||||||
so updating the config file might be necessary after executable update.
|
|
||||||
|
|
||||||
### Embedding with OpenSSL on Windows might fail because of calling convention
|
|
||||||
|
|
||||||
To force Mongoose to use `__stdcall` convention, add `/Gz` compilation
|
|
||||||
flag to the Visual Studio project settings.
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
How To Share Files With Mongoose
|
|
||||||
===========================================
|
|
||||||
|
|
||||||
## 1. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside the directory you want to share:
|
|
||||||
|
|
||||||

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

|
|
||||||
|
|
||||||
## 3. Click on the mongoose icon and choose "Start browser" menu:
|
|
||||||

|
|
||||||
|
|
||||||
## 4. A browser will popup displaying files. That's all! To access those files from another computer, start a browser and type `http://<your_machine_ip_address>:8080/` in the address bar.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Tip: to find out what is your machine IP address, take a look at http://windows.microsoft.com/en-us/windows/find-computers-ip-address
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# Mongoose Internals
|
|
||||||
|
|
||||||
Mongoose has single-threaded, event-driven, asynchronous, non-blocking core.
|
|
||||||
`mg_create_server()` creates a web server instance. An instance is a container
|
|
||||||
for the config options and list of active connections. To do the actual
|
|
||||||
serving, user must call `mg_poll_server()`, which iterates over all
|
|
||||||
active connections, performing `select()` syscall on all sockets with a
|
|
||||||
timeout of specified number of milliseconds. When `select()` returns, Mongoose
|
|
||||||
does an IO for each socket that has data to be sent or received. Application
|
|
||||||
code must call `mg_poll_server()` in a loop.
|
|
||||||
|
|
||||||
Mongoose server instance is designed to be used by a single thread.
|
|
||||||
It is an error to have more then
|
|
||||||
one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
|
|
||||||
that take `struct mg_server *` parameter. Mongoose does not
|
|
||||||
mutex-protect `struct mg_server *`, therefore the best practice is
|
|
||||||
to call server management functions from the same thread (an IO thread).
|
|
||||||
On a multi-core systems, many server instances can be created, sharing the
|
|
||||||
same listening socket and managed by separate threads (see [multi_threaded.c](https://github.com/cesanta/mongoose/blob/master/examples/multi_threaded.c))
|
|
||||||
example.
|
|
||||||
|
|
||||||
It is an error to pass and store `struct mg_connection *` pointers for
|
|
||||||
later use to send data. The reason is that they can be invalidated by the
|
|
||||||
next `mg_poll_server()` call. For such a task,
|
|
||||||
there is `mg_iterate_over_connections()` API
|
|
||||||
exists, which sends a callback function to the IO thread, then IO thread
|
|
||||||
calls specified function for all active connection.
|
|
||||||
|
|
||||||
When mongoose buffers in HTTP request and successfully parses it, it calls
|
|
||||||
appropriate URI handler immediately for GET requests. For POST requests,
|
|
||||||
Mongoose delays the call until the whole POST request is buffered in memory.
|
|
||||||
POST data is available to the callback as `struct mg_connection::content`,
|
|
||||||
and POST data length is in `struct mg_connection::content_len`.
|
|
||||||
|
|
||||||
Note that websocket connections are treated the same way. Mongoose buffers
|
|
||||||
websocket frame in memory, and calls URI handler when frame is fully
|
|
||||||
buffered. Frame data is available `struct mg_connection::content`, and
|
|
||||||
data length is in `struct mg_connection::content_len`, i.e. very similar to
|
|
||||||
the POST request. `struct mg_connection::is_websocket` flag indicates
|
|
||||||
whether the request is websocket or not. Also, for websocket requests,
|
|
||||||
there is `struct mg_connection::wsbits` field which contains first byte
|
|
||||||
of the websocket frame which URI handler can examine. Note that to
|
|
||||||
reply to the websocket client, `mg_websocket_write()` should be used.
|
|
||||||
To reply to the plain HTTP client, `mg_write()` should be used.
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
# Mongoose Lua Server Pages
|
|
||||||
|
|
||||||
Pre-built Windows and Mac mongoose binaries support Lua Server Pages
|
|
||||||
functionality.
|
|
||||||
That means it is possible to write PHP-like scripts with mongoose
|
|
||||||
using Lua programming language instead of PHP. Lua is known
|
|
||||||
for it's speed and small size. Mongoose uses Lua version 5.2.3, the
|
|
||||||
documentation for it can be found at
|
|
||||||
[Lua 5.2 reference manual](http://www.lua.org/manual/5.2/).
|
|
||||||
|
|
||||||
To create a Lua Page, make a file that is called `ANY_NAME.lp`. For example,
|
|
||||||
`my_page.lp`. It is important to have a file
|
|
||||||
name that ends up with `.lp`, cause this is the way mongoose recognises
|
|
||||||
Lua Page file. The contents of the file, just like
|
|
||||||
with PHP, is HTML with embedded Lua code. Lua code must be enclosed within
|
|
||||||
`<? ?>` blocks, and can appear anywhere on the page.
|
|
||||||
|
|
||||||
Mongoose does not send HTTP headers for Lua pages. Therefore,
|
|
||||||
every Lua Page must begin with HTTP status line and headers, like this:
|
|
||||||
|
|
||||||
<? mg.write('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') ?>
|
|
||||||
<html><body>
|
|
||||||
<span>Today is:</span> <? mg.write(os.date("%A")) ?>
|
|
||||||
</body></html>
|
|
||||||
|
|
||||||
Note that this example uses function `mg.write()`, which prints data to the
|
|
||||||
web page. Using function `mg.write()` is the way to generate web content from
|
|
||||||
inside Lua code. In addition to `mg.write()`, all standard library functions
|
|
||||||
are accessible from the Lua code (please check reference manual for details).
|
|
||||||
Information about the request is available via the `mg.request_info` object.
|
|
||||||
I contains request method, all headers, etcetera. Please refer to
|
|
||||||
`struct mg_request_info` definition in
|
|
||||||
[mongoose.h](https://github.com/cesanta/mongoose/blob/master/mongoose.h)
|
|
||||||
to see what is available via the `mg.request_info` object.
|
|
||||||
Check out [prime_numbers.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/prime_numbers.lp) for some example.
|
|
||||||
|
|
||||||
Mongoose exports the following to the Lua Server Page:
|
|
||||||
|
|
||||||
mg.write(str) -- writes string to the client
|
|
||||||
mg.onerror(msg) -- error handler, can be overridden
|
|
||||||
mg.request_info -- a table with request information
|
|
||||||
|
|
||||||
Using Lua scripting it is easy to emulate SSI functionality. For example,
|
|
||||||
to include the content of another file, one can write:
|
|
||||||
|
|
||||||
<? mg.write(io.open('MY_FILE.TXT'):read('*all')) ?>
|
|
||||||
|
|
||||||
To serve a Lua Page, mongoose creates Lua context. That context is used for
|
|
||||||
all Lua blocks within the page. That means, all Lua blocks on the same page
|
|
||||||
share the same context. If one block defines a variable, for example, that
|
|
||||||
variable is visible in all following blocks.
|
|
||||||
-147
@@ -1,147 +0,0 @@
|
|||||||
# Mongoose Configuration Options
|
|
||||||
|
|
||||||
### access\_control\_list
|
|
||||||
An Access Control List (ACL) allows restrictions to be put on the list of IP
|
|
||||||
addresses which have access to the web server. In the case of the Mongoose
|
|
||||||
web server, the ACL is a comma separated list of IP subnets, where each
|
|
||||||
subnet is prepended by either a `-` or a `+` sign. A plus sign means allow,
|
|
||||||
where a minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`,
|
|
||||||
this means to deny only that single IP address.
|
|
||||||
|
|
||||||
Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow
|
|
||||||
all accesses. On each request the full list is traversed, and
|
|
||||||
the last match wins. Example: `$ mongoose -access_control_list -0.0.0.0/0,+192.168/16` to deny all acccesses except those from `192.168/16` subnet. To learn
|
|
||||||
more about subnet masks, see the
|
|
||||||
[Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork)
|
|
||||||
|
|
||||||
Default: not set, all accesses are allowed.
|
|
||||||
|
|
||||||
### access\_log\_file
|
|
||||||
Path to a file for access logs. Either full path, or relative to the
|
|
||||||
mongoose executable. Default: not set, no query logging is done.
|
|
||||||
|
|
||||||
### auth_domain
|
|
||||||
Authorization realm used in `.htpasswd` authorization. Default: `mydomain.com`
|
|
||||||
|
|
||||||
### cgi_interpreter
|
|
||||||
Path to an executable to be used use as an interpreter for __all__ CGI scripts
|
|
||||||
regardless script extension. Default: not set, Mongoose looks at
|
|
||||||
[shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\).
|
|
||||||
|
|
||||||
For example, if both PHP and perl CGIs are used, then
|
|
||||||
`#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the
|
|
||||||
respective CGI scripts. Note that paths should be either full file paths,
|
|
||||||
or file paths relative to the directory where mongoose executable is located.
|
|
||||||
|
|
||||||
If all CGIs use the same interpreter, for example they are all PHP, then
|
|
||||||
`cgi_interpreter` option can be set to the path to `php-cgi.exe` executable and
|
|
||||||
shebang line in the CGI scripts can be omitted.
|
|
||||||
**Note**: PHP scripts must use `php-cgi.exe`, not `php.exe`.
|
|
||||||
|
|
||||||
### cgi_pattern
|
|
||||||
All files that match `cgi_pattern` are treated as CGI files. Default pattern
|
|
||||||
allows CGI files be anywhere. To restrict CGIs to a certain directory,
|
|
||||||
use `/path/to/cgi-bin/**.cgi` as a pattern. Note that **full file path** is
|
|
||||||
matched against the pattern, not the URI.
|
|
||||||
|
|
||||||
When Mongoose starts CGI program, it creates new environment for it (in
|
|
||||||
contrast, usually child program inherits the environment from parent). Several
|
|
||||||
environment variables however are inherited from Mongoose's environment,
|
|
||||||
they are: `PATH`, `TMP`, `TEMP`, `TMPDIR`, `PERLLIB`, `MONGOOSE_CGI`. On UNIX
|
|
||||||
it is also `LD_LIBRARY_PATH`. On Windows it is also `COMSPEC`, `SYSTEMROOT`,
|
|
||||||
`SystemDrive`, `ProgramFiles`, `ProgramFiles(x86)`, `CommonProgramFiles(x86)`.
|
|
||||||
|
|
||||||
Default: `**.cgi$|**.pl$|**.php$`
|
|
||||||
|
|
||||||
### dav\_auth\_file
|
|
||||||
Authentication file for WebDAV mutation requests: `PUT`, `DELETE`, `MKCOL`.
|
|
||||||
The format of that file is the same as for the `.htpasswd` file
|
|
||||||
used for digest authentication. It can be created and managed by
|
|
||||||
`mongoose -A` command. Default: not set, WebDAV mutations are disallowed.
|
|
||||||
|
|
||||||
### document_root
|
|
||||||
A directory to serve. Default: current working directory.
|
|
||||||
|
|
||||||
### enable\_directory\_listing
|
|
||||||
Enable directory listing, either `yes` or `no`. Default: `yes`.
|
|
||||||
|
|
||||||
### extra\_mime\_types
|
|
||||||
Extra mime types to recognize, in form `extension1=type1,extension2=type2,...`.
|
|
||||||
Extension must include dot. Example:
|
|
||||||
`mongoose -extra_mime_types .cpp=plain/text,.java=plain/text`. Default: not set.
|
|
||||||
|
|
||||||
|
|
||||||
### global\_auth\_file
|
|
||||||
Path to a global passwords file, either full path or relative to the mongoose
|
|
||||||
executable. If set, per-directory `.htpasswd` files are ignored,
|
|
||||||
and all requests are authorised against that file. Use `mongoose -A` to
|
|
||||||
manage passwords, or third party utilities like
|
|
||||||
[htpasswd-generator](http://www.askapache.com/online-tools/htpasswd-generator).
|
|
||||||
Default: not set, per-directory `.htpasswd` files are respected.
|
|
||||||
|
|
||||||
### hide\_files\_patterns
|
|
||||||
A pattern for the files to hide. Files that match the pattern will not
|
|
||||||
show up in directory listing and return `404 Not Found` if requested. Pattern
|
|
||||||
must be for a file name only, not including directory name, e.g.
|
|
||||||
`mongoose -hide_files_patterns secret.txt|even_more_secret.txt`. Default:
|
|
||||||
not set.
|
|
||||||
|
|
||||||
### idle\_timeout\_ms
|
|
||||||
Timeout for idle connections in milliseconds. Default: `30000` (30 seconds)
|
|
||||||
|
|
||||||
### index_files
|
|
||||||
Comma-separated list of files to be treated as directory index
|
|
||||||
files. Default: `index.html,index.htm,index.cgi,index.shtml,index.php`
|
|
||||||
|
|
||||||
### ssl_certificate
|
|
||||||
Path to SSL certificate file. The file must be in PEM format,
|
|
||||||
and it must have both private key and certificate, see for example
|
|
||||||
[ssl_cert.pem](https://github.com/cesanta/mongoose/blob/master/build/ssl_cert.pem). If this option is set, then Mongoose serves SSL connections on
|
|
||||||
`listening_port`. Default: not set.
|
|
||||||
|
|
||||||
### listening_port
|
|
||||||
Port to listen on. Port could be prepended by the specific IP address to bind
|
|
||||||
to, e.g. `mongoose -listening_port 127.0.0.1:8080`. Otherwise Mongoose
|
|
||||||
will bind to all addresses. Default: 8080.
|
|
||||||
|
|
||||||
### run\_as\_user
|
|
||||||
Switch to given user credentials after startup. UNIX-only. This option is
|
|
||||||
required when mongoose needs to bind on privileged port on UNIX, e.g.
|
|
||||||
|
|
||||||
$ sudo mongoose -listening_port 80 -run_as_user nobody
|
|
||||||
|
|
||||||
Default: not set.
|
|
||||||
|
|
||||||
### url\_rewrites
|
|
||||||
Comma-separated list of URL rewrites in the form of
|
|
||||||
`uri_pattern=file_or_directory_path`. When Mongoose receives the request,
|
|
||||||
it constructs the file name to show by combining `document_root` and the URI.
|
|
||||||
However, if the rewrite option is used and `uri_pattern` matches the
|
|
||||||
requested URI, then `document_root` is ignored. Instead,
|
|
||||||
`file_or_directory_path` is used, which should be a full path name or
|
|
||||||
a path relative to the web server's current working directory. Note that
|
|
||||||
`uri_pattern`, as all mongoose patterns, is a prefix pattern. If `uri_pattern`
|
|
||||||
is a number, then it is treated as HTTP error code, and `file_or_directory_path`
|
|
||||||
should be an URI to redirect to. Mongoose will issue `302` temporary redirect
|
|
||||||
to the specified URI with following parameters:
|
|
||||||
`?code=HTTP_ERROR_CODE&orig_uri=ORIGINAL_URI&query_string=QUERY_STRING`.
|
|
||||||
|
|
||||||
If `uri_pattern` starts with `@` symbol, then Mongoose compares
|
|
||||||
it with the `HOST` header of the request. If they are equal, Mongoose sets
|
|
||||||
document root to `file_or_directory_path`, implementing virtual hosts support.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
# Redirect all accesses to `.doc` files to a special script
|
|
||||||
mongoose -url_rewrites **.doc$=/path/to/cgi-bin/handle_doc.cgi
|
|
||||||
|
|
||||||
# Implement user home directories support
|
|
||||||
mongoose -url_rewrites /~joe/=/home/joe/,/~bill=/home/bill/
|
|
||||||
|
|
||||||
# Redirect 404 errors to a specific error page
|
|
||||||
mongoose -url_rewrites 404=/cgi-bin/error.cgi
|
|
||||||
|
|
||||||
# Virtual hosts example: serve foo.com domain from different directory
|
|
||||||
mongoose -url_rewrites @foo.com=/var/www/foo.com
|
|
||||||
|
|
||||||
Default: not set.
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
How To Create A PHP Website With Mongoose
|
|
||||||
===========================================
|
|
||||||
|
|
||||||
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
|
|
||||||
|
|
||||||

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

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

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

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

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

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

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

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

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

|
|
||||||
|
|
||||||
## 11. Click on the mongoose icon and choose "Start browser" menu:
|
|
||||||

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

|
|
||||||
-75
@@ -1,75 +0,0 @@
|
|||||||
# Mongoose SSL guide
|
|
||||||
|
|
||||||
SSL is a protocol that makes web communication secure. To enable SSL
|
|
||||||
in mongoose, 3 steps are required:
|
|
||||||
|
|
||||||
1. Valid certificate file must be created
|
|
||||||
2. `ssl_certificate` options must be set to contain path to the
|
|
||||||
certificate file.
|
|
||||||
3. `listening_ports` option must contain a port number with letter `s`
|
|
||||||
appended to it, which instructs Mongoose to use SSL for all connections
|
|
||||||
made to that port.
|
|
||||||
|
|
||||||
Below is the `mongoose.conf` file snippet for typical SSL setup:
|
|
||||||
|
|
||||||
document_root www_root # Serve files in www_root directory
|
|
||||||
listening_ports 80r,443s # Redirect all HTTP requests to HTTPS
|
|
||||||
ssl_certificate ssl_cert.pem # Location of certificate file
|
|
||||||
|
|
||||||
## How to create SSL certificate file
|
|
||||||
|
|
||||||
SSL certificate file is a text file that must contain at least two
|
|
||||||
sections:
|
|
||||||
|
|
||||||
1. A private key
|
|
||||||
2. A certificate
|
|
||||||
|
|
||||||
Both sections should be chunks of text in PEM format. When PEM file is
|
|
||||||
opened in a text editor, it looks like this:
|
|
||||||
|
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH
|
|
||||||
hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg=
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB
|
|
||||||
SEGI4JSxV56lYg==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
Two aforementioned sections are clearly seen. Typically, those section
|
|
||||||
are bigger then in the example shown. The text between the `BEGIN` and
|
|
||||||
`END` is the text representation of binary data, a private key and a
|
|
||||||
certificate. Therefore, in order to create a certificate file,
|
|
||||||
|
|
||||||
* private key must be converted to PEM format
|
|
||||||
* certificate must be converted to PEM format
|
|
||||||
* those two should be concatenated into a single file
|
|
||||||
|
|
||||||
If the certificate chain in used, a chain file also needs to be
|
|
||||||
converted into PEM format and appended to the certificate file.
|
|
||||||
|
|
||||||
## How SSL works
|
|
||||||
|
|
||||||
SSL is a protocol that can encrypt communication between two parties. If third
|
|
||||||
party observes all messages passed by, it would be very
|
|
||||||
hard for the third party (though not impossible) to decrypt the communication.
|
|
||||||
|
|
||||||
The idea is based on so-called public key encryption. Communicating parties
|
|
||||||
have two keys: a public key and a private key. A public key is advertised
|
|
||||||
to everybody, and it is contained in a certificate. A private key is kept
|
|
||||||
secret. Security algorithm works in a way that anybody can encrypt
|
|
||||||
a message using public key, and only private key can decrypt it.
|
|
||||||
|
|
||||||
This is why web server needs both private key and certificate: private key
|
|
||||||
is used to decrypt incoming messages, and certificate is used to tell the
|
|
||||||
public key to the other party. When communication starts, parties exchange
|
|
||||||
their public keys, and keep private keys to themselves. Man-in-the-middle
|
|
||||||
who observes the communication is unable to decrypt the messages cause
|
|
||||||
private keys are required for decryption.
|
|
||||||
|
|
||||||
Encryption algorithms are built on top of hard mathematical problem, which
|
|
||||||
makes it very expensive for man-in-the-middle to compute private keys.
|
|
||||||
For example, RSA algorithm is based on a mathematical problem of factorization.
|
|
||||||
It is easy to generate two very large prime numbers `P` and `Q` and make
|
|
||||||
a product `P * Q`. But given a product, it is very hard to recover these
|
|
||||||
two prime numbers - this is called factorization.
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
# Mongoose User Guide
|
|
||||||
|
|
||||||
Mongoose is small and easy to use web server built on top of
|
|
||||||
mongoose library. It is designed with maximum simplicity in mind. For example,
|
|
||||||
to share any directory, just drop mongoose executable in that directory,
|
|
||||||
double-click it (on UNIX, run it from shell) and launch a browser at
|
|
||||||
[http://localhost:8080](http://localhost:8080) Note that 'localhost' should
|
|
||||||
be changed to a machine's name if a folder is accessed from other computer.
|
|
||||||
|
|
||||||
On Windows and Mac, Mongoose iconifies itself to the system tray when started.
|
|
||||||
Right-click on the icon to pop up a menu, where it is possible to stop
|
|
||||||
mongoose, or configure it.
|
|
||||||
|
|
||||||
On UNIX, `mongoose` is a command line utility. Running `mongoose` in
|
|
||||||
terminal, optionally followed by configuration parameters
|
|
||||||
(`mongoose [OPTIONS]`) or configuration file name
|
|
||||||
(`mongoose [config_file_name]`) starts the
|
|
||||||
web server:
|
|
||||||
|
|
||||||
$ mongoose -document_root /var/www # Running mongoose with cmdline options
|
|
||||||
$ mongoose /etc/my_config.txt # Running mongoose with config file
|
|
||||||
$ mongoose # Running with no parameters. This will
|
|
||||||
# serve current directory on port 8080
|
|
||||||
|
|
||||||
Mongoose does not detach from terminal. Pressing `Ctrl-C` keys
|
|
||||||
stops the server.
|
|
||||||
|
|
||||||
When started, mongoose first searches for the configuration file.
|
|
||||||
If configuration file is specified explicitly in the command line, then
|
|
||||||
specified configuration file is used.
|
|
||||||
Otherwise, mongoose would search for file `mongoose.conf` in the same directory
|
|
||||||
where binary is located, and use it. Configuration file can be absent.
|
|
||||||
|
|
||||||
Configuration file is a sequence of lines, each line containing
|
|
||||||
command line argument name and it's value. Empty lines and lines beginning
|
|
||||||
with `#` are ignored. Here is the example of `mongoose.conf` file:
|
|
||||||
|
|
||||||
# This is a comment
|
|
||||||
document_root C:\www
|
|
||||||
listening_port 80
|
|
||||||
ssl_certificate C:\mongoose\ssl_cert.pem
|
|
||||||
|
|
||||||
Command line arguments are highest priority and can override
|
|
||||||
configuration file settings. For example, if `mongoose.conf` has line
|
|
||||||
`document_root /var/www`, and mongoose has been started as
|
|
||||||
`mongoose -document_root /etc`, then `/etc` directory will be used as
|
|
||||||
document root.
|
|
||||||
|
|
||||||
Note that configuration options on the command line must start with `-`,
|
|
||||||
and their names are the same as in the config file. Exampli gratia,
|
|
||||||
the following two setups are equivalent:
|
|
||||||
|
|
||||||
$ mongoose -listening_port 1234 -document_root /var/www
|
|
||||||
|
|
||||||
$ cat > mongoose.conf
|
|
||||||
listening_ports 1234
|
|
||||||
document_root /var/www
|
|
||||||
^D
|
|
||||||
$ mongoose
|
|
||||||
|
|
||||||
Mongoose can also be used to modify `.htpasswd` passwords file:
|
|
||||||
|
|
||||||
$ mongoose -A .htpasswd mydomain.com user_name user_password
|
|
||||||
|
|
||||||
Unlike other web servers, mongoose does not require CGI scripts be located in
|
|
||||||
a special directory. CGI scripts can be anywhere. CGI (and SSI) files are
|
|
||||||
recognized by the file name pattern. Mongoose uses shell-like glob
|
|
||||||
patterns. Pattern match starts at the beginning of the string, so essentially
|
|
||||||
patterns are prefix patterns. Syntax is as follows:
|
|
||||||
|
|
||||||
** Matches everything
|
|
||||||
* Matches everything but slash character, '/'
|
|
||||||
? Matches any character
|
|
||||||
$ Matches the end of the string
|
|
||||||
| Matches if pattern on the left side or the right side matches.
|
|
||||||
|
|
||||||
All other characters in the pattern match themselves. Examples:
|
|
||||||
|
|
||||||
# Pattern Meaning
|
|
||||||
**.cgi$ Any string that ends with .cgi
|
|
||||||
/foo Any string that begins with /foo
|
|
||||||
**a$|**b$ Any string that ends with a or b
|
|
||||||
|
|
||||||
To restrict CGI files only to `/cgi-bin/` directory, use this setting:
|
|
||||||
|
|
||||||
$ mongoose -cgi_pattern /cgi-bin/*.cgi # Emulate /cgi-bin/ restriction
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
*.exe
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
#############################################################
|
||||||
|
# Required variables for each makefile
|
||||||
|
# Discard this section from all parent makefiles
|
||||||
|
# Expected variables (with automatic defaults):
|
||||||
|
# CSRCS (all "C" files in the dir)
|
||||||
|
# SUBDIRS (all subdirs with a Makefile)
|
||||||
|
# GEN_LIBS - list of libs to be generated ()
|
||||||
|
# GEN_IMAGES - list of object file images to be generated ()
|
||||||
|
# GEN_BINS - list of binaries to be generated ()
|
||||||
|
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||||
|
# subdir/lib to be extracted and rolled up into
|
||||||
|
# a generated lib/image xxx.a ()
|
||||||
|
#
|
||||||
|
TARGET = eagle
|
||||||
|
#FLAVOR = release
|
||||||
|
FLAVOR = debug
|
||||||
|
|
||||||
|
EXTRA_CCFLAGS += -Wall -Werror
|
||||||
|
|
||||||
|
ifndef PDIR # {
|
||||||
|
GEN_IMAGES= eagle.app.v6.out
|
||||||
|
GEN_BINS= eagle.app.v6.bin
|
||||||
|
SPECIAL_MKTARGETS=$(APP_MKTARGETS)
|
||||||
|
SUBDIRS=user
|
||||||
|
|
||||||
|
endif # } PDIR
|
||||||
|
|
||||||
|
LDDIR = $(SDK_PATH)/ld
|
||||||
|
|
||||||
|
CCFLAGS += -Os
|
||||||
|
|
||||||
|
TARGET_LDFLAGS = \
|
||||||
|
-nostdlib \
|
||||||
|
-Wl,-EL \
|
||||||
|
--longcalls \
|
||||||
|
--text-section-literals
|
||||||
|
|
||||||
|
ifeq ($(FLAVOR),debug)
|
||||||
|
TARGET_LDFLAGS += -g -O2
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(FLAVOR),release)
|
||||||
|
TARGET_LDFLAGS += -g -O0
|
||||||
|
endif
|
||||||
|
|
||||||
|
COMPONENTS_eagle.app.v6 = \
|
||||||
|
user/libuser.a
|
||||||
|
|
||||||
|
LINKFLAGS_eagle.app.v6 = \
|
||||||
|
-L$(SDK_PATH)/lib \
|
||||||
|
-Wl,--gc-sections \
|
||||||
|
-nostdlib \
|
||||||
|
-T$(LD_FILE) \
|
||||||
|
-Wl,--no-check-sections \
|
||||||
|
-u call_user_start \
|
||||||
|
-Wl,-static \
|
||||||
|
-Wl,--start-group \
|
||||||
|
-lcirom \
|
||||||
|
-lgcc \
|
||||||
|
-lhal \
|
||||||
|
-lphy \
|
||||||
|
-lpp \
|
||||||
|
-lnet80211 \
|
||||||
|
-lwpa \
|
||||||
|
-lcrypto \
|
||||||
|
-lmain \
|
||||||
|
-lfreertos \
|
||||||
|
-llwip \
|
||||||
|
$(DEP_LIBS_eagle.app.v6) \
|
||||||
|
-Wl,--end-group
|
||||||
|
|
||||||
|
DEPENDS_eagle.app.v6 = \
|
||||||
|
$(LD_FILE) \
|
||||||
|
$(LDDIR)/eagle.rom.addr.v6.ld
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
# Configuration i.e. compile options etc.
|
||||||
|
# Target specific stuff (defines etc.) goes in here!
|
||||||
|
# Generally values applying to a tree are captured in the
|
||||||
|
# makefile at its root level - these are then overridden
|
||||||
|
# for a subtree within the makefile rooted therein
|
||||||
|
#
|
||||||
|
|
||||||
|
#UNIVERSAL_TARGET_DEFINES = \
|
||||||
|
|
||||||
|
# Other potential configuration flags include:
|
||||||
|
# -DTXRX_TXBUF_DEBUG
|
||||||
|
# -DTXRX_RXBUF_DEBUG
|
||||||
|
# -DWLAN_CONFIG_CCX
|
||||||
|
CONFIGURATION_DEFINES = -DICACHE_FLASH
|
||||||
|
|
||||||
|
DEFINES += \
|
||||||
|
$(UNIVERSAL_TARGET_DEFINES) \
|
||||||
|
$(CONFIGURATION_DEFINES)
|
||||||
|
|
||||||
|
DDEFINES += \
|
||||||
|
$(UNIVERSAL_TARGET_DEFINES) \
|
||||||
|
$(CONFIGURATION_DEFINES)
|
||||||
|
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
# Recursion Magic - Don't touch this!!
|
||||||
|
#
|
||||||
|
# Each subtree potentially has an include directory
|
||||||
|
# corresponding to the common APIs applicable to modules
|
||||||
|
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||||
|
# of a module can only contain the include directories up
|
||||||
|
# its parent path, and not its siblings
|
||||||
|
#
|
||||||
|
# Required for each makefile to inherit from the parent
|
||||||
|
#
|
||||||
|
|
||||||
|
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
||||||
|
sinclude $(SDK_PATH)/Makefile
|
||||||
|
|
||||||
|
.PHONY: FORCE
|
||||||
|
FORCE:
|
||||||
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
This is a Mongoose "Hello, world" that can be compiled under ESP8266 RTOS SDK.
|
||||||
|
|
||||||
|
It sets up an AP (SSID `Mongoose`) and serves a "hello world" page on http://192.168.4.1/
|
||||||
|
|
||||||
|
Most of the the boilerplate comes from [project_template](https://github.com/espressif/ESP8266_RTOS_SDK/tree/master/examples/project_template) (@ [3ca6af5](https://github.com/espressif/ESP8266_RTOS_SDK/tree/3ca6af5da68678d809b34c7cd98bee71e0235039/examples/project_template)) with minimal changes.
|
||||||
|
|
||||||
|
To build with no changes to the SDK, you will need a module with 1MB (8Mb) flash or more.
|
||||||
|
|
||||||
|
Compile (for NodeMCU 1.0):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ export SDK_PATH=/path/to/ESP8266_RTOS_SDK
|
||||||
|
$ export BIN_PATH=./bin
|
||||||
|
$ make clean; make BOOT=new APP=1 SPI_SPEED=40 SPI_MODE=dio SPI_SIZE_MAP=6
|
||||||
|
```
|
||||||
|
|
||||||
|
Flash (using [esptool](https://github.com/themadinventor/esptool)):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ esptool.py --port /dev/ttyUSB0 --baud 230400 \
|
||||||
|
write_flash --flash_mode=dio --flash_size=32m \
|
||||||
|
0x00000 ${SDK_PATH}/bin/boot_v1.4\(b1\).bin \
|
||||||
|
0x01000 ${BIN_PATH}/upgrade/user1.4096.new.6.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
The output can be made to fit in 512KB (4Mb), but stock linker scripts do not reserve enough space for code.
|
||||||
|
Custom linker script is provided for that, so you can use it for smaller devices like so (example that will work with ESP-01):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make clean; make BOOT=none APP=0 SPI_SPEED=40 SPI_MODE=qio SPI_SIZE_MAP=0 LD_FILE=ld/eagle.app.v6.512.compact.ld
|
||||||
|
$ esptool.py --port /dev/ttyUSB0 --baud 230400 \
|
||||||
|
write_flash --flash_mode=qio --flash_size=4m \
|
||||||
|
0x00000 ${BIN_PATH}/eagle.flash.bin \
|
||||||
|
0x10000 ${BIN_PATH}/eagle.irom0text.bin
|
||||||
|
```
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
:<<!
|
||||||
|
******NOTICE******
|
||||||
|
MUST set SDK_PATH & BIN_PATH firstly!!!
|
||||||
|
example:
|
||||||
|
export SDK_PATH=~/ESP8266_RTOS_SDK
|
||||||
|
export BIN_PATH=~/esp8266_bin
|
||||||
|
!
|
||||||
|
|
||||||
|
echo "gen_misc.sh version 20150911"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ $SDK_PATH ]; then
|
||||||
|
echo "SDK_PATH:"
|
||||||
|
echo "$SDK_PATH"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo "ERROR: Please export SDK_PATH in gen_misc.sh firstly, exit!!!"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $BIN_PATH ]; then
|
||||||
|
echo "BIN_PATH:"
|
||||||
|
echo "$BIN_PATH"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo "ERROR: Please export BIN_PATH in gen_misc.sh firstly, exit!!!"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Please check SDK_PATH & BIN_PATH, enter (Y/y) to continue:"
|
||||||
|
read input
|
||||||
|
|
||||||
|
if [[ $input != Y ]] && [[ $input != y ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "Please follow below steps(1-5) to generate specific bin(s):"
|
||||||
|
echo "STEP 1: use boot_v1.2+ by default"
|
||||||
|
boot=new
|
||||||
|
|
||||||
|
echo "boot mode: $boot"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "STEP 2: choose bin generate(0=eagle.flash.bin+eagle.irom0text.bin, 1=user1.bin, 2=user2.bin)"
|
||||||
|
echo "enter (0/1/2, default 0):"
|
||||||
|
read input
|
||||||
|
|
||||||
|
if [ -z "$input" ]; then
|
||||||
|
if [ $boot != none ]; then
|
||||||
|
boot=none
|
||||||
|
echo "ignore boot"
|
||||||
|
fi
|
||||||
|
app=0
|
||||||
|
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
|
||||||
|
elif [ $input == 1 ]; then
|
||||||
|
if [ $boot == none ]; then
|
||||||
|
app=0
|
||||||
|
echo "choose no boot before"
|
||||||
|
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
|
||||||
|
else
|
||||||
|
app=1
|
||||||
|
echo "generate bin: user1.bin"
|
||||||
|
fi
|
||||||
|
elif [ $input == 2 ]; then
|
||||||
|
if [ $boot == none ]; then
|
||||||
|
app=0
|
||||||
|
echo "choose no boot before"
|
||||||
|
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
|
||||||
|
else
|
||||||
|
app=2
|
||||||
|
echo "generate bin: user2.bin"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ $boot != none ]; then
|
||||||
|
boot=none
|
||||||
|
echo "ignore boot"
|
||||||
|
fi
|
||||||
|
app=0
|
||||||
|
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "STEP 3: choose spi speed(0=20MHz, 1=26.7MHz, 2=40MHz, 3=80MHz)"
|
||||||
|
echo "enter (0/1/2/3, default 2):"
|
||||||
|
read input
|
||||||
|
|
||||||
|
if [ -z "$input" ]; then
|
||||||
|
spi_speed=40
|
||||||
|
elif [ $input == 0 ]; then
|
||||||
|
spi_speed=20
|
||||||
|
elif [ $input == 1 ]; then
|
||||||
|
spi_speed=26.7
|
||||||
|
elif [ $input == 3 ]; then
|
||||||
|
spi_speed=80
|
||||||
|
else
|
||||||
|
spi_speed=40
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "spi speed: $spi_speed MHz"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "STEP 4: choose spi mode(0=QIO, 1=QOUT, 2=DIO, 3=DOUT)"
|
||||||
|
echo "enter (0/1/2/3, default 0):"
|
||||||
|
read input
|
||||||
|
|
||||||
|
if [ -z "$input" ]; then
|
||||||
|
spi_mode=QIO
|
||||||
|
elif [ $input == 1 ]; then
|
||||||
|
spi_mode=QOUT
|
||||||
|
elif [ $input == 2 ]; then
|
||||||
|
spi_mode=DIO
|
||||||
|
elif [ $input == 3 ]; then
|
||||||
|
spi_mode=DOUT
|
||||||
|
else
|
||||||
|
spi_mode=QIO
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "spi mode: $spi_mode"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "STEP 5: choose spi size and map"
|
||||||
|
echo " 0= 512KB( 256KB+ 256KB)"
|
||||||
|
echo " 2=1024KB( 512KB+ 512KB)"
|
||||||
|
echo " 3=2048KB( 512KB+ 512KB)"
|
||||||
|
echo " 4=4096KB( 512KB+ 512KB)"
|
||||||
|
echo " 5=2048KB(1024KB+1024KB)"
|
||||||
|
echo " 6=4096KB(1024KB+1024KB)"
|
||||||
|
echo "enter (0/2/3/4/5/6, default 0):"
|
||||||
|
read input
|
||||||
|
|
||||||
|
if [ -z "$input" ]; then
|
||||||
|
spi_size_map=0
|
||||||
|
echo "spi size: 512KB"
|
||||||
|
echo "spi ota map: 256KB + 256KB"
|
||||||
|
elif [ $input == 2 ]; then
|
||||||
|
spi_size_map=2
|
||||||
|
echo "spi size: 1024KB"
|
||||||
|
echo "spi ota map: 512KB + 512KB"
|
||||||
|
elif [ $input == 3 ]; then
|
||||||
|
spi_size_map=3
|
||||||
|
echo "spi size: 2048KB"
|
||||||
|
echo "spi ota map: 512KB + 512KB"
|
||||||
|
elif [ $input == 4 ]; then
|
||||||
|
spi_size_map=4
|
||||||
|
echo "spi size: 4096KB"
|
||||||
|
echo "spi ota map: 512KB + 512KB"
|
||||||
|
elif [ $input == 5 ]; then
|
||||||
|
spi_size_map=5
|
||||||
|
echo "spi size: 2048KB"
|
||||||
|
echo "spi ota map: 1024KB + 1024KB"
|
||||||
|
elif [ $input == 6 ]; then
|
||||||
|
spi_size_map=6
|
||||||
|
echo "spi size: 4096KB"
|
||||||
|
echo "spi ota map: 1024KB + 1024KB"
|
||||||
|
else
|
||||||
|
spi_size_map=0
|
||||||
|
echo "spi size: 512KB"
|
||||||
|
echo "spi ota map: 256KB + 256KB"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "start..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
make clean
|
||||||
|
|
||||||
|
make BOOT=$boot APP=$app SPI_SPEED=$spi_speed SPI_MODE=$spi_mode SPI_SIZE_MAP=$spi_size_map
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/* eagle.flash.bin @ 0x00000 */
|
||||||
|
/* eagle.irom0text.bin @ 0x10000 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flash map for 512KB flash.
|
||||||
|
*
|
||||||
|
* There is 0x8000 (32KB) available between 0x8000 and 0x10000.
|
||||||
|
*
|
||||||
|
* Note: IROM images starts at 0x10000 (ignore Makefile output which says 0x40000.
|
||||||
|
*/
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
dport0_0_seg : org = 0x3FF00000, len = 0x10
|
||||||
|
dram0_0_seg : org = 0x3FFE8000, len = 0x18000
|
||||||
|
iram1_0_seg : org = 0x40100000, len = 0x8000
|
||||||
|
irom0_0_seg : org = 0x40210000, len = 0x6C000
|
||||||
|
}
|
||||||
|
|
||||||
|
INCLUDE "../ld/eagle.app.v6.common.ld"
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
This is a simple project template.
|
||||||
|
|
||||||
|
sample_lib is an example for multi-level folder Makefile, notice the folder structure and each Makefile, you can get the clue.
|
||||||
|
|
||||||
|
|
||||||
|
HOWTO:
|
||||||
|
1. Copy this folder to anywhere.
|
||||||
|
Example:
|
||||||
|
Copy to ~/workspace/project_template
|
||||||
|
You can rename this folder as you like.
|
||||||
|
|
||||||
|
2. Export SDK_PATH and BIN_PATH.
|
||||||
|
Example:
|
||||||
|
Your SDK path is ~/esp_iot_rtos_sdk, and want generate bin at ~/esp8266_bin.
|
||||||
|
Do follow steps:
|
||||||
|
1>. export SDK_PATH=~/esp_iot_rtos_sdk
|
||||||
|
2>. export BIN_PATH=~/esp8266_bin
|
||||||
|
SDK and project are seperate, you can update SDK without change your project.
|
||||||
|
|
||||||
|
3. Enter project_template folder, run ./gen_misc.sh, and follow the tips and steps.
|
||||||
|
|
||||||
|
|
||||||
|
Compile Options:
|
||||||
|
(1) COMPILE
|
||||||
|
Possible value: xcc
|
||||||
|
Default value:
|
||||||
|
If not set, use gcc by default.
|
||||||
|
|
||||||
|
(2) BOOT
|
||||||
|
Possible value: none/old/new
|
||||||
|
none: no need boot
|
||||||
|
old: use boot_v1.1
|
||||||
|
new: use boot_v1.2
|
||||||
|
Default value: new
|
||||||
|
|
||||||
|
(3) APP
|
||||||
|
Possible value: 0/1/2
|
||||||
|
0: original mode, generate eagle.app.v6.flash.bin and eagle.app.v6.irom0text.bin
|
||||||
|
1: generate user1
|
||||||
|
2: generate user2
|
||||||
|
Default value: 0
|
||||||
|
|
||||||
|
(3) SPI_SPEED
|
||||||
|
Possible value: 20/26.7/40/80
|
||||||
|
Default value: 40
|
||||||
|
|
||||||
|
(4) SPI_MODE
|
||||||
|
Possible value: QIO/QOUT/DIO/DOUT
|
||||||
|
Default value: QIO
|
||||||
|
|
||||||
|
(4) SPI_SIZE_MAP
|
||||||
|
Possible value: 0/2/3/4/5/6
|
||||||
|
Default value: 0
|
||||||
|
|
||||||
|
For example:
|
||||||
|
make COMPILE=gcc BOOT=new APP=1 SPI_SPEED=40 SPI_MODE=QIO SPI_SIZE_MAP=0
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
#############################################################
|
||||||
|
# Required variables for each makefile
|
||||||
|
# Discard this section from all parent makefiles
|
||||||
|
# Expected variables (with automatic defaults):
|
||||||
|
# CSRCS (all "C" files in the dir)
|
||||||
|
# SUBDIRS (all subdirs with a Makefile)
|
||||||
|
# GEN_LIBS - list of libs to be generated ()
|
||||||
|
# GEN_IMAGES - list of images to be generated ()
|
||||||
|
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||||
|
# subdir/lib to be extracted and rolled up into
|
||||||
|
# a generated lib/image xxx.a ()
|
||||||
|
#
|
||||||
|
ifndef PDIR
|
||||||
|
GEN_LIBS = libuser.a
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
# Configuration i.e. compile options etc.
|
||||||
|
# Target specific stuff (defines etc.) goes in here!
|
||||||
|
# Generally values applying to a tree are captured in the
|
||||||
|
# makefile at its root level - these are then overridden
|
||||||
|
# for a subtree within the makefile rooted therein
|
||||||
|
#
|
||||||
|
DEFINES += -DMG_DISABLE_DAV -DMG_DISABLE_SYNC_RESOLVER -DMG_DISABLE_CGI \
|
||||||
|
-DMG_DISABLE_SOCKETPAIR -DMG_DISABLE_DIRECTORY_LISTING \
|
||||||
|
-DMG_MAX_HTTP_HEADERS=20 -DMG_MAX_HTTP_REQUEST_SIZE=1024 \
|
||||||
|
-DMG_MAX_PATH=40 -DMG_MAX_HTTP_SEND_IOBUF=1024 \
|
||||||
|
-DMG_ESP8266 -DRTOS_SDK -DMG_LWIP
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
# Recursion Magic - Don't touch this!!
|
||||||
|
#
|
||||||
|
# Each subtree potentially has an include directory
|
||||||
|
# corresponding to the common APIs applicable to modules
|
||||||
|
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||||
|
# of a module can only contain the include directories up
|
||||||
|
# its parent path, and not its siblings
|
||||||
|
#
|
||||||
|
# Required for each makefile to inherit from the parent
|
||||||
|
#
|
||||||
|
|
||||||
|
INCLUDES += -I ./
|
||||||
|
PDIR := ../$(PDIR)
|
||||||
|
sinclude $(PDIR)Makefile
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "esp_common.h"
|
||||||
|
|
||||||
|
/* Makes fprintf(stdout) and stderr work. */
|
||||||
|
_ssize_t _write_r(struct _reent *r, int fd, void *buf, size_t len) {
|
||||||
|
if (fd == 1 || fd == 2) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
os_putc(((char *) buf)[i]);
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* You'll need to implement _open_r and friends if you want file operations. See
|
||||||
|
* https://github.com/cesanta/smart.js/blob/master/platforms/esp8266/user/esp_fs.c
|
||||||
|
* for SPIFFS-based implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void abort(void) __attribute__((no_instrument_function));
|
||||||
|
void abort(void) {
|
||||||
|
/* cause an unaligned access exception, that will drop you into gdb */
|
||||||
|
*(int *) 1 = 1;
|
||||||
|
while (1)
|
||||||
|
; /* avoid gcc warning because stdlib abort() has noreturn attribute */
|
||||||
|
}
|
||||||
|
|
||||||
|
void _exit(int status) {
|
||||||
|
printf("_exit(%d)\n", status);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) {
|
||||||
|
uint32_t time = system_get_time();
|
||||||
|
tp->tv_sec = time / 1000000;
|
||||||
|
tp->tv_usec = time % 1000000;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../mongoose.c
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../mongoose.h
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "esp_common.h"
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
#define AP_SSID "Mongoose"
|
||||||
|
#define AP_PASS "Mongoose"
|
||||||
|
#define AP_CHAN 9
|
||||||
|
#define MG_LISTEN_ADDR "80"
|
||||||
|
|
||||||
|
#define MG_TASK_STACK_SIZE 2048
|
||||||
|
#define MG_TASK_PRIORITY 1
|
||||||
|
|
||||||
|
void uart_div_modify(int uart_no, unsigned int freq);
|
||||||
|
|
||||||
|
void ev_handler(struct mg_connection *nc, int ev, void *p) {
|
||||||
|
static const char *reply_fmt =
|
||||||
|
"HTTP/1.0 200 OK\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-Type: text/plain\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"Hello %s\n";
|
||||||
|
LOG(LL_DEBUG, ("conn %p ev %d", nc, ev));
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_ACCEPT: {
|
||||||
|
char addr[32];
|
||||||
|
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
|
||||||
|
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
|
||||||
|
LOG(LL_INFO, ("Connection %p from %s", nc, addr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MG_EV_HTTP_REQUEST: {
|
||||||
|
char addr[32];
|
||||||
|
struct http_message *hm = (struct http_message *) p;
|
||||||
|
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
|
||||||
|
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
|
||||||
|
LOG(LL_INFO,
|
||||||
|
("HTTP request from %s: %.*s %.*s", addr, (int) hm->method.len,
|
||||||
|
hm->method.p, (int) hm->uri.len, hm->uri.p));
|
||||||
|
mg_printf(nc, reply_fmt, addr);
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MG_EV_CLOSE: {
|
||||||
|
LOG(LL_INFO, ("Connection %p closed", nc));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_ap() {
|
||||||
|
struct softap_config cfg;
|
||||||
|
|
||||||
|
wifi_set_opmode_current(SOFTAP_MODE);
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
strcpy((char *) cfg.ssid, AP_SSID);
|
||||||
|
strcpy((char *) cfg.password, AP_PASS);
|
||||||
|
cfg.ssid_len = strlen((const char *) cfg.ssid);
|
||||||
|
cfg.authmode =
|
||||||
|
strlen((const char *) cfg.password) ? AUTH_WPA2_PSK : AUTH_OPEN;
|
||||||
|
cfg.channel = AP_CHAN;
|
||||||
|
cfg.ssid_hidden = 0;
|
||||||
|
cfg.max_connection = 10;
|
||||||
|
cfg.beacon_interval = 100; /* ms */
|
||||||
|
|
||||||
|
LOG(LL_INFO, ("Setting up AP '%s' on channel %d", cfg.ssid, cfg.channel));
|
||||||
|
wifi_softap_set_config_current(&cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mg_task(void *arg) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
|
||||||
|
cs_log_set_level(LL_INFO);
|
||||||
|
|
||||||
|
LOG(LL_INFO, ("SDK version: %s", system_get_sdk_version()));
|
||||||
|
setup_ap();
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
|
||||||
|
nc = mg_bind(&mgr, MG_LISTEN_ADDR, ev_handler);
|
||||||
|
if (nc == NULL) {
|
||||||
|
LOG(LL_ERROR, ("Error setting up listener!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xTaskHandle s_mg_task_handle;
|
||||||
|
|
||||||
|
void user_init(void) {
|
||||||
|
uart_div_modify(0, UART_CLK_FREQ / 115200);
|
||||||
|
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
|
setvbuf(stderr, NULL, _IONBF, 0);
|
||||||
|
|
||||||
|
xTaskCreate(mg_task, (const signed char *) "mongoose", MG_TASK_STACK_SIZE,
|
||||||
|
NULL, MG_TASK_PRIORITY, &s_mg_task_handle);
|
||||||
|
}
|
||||||
+9
-56
@@ -1,62 +1,15 @@
|
|||||||
CFLAGS = -W -Wall -I.. -pthread -g -pipe $(CFLAGS_EXTRA)
|
# Copyright (c) 2014 Cesanta Software
|
||||||
RM = rm -rf
|
# All rights reserved
|
||||||
OUT = -o $@
|
|
||||||
|
|
||||||
ifeq ($(OS),Windows_NT)
|
SUBDIRS = $(sort $(dir $(wildcard */)))
|
||||||
MSVC = ../../vc6
|
X = $(SUBDIRS)
|
||||||
RM = del /q /f
|
|
||||||
OUT =
|
|
||||||
CC = $(MSVC)/bin/cl
|
|
||||||
CFLAGS = /MD /TC /nologo /W3 /I$(MSVC)/include /I..
|
|
||||||
CFLAGS += /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
|
|
||||||
CFLAGS += $(CFLAGS_EXTRA)
|
|
||||||
else
|
|
||||||
UNAME_S := $(shell uname -s)
|
|
||||||
|
|
||||||
ifeq ($(UNAME_S),Linux)
|
.PHONY: $(SUBDIRS)
|
||||||
CFLAGS += -ldl
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(UNAME_S),Darwin)
|
all: $(SUBDIRS)
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
all: hello websocket server post multi_threaded upload auth
|
$(SUBDIRS):
|
||||||
|
@$(MAKE) -C $@
|
||||||
# To enable Lua in a server, uncomment following lines
|
|
||||||
LUA = ../lua-5.2.3/src
|
|
||||||
#CFLAGS += -I$(LUA) -L$(LUA) -llua -DMONGOOSE_USE_LUA
|
|
||||||
|
|
||||||
server: server.c ../mongoose.c
|
|
||||||
$(CC) server.c ../mongoose.c $(OUT) $(CFLAGS)
|
|
||||||
|
|
||||||
hello: hello.c ../mongoose.c
|
|
||||||
$(CC) hello.c ../mongoose.c $(OUT) $(CFLAGS)
|
|
||||||
|
|
||||||
websocket: websocket_html.c websocket.c ../mongoose.c
|
|
||||||
$(CC) websocket.c websocket_html.c ../mongoose.c $(OUT) $(CFLAGS)
|
|
||||||
|
|
||||||
post: post.c ../mongoose.c
|
|
||||||
$(CC) post.c ../mongoose.c $(OUT) $(CFLAGS)
|
|
||||||
|
|
||||||
multi_threaded: multi_threaded.c ../mongoose.c
|
|
||||||
$(CC) multi_threaded.c ../mongoose.c $(OUT) $(CFLAGS)
|
|
||||||
|
|
||||||
upload: upload.c ../mongoose.c
|
|
||||||
$(CC) upload.c ../mongoose.c $(OUT) $(CFLAGS)
|
|
||||||
|
|
||||||
auth: auth.c ../mongoose.c
|
|
||||||
$(CC) auth.c ../mongoose.c $(OUT) $(CFLAGS)
|
|
||||||
|
|
||||||
mjpg: mjpg.c ../mongoose.c
|
|
||||||
$(CC) mjpg.c ../mongoose.c $(OUT) $(CFLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
websocket_html.c: websocket.html
|
|
||||||
perl mkdata.pl $< > $@
|
|
||||||
|
|
||||||
u:
|
|
||||||
g++ unit_test.c -Wall -W -pedantic -lssl -g -O0 $(CFLAGS_EXTRA) && ./a.out
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-@$(RM) hello mjpg upload post websocket auth server multi_threaded websocket_html.c *.exe *.dSYM *.obj .*o
|
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 dns_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(dns_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 dns_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(dns_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);
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
|
||||||
|
|
||||||
if (ev == MG_AUTH) {
|
|
||||||
int result = MG_FALSE; // Not authorized
|
|
||||||
FILE *fp;
|
|
||||||
|
|
||||||
// To populate passwords file, do
|
|
||||||
// mongoose -A my_passwords.txt mydomain.com admin admin
|
|
||||||
if ((fp = fopen("my_passwords.txt", "r")) != NULL) {
|
|
||||||
result = mg_authorize_digest(conn, fp);
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MG_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
|
||||||
mg_set_option(server, "listening_port", "8080");
|
|
||||||
mg_set_option(server, "document_root", ".");
|
|
||||||
|
|
||||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
|
||||||
for (;;) {
|
|
||||||
mg_poll_server(server, 1000);
|
|
||||||
}
|
|
||||||
mg_destroy_server(&server);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
PROG = big_upload
|
||||||
|
include ../examples.mk
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
// Copyright (c) 2015 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// This example demonstrates how to handle very large requests without keeping
|
||||||
|
// them in memory.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static const char *s_http_port = "8000";
|
||||||
|
|
||||||
|
struct file_writer_data {
|
||||||
|
FILE *fp;
|
||||||
|
size_t bytes_left;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_request(struct mg_connection *nc) {
|
||||||
|
// This handler gets called only when we don't reset proto_handler in
|
||||||
|
// handle_recv, so we just return a page unconditionally.
|
||||||
|
mg_printf(nc, "%s",
|
||||||
|
"HTTP/1.1 200 OK\r\n"
|
||||||
|
"Content-Type: text/html\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"<html><body>Upload example."
|
||||||
|
"<form method=\"POST\" action=\"/upload\" "
|
||||||
|
" enctype=\"multipart/form-data\">"
|
||||||
|
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||||
|
"<input type=\"submit\" value=\"Upload\" />"
|
||||||
|
"</form></body></html>");
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_recv(struct mg_connection *nc) {
|
||||||
|
struct file_writer_data *data = (struct file_writer_data *) nc->user_data;
|
||||||
|
|
||||||
|
if (data == NULL) {
|
||||||
|
// This is a new connection, try to parse HTTP request.
|
||||||
|
struct http_message hm;
|
||||||
|
int req_len = mg_parse_http(nc->recv_mbuf.buf, nc->recv_mbuf.len, &hm,
|
||||||
|
1 /* is_req */);
|
||||||
|
|
||||||
|
if (req_len < 0 ||
|
||||||
|
(req_len == 0 && nc->recv_mbuf.len >= MG_MAX_HTTP_REQUEST_SIZE)) {
|
||||||
|
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
|
} else if (req_len == 0) {
|
||||||
|
// Request is not complete yet, do nothing.
|
||||||
|
} else if (mg_vcasecmp(&hm.method, "POST") == 0 &&
|
||||||
|
mg_vcmp(&hm.uri, "/upload") == 0) {
|
||||||
|
// This is the request that we don't want to buffer in memory.
|
||||||
|
|
||||||
|
if (hm.body.len == (size_t) ~0 || hm.body.len == 0) {
|
||||||
|
mg_printf(nc, "%s",
|
||||||
|
"HTTP/1.1 411 Content-Length required\r\n"
|
||||||
|
"Content-Length: 0\r\n\r\n");
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset proto_handler so Mongoose's http_handler() won't get confused
|
||||||
|
// once we start removing data from the buffer.
|
||||||
|
nc->proto_handler = NULL;
|
||||||
|
|
||||||
|
// Headers will be inaccessible later, so put everything we need into
|
||||||
|
// user_data.
|
||||||
|
data = calloc(1, sizeof(struct file_writer_data));
|
||||||
|
data->bytes_left = hm.body.len;
|
||||||
|
data->fp = tmpfile();
|
||||||
|
if (data->fp == NULL) {
|
||||||
|
mg_printf(nc, "%s",
|
||||||
|
"HTTP/1.1 500 Failed to open a file\r\n"
|
||||||
|
"Content-Length: 0\r\n\r\n");
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nc->user_data = (void *) data;
|
||||||
|
|
||||||
|
// Remove the headers from the buffer.
|
||||||
|
mbuf_remove(&nc->recv_mbuf, hm.body.p - nc->recv_mbuf.buf);
|
||||||
|
|
||||||
|
// Invoke itself again to write the piece of the body that is already in
|
||||||
|
// the buffer.
|
||||||
|
handle_recv(nc);
|
||||||
|
}
|
||||||
|
} else { // data != NULL
|
||||||
|
size_t to_write = data->bytes_left, written = 0;
|
||||||
|
if (nc->recv_mbuf.len < to_write) to_write = nc->recv_mbuf.len;
|
||||||
|
written = fwrite(nc->recv_mbuf.buf, 1, to_write, data->fp);
|
||||||
|
mbuf_remove(&nc->recv_mbuf, written);
|
||||||
|
data->bytes_left -= written;
|
||||||
|
if (data->bytes_left <= 0) {
|
||||||
|
// Request is complete, do something meaningful here.
|
||||||
|
|
||||||
|
mg_printf(nc,
|
||||||
|
"HTTP/1.1 200 OK\r\n"
|
||||||
|
"Content-Type: text/plain\r\n"
|
||||||
|
"Connection: close\r\n\r\n"
|
||||||
|
"Written %ld of POST data to a temp file\n\n",
|
||||||
|
(long) ftell(data->fp));
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
// handle_close will free the resources.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we free all allocated resources
|
||||||
|
static void handle_close(struct mg_connection *nc) {
|
||||||
|
struct file_writer_data *data = (struct file_writer_data *) nc->user_data;
|
||||||
|
|
||||||
|
if (data != NULL) {
|
||||||
|
fclose(data->fp);
|
||||||
|
free(data);
|
||||||
|
nc->user_data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||||
|
(void) ev_data;
|
||||||
|
switch (ev) {
|
||||||
|
case MG_EV_HTTP_REQUEST:
|
||||||
|
// Invoked when the full HTTP request is in the buffer (including body).
|
||||||
|
handle_request(nc);
|
||||||
|
break;
|
||||||
|
case MG_EV_RECV:
|
||||||
|
// Invoked every time new data arrives.
|
||||||
|
handle_recv(nc);
|
||||||
|
break;
|
||||||
|
case MG_EV_CLOSE:
|
||||||
|
handle_close(nc);
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Set up HTTP server parameters
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
|
||||||
|
printf("Starting web server on port %s\n", s_http_port);
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
// This file is part of mongoose web server project,
|
|
||||||
// https://github.com/cesanta/mongoose
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
public class Program {
|
|
||||||
static private int EventHandler(MongooseEvent ev) {
|
|
||||||
if (ev.type != 1) {
|
|
||||||
return 0; // Mark as unhandled
|
|
||||||
}
|
|
||||||
|
|
||||||
MongooseRequestInfo request_info = (MongooseRequestInfo)
|
|
||||||
Marshal.PtrToStructure(ev.request_info, typeof(MongooseRequestInfo));
|
|
||||||
|
|
||||||
if (request_info.uri != "/test") {
|
|
||||||
return 0; // Mark as unhandled
|
|
||||||
}
|
|
||||||
|
|
||||||
Mongoose.write(ev.conn, "HTTP/1.1 200 OK\r\n\r\n");
|
|
||||||
Mongoose.write(ev.conn, "Hello from C#!\n");
|
|
||||||
|
|
||||||
return 1; // Mark as handled
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Main() {
|
|
||||||
Mongoose web_server = new Mongoose(".", "9000",
|
|
||||||
new MongooseEventHandler(EventHandler));
|
|
||||||
|
|
||||||
Console.WriteLine("Mongoose v." + web_server.version_ + " started.");
|
|
||||||
Console.WriteLine("Press enter to exit program.");
|
|
||||||
|
|
||||||
// Serve requests until user presses "enter" on a keyboard
|
|
||||||
Console.ReadLine();
|
|
||||||
web_server.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
// This file is part of mongoose web server project,
|
|
||||||
// https://github.com/cesanta/mongoose
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)] public struct MongooseHeader {
|
|
||||||
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr name;
|
|
||||||
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// mongoose.h :: struct mg_request_info
|
|
||||||
[StructLayout(LayoutKind.Sequential)] public struct MongooseRequestInfo {
|
|
||||||
[MarshalAs(UnmanagedType.LPTStr)] public string request_method;
|
|
||||||
[MarshalAs(UnmanagedType.LPTStr)] public string uri;
|
|
||||||
[MarshalAs(UnmanagedType.LPTStr)] public string http_version;
|
|
||||||
[MarshalAs(UnmanagedType.LPTStr)] public string query_string;
|
|
||||||
[MarshalAs(UnmanagedType.LPTStr)] public string remote_user;
|
|
||||||
public int remote_ip;
|
|
||||||
public int remote_port;
|
|
||||||
public int is_ssl;
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]
|
|
||||||
public MongooseHeader[] http_headers;
|
|
||||||
};
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)] public struct MongooseEvent {
|
|
||||||
public int type;
|
|
||||||
public IntPtr user_data;
|
|
||||||
public IntPtr conn_data;
|
|
||||||
public IntPtr event_param;
|
|
||||||
public IntPtr conn;
|
|
||||||
public IntPtr request_info;
|
|
||||||
};
|
|
||||||
|
|
||||||
public delegate int MongooseEventHandlerN(ref MongooseEvent ev);
|
|
||||||
public delegate int MongooseEventHandler(MongooseEvent ev);
|
|
||||||
|
|
||||||
public class Mongoose {
|
|
||||||
public const string dll_name_ = "mongoose";
|
|
||||||
public string version_ = "??";
|
|
||||||
|
|
||||||
// These are here to store a ref to the callbacks
|
|
||||||
// while they are over in unmanaged code, to prevent garbage collection.
|
|
||||||
private event MongooseEventHandlerN delegates;
|
|
||||||
|
|
||||||
private IntPtr ctx_;
|
|
||||||
|
|
||||||
[DllImport(dll_name_)] private static extern
|
|
||||||
IntPtr mg_start([MarshalAs(UnmanagedType.LPArray,
|
|
||||||
ArraySubType=UnmanagedType.LPTStr)] string[] options,
|
|
||||||
MongooseEventHandlerN callback,
|
|
||||||
IntPtr user_data);
|
|
||||||
[DllImport(dll_name_)] private static extern void mg_stop(IntPtr ctx);
|
|
||||||
[DllImport(dll_name_)] private static extern IntPtr mg_version();
|
|
||||||
[DllImport(dll_name_)] public static extern int mg_write(IntPtr conn,
|
|
||||||
string data, int length);
|
|
||||||
|
|
||||||
public Mongoose(string document_root,
|
|
||||||
string listening_ports,
|
|
||||||
MongooseEventHandler event_handler) {
|
|
||||||
version_ = Marshal.PtrToStringAnsi(mg_version());
|
|
||||||
|
|
||||||
string[] options = {
|
|
||||||
"document_root", document_root,
|
|
||||||
"listening_ports", listening_ports,
|
|
||||||
null
|
|
||||||
};
|
|
||||||
|
|
||||||
MongooseEventHandlerN cb = delegate(ref MongooseEvent ev) {
|
|
||||||
return event_handler(ev);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prevent garbage collection
|
|
||||||
delegates += cb;
|
|
||||||
|
|
||||||
ctx_ = mg_start(options, cb, IntPtr.Zero);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int write(IntPtr conn, string data) {
|
|
||||||
return mg_write(conn, data, data.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() {
|
|
||||||
if (this.ctx_ != IntPtr.Zero) {
|
|
||||||
mg_stop(this.ctx_);
|
|
||||||
}
|
|
||||||
this.ctx_ = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
~Mongoose() {
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
PROG = captive_dns_server
|
||||||
|
MODULE_CFLAGS=-DMG_ENABLE_DNS_SERVER
|
||||||
|
|
||||||
|
include ../rules.mk
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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_dns_message *msg;
|
||||||
|
struct mg_dns_resource_record *rr;
|
||||||
|
struct mg_dns_reply reply;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_DNS_MESSAGE:
|
||||||
|
msg = (struct mg_dns_message *) ev_data;
|
||||||
|
reply = mg_dns_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_dns_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_dns_send_reply(nc, &reply);
|
||||||
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_server *server = mg_create_server(NULL);
|
|
||||||
mg_set_option(server, "listening_port", "8080");
|
|
||||||
mg_set_option(server, "document_root", "chat_html_root");
|
|
||||||
|
|
||||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
|
||||||
for (;;) {
|
|
||||||
mg_poll_server(server, 1000);
|
|
||||||
}
|
|
||||||
mg_destroy_server(&server);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
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>
|
|
||||||
-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: 'api.lp',
|
|
||||||
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({
|
|
||||||
data: {last_id: chat.lastMessageId, cmd: 'get_messages'},
|
|
||||||
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({
|
|
||||||
data: { text: input.value, cmd: 'send_message' },
|
|
||||||
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() {
|
|
||||||
$.ajaxSetup({
|
|
||||||
dataType: 'json',
|
|
||||||
url: chat.backendUrl
|
|
||||||
});
|
|
||||||
$('.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 = 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
PROG = server
|
||||||
|
include ../examples.mk
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// 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); // Serves static content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
cs_stat_t st;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||||
|
if (nc == NULL) {
|
||||||
|
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up HTTP server parameters
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
s_http_server_opts.document_root = "web_root"; // Set up web root directory
|
||||||
|
|
||||||
|
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
|
||||||
|
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Starting web server on port %s\n", s_http_port);
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Connected device demo</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
|
||||||
|
<h1>Connected device demo - static content only</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This page shows initial steps on how to make a device connected.
|
||||||
|
Mongoose serves static content from the <code>web_root</code>
|
||||||
|
directory. Images, CSS, HTML, JavaScript files could be dropped into the
|
||||||
|
<code>web_root</code> directory - and they'll be served happily
|
||||||
|
by Mongoose.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Below is a placeholder for the
|
||||||
|
settings section, and a real-time dashboard, which will be
|
||||||
|
implemented in the following examples.
|
||||||
|
Both settings section, and a dashboard section, could be organised
|
||||||
|
differently on this UI - for example, in different tabs. But, to
|
||||||
|
make the demonstration clearer, we show everything on one page.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h1>Settings section</h1>
|
||||||
|
<p>Nothing here yet</p>
|
||||||
|
|
||||||
|
<h1>Dashboard section</h1>
|
||||||
|
<p>Nothing here yet</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,31 @@
|
|||||||
|
* {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #36c;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.content {
|
||||||
|
width: 800px;
|
||||||
|
margin: 1em auto;
|
||||||
|
padding: 2em;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: #f0f0f0;
|
||||||
|
padding: 0 0.3em;
|
||||||
|
color: #396;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link, a:visited {
|
||||||
|
color: #69c;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
PROG = server
|
||||||
|
include ../examples.mk
|
||||||
@@ -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, "%s", "HTTP/1.1 302 OK\r\nLocation: /\r\n\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
} 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(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
cs_stat_t st;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||||
|
if (nc == NULL) {
|
||||||
|
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up HTTP server parameters
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
s_http_server_opts.document_root = "web_root"; // Set up web root directory
|
||||||
|
|
||||||
|
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
|
||||||
|
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Starting web server on port %s\n", s_http_port);
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Connected device demo</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
|
||||||
|
<h1>Connected device demo - settings section</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This page is one of the series of examples that show how to make
|
||||||
|
devices connected with Mongoose. Here, we create a settings section
|
||||||
|
that expose some internal device variables, and allow user to change
|
||||||
|
them. They could be some stored in a database, in a file, or in
|
||||||
|
firmware RAM. In this example, we store them in RAM, and use two
|
||||||
|
variables, one integer and one string. We use SSI
|
||||||
|
<code><!--#call parameter_name --></code> directive to fetch
|
||||||
|
variable values from Mongoose.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h1>Settings section</h1>
|
||||||
|
<form method="POST" action="/save">
|
||||||
|
<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 -->" >
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button type="submit">Save Settings</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h1>Dashboard section</h1>
|
||||||
|
<p>Nothing here yet</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,67 @@
|
|||||||
|
* {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #36c;
|
||||||
|
color: #555;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.content {
|
||||||
|
width: 800px;
|
||||||
|
margin: 1em auto;
|
||||||
|
padding: 2em;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset legend {
|
||||||
|
font-style: italic;
|
||||||
|
color: silver;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset label {
|
||||||
|
text-align: right;
|
||||||
|
padding: 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text] {
|
||||||
|
font-size: 100%;
|
||||||
|
padding: 0.2em;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: #666;
|
||||||
|
background: #eee;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 0.2em 2em;
|
||||||
|
font-size: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: #f0f0f0;
|
||||||
|
padding: 0 0.3em;
|
||||||
|
color: #396;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link, a:visited {
|
||||||
|
color: #69c;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
PROG = server
|
||||||
|
include ../examples.mk
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
// 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, "%s", "HTTP/1.1 302 OK\r\nLocation: /\r\n\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_get_cpu_usage(struct mg_connection *nc) {
|
||||||
|
// Generate random value, as an example of changing CPU usage
|
||||||
|
// Getting real CPU usage depends on the OS.
|
||||||
|
int cpu_usage = (double) rand() / RAND_MAX * 100.0;
|
||||||
|
|
||||||
|
// Use chunked encoding in order to avoid calculating Content-Length
|
||||||
|
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
||||||
|
|
||||||
|
// Output JSON object which holds CPU usage data
|
||||||
|
mg_printf_http_chunk(nc, "{ \"result\": %d }", cpu_usage);
|
||||||
|
|
||||||
|
// Send empty chunk, the end of response
|
||||||
|
mg_send_http_chunk(nc, "", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
} else if (mg_vcmp(&hm->uri, "/get_cpu_usage") == 0) {
|
||||||
|
handle_get_cpu_usage(nc);
|
||||||
|
} 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(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
cs_stat_t st;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||||
|
if (nc == NULL) {
|
||||||
|
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up HTTP server parameters
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
s_http_server_opts.document_root = "web_root"; // Set up web root directory
|
||||||
|
|
||||||
|
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
|
||||||
|
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Starting web server on port %s\n", s_http_port);
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Connected device demo</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
|
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
|
||||||
|
<script type="text/javascript" src="main.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
|
||||||
|
<h1>Connected device demo - RESTful call</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This page is one of the series of examples that show how to make
|
||||||
|
devices connected with Mongoose. Here we add a RESTful call
|
||||||
|
to the Mongoose to show CPU usage stats on a dashboard.
|
||||||
|
RESTful call is done using Jquery's <code>ajax</code> call.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h1>Settings section</h1>
|
||||||
|
<form method="POST" action="/save">
|
||||||
|
<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 -->" >
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button type="submit">Save Settings</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h1>Dashboard section</h1>
|
||||||
|
<label>CPU usage:</label> <span id="cpu_usage"> </span>
|
||||||
|
|
||||||
|
</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
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,12 @@
|
|||||||
|
$(document).ready(function() {
|
||||||
|
// Start 1-second timer to call RESTful endpoint
|
||||||
|
setInterval(function() {
|
||||||
|
$.ajax({
|
||||||
|
url: '/get_cpu_usage',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(json) {
|
||||||
|
$('#cpu_usage').text(json.result + '% ');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
* {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #36c;
|
||||||
|
color: #555;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.content {
|
||||||
|
width: 800px;
|
||||||
|
margin: 1em auto;
|
||||||
|
padding: 2em;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset legend {
|
||||||
|
font-style: italic;
|
||||||
|
color: silver;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset label {
|
||||||
|
text-align: right;
|
||||||
|
padding: 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text] {
|
||||||
|
font-size: 100%;
|
||||||
|
padding: 0.2em;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: #666;
|
||||||
|
background: #eee;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 0.2em 2em;
|
||||||
|
font-size: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: #f0f0f0;
|
||||||
|
padding: 0 0.3em;
|
||||||
|
color: #396;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link, a:visited {
|
||||||
|
color: #69c;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
PROG = server
|
||||||
|
include ../examples.mk
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
// 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, "%s", "HTTP/1.1 302 OK\r\nLocation: /\r\n\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_get_cpu_usage(struct mg_connection *nc) {
|
||||||
|
// Generate random value, as an example of changing CPU usage
|
||||||
|
// Getting real CPU usage depends on the OS.
|
||||||
|
int cpu_usage = (double) rand() / RAND_MAX * 100.0;
|
||||||
|
|
||||||
|
// Use chunked encoding in order to avoid calculating Content-Length
|
||||||
|
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
||||||
|
|
||||||
|
// Output JSON object which holds CPU usage data
|
||||||
|
mg_printf_http_chunk(nc, "{ \"result\": %d }", cpu_usage);
|
||||||
|
|
||||||
|
// Send empty chunk, the end of response
|
||||||
|
mg_send_http_chunk(nc, "", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
} else if (mg_vcmp(&hm->uri, "/get_cpu_usage") == 0) {
|
||||||
|
handle_get_cpu_usage(nc);
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void push_data_to_all_websocket_connections(struct mg_mgr *m) {
|
||||||
|
struct mg_connection *c;
|
||||||
|
int memory_usage = (double) rand() / RAND_MAX * 100.0;
|
||||||
|
|
||||||
|
for (c = mg_next(m, NULL); c != NULL; c = mg_next(m, c)) {
|
||||||
|
if (c->flags & MG_F_IS_WEBSOCKET) {
|
||||||
|
mg_printf_websocket_frame(c, WEBSOCKET_OP_TEXT, "%d", memory_usage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_connection *nc;
|
||||||
|
cs_stat_t st;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
nc = mg_bind(&mgr, s_http_port, ev_handler);
|
||||||
|
if (nc == NULL) {
|
||||||
|
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up HTTP server parameters
|
||||||
|
mg_set_protocol_http_websocket(nc);
|
||||||
|
s_http_server_opts.document_root = "web_root"; // Set up web root directory
|
||||||
|
|
||||||
|
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
|
||||||
|
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Starting web server on port %s\n", s_http_port);
|
||||||
|
for (;;) {
|
||||||
|
static time_t last_time;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
if (now - last_time > 0) {
|
||||||
|
push_data_to_all_websocket_connections(&mgr);
|
||||||
|
last_time = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Connected device demo</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
|
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
|
||||||
|
<script src="jquery.flot.min.js"></script>
|
||||||
|
<script src="jquery.flot.time.js"></script>
|
||||||
|
<script type="text/javascript" src="main.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
|
||||||
|
<h1>Connected device demo - Websocket push</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This page is one of the series of examples that show how to make
|
||||||
|
devices connected with Mongoose. Here we add a real-time Websocket
|
||||||
|
server push, and update dashboard graph as data arrives.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h1>Settings section</h1>
|
||||||
|
<form method="POST" action="/save">
|
||||||
|
<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 -->" >
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button type="submit">Save Settings</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h1>Dashboard section</h1>
|
||||||
|
<label>CPU usage:</label> <span id="cpu_usage"> </span>
|
||||||
|
<div id="graphs"></div>
|
||||||
|
|
||||||
|
</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,8 @@
|
|||||||
|
/* Javascript plotting library for jQuery, version 0.8.3.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2014 IOLA and Ole Laursen.
|
||||||
|
Licensed under the MIT license.
|
||||||
|
|
||||||
|
*/
|
||||||
|
(function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i<c.length;++i)o[c.charAt(i)]+=d;return o.normalize()};o.scale=function(c,f){for(var i=0;i<c.length;++i)o[c.charAt(i)]*=f;return o.normalize()};o.toString=function(){if(o.a>=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return value<min?min:value>max?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);(function($){var hasOwnProperty=Object.prototype.hasOwnProperty;if(!$.fn.detach){$.fn.detach=function(){return this.each(function(){if(this.parentNode){this.parentNode.removeChild(this)}})}}function Canvas(cls,container){var element=container.children("."+cls)[0];if(element==null){element=document.createElement("canvas");element.className=cls;$(element).css({direction:"ltr",position:"absolute",left:0,top:0}).appendTo(container);if(!element.getContext){if(window.G_vmlCanvasManager){element=window.G_vmlCanvasManager.initElement(element)}else{throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.")}}}this.element=element;var context=this.context=element.getContext("2d");var devicePixelRatio=window.devicePixelRatio||1,backingStoreRatio=context.webkitBackingStorePixelRatio||context.mozBackingStorePixelRatio||context.msBackingStorePixelRatio||context.oBackingStorePixelRatio||context.backingStorePixelRatio||1;this.pixelRatio=devicePixelRatio/backingStoreRatio;this.resize(container.width(),container.height());this.textContainer=null;this.text={};this._textCache={}}Canvas.prototype.resize=function(width,height){if(width<=0||height<=0){throw new Error("Invalid dimensions for plot, width = "+width+", height = "+height)}var element=this.element,context=this.context,pixelRatio=this.pixelRatio;if(this.width!=width){element.width=width*pixelRatio;element.style.width=width+"px";this.width=width}if(this.height!=height){element.height=height*pixelRatio;element.style.height=height+"px";this.height=height}context.restore();context.save();context.scale(pixelRatio,pixelRatio)};Canvas.prototype.clear=function(){this.context.clearRect(0,0,this.width,this.height)};Canvas.prototype.render=function(){vaLine truncated
|
||||||
|
if(yrange.from==null)yrange.from=yrange.axis.min;if(yrange.to==null)yrange.to=yrange.axis.max;if(xrange.to<xrange.axis.min||xrange.from>xrange.axis.max||yrange.to<yrange.axis.min||yrange.from>yrange.axis.max)continue;xrange.from=Math.max(xrange.from,xrange.axis.min);xrange.to=Math.min(xrange.to,xrange.axis.max);yrange.from=Math.max(yrange.from,yrange.axis.min);yrange.to=Math.min(yrange.to,yrange.axis.max);var xequal=xrange.from===xrange.to,yequal=yrange.from===yrange.to;if(xequal&&yequal){continue}xrange.from=Math.floor(xrange.axis.p2c(xrange.from));xrange.to=Math.floor(xrange.axis.p2c(xrange.to));yrange.from=Math.floor(yrange.axis.p2c(yrange.from));yrange.to=Math.floor(yrange.axis.p2c(yrange.to));if(xequal||yequal){var lineWidth=m.lineWidth||options.grid.markingsLineWidth,subPixel=lineWidth%2?.5:0;ctx.beginPath();ctx.strokeStyle=m.color||options.grid.markingsColor;ctx.lineWidth=lineWidth;if(xequal){ctx.moveTo(xrange.to+subPixel,yrange.from);ctx.lineTo(xrange.to+subPixel,yrange.to)}else{ctx.moveTo(xrange.from,yrange.to+subPixel);ctx.lineTo(xrange.to,yrange.to+subPixel)}ctx.stroke()}else{ctx.fillStyle=m.color||options.grid.markingsColor;ctx.fillRect(xrange.from,yrange.to,xrange.to-xrange.from,yrange.from-yrange.to)}}}axes=allAxes();bw=options.grid.borderWidth;for(var j=0;j<axes.length;++j){var axis=axes[j],box=axis.box,t=axis.tickLength,x,y,xoff,yoff;if(!axis.show||axis.ticks.length==0)continue;ctx.lineWidth=1;if(axis.direction=="x"){x=0;if(t=="full")y=axis.position=="top"?0:plotHeight;else y=box.top-plotOffset.top+(axis.position=="top"?box.height:0)}else{y=0;if(t=="full")x=axis.position=="left"?0:plotWidth;else x=box.left-plotOffset.left+(axis.position=="left"?box.width:0)}if(!axis.innermost){ctx.strokeStyle=axis.options.color;ctx.beginPath();xoff=yoff=0;if(axis.direction=="x")xoff=plotWidth+1;else yoff=plotHeight+1;if(ctx.lineWidth==1){if(axis.direction=="x"){y=Math.floor(y)+.5}else{x=Math.floor(x)+.5}}ctx.moveTo(x,y);ctx.lineTo(x+xoff,y+yoff);ctx.stroke()}ctx.strokeStyle=axis.options.tickColor;ctx.beginPath();for(i=0;i<axis.ticks.length;++i){var v=axis.ticks[i].v;xoff=yoff=0;if(isNaN(v)||v<axis.min||v>axis.max||t=="full"&&(typeof bw=="object"&&bw[axis.position]>0||bw>0)&&(v==axis.min||v==axis.max))continue;if(axis.direction=="x"){x=axis.p2c(v);yoff=t=="full"?-plotHeight:t;if(axis.position=="top")yoff=-yoff}else{y=axis.p2c(v);xoff=t=="full"?-plotWidth:t;if(axis.position=="left")xoff=-xoff}if(ctx.lineWidth==1){if(axis.direction=="x")x=Math.floor(x)+.5;else y=Math.floor(y)+.5}ctx.moveTo(x,y);ctx.lineTo(x+xoff,y+yoff)}ctx.stroke()}if(bw){bc=options.grid.borderColor;if(typeof bw=="object"||typeof bc=="object"){if(typeof bw!=="object"){bw={top:bw,right:bw,bottom:bw,left:bw}}if(typeof bc!=="object"){bc={top:bc,right:bc,bottom:bc,left:bc}}if(bw.top>0){ctx.strokeStyle=bc.top;ctx.lineWidth=bw.top;ctx.beginPath();ctx.moveTo(0-bw.left,0-bw.top/2);ctx.lineTo(plotWidth,0-bw.top/2);ctx.stroke()}if(bw.right>0){ctx.strokeStyle=bc.right;ctx.lineWidth=bw.right;ctx.beginPath();ctx.moveTo(plotWidth+bw.right/2,0-bw.top);ctx.lineTo(plotWidth+bw.right/2,plotHeight);ctx.stroke()}if(bw.bottom>0){ctx.strokeStyle=bc.bottom;ctx.lineWidth=bw.bottom;ctx.beginPath();ctx.moveTo(plotWidth+bw.right,plotHeight+bw.bottom/2);ctx.lineTo(0,plotHeight+bw.bottom/2);ctx.stroke()}if(bw.left>0){ctx.strokeStyle=bc.left;ctx.lineWidth=bw.left;ctx.beginPath();ctx.moveTo(0-bw.left/2,plotHeight+bw.bottom);ctx.lineTo(0-bw.left/2,0);ctx.stroke()}}else{ctx.lineWidth=bw;ctx.strokeStyle=options.grid.borderColor;ctx.strokeRect(-bw/2,-bw/2,plotWidth+bw,plotHeight+bw)}}ctx.restore()}function drawAxisLabels(){$.each(allAxes(),function(_,axis){var box=axis.box,legacyStyles=axis.direction+"Axis "+axis.direction+axis.n+"Axis",layer="flot-"+axis.direction+"-axis flot-"+axis.direction+axis.n+"-axis "+legacyStyles,font=axis.options.font||"flot-tick-label tickLabel",tick,x,y,halign,valign;surface.removeText(layer);if(!axis.show||axis.ticks.length==0)return;for(var i=0;i<axis.ticks.length;++i){tick=axis.ticks[i];if(!tick.label||tick.v<axis.min||tick.v>axis.max)continue;if(axis.direction=="x"){halign="center";x=plotOffset.left+axis.p2c(tick.v);if(axis.position=="bottom"){y=box.top+box.padding}else{y=box.top+box.height-box.padding;valign="bottom"}}else{valign="middle";y=plotOffset.top+axis.p2c(tick.v);if(axis.position=="left"){x=box.left+box.width-box.padding;halign="right"}else{x=box.left+box.padding}}surface.addText(layer,x,y,tick.label,font,null,null,halign,valign)}})}function drawSeries(series){if(series.lines.show)drawSeriesLines(series);if(series.bars.show)drawSeriesBars(series);if(series.points.show)drawSeriesPoints(series)}function drawSeriesLines(series){function plotLine(datapoints,xoffset,yoffset,axisx,axisy){var points=datapoints.points,ps=datapoints.pointsize,prevx=null,prevy=null;ctx.beginPath();for(var i=ps;i<points.length;i+=ps){var x1=points[i-ps],y1=points[i-ps+1],x2=points[i],y2=points[i+1];if(x1==null||x2==null)continue;if(y1<=y2&&y1<axisy.min){if(y2<axLine truncated
|
||||||
@@ -0,0 +1,432 @@
|
|||||||
|
/* Pretty handling of time axes.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2014 IOLA and Ole Laursen.
|
||||||
|
Licensed under the MIT license.
|
||||||
|
|
||||||
|
Set axis.mode to "time" to enable. See the section "Time series data" in
|
||||||
|
API.txt for details.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function($) {
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
xaxis: {
|
||||||
|
timezone: null, // "browser" for local to the client or timezone for timezone-js
|
||||||
|
timeformat: null, // format string to use
|
||||||
|
twelveHourClock: false, // 12 or 24 time in time mode
|
||||||
|
monthNames: null // list of names of months
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// round to nearby lower multiple of base
|
||||||
|
|
||||||
|
function floorInBase(n, base) {
|
||||||
|
return base * Math.floor(n / base);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a string with the date d formatted according to fmt.
|
||||||
|
// A subset of the Open Group's strftime format is supported.
|
||||||
|
|
||||||
|
function formatDate(d, fmt, monthNames, dayNames) {
|
||||||
|
|
||||||
|
if (typeof d.strftime == "function") {
|
||||||
|
return d.strftime(fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
var leftPad = function(n, pad) {
|
||||||
|
n = "" + n;
|
||||||
|
pad = "" + (pad == null ? "0" : pad);
|
||||||
|
return n.length == 1 ? pad + n : n;
|
||||||
|
};
|
||||||
|
|
||||||
|
var r = [];
|
||||||
|
var escape = false;
|
||||||
|
var hours = d.getHours();
|
||||||
|
var isAM = hours < 12;
|
||||||
|
|
||||||
|
if (monthNames == null) {
|
||||||
|
monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dayNames == null) {
|
||||||
|
dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||||
|
}
|
||||||
|
|
||||||
|
var hours12;
|
||||||
|
|
||||||
|
if (hours > 12) {
|
||||||
|
hours12 = hours - 12;
|
||||||
|
} else if (hours == 0) {
|
||||||
|
hours12 = 12;
|
||||||
|
} else {
|
||||||
|
hours12 = hours;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < fmt.length; ++i) {
|
||||||
|
|
||||||
|
var c = fmt.charAt(i);
|
||||||
|
|
||||||
|
if (escape) {
|
||||||
|
switch (c) {
|
||||||
|
case 'a': c = "" + dayNames[d.getDay()]; break;
|
||||||
|
case 'b': c = "" + monthNames[d.getMonth()]; break;
|
||||||
|
case 'd': c = leftPad(d.getDate()); break;
|
||||||
|
case 'e': c = leftPad(d.getDate(), " "); break;
|
||||||
|
case 'h': // For back-compat with 0.7; remove in 1.0
|
||||||
|
case 'H': c = leftPad(hours); break;
|
||||||
|
case 'I': c = leftPad(hours12); break;
|
||||||
|
case 'l': c = leftPad(hours12, " "); break;
|
||||||
|
case 'm': c = leftPad(d.getMonth() + 1); break;
|
||||||
|
case 'M': c = leftPad(d.getMinutes()); break;
|
||||||
|
// quarters not in Open Group's strftime specification
|
||||||
|
case 'q':
|
||||||
|
c = "" + (Math.floor(d.getMonth() / 3) + 1); break;
|
||||||
|
case 'S': c = leftPad(d.getSeconds()); break;
|
||||||
|
case 'y': c = leftPad(d.getFullYear() % 100); break;
|
||||||
|
case 'Y': c = "" + d.getFullYear(); break;
|
||||||
|
case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
|
||||||
|
case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
|
||||||
|
case 'w': c = "" + d.getDay(); break;
|
||||||
|
}
|
||||||
|
r.push(c);
|
||||||
|
escape = false;
|
||||||
|
} else {
|
||||||
|
if (c == "%") {
|
||||||
|
escape = true;
|
||||||
|
} else {
|
||||||
|
r.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// To have a consistent view of time-based data independent of which time
|
||||||
|
// zone the client happens to be in we need a date-like object independent
|
||||||
|
// of time zones. This is done through a wrapper that only calls the UTC
|
||||||
|
// versions of the accessor methods.
|
||||||
|
|
||||||
|
function makeUtcWrapper(d) {
|
||||||
|
|
||||||
|
function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) {
|
||||||
|
sourceObj[sourceMethod] = function() {
|
||||||
|
return targetObj[targetMethod].apply(targetObj, arguments);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var utc = {
|
||||||
|
date: d
|
||||||
|
};
|
||||||
|
|
||||||
|
// support strftime, if found
|
||||||
|
|
||||||
|
if (d.strftime != undefined) {
|
||||||
|
addProxyMethod(utc, "strftime", d, "strftime");
|
||||||
|
}
|
||||||
|
|
||||||
|
addProxyMethod(utc, "getTime", d, "getTime");
|
||||||
|
addProxyMethod(utc, "setTime", d, "setTime");
|
||||||
|
|
||||||
|
var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"];
|
||||||
|
|
||||||
|
for (var p = 0; p < props.length; p++) {
|
||||||
|
addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]);
|
||||||
|
addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return utc;
|
||||||
|
};
|
||||||
|
|
||||||
|
// select time zone strategy. This returns a date-like object tied to the
|
||||||
|
// desired timezone
|
||||||
|
|
||||||
|
function dateGenerator(ts, opts) {
|
||||||
|
if (opts.timezone == "browser") {
|
||||||
|
return new Date(ts);
|
||||||
|
} else if (!opts.timezone || opts.timezone == "utc") {
|
||||||
|
return makeUtcWrapper(new Date(ts));
|
||||||
|
} else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") {
|
||||||
|
var d = new timezoneJS.Date();
|
||||||
|
// timezone-js is fickle, so be sure to set the time zone before
|
||||||
|
// setting the time.
|
||||||
|
d.setTimezone(opts.timezone);
|
||||||
|
d.setTime(ts);
|
||||||
|
return d;
|
||||||
|
} else {
|
||||||
|
return makeUtcWrapper(new Date(ts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// map of app. size of time units in milliseconds
|
||||||
|
|
||||||
|
var timeUnitSize = {
|
||||||
|
"second": 1000,
|
||||||
|
"minute": 60 * 1000,
|
||||||
|
"hour": 60 * 60 * 1000,
|
||||||
|
"day": 24 * 60 * 60 * 1000,
|
||||||
|
"month": 30 * 24 * 60 * 60 * 1000,
|
||||||
|
"quarter": 3 * 30 * 24 * 60 * 60 * 1000,
|
||||||
|
"year": 365.2425 * 24 * 60 * 60 * 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
// the allowed tick sizes, after 1 year we use
|
||||||
|
// an integer algorithm
|
||||||
|
|
||||||
|
var baseSpec = [
|
||||||
|
[1, "second"], [2, "second"], [5, "second"], [10, "second"],
|
||||||
|
[30, "second"],
|
||||||
|
[1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
|
||||||
|
[30, "minute"],
|
||||||
|
[1, "hour"], [2, "hour"], [4, "hour"],
|
||||||
|
[8, "hour"], [12, "hour"],
|
||||||
|
[1, "day"], [2, "day"], [3, "day"],
|
||||||
|
[0.25, "month"], [0.5, "month"], [1, "month"],
|
||||||
|
[2, "month"]
|
||||||
|
];
|
||||||
|
|
||||||
|
// we don't know which variant(s) we'll need yet, but generating both is
|
||||||
|
// cheap
|
||||||
|
|
||||||
|
var specMonths = baseSpec.concat([[3, "month"], [6, "month"],
|
||||||
|
[1, "year"]]);
|
||||||
|
var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"],
|
||||||
|
[1, "year"]]);
|
||||||
|
|
||||||
|
function init(plot) {
|
||||||
|
plot.hooks.processOptions.push(function (plot, options) {
|
||||||
|
$.each(plot.getAxes(), function(axisName, axis) {
|
||||||
|
|
||||||
|
var opts = axis.options;
|
||||||
|
|
||||||
|
if (opts.mode == "time") {
|
||||||
|
axis.tickGenerator = function(axis) {
|
||||||
|
|
||||||
|
var ticks = [];
|
||||||
|
var d = dateGenerator(axis.min, opts);
|
||||||
|
var minSize = 0;
|
||||||
|
|
||||||
|
// make quarter use a possibility if quarters are
|
||||||
|
// mentioned in either of these options
|
||||||
|
|
||||||
|
var spec = (opts.tickSize && opts.tickSize[1] ===
|
||||||
|
"quarter") ||
|
||||||
|
(opts.minTickSize && opts.minTickSize[1] ===
|
||||||
|
"quarter") ? specQuarters : specMonths;
|
||||||
|
|
||||||
|
if (opts.minTickSize != null) {
|
||||||
|
if (typeof opts.tickSize == "number") {
|
||||||
|
minSize = opts.tickSize;
|
||||||
|
} else {
|
||||||
|
minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < spec.length - 1; ++i) {
|
||||||
|
if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]]
|
||||||
|
+ spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
|
||||||
|
&& spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var size = spec[i][0];
|
||||||
|
var unit = spec[i][1];
|
||||||
|
|
||||||
|
// special-case the possibility of several years
|
||||||
|
|
||||||
|
if (unit == "year") {
|
||||||
|
|
||||||
|
// if given a minTickSize in years, just use it,
|
||||||
|
// ensuring that it's an integer
|
||||||
|
|
||||||
|
if (opts.minTickSize != null && opts.minTickSize[1] == "year") {
|
||||||
|
size = Math.floor(opts.minTickSize[0]);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10));
|
||||||
|
var norm = (axis.delta / timeUnitSize.year) / magn;
|
||||||
|
|
||||||
|
if (norm < 1.5) {
|
||||||
|
size = 1;
|
||||||
|
} else if (norm < 3) {
|
||||||
|
size = 2;
|
||||||
|
} else if (norm < 7.5) {
|
||||||
|
size = 5;
|
||||||
|
} else {
|
||||||
|
size = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
size *= magn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// minimum size for years is 1
|
||||||
|
|
||||||
|
if (size < 1) {
|
||||||
|
size = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
axis.tickSize = opts.tickSize || [size, unit];
|
||||||
|
var tickSize = axis.tickSize[0];
|
||||||
|
unit = axis.tickSize[1];
|
||||||
|
|
||||||
|
var step = tickSize * timeUnitSize[unit];
|
||||||
|
|
||||||
|
if (unit == "second") {
|
||||||
|
d.setSeconds(floorInBase(d.getSeconds(), tickSize));
|
||||||
|
} else if (unit == "minute") {
|
||||||
|
d.setMinutes(floorInBase(d.getMinutes(), tickSize));
|
||||||
|
} else if (unit == "hour") {
|
||||||
|
d.setHours(floorInBase(d.getHours(), tickSize));
|
||||||
|
} else if (unit == "month") {
|
||||||
|
d.setMonth(floorInBase(d.getMonth(), tickSize));
|
||||||
|
} else if (unit == "quarter") {
|
||||||
|
d.setMonth(3 * floorInBase(d.getMonth() / 3,
|
||||||
|
tickSize));
|
||||||
|
} else if (unit == "year") {
|
||||||
|
d.setFullYear(floorInBase(d.getFullYear(), tickSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset smaller components
|
||||||
|
|
||||||
|
d.setMilliseconds(0);
|
||||||
|
|
||||||
|
if (step >= timeUnitSize.minute) {
|
||||||
|
d.setSeconds(0);
|
||||||
|
}
|
||||||
|
if (step >= timeUnitSize.hour) {
|
||||||
|
d.setMinutes(0);
|
||||||
|
}
|
||||||
|
if (step >= timeUnitSize.day) {
|
||||||
|
d.setHours(0);
|
||||||
|
}
|
||||||
|
if (step >= timeUnitSize.day * 4) {
|
||||||
|
d.setDate(1);
|
||||||
|
}
|
||||||
|
if (step >= timeUnitSize.month * 2) {
|
||||||
|
d.setMonth(floorInBase(d.getMonth(), 3));
|
||||||
|
}
|
||||||
|
if (step >= timeUnitSize.quarter * 2) {
|
||||||
|
d.setMonth(floorInBase(d.getMonth(), 6));
|
||||||
|
}
|
||||||
|
if (step >= timeUnitSize.year) {
|
||||||
|
d.setMonth(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var carry = 0;
|
||||||
|
var v = Number.NaN;
|
||||||
|
var prev;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
prev = v;
|
||||||
|
v = d.getTime();
|
||||||
|
ticks.push(v);
|
||||||
|
|
||||||
|
if (unit == "month" || unit == "quarter") {
|
||||||
|
if (tickSize < 1) {
|
||||||
|
|
||||||
|
// a bit complicated - we'll divide the
|
||||||
|
// month/quarter up but we need to take
|
||||||
|
// care of fractions so we don't end up in
|
||||||
|
// the middle of a day
|
||||||
|
|
||||||
|
d.setDate(1);
|
||||||
|
var start = d.getTime();
|
||||||
|
d.setMonth(d.getMonth() +
|
||||||
|
(unit == "quarter" ? 3 : 1));
|
||||||
|
var end = d.getTime();
|
||||||
|
d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
|
||||||
|
carry = d.getHours();
|
||||||
|
d.setHours(0);
|
||||||
|
} else {
|
||||||
|
d.setMonth(d.getMonth() +
|
||||||
|
tickSize * (unit == "quarter" ? 3 : 1));
|
||||||
|
}
|
||||||
|
} else if (unit == "year") {
|
||||||
|
d.setFullYear(d.getFullYear() + tickSize);
|
||||||
|
} else {
|
||||||
|
d.setTime(v + step);
|
||||||
|
}
|
||||||
|
} while (v < axis.max && v != prev);
|
||||||
|
|
||||||
|
return ticks;
|
||||||
|
};
|
||||||
|
|
||||||
|
axis.tickFormatter = function (v, axis) {
|
||||||
|
|
||||||
|
var d = dateGenerator(v, axis.options);
|
||||||
|
|
||||||
|
// first check global format
|
||||||
|
|
||||||
|
if (opts.timeformat != null) {
|
||||||
|
return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
// possibly use quarters if quarters are mentioned in
|
||||||
|
// any of these places
|
||||||
|
|
||||||
|
var useQuarters = (axis.options.tickSize &&
|
||||||
|
axis.options.tickSize[1] == "quarter") ||
|
||||||
|
(axis.options.minTickSize &&
|
||||||
|
axis.options.minTickSize[1] == "quarter");
|
||||||
|
|
||||||
|
var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
|
||||||
|
var span = axis.max - axis.min;
|
||||||
|
var suffix = (opts.twelveHourClock) ? " %p" : "";
|
||||||
|
var hourCode = (opts.twelveHourClock) ? "%I" : "%H";
|
||||||
|
var fmt;
|
||||||
|
|
||||||
|
if (t < timeUnitSize.minute) {
|
||||||
|
fmt = hourCode + ":%M:%S" + suffix;
|
||||||
|
} else if (t < timeUnitSize.day) {
|
||||||
|
if (span < 2 * timeUnitSize.day) {
|
||||||
|
fmt = hourCode + ":%M" + suffix;
|
||||||
|
} else {
|
||||||
|
fmt = "%b %d " + hourCode + ":%M" + suffix;
|
||||||
|
}
|
||||||
|
} else if (t < timeUnitSize.month) {
|
||||||
|
fmt = "%b %d";
|
||||||
|
} else if ((useQuarters && t < timeUnitSize.quarter) ||
|
||||||
|
(!useQuarters && t < timeUnitSize.year)) {
|
||||||
|
if (span < timeUnitSize.year) {
|
||||||
|
fmt = "%b";
|
||||||
|
} else {
|
||||||
|
fmt = "%b %Y";
|
||||||
|
}
|
||||||
|
} else if (useQuarters && t < timeUnitSize.year) {
|
||||||
|
if (span < timeUnitSize.year) {
|
||||||
|
fmt = "Q%q";
|
||||||
|
} else {
|
||||||
|
fmt = "Q%q %Y";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt = "%Y";
|
||||||
|
}
|
||||||
|
|
||||||
|
var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames);
|
||||||
|
|
||||||
|
return rt;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$.plot.plugins.push({
|
||||||
|
init: init,
|
||||||
|
options: options,
|
||||||
|
name: 'time',
|
||||||
|
version: '1.0'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Time-axis support used to be in Flot core, which exposed the
|
||||||
|
// formatDate function on the plot object. Various plugins depend
|
||||||
|
// on the function, so we need to re-expose it here.
|
||||||
|
|
||||||
|
$.plot.formatDate = formatDate;
|
||||||
|
$.plot.dateGenerator = dateGenerator;
|
||||||
|
|
||||||
|
})(jQuery);
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,75 @@
|
|||||||
|
$(document).ready(function() {
|
||||||
|
// Start 1-second timer to call RESTful endpoint
|
||||||
|
setInterval(function() {
|
||||||
|
$.ajax({
|
||||||
|
url: '/get_cpu_usage',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(json) {
|
||||||
|
$('#cpu_usage').text(json.result + '% ');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
// Initialize graph
|
||||||
|
var $c = $('<div class="graph"/>').appendTo('#graphs');
|
||||||
|
var dur = 120;
|
||||||
|
var past = Date.now() - dur * 1000;
|
||||||
|
var options = {
|
||||||
|
lines: {
|
||||||
|
fill: true,
|
||||||
|
lineWidth: 0,
|
||||||
|
fillColor: {colors: [ { opacity: 0 }, { opacity: 1 } ] }
|
||||||
|
},
|
||||||
|
grid: { borderWidth: 1, borderColor: '#ccc'},
|
||||||
|
xaxis: { mode: 'time', ticks: 5 },
|
||||||
|
legend: { labelBoxBorderColor: '#fff' },
|
||||||
|
colors: [ '#fec', '#396', '#e39', '9e2' ],
|
||||||
|
hooks: {
|
||||||
|
draw: [function(plot, canvas) {
|
||||||
|
canvas.font = '13px sans-serif';
|
||||||
|
canvas.fillStyle = '#aaa';
|
||||||
|
canvas.fillText('Resource Usage', 35, 25);
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var plot = $.plot($c, [], options);
|
||||||
|
|
||||||
|
var updateGraph = function(counter) {
|
||||||
|
var data = plot.getData();
|
||||||
|
var now = Date.now();
|
||||||
|
var oldest = now - dur * 1000;
|
||||||
|
data[0] = {
|
||||||
|
show: false,
|
||||||
|
data: [[oldest, null], [now, null]]
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
// Remove old points
|
||||||
|
$.each(data, function(di, d) {
|
||||||
|
while (d.data.length > 0 && d.data[0][0] < oldest) {
|
||||||
|
d.data.shift();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add new points
|
||||||
|
var new_datapoint = [now, counter];
|
||||||
|
if (data[1]) {
|
||||||
|
data[1].label = 'memory';
|
||||||
|
data[1].data.push(new_datapoint);
|
||||||
|
} else {
|
||||||
|
data[1] = [{ data: [new_datapoint] }];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redraw the graph
|
||||||
|
plot.setData(data);
|
||||||
|
plot.setupGrid();
|
||||||
|
plot.draw();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create Websocket connection. For simplicity, no reconnect logic is here.
|
||||||
|
var ws = new WebSocket('ws://' + location.host);
|
||||||
|
ws.onmessage = function(ev) {
|
||||||
|
updateGraph(ev.data);
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
* {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #36c;
|
||||||
|
color: #555;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font: 16px/1.4 Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.content {
|
||||||
|
width: 800px;
|
||||||
|
margin: 1em auto;
|
||||||
|
padding: 2em;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset legend {
|
||||||
|
font-style: italic;
|
||||||
|
color: silver;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset label {
|
||||||
|
text-align: right;
|
||||||
|
padding: 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text] {
|
||||||
|
font-size: 100%;
|
||||||
|
padding: 0.2em;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: #666;
|
||||||
|
background: #eee;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 0.2em 2em;
|
||||||
|
font-size: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: #f0f0f0;
|
||||||
|
padding: 0 0.3em;
|
||||||
|
color: #396;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link, a:visited {
|
||||||
|
color: #69c;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#graphs {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graph {
|
||||||
|
min-height: 12em;
|
||||||
|
border: 0px solid #ccc;
|
||||||
|
position: relative;
|
||||||
|
margin: 0 1em 1em 0;
|
||||||
|
width: 36em;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graph_title {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.5em; left: 1em;
|
||||||
|
color: #ccc;
|
||||||
|
font-weight: bold;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
SOURCES = $(PROG).c ../../mongoose.c
|
||||||
|
CFLAGS = -W -Wall -I../.. -Wno-unused-function $(CFLAGS_EXTRA) $(MODULE_CFLAGS)
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
|
|
||||||
static int index_html(struct mg_connection *conn) {
|
|
||||||
FILE *fp = (FILE *) conn->connection_param;
|
|
||||||
char buf[200];
|
|
||||||
size_t n = 0;
|
|
||||||
|
|
||||||
if (fp == NULL) {
|
|
||||||
fp = fopen("../mongoose.c", "r");
|
|
||||||
conn->connection_param = (void *) fp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fp != NULL) {
|
|
||||||
n = fread(buf, 1, sizeof(buf), fp);
|
|
||||||
mg_send_data(conn, buf, n);
|
|
||||||
|
|
||||||
if (n < sizeof(buf) || conn->wsbits != 0) {
|
|
||||||
fclose(fp);
|
|
||||||
conn->connection_param = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n < sizeof(buf) ? MG_REQUEST_PROCESSED : MG_REQUEST_CALL_AGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_server *server;
|
|
||||||
|
|
||||||
// Create and configure the server
|
|
||||||
server = mg_create_server(NULL);
|
|
||||||
mg_set_option(server, "listening_port", "8080");
|
|
||||||
mg_set_request_handler(server, index_html);
|
|
||||||
|
|
||||||
// Serve request. Hit Ctrl-C to terminate the program
|
|
||||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
|
||||||
for (;;) {
|
|
||||||
mg_poll_server(server, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup, and free server instance
|
|
||||||
mg_destroy_server(&server);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
|
||||||
int result = MG_FALSE;
|
|
||||||
|
|
||||||
if (ev == MG_REQUEST) {
|
|
||||||
mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
|
|
||||||
result = MG_TRUE;
|
|
||||||
} else if (ev == MG_AUTH) {
|
|
||||||
result = MG_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_server *server;
|
|
||||||
|
|
||||||
// Create and configure the server
|
|
||||||
server = mg_create_server(NULL, ev_handler);
|
|
||||||
mg_set_option(server, "listening_port", "8080");
|
|
||||||
|
|
||||||
// Serve request. Hit Ctrl-C to terminate the program
|
|
||||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
|
||||||
for (;;) {
|
|
||||||
mg_poll_server(server, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup, and free server instance
|
|
||||||
mg_destroy_server(&server);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,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,163 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\..\mongoose.c">
|
||||||
|
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">TurnOffAllWarnings</WarningLevel>
|
||||||
|
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">TurnOffAllWarnings</WarningLevel>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="http_client.c" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\..\mongoose.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{BDF3FC6F-2658-4A27-846D-6A45FB53C602}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>http_client</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;MG_ENABLE_THREADS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>./../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;MG_ENABLE_THREADS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>./../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
LOCAL_PATH := $(call my-dir)/../..
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
|
|
||||||
# To build with lua support, uncomment two lines below:
|
|
||||||
#LUA_SOURCES := build/lua_5.2.1.c build/sqlite3.c build/lsqlite3.c
|
|
||||||
#LUA_FLAGS := -I$(LOCAL_PATH)/build -DTHREADSAFE=1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DUSE_LUA -DUSE_LUA_SQLITE3 -DLUA_COMPAT_ALL -D"getlocaledecpoint() ='.'"
|
|
||||||
|
|
||||||
LOCAL_CFLAGS := -std=c99 -O2 -W -Wall -pthread -pipe $(LUA_FLAGS) $(COPT)
|
|
||||||
LOCAL_MODULE := mongoose
|
|
||||||
LOCAL_SRC_FILES := examples/server.c mongoose.c $(LUA_SOURCES)
|
|
||||||
|
|
||||||
include $(BUILD_EXECUTABLE)
|
|
||||||
@@ -0,0 +1,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 $?
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
HTTP/1.0 200 OK
|
|
||||||
Content-Type: text/plain
|
|
||||||
|
|
||||||
<?
|
|
||||||
-- Directory scanning example
|
|
||||||
|
|
||||||
function scandir(dir)
|
|
||||||
local t = {}
|
|
||||||
for f in io.popen('dir ' .. dir):lines() do table.insert(t, f) end
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
local tab = scandir('C:\\')
|
|
||||||
for k,v in pairs(tab) do mg.write(v, '\n') end
|
|
||||||
?>
|
|
||||||
Loaded 100 of 159 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user