Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a265e79a6 | ||
|
|
7f30c01c58 | ||
|
|
0661f5921a | ||
|
|
1c16e95b3a | ||
|
|
44efa45d75 | ||
|
|
ef89776517 | ||
|
|
2f3f1357c8 | ||
|
|
dcdeffff9a | ||
|
|
229b492ccc | ||
|
|
2d45ba1981 | ||
|
|
b73022096d | ||
|
|
d1124703b7 | ||
|
|
4a8dd81c93 | ||
|
|
f474a31484 | ||
|
|
e9ec2c5304 | ||
|
|
c7b9ca68c2 | ||
|
|
ffa033cca9 | ||
|
|
3d3f6e09b6 | ||
|
|
7252f5385b | ||
|
|
ad641880a6 | ||
|
|
1f6d62a635 | ||
|
|
3cd4fd2d61 | ||
|
|
d565faa75d | ||
|
|
786e118f06 | ||
|
|
eca612d514 | ||
|
|
060f43839d | ||
|
|
9c3c4a6f4b | ||
|
|
101cc1a672 | ||
|
|
e47f71344d | ||
|
|
fee6de6a7f | ||
|
|
2bf25a1406 | ||
|
|
dd59296be9 | ||
|
|
b382726ed0 | ||
|
|
01390fe540 | ||
|
|
8596e1bef7 | ||
|
|
13fffbc5b6 | ||
|
|
5f4857ddae | ||
|
|
5f36b41785 | ||
|
|
bd8f0c90e1 | ||
|
|
f49683353d | ||
|
|
4f012d6bc6 | ||
|
|
e48e353066 | ||
|
|
6ad7af54dc | ||
|
|
6b69bef2ef | ||
|
|
d2a27d3d91 | ||
|
|
189aa80743 | ||
|
|
f122bd5797 | ||
|
|
d76f86f7db | ||
|
|
ccf5364187 | ||
|
|
a3bfe3e61a | ||
|
|
de10e340b6 | ||
|
|
40ca26f23b | ||
|
|
da28213613 | ||
|
|
6615a821f1 | ||
|
|
d627d669bc | ||
|
|
ef6fbad0cb | ||
|
|
ef9f7d4c16 | ||
|
|
44a0fa0986 | ||
|
|
4d9338323a | ||
|
|
1a6bfb65e6 | ||
|
|
3b112cc8c8 | ||
|
|
014a008c3a | ||
|
|
374be7b065 | ||
|
|
2ba3630de9 | ||
|
|
24cb9467c0 | ||
|
|
79c49e64e8 | ||
|
|
c8c0b8fb91 | ||
|
|
e5585cb0e7 | ||
|
|
bd32f6e2ef | ||
|
|
08f8ea0cd9 | ||
|
|
2d48b28b0a | ||
|
|
9cfaee9b21 | ||
|
|
9393e271de | ||
|
|
948595fb3b | ||
|
|
6149b619e7 | ||
|
|
b7406357c5 | ||
|
|
2279a51d52 | ||
|
|
4d3d0995c9 | ||
|
|
0ca752662d | ||
|
|
4246f5d206 | ||
|
|
e8289ec17d | ||
|
|
cde441bca6 | ||
|
|
740b609e1e | ||
|
|
c459c17fab | ||
|
|
11bbda9bb3 | ||
|
|
6a5c523948 | ||
|
|
99c23606c5 | ||
|
|
608b1ee156 | ||
|
|
12ab5c9964 | ||
|
|
779c825e92 | ||
|
|
96df718304 | ||
|
|
4bee440a56 | ||
|
|
bb4e4c7cda | ||
|
|
582ab220b3 | ||
|
|
3e2ad83b2c | ||
|
|
ae29a1734c | ||
|
|
457ce5e563 | ||
|
|
154bdb08a9 | ||
|
|
22b2dc9ea8 | ||
|
|
3ede1a4a5a | ||
|
|
9e3138b892 | ||
|
|
51cb40acd2 | ||
|
|
4702073458 | ||
|
|
e01cffff46 | ||
|
|
d88e438679 | ||
|
|
70683fc8e7 | ||
|
|
bd7ee0d1e8 | ||
|
|
17e2fdd4cb | ||
|
|
a468f58128 | ||
|
|
581a0698af | ||
|
|
f02f88f336 | ||
|
|
43d53b0616 | ||
|
|
54d6bad5e5 | ||
|
|
c37d4f768b | ||
|
|
a4b003b7ac | ||
|
|
433120ab22 | ||
|
|
109da5f515 | ||
|
|
56e6cf406a | ||
|
|
a2289a0625 | ||
|
|
02943a1082 | ||
|
|
64a36496e3 | ||
|
|
f2009ebd62 | ||
|
|
638892ca70 | ||
|
|
90fdbd36ec | ||
|
|
0dabc45780 | ||
|
|
0ffec91984 | ||
|
|
9cacf443e1 | ||
|
|
3f21be4fb0 | ||
|
|
7587f8212e | ||
|
|
1eed80375d | ||
|
|
806eed6040 | ||
|
|
d1828ecdb4 | ||
|
|
5cddb8ecda | ||
|
|
1651d9d6d9 | ||
|
|
40fc049cdc | ||
|
|
89afe62121 | ||
|
|
e5b40ee29e | ||
|
|
4a3d760c06 | ||
|
|
aa326f975d | ||
|
|
1fbf534f9a | ||
|
|
86aa38bfd5 | ||
|
|
ca929031ee | ||
|
|
f80477a093 | ||
|
|
abd7698d6d | ||
|
|
d5fe3261f4 | ||
|
|
3f46ae7beb | ||
|
|
46af09f776 | ||
|
|
f40b272eeb | ||
|
|
9876e3afd7 | ||
|
|
41df6a6c0b | ||
|
|
dbd25daff8 | ||
|
|
71eefb6272 | ||
|
|
a4a340dbd4 | ||
|
|
2c01893bb1 | ||
|
|
c4e496b001 | ||
|
|
16e90c3082 | ||
|
|
543b4a132b | ||
|
|
e4a5296fbf | ||
|
|
6c796b8552 | ||
|
|
e30ad43d36 | ||
|
|
f841b85d7d | ||
|
|
735b7bece6 | ||
|
|
4c9fdaf8b0 | ||
|
|
2ccc4acf34 | ||
|
|
c24343f107 | ||
|
|
81ffcd8ee9 | ||
|
|
432452ea9a | ||
|
|
6c14c7d623 | ||
|
|
30126072dc | ||
|
|
f7812c9b0a | ||
|
|
dca43f430b | ||
|
|
0947ce9234 | ||
|
|
91fa2ad23b | ||
|
|
1b57c56fdc | ||
|
|
d13509e302 | ||
|
|
85d300ce71 | ||
|
|
d6e1a3ab4c | ||
|
|
f1da8542ce | ||
|
|
a183621268 | ||
|
|
035de980e3 | ||
|
|
99f0688377 | ||
|
|
56412193e7 | ||
|
|
c5751b0bd3 | ||
|
|
86e57d243d | ||
|
|
7fc57a3be7 | ||
|
|
75069a44c7 | ||
|
|
2536865c4d | ||
|
|
d266f822ca | ||
|
|
5b8979a0f1 | ||
|
|
3bc79b9dfd | ||
|
|
0ae75e6db6 | ||
|
|
89e2a15eb7 | ||
|
|
b1896d9944 | ||
|
|
d0dedcfd2e | ||
|
|
73297c8e65 | ||
|
|
017c7290d6 | ||
|
|
6a8b5b06d0 | ||
|
|
93a6bc501e | ||
|
|
e8f050c194 | ||
|
|
712517b6fa | ||
|
|
434378eda5 | ||
|
|
cf38fdb720 | ||
|
|
95d30cf482 | ||
|
|
6542c02b7f | ||
|
|
a13144932a | ||
|
|
6597830631 | ||
|
|
ba08140197 | ||
|
|
d65ce3b596 | ||
|
|
4864a6e618 | ||
|
|
2bbb2bb1db | ||
|
|
47ce004f61 | ||
|
|
507e392509 | ||
|
|
dcfb014d1f | ||
|
|
4e880fb6e0 | ||
|
|
898e2b1e30 | ||
|
|
a68f3dc4eb | ||
|
|
c446fe0c6d | ||
|
|
4246e6c768 | ||
|
|
4f9cfdf65d | ||
|
|
b1cd233dda | ||
|
|
4f63b6093f | ||
|
|
380a50e39b | ||
|
|
3bd3a4ff7b | ||
|
|
46bf79455c | ||
|
|
2d5c26d44f | ||
|
|
9760b7c9d6 | ||
|
|
0b5a00c99a | ||
|
|
ba8482c707 | ||
|
|
1b34360b05 | ||
|
|
c84dab21e4 | ||
|
|
1640287897 | ||
|
|
adcf5bab0e | ||
|
|
e024700851 | ||
|
|
f4fc33d111 | ||
|
|
fb446022e2 | ||
|
|
e021d7cd9d | ||
|
|
3331ed2f49 | ||
|
|
939a8ddc26 | ||
|
|
3774f676cf | ||
|
|
7923509d1e | ||
|
|
30d6a44b0d | ||
|
|
6d46ecaa6c | ||
|
|
5a448ab4f5 | ||
|
|
a71262437d | ||
|
|
52ab963845 | ||
|
|
67280724ba | ||
|
|
d8a4b5aa34 | ||
|
|
fe0b2d0646 | ||
|
|
d3368e4aad | ||
|
|
903b510411 | ||
|
|
0dbc99d9ee | ||
|
|
505ca96547 | ||
|
|
89623d00f9 | ||
|
|
e47bcb3fb6 | ||
|
|
50d0d68f45 | ||
|
|
2f75418c70 | ||
|
|
013bc58371 | ||
|
|
55948cd431 | ||
|
|
dfca896a64 | ||
|
|
8fc6f3f744 | ||
|
|
f65058fa03 | ||
|
|
33567bab41 | ||
|
|
106662b1d7 | ||
|
|
b73115c002 | ||
|
|
cff208c5a9 | ||
|
|
a83dd9569d | ||
|
|
807cd4df9e | ||
|
|
ac3151f871 | ||
|
|
d4106b5cbd | ||
|
|
bcb6c2675f | ||
|
|
6b51666248 | ||
|
|
628a007112 | ||
|
|
ee65342545 | ||
|
|
94ea973fb5 | ||
|
|
9f8424e93f | ||
|
|
2e750236be | ||
|
|
c701315ab5 | ||
|
|
a53dc5c0a0 | ||
|
|
895ca80b2b | ||
|
|
1dcb7dd13d | ||
|
|
e75afcba42 | ||
|
|
0f0cf7a5db | ||
|
|
31c7d66245 | ||
|
|
cac7f653c9 | ||
|
|
219428c249 | ||
|
|
f4fa1e097d | ||
|
|
35b517b434 | ||
|
|
b3f956ba63 | ||
|
|
3ae8b3f8a7 | ||
|
|
c48349ba56 | ||
|
|
0763146254 | ||
|
|
7da5d42009 | ||
|
|
5ac847076a | ||
|
|
a88ea776a9 | ||
|
|
73a5ff1fb6 | ||
|
|
7e8901c99f | ||
|
|
45816ef5db | ||
|
|
fe682f8399 | ||
|
|
9ffb6fb26a | ||
|
|
063244e4ca | ||
|
|
d7b7a0a577 | ||
|
|
f735585239 | ||
|
|
30349ec217 | ||
|
|
55517d48ed | ||
|
|
0f93ab84e9 | ||
|
|
4dd1891594 | ||
|
|
27e1472d88 | ||
|
|
a3d63c095a | ||
|
|
dc486a2aa3 | ||
|
|
9963bdee09 | ||
|
|
499401d457 | ||
|
|
e2fcf5f92c | ||
|
|
b8bba7513a | ||
|
|
65957527cb | ||
|
|
e2ce68e3ef | ||
|
|
44cf02e5ba | ||
|
|
a4a70270ee | ||
|
|
82e1cac1c7 | ||
|
|
21ffe8e3f3 | ||
|
|
45f20d5089 | ||
|
|
cea12a35b7 | ||
|
|
686320a805 | ||
|
|
14ad51d2d2 | ||
|
|
28c15b3ff4 | ||
|
|
f5af28c886 | ||
|
|
6b50039863 | ||
|
|
34c5f63c00 | ||
|
|
53bca4efab | ||
|
|
9029593691 | ||
|
|
4aad859e20 | ||
|
|
b9d1d4a94f | ||
|
|
be8b01e289 | ||
|
|
360dedd9b2 | ||
|
|
e5d3d02307 | ||
|
|
d3e14ca7e5 | ||
|
|
5998208449 | ||
|
|
4fc84621ae | ||
|
|
8253a84689 | ||
|
|
6b7f14c69d | ||
|
|
cfaca604f5 | ||
|
|
0226b9db31 | ||
|
|
7f8d12b9f7 | ||
|
|
2ae341cf5e | ||
|
|
7850b550d8 | ||
|
|
10596a8bdc | ||
|
|
252e4715d4 | ||
|
|
420b989adb | ||
|
|
bdb388c79d | ||
|
|
87148f95f2 | ||
|
|
fe124354cd | ||
|
|
9a28fa71f0 | ||
|
|
4139cdaf35 | ||
|
|
ebe04620e1 | ||
|
|
7e053cd632 | ||
|
|
1fd65c77fe | ||
|
|
2bfc838614 | ||
|
|
69082db381 | ||
|
|
5ce2130f05 | ||
|
|
5b448ec7a6 | ||
|
|
f72b27d1c1 | ||
|
|
9973c5f2b7 | ||
|
|
88ee415dd1 | ||
|
|
4f168cf346 | ||
|
|
ad7b5d2cd8 | ||
|
|
1e522de4a7 | ||
|
|
c2d2d085cd | ||
|
|
b3a4c12d67 | ||
|
|
d5993ba27e | ||
|
|
ef563603a9 | ||
|
|
7e2ca72310 | ||
|
|
7c17c806ad | ||
|
|
8e0f529a00 | ||
|
|
47e15a3d47 | ||
|
|
cac7007069 | ||
|
|
ac55c900e7 | ||
|
|
25806e48dc | ||
|
|
3871f4b6ab | ||
|
|
27c892806b | ||
|
|
2b9f0480a1 | ||
|
|
72cc433265 | ||
|
|
7721b074e6 | ||
|
|
f5fd176b35 | ||
|
|
6db1d7925e | ||
|
|
0ed7545947 | ||
|
|
95e22e8d4f | ||
|
|
cf7d1d8041 | ||
|
|
443d0f0c6c | ||
|
|
c20e48179f | ||
|
|
3fbb7de712 | ||
|
|
0294603dfc | ||
|
|
5458252e4a | ||
|
|
31ce219544 | ||
|
|
86cd567968 | ||
|
|
5c66d32bc0 | ||
|
|
5ac5c93c90 | ||
|
|
84480f4072 | ||
|
|
b82953405d | ||
|
|
5271626947 | ||
|
|
bc16854ccb | ||
|
|
6db0485c7b | ||
|
|
b5fc0d30a8 | ||
|
|
38557aac7f | ||
|
|
d621fbfab1 | ||
|
|
f19eec7fae | ||
|
|
8174842b05 | ||
|
|
ff07d261b2 | ||
|
|
c55db8a895 | ||
|
|
70a9554b72 | ||
|
|
bdd7d92108 | ||
|
|
a7cbc19755 | ||
|
|
93ff3e4d1f | ||
|
|
1dfc748e87 | ||
|
|
e1f17a7ff1 | ||
|
|
56c40ff0fd | ||
|
|
9dd3b75165 | ||
|
|
20fc8f0279 | ||
|
|
944704a12d | ||
|
|
fba9f14b2c | ||
|
|
a7c24aeeee | ||
|
|
186eb998ba | ||
|
|
d630ba743d | ||
|
|
e9fabdcc96 | ||
|
|
31698678fb | ||
|
|
ebd2e89faa | ||
|
|
17e0fb1c15 | ||
|
|
6e3e77a1a0 | ||
|
|
ef44f90491 | ||
|
|
66376885e9 | ||
|
|
db941e2627 | ||
|
|
5f78d34f4d | ||
|
|
85281ae128 | ||
|
|
e5d1b213c9 | ||
|
|
e4ab9a4130 | ||
|
|
a1ec179229 | ||
|
|
2f77855ec1 | ||
|
|
d11173fe90 | ||
|
|
fde8b82b78 | ||
|
|
b20eded30e | ||
|
|
3ec17c41be | ||
|
|
0ed64707f3 | ||
|
|
417533e90c | ||
|
|
e268affb5a | ||
|
|
4474ada291 | ||
|
|
a65c3288d8 | ||
|
|
affcef7e37 | ||
|
|
173c2ba338 | ||
|
|
b5fcf95b88 | ||
|
|
d954851d43 | ||
|
|
df602c27d5 | ||
|
|
3d9fa10485 | ||
|
|
6990040626 | ||
|
|
b42ea6cd3c | ||
|
|
8c7fd8b330 | ||
|
|
4e0039625e | ||
|
|
6c909f8935 | ||
|
|
4f5d385e52 | ||
|
|
84fe1d87b3 | ||
|
|
bb0bc41f99 | ||
|
|
1c2d67ded7 | ||
|
|
7372a05ce5 | ||
|
|
642befa8d5 | ||
|
|
3b5a9e2d4b | ||
|
|
6b3d9d9baf | ||
|
|
436852d077 | ||
|
|
b78ced4fa9 | ||
|
|
1bc81df00e | ||
|
|
bf025537b6 | ||
|
|
ad4812218d | ||
|
|
0fbc4b8ff4 | ||
|
|
7ef82f5caf | ||
|
|
1141ea7933 | ||
|
|
1e9344161b | ||
|
|
6c021765bb | ||
|
|
bc151af51d | ||
|
|
8bd09e0e80 | ||
|
|
5978cb9703 | ||
|
|
8c447a95dd | ||
|
|
870431dc1a | ||
|
|
1125b1f545 | ||
|
|
d255fc737a | ||
|
|
37cb51ee94 | ||
|
|
02cabcc0ec | ||
|
|
3ffb6c046e | ||
|
|
7bf8b81995 | ||
|
|
0ddda4a267 | ||
|
|
c46ad970cb | ||
|
|
733abcffbc | ||
|
|
139f928731 | ||
|
|
3da9986232 | ||
|
|
a284df01d1 | ||
|
|
412f5cf3e0 | ||
|
|
42ba1e4aed | ||
|
|
1893b06c13 | ||
|
|
b0cbb70e53 | ||
|
|
78e81fef27 | ||
|
|
dd08231a34 | ||
|
|
8c78b7ef53 | ||
|
|
c650fdcdfc | ||
|
|
e99973d4b7 | ||
|
|
4012a86bd8 | ||
|
|
048fee5c88 | ||
|
|
a287fd97e4 | ||
|
|
3a249183f8 | ||
|
|
393dad97e0 | ||
|
|
113abc71d4 | ||
|
|
63261ec0d5 | ||
|
|
507935d067 | ||
|
|
c579b73f75 | ||
|
|
e69ec148cc | ||
|
|
a76ade9bb2 | ||
|
|
4fec149345 | ||
|
|
5bc164de76 | ||
|
|
969b84043c | ||
|
|
da72dafbfb | ||
|
|
1adf7f6b2b | ||
|
|
3e085ccbaa | ||
|
|
3e8e2ab513 | ||
|
|
4f802ece52 | ||
|
|
313ac34e59 | ||
|
|
54f75889fa | ||
|
|
f64c86d23f | ||
|
|
9e634310b4 | ||
|
|
33666e7d6b | ||
|
|
40d4c7e410 | ||
|
|
8cd4699dbc | ||
|
|
49d5aff8f9 | ||
|
|
b2ee9cb363 | ||
|
|
e9c20ad992 | ||
|
|
63b880e624 | ||
|
|
768f67ae0a | ||
|
|
fbf5fa53c6 | ||
|
|
4a5824e7a7 | ||
|
|
32f3c48b65 | ||
|
|
f7fbcd2451 | ||
|
|
c6253854ba | ||
|
|
544644e94b | ||
|
|
f6cebf6956 | ||
|
|
1b82aa02aa | ||
|
|
e565a7e73d | ||
|
|
59c4dfa33b | ||
|
|
1a8e3c2e31 | ||
|
|
226917e1cd | ||
|
|
fe813ee247 | ||
|
|
4cd830a0bf | ||
|
|
1259ca9ac9 | ||
|
|
d0857fe36c | ||
|
|
56a7438114 | ||
|
|
09df542db8 | ||
|
|
1df102ab93 | ||
|
|
02b3b780bb | ||
|
|
f84f3da012 | ||
|
|
c65c8fdaaa | ||
|
|
b484123f1b | ||
|
|
b60119790e | ||
|
|
216427cea0 | ||
|
|
c53e2fcc14 | ||
|
|
9fd80fd136 | ||
|
|
5fc284722f | ||
|
|
ad6e395da0 | ||
|
|
6c79a406cd | ||
|
|
456e8bd249 | ||
|
|
de95f28598 | ||
|
|
43405afdb2 | ||
|
|
97f95e17db | ||
|
|
6725d61b3f | ||
|
|
ebda2cb100 | ||
|
|
bb72b2494d | ||
|
|
3b0a509cae | ||
|
|
2f981f9383 | ||
|
|
fc3c08e411 | ||
|
|
ff811f287a | ||
|
|
8ebb3bc37b | ||
|
|
f7ff571c5f | ||
|
|
6a82cbee71 | ||
|
|
b0585bd0a9 | ||
|
|
6ad4d242c2 | ||
|
|
030b1d9826 | ||
|
|
f6ffa7b3b9 | ||
|
|
137efe2233 | ||
|
|
62411d039e | ||
|
|
54b1716e77 | ||
|
|
9f7aba8039 | ||
|
|
a176b8d566 | ||
|
|
26a97ec36c | ||
|
|
f29807aad8 | ||
|
|
92d3c7a4cd | ||
|
|
af3a0f6af0 | ||
|
|
ce6164ca20 | ||
|
|
e9fc3fcecf | ||
|
|
25a1a24890 | ||
|
|
bf7fc1d5a9 | ||
|
|
2126b49ab3 | ||
|
|
ae6767b1d2 | ||
|
|
133629dbee | ||
|
|
fb0a9bc7e3 | ||
|
|
2a3492766f | ||
|
|
2ec7e5653e | ||
|
|
30bfb245f5 | ||
|
|
84d89bc4f5 | ||
|
|
7f55fabfec | ||
|
|
9e0f04dc8b | ||
|
|
574c2954b3 | ||
|
|
9d38e26b81 | ||
|
|
4e75a0c81e | ||
|
|
d311e967fa | ||
|
|
d0a6c111cb | ||
|
|
aa6d8b4662 | ||
|
|
3f5fd89fd7 | ||
|
|
f43a5a005c | ||
|
|
755bf3c412 | ||
|
|
84f3fb0ba5 | ||
|
|
f2df6c114d | ||
|
|
f93659e303 | ||
|
|
226404d39c | ||
|
|
c1bcc143b9 | ||
|
|
5ca9062d45 | ||
|
|
0b209eaabb | ||
|
|
47ef191ea2 | ||
|
|
25e76d761e | ||
|
|
9731a51dae | ||
|
|
81220fa590 | ||
|
|
24e8c4d81a | ||
|
|
013520b0c0 | ||
|
|
f012bcb027 | ||
|
|
922e1ce529 | ||
|
|
7a707cc64b | ||
|
|
1967acfef2 | ||
|
|
e24cb04003 | ||
|
|
67974746ff | ||
|
|
8d916d41d7 | ||
|
|
08444e3942 | ||
|
|
9c41a0cd26 | ||
|
|
2c50069d98 | ||
|
|
31059f6728 | ||
|
|
2000752b2c | ||
|
|
dd34830443 | ||
|
|
c58f4ee51b | ||
|
|
00f7e8cb2a | ||
|
|
439904e27f | ||
|
|
4d6590ece2 | ||
|
|
9b0c4b970d | ||
|
|
a80ce1b12a | ||
|
|
57dd74c180 | ||
|
|
90c67e4226 | ||
|
|
093cd6a6a3 | ||
|
|
975db86306 | ||
|
|
eba688e11b | ||
|
|
05b44204d5 | ||
|
|
0bb017c1d5 | ||
|
|
a6efd0b335 | ||
|
|
c3d237e9c6 | ||
|
|
09d28f5570 | ||
|
|
ba02937ec6 | ||
|
|
3d78b14deb | ||
|
|
f558551d1d | ||
|
|
12fe66370f | ||
|
|
423aaa492c | ||
|
|
2016586475 | ||
|
|
bf88167152 | ||
|
|
52be84f04e | ||
|
|
03d187b147 | ||
|
|
3329047579 | ||
|
|
86c70e5abb | ||
|
|
b17f4108ea | ||
|
|
4e523cc455 | ||
|
|
49c9be9159 | ||
|
|
f1bfe0760f | ||
|
|
35f02fdaad | ||
|
|
6ac09966ba | ||
|
|
af9a7617f5 | ||
|
|
42c89732c8 | ||
|
|
054475ddbf | ||
|
|
126d556d6a | ||
|
|
529a302e77 | ||
|
|
acbf978452 | ||
|
|
90b22716ea | ||
|
|
fb90c504a6 | ||
|
|
65fd834654 | ||
|
|
a84acf5b8b | ||
|
|
e9505db146 | ||
|
|
4fb49b011e | ||
|
|
6742991469 | ||
|
|
2430cc5aac | ||
|
|
54590aaf26 | ||
|
|
7c5e67b272 | ||
|
|
c62c8ea4dd | ||
|
|
383653d608 | ||
|
|
001dc3f33d | ||
|
|
37d45b3fbb | ||
|
|
f72b8c6034 | ||
|
|
c11e5a9383 | ||
|
|
b5147a149e | ||
|
|
655529f30b | ||
|
|
fd5319e0ce | ||
|
|
f8c84ca672 | ||
|
|
1b81ac8916 | ||
|
|
c64e2ebb16 | ||
|
|
3771f2c088 | ||
|
|
6d398bd5c7 | ||
|
|
5c355a5ff3 | ||
|
|
d2f8b346e6 | ||
|
|
5cdaa931f9 | ||
|
|
693863a51e | ||
|
|
a204c80308 | ||
|
|
7d620965e2 | ||
|
|
0149654791 | ||
|
|
f08d280429 | ||
|
|
6fbb749431 | ||
|
|
bc7178e5f7 | ||
|
|
61070d136d | ||
|
|
ea85f93e37 | ||
|
|
46029c501a | ||
|
|
78e1fdd1c9 | ||
|
|
9ef5c7c667 | ||
|
|
cdf279e438 | ||
|
|
cdd192abe4 | ||
|
|
9af6f4b02e | ||
|
|
d86ceecd8c | ||
|
|
8f693b92a7 | ||
|
|
6b1ff37cf3 | ||
|
|
c490ff8ae7 | ||
|
|
f73ebbf689 | ||
|
|
1928ba9c1f | ||
|
|
ad3655fb92 | ||
|
|
29e0158689 | ||
|
|
aaf55b6da0 | ||
|
|
90a131bd29 | ||
|
|
8652006aea | ||
|
|
2139fbc4b7 | ||
|
|
2b48e8b03a | ||
|
|
a7afea0963 | ||
|
|
9d1e61de93 | ||
|
|
9b6b1a68f4 | ||
|
|
c7bd63ef53 | ||
|
|
40b3382023 | ||
|
|
a70c5e726d | ||
|
|
409fdaec59 | ||
|
|
372aa369c4 | ||
|
|
eb94704d8d | ||
|
|
894a168833 | ||
|
|
f3eb466af3 | ||
|
|
df848973dc | ||
|
|
f37e5ecc38 | ||
|
|
6316609a5f | ||
|
|
dca08d32bd | ||
|
|
2b287f50f1 | ||
|
|
acf58972f7 | ||
|
|
5bf5516b29 | ||
|
|
40b99e3751 | ||
|
|
28a56370d4 | ||
|
|
e3589577ed | ||
|
|
38beb4cbc0 | ||
|
|
167af7f160 | ||
|
|
7d91885413 | ||
|
|
0de83157dc | ||
|
|
cc9639b3cb | ||
|
|
cb0ac1868f | ||
|
|
e653147119 | ||
|
|
f3d15a0440 | ||
|
|
0bc736d384 | ||
|
|
3bf7b817f6 | ||
|
|
477e48bf54 | ||
|
|
913e730ec2 | ||
|
|
bd64043377 | ||
|
|
9f827feeaa | ||
|
|
714b7a8145 | ||
|
|
7ba6fda236 | ||
|
|
fc9c454518 | ||
|
|
1c456fc2df | ||
|
|
9b37ac384f | ||
|
|
99b279d5e5 | ||
|
|
4d07a99ee2 | ||
|
|
f6f0e49de4 | ||
|
|
07568a8f06 | ||
|
|
71e9019211 | ||
|
|
0937213f4b | ||
|
|
6c42350fcf | ||
|
|
453d4be4d0 | ||
|
|
104c4fab19 | ||
|
|
fd5eb2d3f9 | ||
|
|
8b770d2a0d | ||
|
|
b739aa89f1 | ||
|
|
11c07470bb | ||
|
|
76865cbe14 | ||
|
|
b7ce8213d7 | ||
|
|
86f43cd8d6 | ||
|
|
2407509ce0 | ||
|
|
47ce072af7 | ||
|
|
c2c6ff11ed | ||
|
|
9b18a6350a | ||
|
|
a36050ab4b | ||
|
|
568484f079 | ||
|
|
3ea4f1fda8 | ||
|
|
5366844581 | ||
|
|
e52a6184c4 | ||
|
|
b40b1232cc | ||
|
|
3a46055e52 | ||
|
|
2af5d07467 | ||
|
|
c2176f969b | ||
|
|
9346122c29 | ||
|
|
b98556a4dc | ||
|
|
55d102e450 | ||
|
|
dfc22af10f | ||
|
|
2d585474c1 | ||
|
|
53ee984eb7 | ||
|
|
bdb747f9de | ||
|
|
299075c930 | ||
|
|
b0e0247b21 | ||
|
|
35abd695cd | ||
|
|
df2ffd1187 | ||
|
|
b378082ec4 | ||
|
|
fb4fae9174 | ||
|
|
cddff2b1b8 | ||
|
|
d6dbf831f7 | ||
|
|
7e1d4325ec | ||
|
|
2a2c17a232 | ||
|
|
d130c6f42d | ||
|
|
a09238debd | ||
|
|
328a87b02a | ||
|
|
529faa1172 | ||
|
|
8e5efd4804 | ||
|
|
32406b678b | ||
|
|
55e2077a46 | ||
|
|
a771d6a19d | ||
|
|
0d5d7bb26f | ||
|
|
3808dd3222 | ||
|
|
8fc94e12af | ||
|
|
824e33ef8a | ||
|
|
655f9a9f79 | ||
|
|
6ac6aa521c | ||
|
|
3982e065c8 | ||
|
|
497b8027f5 | ||
|
|
89757388c7 | ||
|
|
77fae5c562 | ||
|
|
80fcba91c8 | ||
|
|
39d64be581 | ||
|
|
2bfd30ab4b | ||
|
|
60004eb209 | ||
|
|
db11897d71 | ||
|
|
5e466f9037 | ||
|
|
6b2698a503 | ||
|
|
4788db7e00 | ||
|
|
d9680df510 | ||
|
|
f20ee1f305 | ||
|
|
302bf865f9 | ||
|
|
03929dcdaa | ||
|
|
89af2e9638 | ||
|
|
015586950f | ||
|
|
9df261a0eb | ||
|
|
a2a3c84450 | ||
|
|
6611348ebf | ||
|
|
e69ee90acf | ||
|
|
8f3554d08a | ||
|
|
b6ee9e637b | ||
|
|
f46453f80f | ||
|
|
3deafaea7f | ||
|
|
a15d518571 | ||
|
|
886a29d659 | ||
|
|
e69de57def | ||
|
|
9d68e20dcc | ||
|
|
c0a8546330 | ||
|
|
4c0002e28a | ||
|
|
7c37d75bf4 | ||
|
|
879261981f | ||
|
|
838b0bcd27 | ||
|
|
736b8ad7b7 | ||
|
|
db27fae40e | ||
|
|
594111a255 | ||
|
|
53a46a2739 | ||
|
|
eef7a55904 | ||
|
|
a68fa5e6e0 | ||
|
|
7c0d126edc | ||
|
|
1a5e3d3151 | ||
|
|
95ff6c56af | ||
|
|
e2ef392633 | ||
|
|
dbc2356768 | ||
|
|
47e5e984df | ||
|
|
0f32f50c50 | ||
|
|
606cc1f538 | ||
|
|
2bb11888d5 | ||
|
|
09b1d6aa72 | ||
|
|
72f0489ec2 | ||
|
|
7b39973065 | ||
|
|
30b64483fd | ||
|
|
5369fb0376 | ||
|
|
5fe34fd6c3 | ||
|
|
0fbdda4d2e | ||
|
|
a98e980c8f | ||
|
|
2b836504ab | ||
|
|
0de3f0d985 | ||
|
|
151e375091 | ||
|
|
c44549bfa5 | ||
|
|
d6852f5e74 | ||
|
|
9173034b96 | ||
|
|
2888ab1f52 | ||
|
|
26b5416641 | ||
|
|
8bcac7e955 | ||
|
|
ea25e8129c | ||
|
|
26cb44f9d9 | ||
|
|
7a37a44433 | ||
|
|
a752f3f8c7 | ||
|
|
946e07644c | ||
|
|
497b26a249 | ||
|
|
79aad7fabf | ||
|
|
7851930ac3 | ||
|
|
758572872d | ||
|
|
111551477a | ||
|
|
31c9e3fb7e | ||
|
|
7061b7202f | ||
|
|
d88b6a3c95 | ||
|
|
cea2a62ee0 | ||
|
|
35a8b8e8e8 | ||
|
|
1b8624f135 | ||
|
|
e217066bf6 | ||
|
|
5430408218 | ||
|
|
d2744063a0 | ||
|
|
03356616ef | ||
|
|
6ce2e48dd3 | ||
|
|
16a7ba6bf0 | ||
|
|
e9d122f231 | ||
|
|
0ae5c61759 | ||
|
|
691460c5fc | ||
|
|
ab1c924388 | ||
|
|
05050eba38 | ||
|
|
912dd518bf | ||
|
|
e0395a9907 | ||
|
|
0051f23e12 | ||
|
|
a9bb8fa9fc | ||
|
|
09f5d2fdae | ||
|
|
006a9d914c | ||
|
|
9009cbc528 | ||
|
|
a1862986b8 | ||
|
|
cc2989a848 | ||
|
|
4deb95776f | ||
|
|
f195b27884 | ||
|
|
5036a71334 | ||
|
|
531c47b47c | ||
|
|
4dbb2da78f | ||
|
|
41c1e0f08d | ||
|
|
e7b1905e49 | ||
|
|
e00265f855 | ||
|
|
cd36708b6d | ||
|
|
5bd22cb5e4 | ||
|
|
bc00d5159a | ||
|
|
bcb0874477 | ||
|
|
5326e8d212 | ||
|
|
4528cc28ef | ||
|
|
381d7cce54 | ||
|
|
2594e7b37a | ||
|
|
4356b953ca | ||
|
|
e788853156 | ||
|
|
91ed6de166 | ||
|
|
cf3aafc32e | ||
|
|
c2d7ff8179 | ||
|
|
e5eaa1c633 | ||
|
|
6366c832b0 | ||
|
|
f326cbedbe | ||
|
|
5b272c2b5c |
No files matched your search
@@ -0,0 +1,12 @@
|
|||||||
|
- My goal is: _describe_
|
||||||
|
- My actions were: _describe_
|
||||||
|
- My expectation was: _describe_
|
||||||
|
- The result I saw: _provide logs or description_
|
||||||
|
- My question is: _your question_
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
|
||||||
|
- mongoose version: _X.Y_
|
||||||
|
- Compiler/IDE and SDK: ZZZ version _X.Y_
|
||||||
|
- Target hardware/board: _?_
|
||||||
|
- Connectivity chip/module: _?_
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
name: CIFuzz
|
||||||
|
on: [pull_request]
|
||||||
|
jobs:
|
||||||
|
Fuzzing:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Build Fuzzers
|
||||||
|
id: build
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'mongoose'
|
||||||
|
dry-run: false
|
||||||
|
language: c++
|
||||||
|
- name: Run Fuzzers
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'mongoose'
|
||||||
|
fuzz-seconds: 600
|
||||||
|
dry-run: false
|
||||||
|
language: c++
|
||||||
|
- name: Upload Crash
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
if: failure() && steps.build.outcome == 'success'
|
||||||
|
with:
|
||||||
|
name: artifacts
|
||||||
|
path: ./out/artifacts
|
||||||
@@ -3,38 +3,126 @@ on: [push, pull_request]
|
|||||||
jobs:
|
jobs:
|
||||||
linux:
|
linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
cc: [gcc, clang]
|
||||||
|
target: [test, test++, valgrind]
|
||||||
|
ssl: [MBEDTLS, OPENSSL]
|
||||||
|
name: linux ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }}
|
||||||
|
env:
|
||||||
|
IPV6: 0
|
||||||
|
CC: ${{ matrix.cc }}
|
||||||
|
SSL: ${{ matrix.ssl }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: sudo apt-get install libmbedtls-dev
|
run: sudo apt-get update ; sudo apt-get install libmbedtls-dev valgrind
|
||||||
- name: make
|
- name: test
|
||||||
run: make test test++ IPV6=0
|
run: make ${{ matrix.target }}
|
||||||
- name: openssl
|
- name: unamalgamated
|
||||||
run: make test SSL=OPENSSL IPV6=0
|
run: make unamalgamated
|
||||||
- name: examples
|
examples:
|
||||||
run: make ex
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- run: sudo apt-get install libmbedtls-dev libpcap-dev
|
||||||
|
- run: make clean examples
|
||||||
|
- run: make clean test IPV6=0 MG_ENABLE_POLL=1
|
||||||
macos:
|
macos:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Install packages
|
- run: HOMEBREW_NO_AUTO_UPDATE=1 brew install jq mbedtls openssl
|
||||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install jq mbedtls openssl
|
- run: make test++ upload-coverage IPV6=0 ASAN_OPTIONS=
|
||||||
- name: make
|
- run: make test SSL=OPENSSL IPV6=0 ASAN_OPTIONS= OPENSSL=`echo /usr/local/Cellar/openssl*/*`
|
||||||
run: make test++ upload-coverage IPV6=0
|
- run: make mg_prefix
|
||||||
- name: openssl
|
|
||||||
run: make test SSL=OPENSSL IPV6=0
|
|
||||||
- name: exports
|
|
||||||
run: make mg_prefix
|
|
||||||
windows:
|
windows:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: vc98
|
- run: make vc98
|
||||||
run: make vc98
|
- run: make vc17
|
||||||
- name: vc2017
|
- run: make vc22
|
||||||
run: make vc2017
|
- run: make mingw
|
||||||
- name: mingw
|
- run: make mingw++
|
||||||
run: make mingw
|
arm:
|
||||||
- name: mingw++
|
runs-on: ubuntu-latest
|
||||||
run: make mingw++
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- run: make arm
|
||||||
|
matrix_examples:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
example:
|
||||||
|
- name: esp32/device-dashboard
|
||||||
|
path: esp32/device-dashboard
|
||||||
|
- name: esp8266
|
||||||
|
path: esp8266
|
||||||
|
- name: stm32-freertos-tcp
|
||||||
|
path: stm32/stm32-freertos-tcp
|
||||||
|
- name: stm32-nucleo-f746z
|
||||||
|
path: stm32/stm32-nucleo-f746z
|
||||||
|
- name: stm32-nucleo-f429z
|
||||||
|
path: stm32/stm32-nucleo-f429z
|
||||||
|
- name: stm32-nucleo-h743z
|
||||||
|
path: stm32/stm32-nucleo-h743z
|
||||||
|
- name: stm32-nucleo-f746zg-baremetal
|
||||||
|
path: stm32/nucleo-f746zg-baremetal
|
||||||
|
- name: nxp-mimxrt1020-azurertos
|
||||||
|
path: nxp/nxp-mimxrt1020-azurertos
|
||||||
|
- name: nxp-frdmk66f-freertos
|
||||||
|
path: nxp/nxp-frdmk66f-freertos
|
||||||
|
- name: nxp-lpcxpresso54s018m-freertos
|
||||||
|
path: nxp/nxp-lpcxpresso54s018m-freertos
|
||||||
|
- name: nxp-mimxrt1020-freertos
|
||||||
|
path: nxp/nxp-mimxrt1020-freertos
|
||||||
|
- name: nxp-evkbimxrt1050-lwip-freertos
|
||||||
|
path: nxp/nxp-evkbimxrt1050-lwip-freertos
|
||||||
|
- name: nxp-evkmimxrt1020-lwip-freertos
|
||||||
|
path: nxp/nxp-evkmimxrt1020-lwip-freertos
|
||||||
|
- name: nxp-evkmimxrt1024-lwip-freertos
|
||||||
|
path: nxp/nxp-evkmimxrt1024-lwip-freertos
|
||||||
|
- name: nxp-evkmimxrt1060-lwip-freertos
|
||||||
|
path: nxp/nxp-evkmimxrt1060-lwip-freertos
|
||||||
|
- name: nxp-evkmimxrt1064-lwip-freertos
|
||||||
|
path: nxp/nxp-evkmimxrt1064-lwip-freertos
|
||||||
|
- name: nxp-evkmimxrt1160-cm7-lwip-freertos
|
||||||
|
path: nxp/nxp-evkmimxrt1160-cm7-lwip-freertos
|
||||||
|
- name: nxp-evkmimxrt1170-cm7-lwip-freertos
|
||||||
|
path: nxp/nxp-evkmimxrt1170-cm7-lwip-freertos
|
||||||
|
- name: nxp-frdmk64f-lwip-freertos
|
||||||
|
path: nxp/nxp-frdmk64f-lwip-freertos
|
||||||
|
- name: nxp-frdmk66f-lwip-freertos
|
||||||
|
path: nxp/nxp-frdmk66f-lwip-freertos
|
||||||
|
- name: nxp-lpcxpresso54018-lwip-freertos
|
||||||
|
path: nxp/nxp-lpcxpresso54018-lwip-freertos
|
||||||
|
- name: nxp-lpcxpresso54608-lwip-freertos
|
||||||
|
path: nxp/nxp-lpcxpresso54608-lwip-freertos
|
||||||
|
- name: nxp-lpcxpresso54618-lwip-freertos
|
||||||
|
path: nxp/nxp-lpcxpresso54618-lwip-freertos
|
||||||
|
- name: nxp-lpcxpresso54628-lwip-freertos
|
||||||
|
path: nxp/nxp-lpcxpresso54628-lwip-freertos
|
||||||
|
- name: nxp-twrk65f180m-lwip-freertos
|
||||||
|
path: nxp/nxp-twrk65f180m-lwip-freertos
|
||||||
|
- name: nxp-twrkv58f220m-lwip-freertos
|
||||||
|
path: nxp/nxp-twrkv58f220m-lwip-freertos
|
||||||
|
- name: infineon-xmc4700_4800-lwip-rtx-rtos
|
||||||
|
path: infineon/infineon-xmc4700_4800-lwip-rtx-rtos
|
||||||
|
- name: ti
|
||||||
|
path: ti/ti-ek-tm4c1294xl-http-server
|
||||||
|
- name: nucleo_f746zg-zephyr-http-client
|
||||||
|
path: zephyr/http-client
|
||||||
|
- name: nucleo_f746zg-zephyr-http-server
|
||||||
|
path: zephyr/http-server
|
||||||
|
- name: nucleo_f746zg-zephyr-mqtt-aws-client
|
||||||
|
path: zephyr/mqtt-aws-client
|
||||||
|
- name: nucleo_f746zg-zephyr-websocket-server
|
||||||
|
path: zephyr/websocket-server
|
||||||
|
name: ${{ matrix.example.name }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: ${{ matrix.example.name }}
|
||||||
|
run: make -C examples/${{ matrix.example.path }} build
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
Copyright (c) 2004-2013 Sergey Lyubka
|
Copyright (c) 2004-2013 Sergey Lyubka
|
||||||
Copyright (c) 2013-2020 Cesanta Software Limited
|
Copyright (c) 2013-2021 Cesanta Software Limited
|
||||||
All rights reserved
|
All rights reserved
|
||||||
|
|
||||||
This software is dual-licensed: you can redistribute it and/or modify
|
This software is dual-licensed: you can redistribute it and/or modify
|
||||||
@@ -13,4 +13,4 @@ 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 software under a commercial
|
Alternatively, you can license this software under a commercial
|
||||||
license, as set out in <https://www.cesanta.com/license>.
|
license, as set out in <https://mongoose.ws/licensing/>.
|
||||||
@@ -1,106 +1,164 @@
|
|||||||
SRCS = $(wildcard src/*.c)
|
SRCS = mongoose.c test/unit_test.c test/packed_fs.c
|
||||||
HDRS = $(wildcard src/*.h)
|
HDRS = $(wildcard src/*.h) $(wildcard mip/*.h)
|
||||||
DEFS ?= -DMG_MAX_HTTP_HEADERS=5 -DMG_ENABLE_LINES -DMG_ENABLE_DIRECTORY_LISTING=1 -DMG_ENABLE_SSI=1
|
DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_PACKED_FS=1 -DMG_ENABLE_SSI=1
|
||||||
CFLAGS ?= -W -Wall -Werror -Isrc -I. -O0 -g $(DEFS) $(TFLAGS) $(EXTRA)
|
C_WARN ?= -Wmissing-prototypes -Wstrict-prototypes
|
||||||
|
WARN ?= -pedantic -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wundef $(C_WARN)
|
||||||
|
OPTS ?= -O3 -g3
|
||||||
|
VALGRIND_OPTS ?= -O0 -g3
|
||||||
|
INCS ?= -Isrc -I.
|
||||||
SSL ?= MBEDTLS
|
SSL ?= MBEDTLS
|
||||||
CDIR ?= $(realpath $(CURDIR))
|
CWD ?= $(realpath $(CURDIR))
|
||||||
VC98 = docker run --rm -e WINEDEBUG=-all -v $(CDIR):$(CDIR) -w $(CDIR) docker.io/mdashnet/vc98
|
ENV ?= -e Tmp=. -e WINEDEBUG=-all
|
||||||
VC2017 = docker run --rm -e WINEDEBUG=-all -v $(CDIR):$(CDIR) -w $(CDIR) docker.io/mdashnet/vc2017
|
DOCKER ?= docker run --platform linux/amd64 --rm $(ENV) -v $(CWD):$(CWD) -w $(CWD)
|
||||||
MINGW = docker run --rm -v $(CDIR):$(CDIR) -w $(CDIR) docker.io/mdashnet/mingw
|
VCFLAGS = /nologo /W3 /O2 /MD /I. $(DEFS) $(TFLAGS)
|
||||||
GCC = docker run --rm -v $(CDIR):$(CDIR) -w $(CDIR) mdashnet/cc2
|
|
||||||
VCFLAGS = /nologo /W3 /O2 /I. $(DEFS) $(TFLAGS)
|
|
||||||
CLANG ?= clang # /usr/local/opt/llvm\@9/bin/clang
|
|
||||||
IPV6 ?= 1
|
IPV6 ?= 1
|
||||||
ASAN_OPTIONS ?=
|
ASAN ?= -fsanitize=address,undefined -fno-sanitize-recover=all
|
||||||
EXAMPLES := $(wildcard examples/*)
|
ASAN_OPTIONS ?= detect_leaks=1
|
||||||
EXAMPLE_TARGET ?= example
|
EXAMPLES := $(dir $(wildcard examples/*/Makefile)) examples/stm32/nucleo-f746zg-baremetal
|
||||||
.PHONY: ex test
|
PREFIX ?= /usr/local
|
||||||
|
VERSION ?= $(shell cut -d'"' -f2 src/version.h)
|
||||||
|
COMMON_CFLAGS ?= $(WARN) $(INCS) $(DEFS) -DMG_ENABLE_IPV6=$(IPV6) $(TFLAGS)
|
||||||
|
CFLAGS ?= $(OPTS) $(ASAN) $(COMMON_CFLAGS)
|
||||||
|
VALGRIND_CFLAGS ?= $(VALGRIND_OPTS) $(COMMON_CFLAGS)
|
||||||
|
VALGRIND_RUN ?= valgrind --tool=memcheck --gen-suppressions=all --leak-check=full --show-leak-kinds=all --leak-resolution=high --track-origins=yes --error-exitcode=1 --exit-on-first-error=yes
|
||||||
|
.PHONY: examples test valgrind
|
||||||
|
|
||||||
ifeq "$(SSL)" "MBEDTLS"
|
ifeq "$(SSL)" "MBEDTLS"
|
||||||
MBEDTLS_DIR ?= $(shell brew --cellar mbedtls)
|
MBEDTLS ?= /usr/local
|
||||||
MBEDTLS_VER ?= $(shell brew info mbedtls --json | jq -j .[0].installed[0].version)
|
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS)/include -I/usr/include
|
||||||
MBEDTLS ?= $(MBEDTLS_DIR)/$(MBEDTLS_VER)
|
|
||||||
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS)/include -I/usr/include
|
|
||||||
LDFLAGS ?= -L$(MBEDTLS)/lib -lmbedtls -lmbedcrypto -lmbedx509
|
LDFLAGS ?= -L$(MBEDTLS)/lib -lmbedtls -lmbedcrypto -lmbedx509
|
||||||
endif
|
endif
|
||||||
ifeq "$(SSL)" "OPENSSL"
|
ifeq "$(SSL)" "OPENSSL"
|
||||||
OPENSSL_DIR ?= $(shell brew --cellar openssl)
|
OPENSSL ?= /usr/local
|
||||||
OPENSSL_VER ?= $(shell brew info openssl --json | jq -j .[0].installed[0].version)
|
CFLAGS += -DMG_ENABLE_OPENSSL=1 -I$(OPENSSL)/include
|
||||||
OPENSSL ?= $(OPENSSL_DIR)/$(OPENSSL_VER)
|
|
||||||
CFLAGS += -DMG_ENABLE_OPENSSL=1 -I$(OPENSSL)/include
|
|
||||||
LDFLAGS ?= -L$(OPENSSL)/lib -lssl -lcrypto
|
LDFLAGS ?= -L$(OPENSSL)/lib -lssl -lcrypto
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: mg_prefix test test++ ex vc98 vc2017 mingw mingw++ linux linux++ infer fuzz
|
all: mg_prefix unamalgamated unpacked test test++ arm examples vc98 vc17 vc22 mingw mingw++ linux linux++ fuzz
|
||||||
|
|
||||||
ex:
|
examples:
|
||||||
@for X in $(EXAMPLES); do $(MAKE) -C $$X $(EXAMPLE_TARGET); done
|
@for X in $(EXAMPLES); do test -f $$X/Makefile || continue; $(MAKE) -C $$X example || exit 1; done
|
||||||
|
|
||||||
|
test/packed_fs.c: Makefile src/ssi.h test/fuzz.c test/data/a.txt
|
||||||
|
$(CC) $(CFLAGS) test/pack.c -o pack
|
||||||
|
$(RUN) ./pack Makefile src/ssi.h test/fuzz.c test/data/a.txt test/data/range.txt > $@
|
||||||
|
|
||||||
|
DIR ?= test/data/
|
||||||
|
OUT ?= fs_packed.c
|
||||||
|
mkfs:
|
||||||
|
$(CC) $(CFLAGS) test/pack.c -o pack
|
||||||
|
$(RUN) ./pack -s $(DIR) `find $(DIR) -type f` > $(OUT)
|
||||||
|
# find $(DIR) -type f | sed -e s,^$(DIR),,g -e s,^/,,g
|
||||||
|
|
||||||
# Check that all external (exported) symbols have "mg_" prefix
|
# Check that all external (exported) symbols have "mg_" prefix
|
||||||
mg_prefix: mongoose.c mongoose.h
|
mg_prefix: mongoose.c mongoose.h
|
||||||
$(CLANG) mongoose.c $(CFLAGS) -c -o /tmp/x.o && nm /tmp/x.o | grep ' T' | grep -v 'mg_' ; test $$? = 1
|
$(CC) mongoose.c $(CFLAGS) -c -o /tmp/x.o && nm /tmp/x.o | grep ' T' | grep -v 'mg_' ; test $$? = 1
|
||||||
|
|
||||||
# C++ build
|
# C++ build
|
||||||
test++: CLANG = g++ -Wno-deprecated -Wno-missing-field-initializers
|
test++: test
|
||||||
test++: unamalgamated
|
test++: CC = g++
|
||||||
|
test++: C_WARN = -std=c++2a -Wno-vla -Wno-shadow -Wno-missing-field-initializers -Wno-deprecated
|
||||||
|
|
||||||
|
musl: test
|
||||||
|
musl: ASAN =
|
||||||
|
musl: WARN += -Wno-sign-conversion
|
||||||
|
musl: CC = $(DOCKER) mdashnet/cc1 gcc
|
||||||
|
musl: RUN = $(DOCKER) mdashnet/cc1
|
||||||
|
|
||||||
# Make sure we can build from an unamalgamated sources
|
# Make sure we can build from an unamalgamated sources
|
||||||
unamalgamated: $(SRCS) $(HDRS) Makefile
|
unamalgamated: $(HDRS) Makefile test/packed_fs.c
|
||||||
$(CLANG) src/*.c test/unit_test.c $(CFLAGS) $(LDFLAGS) -g -o unit_test
|
$(CC) src/*.c test/packed_fs.c test/unit_test.c $(CFLAGS) $(LDFLAGS) -g -o unit_test
|
||||||
|
|
||||||
fuzz: mongoose.c mongoose.h Makefile test/fuzz.c
|
unpacked:
|
||||||
$(CLANG) mongoose.c test/fuzz.c $(CFLAGS) -DMG_ENABLE_LINES -DMG_ENABLE_LOG=0 -fsanitize=fuzzer,signed-integer-overflow,address $(LDFLAGS) -g -o fuzzer
|
$(CC) -I. mongoose.c test/unit_test.c -o $@
|
||||||
$(DEBUGGER) ./fuzzer
|
|
||||||
|
|
||||||
# make CLANG=/usr/local/opt/llvm\@8/bin/clang ASAN_OPTIONS=detect_leaks=1
|
fuzzer: mongoose.c mongoose.h Makefile test/fuzz.c
|
||||||
test: CFLAGS += -DMG_ENABLE_IPV6=$(IPV6) -fsanitize=address#,undefined
|
clang++ mongoose.c test/fuzz.c $(WARN) $(INCS) $(TFLAGS) -DMG_ENABLE_LINES -fsanitize=fuzzer,signed-integer-overflow,address -Wno-deprecated -Wno-vla-extension -o $@
|
||||||
test: mongoose.c mongoose.h Makefile test/unit_test.c
|
|
||||||
$(CLANG) mongoose.c test/unit_test.c $(CFLAGS) -coverage $(LDFLAGS) -g -o unit_test
|
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(DEBUGGER) ./unit_test
|
|
||||||
|
|
||||||
|
fuzz: fuzzer
|
||||||
|
$(RUN) ./fuzzer
|
||||||
|
|
||||||
|
unit_test: Makefile mongoose.h $(SRCS)
|
||||||
|
$(CC) $(SRCS) $(CFLAGS) $(LDFLAGS) -g -o unit_test
|
||||||
|
|
||||||
|
# make CC=/usr/local/opt/llvm\@8/bin/clang ASAN_OPTIONS=detect_leaks=1
|
||||||
|
test: unit_test
|
||||||
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./unit_test
|
||||||
|
|
||||||
|
coverage: CFLAGS += -coverage
|
||||||
coverage: test
|
coverage: test
|
||||||
gcov -l -n *.gcno | sed '/^$$/d' | sed 'N;s/\n/ /'
|
gcov -l -n *.gcno | sed '/^$$/d' | sed 'N;s/\n/ /'
|
||||||
gcov mongoose.gcno >/dev/null
|
gcov -t mongoose.c > mongoose.gcov
|
||||||
|
|
||||||
upload-coverage: coverage
|
upload-coverage: coverage
|
||||||
curl -s https://codecov.io/bash | /bin/bash
|
curl -s https://codecov.io/bash | /bin/bash
|
||||||
|
|
||||||
|
valgrind_unit_test: Makefile mongoose.h $(SRCS)
|
||||||
|
$(CC) $(SRCS) $(VALGRIND_CFLAGS) $(LDFLAGS) -g -o valgrind_unit_test
|
||||||
|
|
||||||
|
valgrind: valgrind_unit_test
|
||||||
|
$(VALGRIND_RUN) ./valgrind_unit_test
|
||||||
|
|
||||||
infer:
|
infer:
|
||||||
infer run -- cc test/unit_test.c -c -W -Wall -Werror -Isrc -I. -O2 -DMG_ENABLE_MBEDTLS=1 -DMG_ENABLE_LINES -I/usr/local/Cellar/mbedtls/2.23.0/include -DMG_ENABLE_IPV6=1 -g -o /dev/null
|
infer run -- cc test/unit_test.c -c -W -Wall -Werror -Isrc -I. -O2 -DMG_ENABLE_MBEDTLS=1 -DMG_ENABLE_LINES -I/usr/local/Cellar/mbedtls/2.23.0/include -DMG_ENABLE_IPV6=1 -g -o /dev/null
|
||||||
|
|
||||||
#vc98: VCFLAGS += -DMG_ENABLE_IPV6=1
|
arm: DEFS += -DMG_ENABLE_FILE=0 -DMG_ENABLE_MIP=1 -DMG_ARCH=MG_ARCH_NEWLIB
|
||||||
vc98: Makefile mongoose.c mongoose.h test/unit_test.c
|
arm: mongoose.h $(SRCS)
|
||||||
$(VC98) wine cl mongoose.c test/unit_test.c $(VCFLAGS) ws2_32.lib /Fe$@.exe
|
$(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test -nostartfiles --specs nosys.specs -e 0
|
||||||
$(VC98) wine $@.exe
|
|
||||||
|
|
||||||
#vc2017: VCFLAGS += -DMG_ENABLE_IPV6=1
|
riscv: DEFS += -DMG_ENABLE_FILE=0 -DMG_ENABLE_MIP=1 -DMG_ARCH=MG_ARCH_NEWLIB
|
||||||
vc2017: Makefile mongoose.c mongoose.h test/unit_test.c
|
riscv: mongoose.h $(SRCS)
|
||||||
$(VC2017) wine64 cl mongoose.c test/unit_test.c $(VCFLAGS) ws2_32.lib /Fe$@.exe
|
$(DOCKER) mdashnet/riscv riscv-none-elf-gcc -march=rv32imc -mabi=ilp32 $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test
|
||||||
$(VC2017) wine64 $@.exe
|
|
||||||
|
|
||||||
mingw: Makefile mongoose.c mongoose.h test/unit_test.c
|
vc98: Makefile mongoose.h $(SRCS)
|
||||||
$(MINGW) i686-w64-mingw32-gcc mongoose.c test/unit_test.c -W -Wall -Werror -I. $(DEFS) -lwsock32 -o test.exe
|
$(DOCKER) mdashnet/vc98 wine cl $(SRCS) $(VCFLAGS) ws2_32.lib /Fe$@.exe
|
||||||
$(VC98) wine test.exe
|
$(DOCKER) mdashnet/vc98 wine $@.exe
|
||||||
|
|
||||||
mingw++: Makefile mongoose.c mongoose.h test/unit_test.c
|
# vc17: DEFS += -DMG_ENABLE_IPV6=1
|
||||||
$(MINGW) i686-w64-mingw32-g++ mongoose.c test/unit_test.c -W -Wall -Werror -I. $(DEFS) -lwsock32 -o test.exe
|
vc17: Makefile mongoose.h $(SRCS)
|
||||||
# Note: for some reason, a binary built with mingw g++, fails to run
|
$(DOCKER) mdashnet/vc17 wine64 cl $(SRCS) $(VCFLAGS) ws2_32.lib /Fe$@.exe
|
||||||
|
$(DOCKER) mdashnet/vc17 wine64 $@.exe
|
||||||
|
|
||||||
#linux: CFLAGS += -DMG_ENABLE_IPV6=$(IPV6)
|
# vc22: DEFS += -DMG_ENABLE_IPV6=$(IPV6)
|
||||||
linux: CFLAGS += -fsanitize=address,undefined
|
vc22: Makefile mongoose.h $(SRCS)
|
||||||
linux: Makefile mongoose.c mongoose.h test/unit_test.c
|
$(DOCKER) mdashnet/vc22 wine64 cl $(SRCS) $(VCFLAGS) ws2_32.lib /Fe$@.exe
|
||||||
$(GCC) $(CC) mongoose.c test/unit_test.c $(CFLAGS) $(LDFLAGS) -o unit_test_gcc
|
$(DOCKER) mdashnet/vc22 wine64 $@.exe
|
||||||
$(GCC) ./unit_test_gcc
|
|
||||||
|
|
||||||
linux++: CC = g++ -Wno-missing-field-initializers
|
mingw: Makefile mongoose.h $(SRCS)
|
||||||
|
$(DOCKER) mdashnet/mingw x86_64-w64-mingw32-gcc $(SRCS) -W -Wall -Werror -I. $(DEFS) -lwsock32 -o $@.exe
|
||||||
|
$(DOCKER) mdashnet/mingw wine64 $@.exe
|
||||||
|
|
||||||
|
mingw++: Makefile mongoose.h $(SRCS)
|
||||||
|
$(DOCKER) mdashnet/mingw x86_64-w64-mingw32-g++ $(SRCS) -W -Wall -Werror -I. $(DEFS) -lwsock32 -o $@.exe
|
||||||
|
|
||||||
|
linux: IPV6=0
|
||||||
|
linux: Makefile mongoose.h $(SRCS)
|
||||||
|
$(DOCKER) mdashnet/cc2 gcc $(SRCS) $(CFLAGS) $(LDFLAGS) -o unit_test_gcc
|
||||||
|
$(DOCKER) mdashnet/cc2 ./unit_test_gcc
|
||||||
|
|
||||||
|
linux++: CC = g++
|
||||||
|
linux++: WARN += -Wno-missing-field-initializers
|
||||||
linux++: linux
|
linux++: linux
|
||||||
|
|
||||||
mongoose.c: $(SRCS) Makefile
|
linux-libs: CFLAGS += -fPIC
|
||||||
(cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/private.h src/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
|
linux-libs: mongoose.o
|
||||||
|
$(CC) mongoose.o $(LDFLAGS) -shared -o libmongoose.so.$(VERSION)
|
||||||
|
$(AR) rcs libmongoose.a mongoose.o
|
||||||
|
|
||||||
|
install: linux-libs
|
||||||
|
install -Dm644 libmongoose.a libmongoose.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib
|
||||||
|
ln -s libmongoose.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libmongoose.so
|
||||||
|
install -Dm644 mongoose.h $(DESTDIR)$(PREFIX)/include/mongoose.h
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -rf $(DESTDIR)$(PREFIX)/lib/libmongoose.a $(DESTDIR)$(PREFIX)/lib/libmongoose.so.$(VERSION) $(DESTDIR)$(PREFIX)/include/mongoose.h $(DESTDIR)$(PREFIX)/lib/libmongoose.so
|
||||||
|
|
||||||
|
mongoose.c: Makefile $(wildcard src/*) $(wildcard mip/*.c)
|
||||||
|
(cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c mip/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
|
||||||
|
|
||||||
mongoose.h: $(HDRS) Makefile
|
mongoose.h: $(HDRS) Makefile
|
||||||
(cat src/license.h src/version.h ; cat src/arch.h src/arch_*.h src/config.h src/str.h src/log.h src/timer.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/ws.h src/sntp.h src/mqtt.h src/dns.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,')> $@
|
(cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/config.h src/str.h src/fmt.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h mip/mip.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
|
||||||
|
|
||||||
clean: EXAMPLE_TARGET = clean
|
clean:
|
||||||
clean: ex
|
rm -rf $(PROG) *.exe *.o *.dSYM unit_test* valgrind_unit_test* ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* test/packed_fs.c pack unpacked
|
||||||
rm -rf $(PROG) *.o *.dSYM unit_test* ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-*
|
@for X in $(EXAMPLES); do $(MAKE) -C $$X clean; done
|
||||||
@@ -1,75 +1,73 @@
|
|||||||
# Mongoose - Embedded Web Server / Embedded Networking Library
|
# Mongoose - Embedded Web Server / Embedded Networking Library
|
||||||
|
|
||||||

|
[](https://opensource.org/licenses/gpl-2.0.php)
|
||||||
[](https://github.com/cesanta/mongoose/actions)
|
[](https://github.com/cesanta/mongoose/actions)
|
||||||
[](https://codecov.io/gh/cesanta/mongoose)
|
[](https://codecov.io/gh/cesanta/mongoose)
|
||||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:mongoose)
|
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:mongoose)
|
||||||
|
|
||||||
Mongoose is a networking library for C/C++. It implements event-driven
|
Mongoose is a networking library for C/C++. It implements event-driven
|
||||||
non-blocking APIs for TCP, UDP, HTTP, WebSocket, MQTT. It has been designed
|
non-blocking APIs for TCP, UDP, HTTP, WebSocket, MQTT. It is designed for
|
||||||
for connecting devices and bringing them online. On the market since 2004, used
|
connecting devices and bringing them online. On the market since 2004, used by
|
||||||
by vast number of open source and commercial products - it even runs on the
|
vast number of open source and commercial products - it even runs on the
|
||||||
International Space Station! Mongoose makes embedded network programming fast,
|
International Space Station! Mongoose makes embedded network programming fast,
|
||||||
robust, and easy.
|
robust, and easy. Features include:
|
||||||
|
|
||||||
- [Download Mongoose Source Code here](https://www.cesanta.com/download.html)
|
- Cross-platform: works on Linux/UNIX, MacOS, Windows, Android, FreeRTOS, etc.
|
||||||
- [Read Mongoose documentation](https://cesanta.com/docs/)
|
- Supported embedded architectures: ESP32, NRF52, STM32, NXP, and more
|
||||||
|
- Built-in protocols: plain TCP/UDP, HTTP, MQTT, Websocket
|
||||||
|
- SSL/TLS support: mbedTLS, OpenSSL or custom (via API)
|
||||||
|
- Asynchronous DNS resolver
|
||||||
|
- Tiny static and run-time footprint
|
||||||
|
- Source code is both ISO C and ISO C++ compliant
|
||||||
|
- Works with any network stack with socket API, like LwIP or FreeRTOS-Plus-TCP
|
||||||
|
- Very easy to integrate: just copy `mongoose.c` and `mongoose.h` files to your build tree
|
||||||
|
- Detailed [documentation](https://mongoose.ws/documentation/) and
|
||||||
|
[tutorials](https://mongoose.ws/tutorials/)
|
||||||
|
|
||||||
Connectivity is vital for software and embedded devices, but there are many
|
|
||||||
pitfalls to consider when embedding a web server. This white paper breaks down
|
|
||||||
on the top 9 things to avoid when embedding a web server.
|
|
||||||
|
|
||||||
- [Download “9 Things NOT to do when embedding a web server” white paper here](https://www.cesanta.com/whitepaper.html)
|
# Commercial use
|
||||||
|
- Mongoose is used by hundreds of businesses, from Fortune500 giants like
|
||||||
|
Siemens, Schneider Electric, Broadcom, Bosch, Google, Samsung, Qualcomm, Caterpillar to the small businesses
|
||||||
|
- Used to solve a wide range of business needs, like implementing Web UI
|
||||||
|
interface on devices, RESTful API services, telemetry data exchange, remote
|
||||||
|
control for a product, remote software updates, remote monitoring, and others
|
||||||
|
- Deployed to hundreds of millions devices in production environment worldwide
|
||||||
|
- See [Case Studies](https://mongoose.ws/case-studies/) from our respected
|
||||||
|
customers like [Schneider Electric](https://mongoose.ws/case-studies/schneider-electric/) (industrial automation), [Broadcom](https://mongoose.ws/case-studies/broadcom/) (semiconductors), [Pilz](https://mongoose.ws/case-studies/pilz/) (industrial automation), and others
|
||||||
|
- See [Testimonials](https://mongoose.ws/testimonials/) from engineers that integrated Mongoose in their commercial products
|
||||||
|
- We provide [Evaluation and Commercial licensing](https://mongoose.ws/licensing/), [support](https://mongoose.ws/support/), consultancy and integration
|
||||||
|
assistance - don't hesitate to [contact us](https://mongoose.ws/contact/)
|
||||||
|
|
||||||
# Looking for a pre-compiled Mongoose web server Windows or Mac binary?
|
|
||||||
- [Download pre-compiled Mongoose web server binary](https://mongoose.ws/)
|
|
||||||
|
|
||||||
# Looking for a complete IoT solution? Check out
|
# Security
|
||||||
- [VCON](https://vcon.io) - Arduino compatible MCU modules with built-in firmware OTA updates
|
|
||||||
- [Mongoose OS](https://mongoose-os.com) - open source embedded operating system for low-power connected microcontrollers. Secure, designed for commercial Internet of Things products
|
|
||||||
|
|
||||||
# Support
|
We take security seriously:
|
||||||
- [Study Mongoose examples](https://github.com/cesanta/mongoose/tree/master/examples)
|
1. Mongoose repository runs a
|
||||||
- [Support Forum - ask your technical questions here](https://forum.cesanta.com/c/mongoose-library/6)
|
[continuous integration test powered by GitHub](https://github.com/cesanta/mongoose/actions),
|
||||||
- [Commercial licensing and support available](https://www.cesanta.com/licensing.html)
|
which runs through hundreds of unit tests on every commit to the repository.
|
||||||
- [Check out latest releases](https://github.com/cesanta/mongoose/releases)
|
Our [unit tests](https://github.com/cesanta/mongoose/tree/master/test)
|
||||||
|
are built with modern address sanitizer technologies, which help to find
|
||||||
|
security vulnerabilities early
|
||||||
|
2. Mongoose repository is integrated into Google's
|
||||||
|
[oss-fuzz continuous fuzzer](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:mongoose)
|
||||||
|
which scans for potential vulnerabilities continuously
|
||||||
|
3. We receive periodic vulnerability reports from the independent security
|
||||||
|
groups like
|
||||||
|
[Cisco Talos](https://www.cisco.com/c/en/us/products/security/talos.html),
|
||||||
|
[Microsoft Security Response Center](https://www.microsoft.com/en-us/msrc),
|
||||||
|
[MITRE Corporation](https://www.mitre.org/),
|
||||||
|
[Compass Security](https://www.compass-security.com/en/) and others.
|
||||||
|
In case of the vulnerability found, we act according to the industry best
|
||||||
|
practice: hold on to the publication, fix the software and notify all our
|
||||||
|
customers that have an appropriate subscription
|
||||||
|
4. Some of our customers (for example NASA)
|
||||||
|
have specific security requirements and run independent security audits,
|
||||||
|
of which we get notified and in case of any issue, act similar to (3).
|
||||||
|
|
||||||
# Features
|
|
||||||
|
|
||||||
* Cross-platform: works on Linux/UNIX, MacOS, Windows, Android, FreeRTOS, etc
|
|
||||||
* Supported embedded architectures: ESP32, ESP8266, TI, NRF52, STM32, PIC32, NXP, and more
|
|
||||||
* Builtin protocols: plain TCP/UDP, HTTP, MQTT, Websocket
|
|
||||||
* SSL/TLS support: mbedTLS, OpenSSL or custom (via API)
|
|
||||||
* Asynchronous DNS resolver
|
|
||||||
* Tiny static and run-time footprint
|
|
||||||
* Source code is both ISO C and ISO C++ compliant
|
|
||||||
* Very easy to integrate: just copy
|
|
||||||
[mongoose.c](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.c) and
|
|
||||||
[mongoose.h](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.h)
|
|
||||||
files to your build tree
|
|
||||||
|
|
||||||
# Licensing
|
|
||||||
|
|
||||||
Mongoose is released under Commercial and [GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) open source licenses.
|
|
||||||
|
|
||||||
Commercial Projects: [Contact us for commercial license.](https://www.cesanta.com/contact.html)
|
|
||||||
|
|
||||||
# Dashboard Example
|
|
||||||
|
|
||||||
Mongoose is often used to implement device dashboards and real-time
|
|
||||||
data exchange over Websocket. Here is a dashboard example that illustrates
|
|
||||||
the functionality:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
[Developing a new product? Contact us today to discuss how Mongoose can help.](https://www.cesanta.com/contact.html)
|
|
||||||
|
|
||||||
# Contributions
|
# Contributions
|
||||||
|
|
||||||
Contributions are welcome! Please follow the guidelines below:
|
Contributions are welcome! Please follow the guidelines below:
|
||||||
|
|
||||||
- Sign [Cesanta CLA](https://cesanta.com/cla.html) and send GitHub pull request
|
- Sign [Cesanta CLA](https://cesanta.com/cla.html) and send GitHub pull request
|
||||||
- When making pull requests, please make sure that it has only one commit,
|
- Make sure that PRs have only one commit, and deal with one issue only
|
||||||
and imlements/fixes only one piece of functionality
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 1034 239" style="enable-background:new 0 0 1034 239;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#576174;}
|
||||||
|
.st1{fill:#FFFFFF;}
|
||||||
|
.st2{font-family:'Courier-Bold';}
|
||||||
|
.st3{font-size:16.7604px;}
|
||||||
|
.st4{fill:#00983A;}
|
||||||
|
.st5{fill:none;stroke:#45CFFF;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st6{fill:#45CFFF;}
|
||||||
|
.st7{fill:none;stroke:#A000FF;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st8{fill:#A000FF;}
|
||||||
|
.st9{fill:none;stroke:#FFDE00;stroke-width:2.793;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st10{fill:#FFDE00;}
|
||||||
|
.st11{fill:none;stroke:#EA5B0C;stroke-width:2.793;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st12{fill:#EA5B0C;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M485.3,224.22H8.5c-4.7,0-8.5-3.8-8.5-8.5V27.22c0-4.7,3.8-8.5,8.5-8.5h476.8c4.7,0,8.5,3.8,8.5,8.5v188.5
|
||||||
|
C493.8,220.32,490,224.22,485.3,224.22z"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 21.294 60.1267)" class="st1 st2 st3">struct mg_str k,v,s = mg_str(”a=333, b=777”);</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 21.294 136.2499)" class="st1 st2 st3">mg_commalist(&s, &k, &v); </text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 21.294 197.1485)" class="st1 st2 st3">mg_commalist(&s, &k, &v); </text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 291.5096 140.18)" class="st4 st2 st3">// 1st call</text>
|
||||||
|
<text transform="matrix(1 0 0 1 291.5096 199.8101)" class="st4 st2 st3">// 2nd call</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st5" points="336.4,64.52 336.4,68.52 317.8,68.52 317.8,64.52 "/>
|
||||||
|
<g>
|
||||||
|
<path class="st5" d="M122.3,59.62"/>
|
||||||
|
<g>
|
||||||
|
<polyline class="st5" points="327.5,76.82 327.6,94.02 203.3,94.02 203.3,116.12 "/>
|
||||||
|
<path class="st6" d="M331,77.62c0,1.8-1.4,3.2-3.2,3.3c-1.8,0.1-3.2-1.4-3.3-3.2c0-1.8,1.4-3.2,3.2-3.3l0,0
|
||||||
|
C329.5,74.42,331,75.92,331,77.62z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st6" d="M203.2,114.92c-1.8,0-3.2,1.3-3.3,3s1.4,3,3.2,3s3.2-1.3,3.3-3l0,0C206.5,116.32,205,114.92,203.2,114.92z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st7" points="407.4,64.52 407.4,68.52 388.8,68.52 388.8,64.52 "/>
|
||||||
|
<g>
|
||||||
|
<path class="st5" d="M193.4,59.62"/>
|
||||||
|
<g>
|
||||||
|
<polyline class="st7" points="398.6,76.82 398.6,149.02 203.4,149.02 203.4,177.12 "/>
|
||||||
|
<path class="st8" d="M402,77.62c0,1.8-1.4,3.2-3.2,3.3c-1.8,0.1-3.2-1.4-3.3-3.2c0-1.8,1.4-3.2,3.2-3.3l0,0
|
||||||
|
C400.6,74.42,402,75.92,402,77.62z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st8" d="M203.2,175.42c-1.8,0-3.2,1.3-3.3,3s1.4,3,3.2,3s3.2-1.3,3.3-3l0,0C206.5,176.82,205,175.42,203.2,175.42z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st9" points="440.8,64.52 440.8,68.52 414.2,68.52 414.2,64.52 "/>
|
||||||
|
<g>
|
||||||
|
<path class="st5" d="M218.8,59.62"/>
|
||||||
|
<g>
|
||||||
|
<polyline class="st9" points="428,76.82 428,163.02 245.7,163.02 245.7,176.12 "/>
|
||||||
|
<path class="st10" d="M431.4,77.62c0,1.8-1.4,3.2-3.2,3.3c-1.8,0.1-3.2-1.4-3.3-3.2c0-1.8,1.4-3.2,3.2-3.3l0,0
|
||||||
|
C430,74.42,431.4,75.92,431.4,77.62z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st10" d="M245.4,175.42c-1.8,0-3.2,1.3-3.3,3s1.4,3,3.2,3s3.2-1.3,3.3-3l0,0C248.7,176.82,247.2,175.42,245.4,175.42z
|
||||||
|
"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st11" points="372.8,64.52 372.8,68.52 343.2,68.52 343.2,64.52 "/>
|
||||||
|
<g>
|
||||||
|
<path class="st5" d="M164.7,59.62"/>
|
||||||
|
<g>
|
||||||
|
<polyline class="st11" points="358.3,76.62 358.3,104.82 245.7,105.02 245.7,116.12 "/>
|
||||||
|
<path class="st12" d="M361.4,77.62c0,1.8-1.4,3.2-3.2,3.3c-1.8,0.1-3.2-1.4-3.3-3.2c0-1.8,1.4-3.2,3.2-3.3l0,0
|
||||||
|
C359.9,74.42,361.4,75.92,361.4,77.62z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st12" d="M245.7,114.92c-1.8,0-3.2,1.3-3.3,3s1.4,3,3.2,3s3.2-1.3,3.3-3l0,0C248.9,116.32,247.4,114.92,245.7,114.92z
|
||||||
|
"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 1034 158.07" style="enable-background:new 0 0 1034 158.07;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#67748A;}
|
||||||
|
.st1{fill:#FFFFFF;}
|
||||||
|
.st2{font-family:'Courier-Bold';}
|
||||||
|
.st3{font-size:16.7604px;}
|
||||||
|
.st4{fill:none;stroke:#45CFFF;stroke-width:2.793;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st5{fill:#45CFFF;}
|
||||||
|
.st6{font-family:'Arial-BoldMT';}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M516.7,39.8v56.33c0,4.17-3.8,7.54-8.5,7.54H14.6H8.5c-4.7,0-8.5-3.37-8.5-7.54V39.8c0-4.17,3.8-7.54,8.5-7.54
|
||||||
|
h499.7C512.9,32.26,516.7,35.63,516.7,39.8z"/>
|
||||||
|
<text transform="matrix(1 0 0 1 51.0379 69.3318)" class="st1 st2 st3">GET /test \n\nGET /test \n\nGET test \n\n</text>
|
||||||
|
<g>
|
||||||
|
<polyline class="st4" points="189.5,76.47 189.5,80.47 51.8,80.47 51.8,76.47 "/>
|
||||||
|
<polyline class="st4" points="331.3,76.47 331.3,80.47 195.6,80.47 195.6,76.47 "/>
|
||||||
|
<polyline class="st4" points="462.7,76.47 462.7,80.47 336.9,80.47 336.9,76.47 "/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 78.8909 148.8896)" class="st5 st6 st3">1st request</text>
|
||||||
|
<g>
|
||||||
|
<line class="st4" x1="120.2" y1="93.57" x2="120.3" y2="129.57"/>
|
||||||
|
<path class="st5" d="M117.1,92.57c0-1.8,1.4-3.2,3.2-3.3c1.8,0,3.2,1.4,3.3,3.2c0,1.8-1.4,3.2-3.2,3.3l0,0
|
||||||
|
C118.5,95.77,117.1,94.37,117.1,92.57z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 217.3594 148.8891)" class="st5 st6 st3">2nd request </text>
|
||||||
|
<g>
|
||||||
|
<line class="st4" x1="263.8" y1="93.57" x2="263.9" y2="129.57"/>
|
||||||
|
<path class="st5" d="M260.7,92.57c0-1.8,1.4-3.2,3.2-3.3c1.8,0,3.2,1.4,3.3,3.2c0,1.8-1.4,3.2-3.2,3.3l0,0
|
||||||
|
C262.1,95.77,260.7,94.37,260.7,92.57z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 354.7297 148.8896)" class="st5 st6 st3">3rd request </text>
|
||||||
|
<g>
|
||||||
|
<line class="st4" x1="399.4" y1="93.57" x2="399.4" y2="129.57"/>
|
||||||
|
<path class="st5" d="M396.2,92.57c0-1.8,1.4-3.2,3.2-3.3c1.8,0,3.2,1.4,3.3,3.2c0,1.8-1.4,3.2-3.2,3.3l0,0
|
||||||
|
C397.7,95.77,396.2,94.37,396.2,92.57z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,113 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 1035.4 429.9" style="enable-background:new 0 0 1035.4 429.9;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#576174;}
|
||||||
|
.st1{fill:#67748A;}
|
||||||
|
.st2{fill:none;stroke:#87005B;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st3{fill:#87005B;}
|
||||||
|
.st4{fill:#FFFFFF;}
|
||||||
|
.st5{font-family:'Courier-Bold';}
|
||||||
|
.st6{font-size:16.7604px;}
|
||||||
|
.st7{fill:none;stroke:#EA5B0C;stroke-width:2.793;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st8{fill:#EA5B0C;}
|
||||||
|
.st9{fill:none;stroke:#CA70FF;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st10{fill:#CA70FF;}
|
||||||
|
.st11{fill:none;stroke:#FFDE00;stroke-width:2.793;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st12{fill:#FFDE00;}
|
||||||
|
.st13{fill:none;stroke:#00CF4F;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st14{fill:#00CF4F;}
|
||||||
|
.st15{fill:none;stroke:#45CFFF;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st16{fill:none;stroke:#EA5B0C;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st17{font-family:'Arial-BoldMT';}
|
||||||
|
.st18{font-size:24px;}
|
||||||
|
.st19{fill:#45CFFF;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M755.8,391.8H8.5c-4.7,0-8.5-3.8-8.5-8.5v-153c0-4.7,3.8-8.5,8.5-8.5h747.3c4.7,0,8.5,3.8,8.5,8.5v153
|
||||||
|
C764.3,388,760.5,391.8,755.8,391.8z"/>
|
||||||
|
<path class="st1" d="M996.9,261.5h-427c-4.7,0-8.5-3.8-8.5-8.5V81.2c0-4.7,3.8-8.5,8.5-8.5h427c4.7,0,8.5,3.8,8.5,8.5V253
|
||||||
|
C1005.4,257.7,1001.5,261.5,996.9,261.5z"/>
|
||||||
|
<g>
|
||||||
|
<polyline class="st2" points="598.1,238.2 598.1,242.3 787.7,242.3 787.7,238.2 "/>
|
||||||
|
<g>
|
||||||
|
<line class="st2" x1="689.3" y1="342.2" x2="276.4" y2="343.3"/>
|
||||||
|
<g>
|
||||||
|
<line class="st2" x1="689.4" y1="253.3" x2="689.4" y2="342.1"/>
|
||||||
|
<path class="st3" d="M689.4,249.3c1.8,0,3.2,1.4,3.3,3.2c0,1.8-1.4,3.2-3.2,3.3c-1.8,0-3.2-1.4-3.3-3.2l0,0
|
||||||
|
C686.1,250.7,687.6,249.3,689.4,249.3z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st3" d="M274.2,340.4c1.8,0,3.2,1.4,3.3,3.2s-1.4,3.2-3.2,3.3c-1.8,0-3.2-1.4-3.3-3.2l0,0
|
||||||
|
C270.9,341.8,272.4,340.4,274.2,340.4z"/>
|
||||||
|
</g>
|
||||||
|
<text transform="matrix(1 0 0 1 26.0923 262.5192)" class="st4 st5 st6">struct mg_http_message {</text>
|
||||||
|
<text transform="matrix(1 0 0 1 45.542 286.2874)" class="st4 st5 st6">struct mg_str method; uri, query, proto; </text>
|
||||||
|
<text transform="matrix(1 0 0 1 45.542 306.3874)" class="st4 st5 st6">struct mg_http_header headers[MG_MAX_HTTP_HEADERS];</text>
|
||||||
|
<text transform="matrix(1 0 0 1 45.542 326.4874)" class="st4 st5 st6">struct mg_str body;</text>
|
||||||
|
<text transform="matrix(1 0 0 1 45.542 346.5874)" class="st4 st5 st6">struct mg_str message; </text>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st7" points="621.1,89.6 621.1,78.6 215.4,79.7 215.3,269.4 "/>
|
||||||
|
<path class="st8" d="M624.3,90.6c0,1.8-1.4,3.2-3.2,3.3c-1.8,0-3.2-1.4-3.3-3.2c0-1.8,1.4-3.2,3.2-3.3l0,0
|
||||||
|
C622.8,87.4,624.3,88.8,624.3,90.6z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st8" d="M212.3,269.4c0-1.8,1.4-3.2,3.2-3.3c1.8,0,3.2,1.4,3.3,3.2c0,1.8-1.4,3.2-3.2,3.3l0,0
|
||||||
|
C213.7,272.6,212.3,271.2,212.3,269.4z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st9" points="597.2,183.6 593.1,183.6 593.1,127.8 597.2,127.8 "/>
|
||||||
|
<g>
|
||||||
|
<polyline class="st9" points="582.4,156.2 575.2,156.2 575,302.8 563.4,302.8 "/>
|
||||||
|
<path class="st10" d="M586.1,156.2c0-1.8-1.4-3.2-3.2-3.3c-1.8,0-3.2,1.4-3.3,3.2c0,1.8,1.4,3.2,3.2,3.3l0,0
|
||||||
|
C584.7,159.4,586.1,158,586.1,156.2z"/>
|
||||||
|
<path class="st10" d="M564.2,303c0-1.8-1.4-3.2-3.2-3.3c-1.8,0-3.2,1.4-3.3,3.2s1.4,3.2,3.2,3.3l0,0
|
||||||
|
C562.7,306.2,564.2,304.7,564.2,303z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text transform="matrix(1 0 0 1 601.1215 118.1291)" class="st4 st5 st6">POST /foo/bar/baz?aa=b&cc=ddd HTTP/1.1</text>
|
||||||
|
<text transform="matrix(1 0 0 1 601.1215 138.2291)" class="st4 st5 st6">Content-Type: application/json</text>
|
||||||
|
<text transform="matrix(1 0 0 1 601.1215 158.3291)" class="st4 st5 st6">Content-Length: 19</text>
|
||||||
|
<text transform="matrix(1 0 0 1 601.1215 178.4291)" class="st4 st5 st6">Host: reqbin.com</text>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st11" points="712.3,90.6 712.4,67.6 277.8,67.6 279.7,269.3 "/>
|
||||||
|
<path class="st12" d="M715.6,90.8c0,1.8-1.4,3.2-3.2,3.3c-1.8,0-3.2-1.4-3.3-3.2c0-1.8,1.4-3.2,3.2-3.3l0,0
|
||||||
|
C714.1,87.6,715.6,89.1,715.6,90.8z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st12" d="M276.5,269.4c0-1.8,1.4-3.2,3.2-3.3s3.2,1.4,3.3,3.2s-1.4,3.2-3.2,3.3l0,0
|
||||||
|
C278,272.6,276.5,271.2,276.5,269.4z"/>
|
||||||
|
</g>
|
||||||
|
<polyline class="st11" points="771.5,104.3 771.5,100.3 652.7,100.3 652.7,104.3 "/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st13" points="836.3,90.6 836.3,56.6 342.8,56.6 344.7,268.3 "/>
|
||||||
|
<path class="st14" d="M839.5,90.6c0,1.8-1.4,3.2-3.2,3.3c-1.8,0-3.2-1.4-3.3-3.2c0-1.8,1.4-3.2,3.2-3.3l0,0
|
||||||
|
C838,87.4,839.5,88.9,839.5,90.6z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st14" d="M341.4,269.3c0-1.8,1.4-3.2,3.2-3.3c1.8,0,3.2,1.4,3.3,3.2c0,1.8-1.4,3.2-3.2,3.3l0,0
|
||||||
|
C342.9,272.5,341.4,271.1,341.4,269.3z"/>
|
||||||
|
</g>
|
||||||
|
<polyline class="st15" points="981.4,104.2 981.4,100.2 902.6,100.2 902.6,104.2 "/>
|
||||||
|
</g>
|
||||||
|
<text transform="matrix(1 0 0 1 596.891 232.3658)" class="st4 st5 st6">{"success": "true"}</text>
|
||||||
|
<polyline class="st16" points="643.1,104.4 643.1,100.4 599.3,100.4 599.3,104.4 "/>
|
||||||
|
<text transform="matrix(1 0 0 1 9.5506 207.5157)" class="st17 st18">Source Code</text>
|
||||||
|
<text transform="matrix(1 0 0 1 797.0799 32.3923)" class="st17 st18">HTTP message</text>
|
||||||
|
<text transform="matrix(1 0 0 1 26.0924 359.209)" class="st4 st5 st6">};</text>
|
||||||
|
<polyline class="st13" points="894.2,104.2 894.2,100.2 774.4,100.2 774.4,104.2 "/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st15" points="941.3,90.6 941.3,45.6 412.8,45.6 414.7,268.3 "/>
|
||||||
|
<path class="st19" d="M944.5,90.6c0,1.8-1.4,3.2-3.2,3.3c-1.8,0-3.2-1.4-3.3-3.2c0-1.8,1.4-3.2,3.2-3.3l0,0
|
||||||
|
C943,87.4,944.5,88.9,944.5,90.6z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st19" d="M411.4,269.3c0-1.8,1.4-3.2,3.2-3.3c1.8,0,3.2,1.4,3.3,3.2c0,1.8-1.4,3.2-3.2,3.3l0,0
|
||||||
|
C412.9,272.5,411.4,271.1,411.4,269.3z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.0 KiB |
@@ -0,0 +1,109 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 1034 663.54" style="enable-background:new 0 0 1034 663.54;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:none;stroke:#45CFFF;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st1{fill:#45CFFF;}
|
||||||
|
.st2{fill:#576174;}
|
||||||
|
.st3{fill:#FFFFFF;}
|
||||||
|
.st4{font-family:'Courier-Bold';}
|
||||||
|
.st5{font-size:16.7604px;}
|
||||||
|
.st6{font-family:'Arial-BoldMT';}
|
||||||
|
.st7{font-size:24px;}
|
||||||
|
.st8{fill:#67748A;}
|
||||||
|
.st9{fill:#8B9CBA;}
|
||||||
|
.st10{fill:none;stroke:#00CF4F;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st11{fill:#00CF4F;}
|
||||||
|
.st12{fill:none;stroke:#EA5B0C;stroke-width:2.793;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st13{fill:#EA5B0C;}
|
||||||
|
.st14{fill:#414857;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st0" points="498.5,624.21 498.5,628.21 447.9,628.21 447.9,624.21 "/>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M491.8,844.81"/>
|
||||||
|
<g>
|
||||||
|
<path class="st1" d="M473.8,636.11c-1.8,0-3.2,1.4-3.3,3.2c0,1.8,1.4,3.2,3.2,3.3c1.8,0,3.2-1.4,3.3-3.2l0,0
|
||||||
|
C477.1,637.61,475.6,636.11,473.8,636.11z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st1" d="M296.7,852.71c-1.8,0-3.2,1.3-3.3,3c0,1.7,1.4,3,3.2,3c1.8,0,3.2-1.3,3.3-3l0,0
|
||||||
|
C299.9,854.01,298.5,852.71,296.7,852.71z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st2" d="M584.6,199.01H8.5c-4.7,0-8.5-3.8-8.5-8.5V60.71c0-4.7,3.8-8.5,8.5-8.5h576.1c4.7,0,8.5,3.8,8.5,8.5v129.8
|
||||||
|
C593.1,195.21,589.3,199.01,584.6,199.01z"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 90.8107)" class="st3 st4 st5">struct mg_http_part part;</text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 110.9108)" class="st3 st4 st5">size_t pos = 0;</text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 151.1107)" class="st3 st4 st5">pos = mg_http_next_multipart(&hm->body, pos, &part);</text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 171.3107)" class="st3 st4 st5">pos = mg_http_next_multipart(&hm->body, pos, &part);</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text transform="matrix(1 0 0 1 1.3738 40.559)" class="st6 st7">Source Code</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st8" d="M685.5,646.41H8.5c-4.7,0-8.5-3.8-8.5-8.5v-362.9c0-4.7,3.8-8.5,8.5-8.5h677c4.7,0,8.5,3.8,8.5,8.5v362.9
|
||||||
|
C694,642.61,690.2,646.41,685.5,646.41z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st9" d="M640.2,629.41H26.3c-4.7,0-8.5-3.8-8.5-8.5v-106.5c0-4.7,3.8-8.5,8.5-8.5h613.9c4.7,0,8.5,3.8,8.5,8.5v106.5
|
||||||
|
C648.7,625.61,644.8,629.41,640.2,629.41z"/>
|
||||||
|
<path class="st9" d="M644,490.91H30.1c-4.7,0-8.5-3.8-8.5-8.5v-106.5c0-4.7,3.8-8.5,8.5-8.5H644c4.7,0,8.5,3.8,8.5,8.5v106.5
|
||||||
|
C652.5,487.11,648.7,490.91,644,490.91z"/>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 0.0299 253.0343)" class="st6 st7">HTTP message</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 302.8107)" class="st3 st4 st5">POST /upload HTTP/1.1 </text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 322.9107)" class="st3 st4 st5">Content-Type: multipart/form-data; boundary="--xyz"</text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 363.1107)" class="st3 st4 st5">--xyz</text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 383.3107)" class="st3 st4 st5">Content-Disposition: form-data; name="foo"; filename="a.txt"</text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 403.4107)" class="st3 st4 st5">Content-Type: text/plain</text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 443.6107)" class="st3 st4 st5">hello world</text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 483.8107)" class="st3 st4 st5">--xyz---</text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 524.0107)" class="st3 st4 st5">Content-Disposition: form-data; name="bar"; filename="b.txt"</text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 544.2108)" class="st3 st4 st5">Content-Type: text/plain</text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 584.4108)" class="st3 st4 st5">hello world again</text>
|
||||||
|
<text transform="matrix(1 0 0 1 28.7 624.6107)" class="st3 st4 st5">--xyz--</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st10" points="569.5,162.51 574.5,162.51 574.5,176.11 569.5,176.11 "/>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M726.8,392.91"/>
|
||||||
|
<g>
|
||||||
|
<polyline class="st10" points="587.3,169.71 726.7,169.71 726.6,569.31 684.3,569.31 "/>
|
||||||
|
<path class="st11" d="M585,166.41c-1.8,0-3.2,1.4-3.3,3.2c0,1.8,1.4,3.2,3.2,3.3c1.8,0,3.2-1.4,3.3-3.2l0,0
|
||||||
|
C588.2,167.91,586.8,166.41,585,166.41z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st11" d="M685,566.31c-1.7,0-3,1.3-3,3s1.3,3,3,3s3-1.3,3-3l0,0C688,567.61,686.6,566.31,685,566.31z"/>
|
||||||
|
</g>
|
||||||
|
<polyline class="st10" points="657.9,501.61 673.9,501.61 673.9,634.21 657.9,634.21 "/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st12" points="569.5,143.21 574.5,143.21 574.5,156.81 569.5,156.81 "/>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M726.8,254.61"/>
|
||||||
|
<g>
|
||||||
|
<polyline class="st12" points="587.3,150.41 710.7,150.41 710.6,431.01 684.3,431.01 "/>
|
||||||
|
<path class="st13" d="M585,147.11c-1.8,0-3.2,1.4-3.3,3.2s1.4,3.2,3.2,3.3c1.8,0,3.2-1.4,3.3-3.2l0,0
|
||||||
|
C588.2,148.61,586.8,147.11,585,147.11z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st13" d="M685,428.01c-1.7,0-3,1.3-3,3s1.3,3,3,3s3-1.3,3-3l0,0C688,429.31,686.6,428.01,685,428.01z"/>
|
||||||
|
</g>
|
||||||
|
<polyline class="st12" points="657.9,363.31 673.9,363.31 673.9,495.91 657.9,495.91 "/>
|
||||||
|
</g>
|
||||||
|
<text transform="matrix(1 0 0 1 537.9671 462.1467)" class="st14 st6 st7">Part 1</text>
|
||||||
|
<text transform="matrix(1 0 0 1 537.9671 600.4373)" class="st14 st6 st7">Part 2</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,94 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 1034 480.6" style="enable-background:new 0 0 1034 480.6;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#67748A;}
|
||||||
|
.st1{fill:#8B9CBA;}
|
||||||
|
.st2{fill:#FFFFFF;}
|
||||||
|
.st3{font-family:'Courier-Bold';}
|
||||||
|
.st4{font-size:16.7604px;}
|
||||||
|
.st5{fill:#414857;}
|
||||||
|
.st6{font-family:'Arial-BoldMT';}
|
||||||
|
.st7{font-size:30px;}
|
||||||
|
.st8{font-size:24px;}
|
||||||
|
.st9{fill:#576174;}
|
||||||
|
.st10{fill:none;stroke:#45CFFF;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st11{fill:#45CFFF;}
|
||||||
|
.st12{fill:none;stroke:#EA5B0C;stroke-width:2.793;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st13{fill:#EA5B0C;}
|
||||||
|
.st14{fill:none;stroke:#EA5B0C;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st15{fill:none;stroke:#FFDE00;stroke-width:2.793;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st16{fill:#FFDE00;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M908.4,454.9H272c-4.7,0-8.5-3.8-8.5-8.5V216c0-4.7,3.8-8.5,8.5-8.5h636.4c4.7,0,8.5,3.8,8.5,8.5v230.4
|
||||||
|
C916.9,451.1,913.1,454.9,908.4,454.9z"/>
|
||||||
|
<g>
|
||||||
|
<path class="st1" d="M898.6,430.8H284.7c-4.7,0-8.5-3.8-8.5-8.5V315.9c0-4.7,3.8-8.5,8.5-8.5h613.9c4.7,0,8.5,3.8,8.5,8.5v106.5
|
||||||
|
C907.1,427,903.3,430.8,898.6,430.8z"/>
|
||||||
|
<text transform="matrix(1 0 0 1 295.3688 243.086)" class="st2 st3 st4">POST /upload HTTP/1.1 </text>
|
||||||
|
<text transform="matrix(1 0 0 1 295.3688 263.186)" class="st2 st3 st4">Content-Type: multipart/form-data: boundary="--xyz"</text>
|
||||||
|
<text transform="matrix(1 0 0 1 295.3688 303.386)" class="st2 st3 st4">--xyz</text>
|
||||||
|
<text transform="matrix(1 0 0 1 295.3688 323.586)" class="st2 st3 st4">Content-Disposition: form-data; name="foo"; filename="a.txt"</text>
|
||||||
|
<text transform="matrix(1 0 0 1 295.3688 343.686)" class="st2 st3 st4">Content-Type: text/plain</text>
|
||||||
|
<text transform="matrix(1 0 0 1 295.3688 383.886)" class="st2 st3 st4">hello world</text>
|
||||||
|
<text transform="matrix(1 0 0 1 295.3688 424.086)" class="st2 st3 st4">--xyz---</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text transform="matrix(1 0 0 1 739.2766 414.2906)" class="st5 st6 st7">HTTP part</text>
|
||||||
|
<text transform="matrix(1 0 0 1 739.2764 193.4305)" class="st6 st8">HTTP message</text>
|
||||||
|
<path class="st9" d="M304.9,198H8.5c-4.7,0-8.5-3.8-8.5-8.5V64.8c0-4.7,3.8-8.5,8.5-8.5H305c4.7,0,8.5,3.8,8.5,8.5v124.7
|
||||||
|
C313.4,194.2,309.6,198,304.9,198z"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 25.2409 91.1617)" class="st2 st3 st4">struct mg_http_part {</text>
|
||||||
|
<text transform="matrix(1 0 0 1 47.0734 113.7079)" class="st2 st3 st4">struct mg_str name;</text>
|
||||||
|
<text transform="matrix(1 0 0 1 47.0734 133.8079)" class="st2 st3 st4">strucy mg_str filename;</text>
|
||||||
|
<text transform="matrix(1 0 0 1 47.0734 153.9079)" class="st2 st3 st4">struct mg_str body;</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st10" points="237.4,159 237.4,163 185.8,163 185.8,159 "/>
|
||||||
|
<g>
|
||||||
|
<polyline class="st10" points="211.7,348.3 349.5,348.4 349.5,355.6 "/>
|
||||||
|
<g>
|
||||||
|
<line class="st10" x1="211.6" y1="174.4" x2="211.6" y2="348.1"/>
|
||||||
|
<path class="st11" d="M211.6,170.3c-1.8,0-3.2,1.4-3.3,3.2c0,1.8,1.4,3.2,3.2,3.3c1.8,0,3.2-1.4,3.3-3.2l0,0
|
||||||
|
C214.8,171.8,213.4,170.3,211.6,170.3z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st11" d="M349.6,353.6c-1.8,0-3.2,1.4-3.3,3.2s1.4,3.2,3.2,3.3c1.8,0,3.2-1.4,3.3-3.2l0,0
|
||||||
|
C352.8,355.1,351.4,353.6,349.6,353.6z"/>
|
||||||
|
<polyline class="st10" points="237.4,159 237.4,163 185.8,163 185.8,159 "/>
|
||||||
|
<polyline class="st10" points="407.3,371.1 407.3,367.1 293.3,367.1 293.3,371.1 "/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st12" points="693.7,291.6 693.7,277.7 280.7,278.8 280.5,109.4 248.3,109.4 "/>
|
||||||
|
<path class="st13" d="M696.9,293.6c0,1.8-1.4,3.2-3.2,3.3c-1.8,0-3.2-1.4-3.3-3.2c0-1.8,1.4-3.2,3.2-3.3l0,0
|
||||||
|
C695.4,290.4,696.9,291.9,696.9,293.6z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st13" d="M243.9,109.5c0-1.8,1.4-3.2,3.2-3.3c1.8,0,3.2,1.4,3.3,3.2c0,1.8-1.4,3.2-3.2,3.3l0,0
|
||||||
|
C245.3,112.7,243.9,111.3,243.9,109.5z"/>
|
||||||
|
</g>
|
||||||
|
<polyline class="st14" points="707.6,309.2 707.6,305.2 679.9,305.2 679.9,309.2 "/>
|
||||||
|
</g>
|
||||||
|
<text transform="matrix(1 0 0 1 0.8627 37.8099)" class="st6 st8">Source Code</text>
|
||||||
|
<text transform="matrix(1 0 0 1 25.2408 177.7241)" class="st2 st3 st4">};</text>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st15" points="863.8,291.5 863.7,218.6 329.7,218.7 329.6,130.3 298.4,130.3 "/>
|
||||||
|
<path class="st16" d="M867.1,293.6c0,1.8-1.4,3.2-3.2,3.3c-1.8,0-3.2-1.4-3.3-3.2c0-1.8,1.4-3.2,3.2-3.3l0,0
|
||||||
|
C865.6,290.3,867.1,291.8,867.1,293.6z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st16" d="M293,130.1c0-1.8,1.4-3.2,3.2-3.3c1.8,0,3.2,1.4,3.3,3.2c0,1.8-1.4,3.2-3.2,3.3l0,0
|
||||||
|
C294.4,133.3,293,131.9,293,130.1z"/>
|
||||||
|
</g>
|
||||||
|
<polyline class="st15" points="888.7,309.1 888.7,305.1 838.9,305.1 838.9,309.1 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,84 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 1034 331.2" style="enable-background:new 0 0 1034 331.2;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#576174;}
|
||||||
|
.st1{fill:#67748A;}
|
||||||
|
.st2{fill:#FFFFFF;}
|
||||||
|
.st3{font-family:'Courier-Bold';}
|
||||||
|
.st4{font-size:16.7604px;}
|
||||||
|
.st5{font-family:'Arial-BoldMT';}
|
||||||
|
.st6{font-size:24px;}
|
||||||
|
.st7{fill:none;stroke:#45CFFF;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st8{fill:#45CFFF;}
|
||||||
|
.st9{fill:none;stroke:#EA5B0C;stroke-width:2.793;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st10{fill:#EA5B0C;}
|
||||||
|
.st11{fill:none;stroke:#EA5B0C;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st12{fill:none;stroke:#00CF4F;stroke-width:2.7934;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st13{fill:#00CF4F;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M541.43,184.2H8.53c-4.7,0-8.5-3.8-8.5-8.5V54.8c0-4.7,3.8-8.5,8.5-8.5h532.9c4.7,0,8.5,3.8,8.5,8.5v120.9
|
||||||
|
C549.93,180.4,546.13,184.2,541.43,184.2z"/>
|
||||||
|
<g>
|
||||||
|
<path class="st1" d="M829.23,315.5h-370c-4.7,0-8.5-3.8-8.5-8.5V157c0-4.7,3.8-8.5,8.5-8.5h370c4.7,0,8.5,3.8,8.5,8.5v150
|
||||||
|
C837.83,311.7,833.93,315.5,829.23,315.5z"/>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 490.6096 184.0905)" class="st2 st3 st4">HTTP/1.1 200 OK</text>
|
||||||
|
<text transform="matrix(1 0 0 1 490.6096 204.1905)" class="st2 st3 st4">Content-Length; 13</text>
|
||||||
|
<text transform="matrix(1 0 0 1 490.6096 224.2905)" class="st2 st3 st4">X-Header-1: additional value 1</text>
|
||||||
|
<text transform="matrix(1 0 0 1 490.6096 244.3905)" class="st2 st3 st4">X-Header-2: additional value 2</text>
|
||||||
|
<text transform="matrix(1 0 0 1 490.8657 293.1559)" class="st2 st3 st4">Hello, world!</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text transform="matrix(1 0 0 1 660.5036 137.3383)" class="st5 st6">HTTP message</text>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 20.7331 85.1617)" class="st2 st3 st4">mg_http_reply(c, 200</text>
|
||||||
|
<text transform="matrix(1 0 0 1 159.5657 113.7079)" class="st2 st3 st4">"X-Header-1: additional value 1\r\n"</text>
|
||||||
|
<text transform="matrix(1 0 0 1 159.5657 133.8079)" class="st2 st3 st4">"X-Header-2: additional value 2\r\n",</text>
|
||||||
|
<text transform="matrix(1 0 0 1 159.5657 153.9079)" class="st2 st3 st4">"Hello, %s!", "world");</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st7" points="362.93,159 362.93,163 167.33,163 167.33,159 "/>
|
||||||
|
<g>
|
||||||
|
<polyline class="st7" points="265.93,256.6 552.13,256.7 552.13,264.4 "/>
|
||||||
|
<g>
|
||||||
|
<line class="st7" x1="265.83" y1="174.4" x2="265.83" y2="256.6"/>
|
||||||
|
<path class="st8" d="M265.83,170.3c-1.8,0-3.2,1.4-3.3,3.2c0,1.8,1.4,3.2,3.2,3.3c1.8,0,3.2-1.4,3.3-3.2l0,0
|
||||||
|
C269.03,171.8,267.53,170.3,265.83,170.3z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st8" d="M552.23,261.6c-1.8,0-3.2,1.4-3.3,3.2c0,1.8,1.4,3.2,3.2,3.3c1.8,0,3.2-1.4,3.3-3.2l0,0
|
||||||
|
C555.43,263.1,554.03,261.6,552.23,261.6z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st9" points="206.33,53.6 206.43,37.6 596.43,38.7 596.53,161.3 "/>
|
||||||
|
<path class="st10" d="M203.23,54.6c0,1.8,1.4,3.2,3.2,3.3c1.8,0,3.2-1.4,3.3-3.2c0-1.8-1.4-3.2-3.2-3.3l0,0
|
||||||
|
C204.63,51.4,203.23,52.8,203.23,54.6z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st10" d="M599.83,164c0-1.8-1.4-3.2-3.2-3.3c-1.8,0-3.2,1.4-3.3,3.2c0,1.8,1.4,3.2,3.2,3.3l0,0
|
||||||
|
C598.33,167.2,599.83,165.8,599.83,164z"/>
|
||||||
|
</g>
|
||||||
|
<polyline class="st11" points="192.43,69.2 192.43,65.2 220.23,65.2 220.23,69.2 "/>
|
||||||
|
</g>
|
||||||
|
<text transform="matrix(1 0 0 1 -1.917133e-04 35.0037)" class="st5 st6">Source Code</text>
|
||||||
|
<g>
|
||||||
|
<polyline class="st12" points="160.13,135.9 156.03,135.9 156.03,100.1 160.13,100.1 "/>
|
||||||
|
<g>
|
||||||
|
<line class="st12" x1="123.93" y1="231" x2="469.73" y2="231"/>
|
||||||
|
<polyline class="st12" points="144.73,117.2 123.83,117.2 123.73,230.8 "/>
|
||||||
|
<path class="st13" d="M147.83,117.2c0-1.8-1.4-3.2-3.2-3.3c-1.8,0-3.2,1.4-3.3,3.2c0,1.8,1.4,3.2,3.2,3.3l0,0
|
||||||
|
C146.33,120.4,147.83,119,147.83,117.2z"/>
|
||||||
|
<path class="st13" d="M474.83,230.8c0-1.8-1.4-3.2-3.2-3.3c-1.8,0-3.2,1.4-3.3,3.2c0,1.8,1.4,3.2,3.2,3.3l0,0
|
||||||
|
C473.43,234,474.83,232.5,474.83,230.8z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<polyline class="st7" points="489.03,279.6 489.03,275.6 620.63,275.6 620.63,279.6 "/>
|
||||||
|
<polyline class="st12" points="487.63,248.3 483.63,248.3 483.63,211.5 487.63,211.5 "/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 869.6 185.67" style="enable-background:new 0 0 869.6 185.67;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{font-family:'Arial-BoldMT';}
|
||||||
|
.st1{font-size:24px;}
|
||||||
|
.st2{opacity:0.2;fill:#00983A;enable-background:new ;}
|
||||||
|
.st3{fill:none;stroke:#7A8387;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st4{fill:#00983A;}
|
||||||
|
.st5{fill:none;stroke:#576174;stroke-miterlimit:10;}
|
||||||
|
.st6{fill:#F2F3F3;stroke:#7A8387;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st7{fill:none;stroke:#576174;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st8{font-family:'Courier-Bold';}
|
||||||
|
.st9{font-size:16.7604px;}
|
||||||
|
.st10{fill:none;stroke:#00983A;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st11{fill:#576174;}
|
||||||
|
.st12{font-family:'Courier';}
|
||||||
|
.st13{fill:none;}
|
||||||
|
.st14{fill:#7A8387;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 1.0956 32.4549)" class="st0 st1">Memory</text>
|
||||||
|
<polyline class="st2" points="424.58,115.4 219.88,115.4 219.88,90.7 424.58,90.7 "/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st3" points="458.98,115.4 218.98,115.4 218.98,90.7 458.98,90.7 "/>
|
||||||
|
<polyline class="st4" points="219.18,115.1 2.98,115.1 2.98,91.3 219.18,91.3 "/>
|
||||||
|
<line class="st3" x1="2.98" y1="69.2" x2="2.98" y2="130.4"/>
|
||||||
|
<line class="st5" x1="219.78" y1="69.2" x2="219.78" y2="130.4"/>
|
||||||
|
<line class="st5" x1="425.28" y1="69.2" x2="425.28" y2="130.4"/>
|
||||||
|
<path class="st6" d="M458.98,115.4"/>
|
||||||
|
<path class="st7" d="M33.98,114.6"/>
|
||||||
|
<path class="st7" d="M33.98,90.9"/>
|
||||||
|
<path class="st7" d="M2.98,90.7h216.8v24.9l-216.8-0.2C2.98,115.4,3.18,90.7,2.98,90.7z"/>
|
||||||
|
<line class="st3" x1="425.18" y1="90.8" x2="425.18" y2="115.3"/>
|
||||||
|
<path class="st6" d="M251.98,115.6"/>
|
||||||
|
<path class="st6" d="M251.98,91.9"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 -2.290726e-04 175.3858)" class="st4 st8 st9">buf</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st10" x1="2.68" y1="158.2" x2="2.68" y2="137.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st4" points="0.28,137.6 5.08,137.6 2.68,135.1 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 214.6515 175.3858)" class="st4 st8 st9">len</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st10" x1="219.78" y1="158.2" x2="219.78" y2="137.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st4" points="217.38,137.6 222.18,137.6 219.78,135.1 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 421.6186 175.3858)" class="st4 st8 st9">size</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st10" x1="425.38" y1="158.2" x2="425.38" y2="137.2"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st4" points="422.98,137.6 427.78,137.6 425.38,135.1 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 93.1143 69.4458)" class="st11 st12 st9">data</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 273.8231 69.4458)" class="st11 st12 st9">free space</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st5" x1="132.98" y1="76.3" x2="13.98" y2="76.3"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st11" points="14.28,78.7 14.28,73.8 11.88,76.3 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<line class="st13" x1="123.88" y1="76.3" x2="210.88" y2="76.3"/>
|
||||||
|
<g>
|
||||||
|
<line class="st5" x1="123.88" y1="76.3" x2="208.78" y2="76.3"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st11" points="208.48,73.8 208.48,78.7 210.88,76.3 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st5" x1="320.38" y1="76.3" x2="414.28" y2="76.3"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="413.98,73.8 413.98,78.7 416.38,76.3 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st5" x1="322.28" y1="76.3" x2="233.28" y2="76.3"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st14" points="233.68,78.7 233.68,73.8 231.18,76.3 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.1 KiB |
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 869 124.66" style="enable-background:new 0 0 869 124.66;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#F2F3F3;stroke:#7A8387;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st1{fill:none;stroke:#7A8387;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st2{fill:#00983A;}
|
||||||
|
.st3{font-family:'Courier-Bold';}
|
||||||
|
.st4{font-size:16.7604px;}
|
||||||
|
.st5{fill:none;stroke:#00983A;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st6{fill:#7A8387;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st0" points="830.92,78.4 5.52,78.4 5.52,53.7 830.92,53.6 "/>
|
||||||
|
<line class="st1" x1="5.52" y1="32.2" x2="5.52" y2="93.4"/>
|
||||||
|
<path class="st0" d="M830.92,78.4"/>
|
||||||
|
<line class="st1" x1="36.52" y1="53.9" x2="36.52" y2="77.6"/>
|
||||||
|
<line class="st1" x1="68.92" y1="53.9" x2="68.92" y2="77.6"/>
|
||||||
|
<line class="st1" x1="101.42" y1="53.9" x2="101.42" y2="77.6"/>
|
||||||
|
<line class="st1" x1="133.72" y1="53.9" x2="133.72" y2="77.6"/>
|
||||||
|
<line class="st1" x1="166.22" y1="53.9" x2="166.22" y2="77.6"/>
|
||||||
|
<line class="st1" x1="198.52" y1="54.6" x2="198.52" y2="78.3"/>
|
||||||
|
<line class="st1" x1="230.92" y1="54.6" x2="230.92" y2="78.3"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 59.1908 40.8585)" class="st2 st3 st4">buf=NULL, len=0, size=0 </text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 -1.811981e-04 109.9024)" class="st2 st3 st4">0</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st5" x1="46.72" y1="37.7" x2="17.72" y2="37.7"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st2" points="18.12,40.1 18.12,35.2 15.62,37.7 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st6" d="M511.52,66.4c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6s0.7-1.6,1.6-1.6S511.52,65.5,511.52,66.4z"/>
|
||||||
|
<path class="st6" d="M525.32,66.4c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6s0.7-1.6,1.6-1.6S525.32,65.5,525.32,66.4z"/>
|
||||||
|
<path class="st6" d="M539.02,66.4c0,0.9-0.7,1.6-1.6,1.6c-0.9,0-1.6-0.7-1.6-1.6s0.7-1.6,1.6-1.6S539.02,65.5,539.02,66.4z"/>
|
||||||
|
<path class="st6" d="M552.82,66.4c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6s0.7-1.6,1.6-1.6S552.82,65.5,552.82,66.4z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,185 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 869.6 127.5" style="enable-background:new 0 0 869.6 127.5;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{opacity:0.2;fill:#00983A;enable-background:new ;}
|
||||||
|
.st1{fill:#F2F3F3;}
|
||||||
|
.st2{fill:none;stroke:#7A8387;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st3{fill:#00983A;}
|
||||||
|
.st4{fill:#F2F3F3;stroke:#7A8387;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st5{fill:none;stroke:#576174;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st6{fill:none;stroke:#576174;stroke-width:1.2;stroke-linecap:square;stroke-linejoin:round;}
|
||||||
|
.st7{font-family:'Courier-Bold';}
|
||||||
|
.st8{font-size:16.7604px;}
|
||||||
|
.st9{fill:none;stroke:#00983A;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st10{fill:#7A8387;}
|
||||||
|
.st11{fill:#576174;}
|
||||||
|
.st12{fill:none;stroke:#576174;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st13{fill:none;stroke:#7A8387;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st14{fill:#333A45;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st0" points="577.2,72.8 282.4,72.8 282.4,48 577.2,48 "/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st1" points="122.6,72.8 5.9,72.8 5.9,48 122.6,48 "/>
|
||||||
|
<polyline class="st2" points="831.51,72.8 5.5,72.8 5.5,48 831.51,48 "/>
|
||||||
|
<polyline class="st3" points="282.7,72.5 122.5,72.5 122.5,47.7 282.7,47.7 "/>
|
||||||
|
<line class="st2" x1="122.5" y1="26.6" x2="122.5" y2="87.8"/>
|
||||||
|
<line class="st2" x1="283.3" y1="26.6" x2="283.3" y2="87.8"/>
|
||||||
|
<line class="st2" x1="576.8" y1="26.6" x2="576.8" y2="87.8"/>
|
||||||
|
<path class="st4" d="M831.51,72.8"/>
|
||||||
|
<line class="st5" x1="153.5" y1="48.3" x2="153.5" y2="72"/>
|
||||||
|
<line class="st2" x1="36.5" y1="48.3" x2="36.5" y2="72"/>
|
||||||
|
<line class="st2" x1="67.6" y1="48.3" x2="67.6" y2="72"/>
|
||||||
|
<line class="st5" x1="186" y1="48.3" x2="186" y2="72"/>
|
||||||
|
<line class="st5" x1="218.4" y1="48.3" x2="218.4" y2="72"/>
|
||||||
|
<line class="st5" x1="250.9" y1="48.3" x2="250.9" y2="72"/>
|
||||||
|
<line class="st2" x1="315.9" y1="48.3" x2="315.9" y2="72"/>
|
||||||
|
<line class="st2" x1="348.4" y1="48.3" x2="348.4" y2="72"/>
|
||||||
|
<line class="st2" x1="381.2" y1="48.3" x2="381.2" y2="72"/>
|
||||||
|
<line class="st2" x1="413.8" y1="48.3" x2="413.8" y2="72"/>
|
||||||
|
<line class="st2" x1="446.3" y1="48.3" x2="446.3" y2="72"/>
|
||||||
|
<line class="st2" x1="478.9" y1="48.3" x2="478.9" y2="72"/>
|
||||||
|
<line class="st5" x1="511.51" y1="48.3" x2="511.51" y2="72"/>
|
||||||
|
<line class="st2" x1="511.51" y1="48.3" x2="511.51" y2="72"/>
|
||||||
|
<line class="st5" x1="544.01" y1="48.3" x2="544.01" y2="72"/>
|
||||||
|
<line class="st2" x1="544.1" y1="48.3" x2="544.1" y2="72"/>
|
||||||
|
<line class="st6" x1="283.3" y1="73" x2="122.5" y2="72.8"/>
|
||||||
|
<line class="st6" x1="122.5" y1="48" x2="283.3" y2="48.1"/>
|
||||||
|
<path class="st4" d="M385.5,73"/>
|
||||||
|
<path class="st4" d="M385.5,49.3"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 127.5176 105.8013)" class="st3 st7 st8">buf</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st9" x1="122.2" y1="105.6" x2="122.2" y2="94.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st3" points="119.8,94.9 124.6,94.9 122.2,92.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 286.6495 105.8008)" class="st3 st7 st8">len=5</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st9" x1="283.3" y1="105.6" x2="283.3" y2="94.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st3" points="280.9,94.9 285.8,94.9 283.3,92.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 581.4624 105.8013)" class="st3 st7 st8">size=16</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st9" x1="576.9" y1="105.6" x2="576.9" y2="94.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st3" points="574.51,94.9 579.4,94.9 576.9,92.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st10" d="M687.51,60.7c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6S687.51,59.9,687.51,60.7z"/>
|
||||||
|
<path class="st10" d="M701.2,60.7c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6S701.2,59.9,701.2,60.7z"/>
|
||||||
|
<path class="st10" d="M714.9,60.7c0,0.9-0.7,1.6-1.6,1.6c-0.9,0-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6
|
||||||
|
C714.2,59.1,714.9,59.9,714.9,60.7z"/>
|
||||||
|
<path class="st10" d="M728.7,60.7c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6
|
||||||
|
C728.01,59.1,728.7,59.9,728.7,60.7z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st10" d="M82.8,60.7c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6S82.8,59.9,82.8,60.7z"/>
|
||||||
|
<path class="st10" d="M96.5,60.7c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6C95.8,59.1,96.5,59.9,96.5,60.7
|
||||||
|
z"/>
|
||||||
|
<path class="st10" d="M110.2,60.7c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6S110.2,59.9,110.2,60.7z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 180.6541 26.8123)" class="st11 st7 st8">data</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 387.363 26.8123)" class="st10 st7 st8">free space</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st12" x1="272.2" y1="33.6" x2="133.5" y2="33.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st11" points="271.9,36.2 273.5,33.6 271.9,31.1 271.8,31.1 271.8,36.2 "/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polygon class="st11" points="133.8,36.1 133.8,31.2 131.4,33.6 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st13" x1="563.7" y1="33.6" x2="295.9" y2="33.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st10" points="563.4,36.1 563.4,31.2 565.8,33.6 "/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polygon class="st10" points="296.2,36.1 296.2,31.2 293.7,33.6 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 615.4679 25.1898)" class="st10 st7 st8">non-allocated space</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st13" x1="593.7" y1="33.6" x2="829.7" y2="33.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st10" points="829.4,31.2 829.4,36.1 831.8,33.6 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st13" x1="678.7" y1="33.6" x2="589.7" y2="33.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st10" points="590.01,36.1 590.01,31.2 587.6,33.6 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 133.1212 64.9593)" class="st14 st7 st8">h</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 165.1564 64.9593)" class="st14 st7 st8">e</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 196.8744 64.9593)" class="st14 st7 st8">l</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 229.8611 64.9593)" class="st14 st7 st8">l</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 262.2135 64.9593)" class="st14 st7 st8">o</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 1.811981e-04 105.8011)" class="st3 st7 st8">0</text>
|
||||||
|
</g>
|
||||||
|
<line class="st2" x1="5.5" y1="26.4" x2="5.5" y2="87.6"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,347 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 869.6 247.73" style="enable-background:new 0 0 869.6 247.73;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{opacity:0.2;fill:#00983A;enable-background:new ;}
|
||||||
|
.st1{fill:#00983A;}
|
||||||
|
.st2{fill:#FF80A4;}
|
||||||
|
.st3{fill:#F2F3F3;}
|
||||||
|
.st4{fill:none;stroke:#7A8387;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st5{fill:#F2F3F3;stroke:#7A8387;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st6{fill:none;stroke:#576174;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st7{fill:none;stroke:#576174;stroke-width:1.2;stroke-linejoin:round;}
|
||||||
|
.st8{fill:none;stroke:#576174;stroke-width:1.2;stroke-linecap:square;stroke-linejoin:round;}
|
||||||
|
.st9{font-family:'Courier-Bold';}
|
||||||
|
.st10{font-size:16.7604px;}
|
||||||
|
.st11{fill:none;stroke:#00983A;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st12{fill:#7A8387;}
|
||||||
|
.st13{fill:#576174;}
|
||||||
|
.st14{fill:none;stroke:#576174;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st15{fill:none;stroke:#7A8387;stroke-linecap:round;stroke-linejoin:round;}
|
||||||
|
.st16{fill:#333A45;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st0" points="577.2,72.8 282.4,72.8 282.4,48 577.2,48 "/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<rect x="250.9" y="47.7" class="st1" width="32.4" height="24.8"/>
|
||||||
|
<rect x="153.5" y="47.7" class="st2" width="97.3" height="24.8"/>
|
||||||
|
<polyline class="st3" points="122.6,72.8 5.9,72.8 5.9,48 122.6,48 "/>
|
||||||
|
<polyline class="st4" points="831.51,72.8 5.5,72.8 5.5,48 831.51,48 "/>
|
||||||
|
<rect x="122.5" y="47.7" class="st1" width="31" height="24.8"/>
|
||||||
|
<line class="st4" x1="122.5" y1="26.6" x2="122.5" y2="87.8"/>
|
||||||
|
<line class="st4" x1="576.8" y1="26.6" x2="576.8" y2="87.8"/>
|
||||||
|
<path class="st5" d="M831.51,72.8"/>
|
||||||
|
<line class="st6" x1="153.5" y1="48.3" x2="153.5" y2="72"/>
|
||||||
|
<line class="st4" x1="36.5" y1="48.3" x2="36.5" y2="72"/>
|
||||||
|
<line class="st4" x1="67.6" y1="48.3" x2="67.6" y2="72"/>
|
||||||
|
<line class="st6" x1="186" y1="48.3" x2="186" y2="72"/>
|
||||||
|
<line class="st6" x1="218.4" y1="48.3" x2="218.4" y2="72"/>
|
||||||
|
<line class="st7" x1="250.9" y1="73" x2="250.81" y2="47.7"/>
|
||||||
|
<line class="st4" x1="315.9" y1="48.3" x2="315.9" y2="72"/>
|
||||||
|
<line class="st4" x1="348.4" y1="48.3" x2="348.4" y2="72"/>
|
||||||
|
<line class="st4" x1="381.2" y1="48.3" x2="381.2" y2="72"/>
|
||||||
|
<line class="st4" x1="413.8" y1="48.3" x2="413.8" y2="72"/>
|
||||||
|
<line class="st4" x1="446.3" y1="48.3" x2="446.3" y2="72"/>
|
||||||
|
<line class="st4" x1="478.9" y1="48.3" x2="478.9" y2="72"/>
|
||||||
|
<line class="st6" x1="511.51" y1="48.3" x2="511.51" y2="72"/>
|
||||||
|
<line class="st4" x1="511.51" y1="48.3" x2="511.51" y2="72"/>
|
||||||
|
<line class="st6" x1="544.01" y1="48.3" x2="544.01" y2="72"/>
|
||||||
|
<line class="st4" x1="544.1" y1="48.3" x2="544.1" y2="72"/>
|
||||||
|
<line class="st8" x1="283.3" y1="73" x2="122.5" y2="72.8"/>
|
||||||
|
<line class="st8" x1="122.5" y1="48" x2="283.3" y2="48.1"/>
|
||||||
|
<path class="st5" d="M385.5,73"/>
|
||||||
|
<path class="st5" d="M385.5,49.3"/>
|
||||||
|
<line class="st4" x1="283.3" y1="26.6" x2="283.3" y2="87.8"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 127.5176 105.8013)" class="st1 st9 st10">buf</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="122.2" y1="105.6" x2="122.2" y2="94.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st1" points="119.8,94.9 124.6,94.9 122.2,92.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 286.6495 105.8008)" class="st1 st9 st10">len=5</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="283.3" y1="105.6" x2="283.3" y2="94.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st1" points="280.9,94.9 285.8,94.9 283.3,92.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 581.4624 105.8013)" class="st1 st9 st10">size=16</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="576.9" y1="105.6" x2="576.9" y2="94.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st1" points="574.51,94.9 579.4,94.9 576.9,92.5 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st12" d="M687.51,60.7c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6S687.51,59.9,687.51,60.7z"/>
|
||||||
|
<path class="st12" d="M701.2,60.7c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6S701.2,59.9,701.2,60.7z"/>
|
||||||
|
<path class="st12" d="M714.9,60.7c0,0.9-0.7,1.6-1.6,1.6c-0.9,0-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6
|
||||||
|
C714.2,59.1,714.9,59.9,714.9,60.7z"/>
|
||||||
|
<path class="st12" d="M728.7,60.7c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6
|
||||||
|
C728.01,59.1,728.7,59.9,728.7,60.7z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st12" d="M82.8,60.7c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6S82.8,59.9,82.8,60.7z"/>
|
||||||
|
<path class="st12" d="M96.5,60.7c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6C95.8,59.1,96.5,59.9,96.5,60.7
|
||||||
|
z"/>
|
||||||
|
<path class="st12" d="M110.2,60.7c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6S110.2,59.9,110.2,60.7z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 180.6541 26.8123)" class="st13 st9 st10">data</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 387.363 26.8123)" class="st12 st9 st10">free space</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st14" x1="272.2" y1="33.6" x2="133.5" y2="33.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st13" points="271.9,36.2 273.5,33.6 271.9,31.1 271.8,31.1 271.8,36.2 "/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polygon class="st13" points="133.8,36.1 133.8,31.2 131.4,33.6 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="563.7" y1="33.6" x2="295.9" y2="33.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st12" points="563.4,36.1 563.4,31.2 565.8,33.6 "/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polygon class="st12" points="296.2,36.1 296.2,31.2 293.7,33.6 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 615.4679 25.1898)" class="st12 st9 st10">non-allocated space</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="593.7" y1="33.6" x2="829.7" y2="33.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st12" points="829.4,31.2 829.4,36.1 831.8,33.6 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="678.7" y1="33.6" x2="589.7" y2="33.6"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st12" points="590.01,36.1 590.01,31.2 587.6,33.6 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 133.1212 64.9593)" class="st16 st9 st10">h</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 165.1564 64.9593)" class="st16 st9 st10">e</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 196.8744 64.9593)" class="st16 st9 st10">l</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 229.8611 64.9593)" class="st16 st9 st10">l</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 262.2135 64.9593)" class="st16 st9 st10">o</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 1.811981e-04 105.8011)" class="st1 st9 st10">0</text>
|
||||||
|
</g>
|
||||||
|
<line class="st4" x1="5.5" y1="26.4" x2="5.5" y2="87.6"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st0" points="577.2,207.99 186.3,207.99 186.3,183.19 577.2,183.19 "/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polyline class="st3" points="122.6,207.99 5.9,207.99 5.9,183.19 122.6,183.19 "/>
|
||||||
|
<polyline class="st4" points="831.51,207.99 5.5,207.99 5.5,183.19 831.51,183.19 "/>
|
||||||
|
<polyline class="st1" points="186.3,207.69 122.5,207.69 122.5,182.89 186.3,182.89 "/>
|
||||||
|
<line class="st4" x1="122.5" y1="161.79" x2="122.5" y2="222.99"/>
|
||||||
|
<line class="st4" x1="186" y1="161.79" x2="186" y2="222.99"/>
|
||||||
|
<line class="st4" x1="576.8" y1="161.79" x2="576.8" y2="222.99"/>
|
||||||
|
<path class="st5" d="M831.51,207.99"/>
|
||||||
|
<line class="st6" x1="153.5" y1="183.49" x2="153.5" y2="207.19"/>
|
||||||
|
<line class="st4" x1="36.5" y1="183.49" x2="36.5" y2="207.19"/>
|
||||||
|
<line class="st4" x1="67.6" y1="183.49" x2="67.6" y2="207.19"/>
|
||||||
|
<line class="st6" x1="186" y1="183.49" x2="186" y2="207.99"/>
|
||||||
|
<line class="st4" x1="218.4" y1="183.49" x2="218.4" y2="207.19"/>
|
||||||
|
<line class="st4" x1="250.9" y1="183.49" x2="250.9" y2="207.19"/>
|
||||||
|
<line class="st4" x1="283.3" y1="183.49" x2="283.3" y2="207.19"/>
|
||||||
|
<line class="st4" x1="315.9" y1="183.49" x2="315.9" y2="207.19"/>
|
||||||
|
<line class="st4" x1="348.4" y1="183.49" x2="348.4" y2="207.19"/>
|
||||||
|
<line class="st4" x1="381.2" y1="183.49" x2="381.2" y2="207.19"/>
|
||||||
|
<line class="st4" x1="413.8" y1="183.49" x2="413.8" y2="207.19"/>
|
||||||
|
<line class="st4" x1="446.3" y1="183.49" x2="446.3" y2="207.19"/>
|
||||||
|
<line class="st4" x1="478.9" y1="183.49" x2="478.9" y2="207.19"/>
|
||||||
|
<line class="st6" x1="511.51" y1="183.49" x2="511.51" y2="207.19"/>
|
||||||
|
<line class="st4" x1="511.51" y1="183.49" x2="511.51" y2="207.19"/>
|
||||||
|
<line class="st6" x1="544.01" y1="183.49" x2="544.01" y2="207.19"/>
|
||||||
|
<line class="st4" x1="544.1" y1="183.49" x2="544.1" y2="207.19"/>
|
||||||
|
<line class="st8" x1="186" y1="208.19" x2="122.5" y2="207.99"/>
|
||||||
|
<line class="st8" x1="122.5" y1="183.19" x2="186" y2="183.29"/>
|
||||||
|
<path class="st5" d="M385.5,208.19"/>
|
||||||
|
<path class="st5" d="M385.5,184.49"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 127.5176 240.9952)" class="st1 st9 st10">buf</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="122.2" y1="240.79" x2="122.2" y2="229.79"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st1" points="119.8,230.09 124.6,230.09 122.2,227.69 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 189.2273 240.9947)" class="st1 st9 st10">len=2</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="185.88" y1="240.79" x2="185.88" y2="229.79"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st1" points="183.48,230.09 188.38,230.09 185.88,227.69 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 581.4624 240.9952)" class="st1 st9 st10">size=16</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st11" x1="576.9" y1="240.79" x2="576.9" y2="229.79"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st1" points="574.51,230.09 579.4,230.09 576.9,227.69 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st12" d="M687.51,195.89c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6
|
||||||
|
S687.51,195.09,687.51,195.89z"/>
|
||||||
|
<path class="st12" d="M701.2,195.89c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6S701.2,195.09,701.2,195.89z
|
||||||
|
"/>
|
||||||
|
<path class="st12" d="M714.9,195.89c0,0.9-0.7,1.6-1.6,1.6c-0.9,0-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6
|
||||||
|
S714.9,195.09,714.9,195.89z"/>
|
||||||
|
<path class="st12" d="M728.7,195.89c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6
|
||||||
|
C728.01,194.29,728.7,195.09,728.7,195.89z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st12" d="M82.8,195.89c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6S82.8,195.09,82.8,195.89z"/>
|
||||||
|
<path class="st12" d="M96.5,195.89c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6
|
||||||
|
C95.8,194.29,96.5,195.09,96.5,195.89z"/>
|
||||||
|
<path class="st12" d="M110.2,195.89c0,0.9-0.7,1.6-1.6,1.6s-1.6-0.7-1.6-1.6c0-0.9,0.7-1.6,1.6-1.6S110.2,195.09,110.2,195.89z
|
||||||
|
"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 133.6541 162.0062)" class="st13 st9 st10">data</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 327.363 162.0062)" class="st12 st9 st10">free space</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st14" x1="175.2" y1="168.79" x2="133.5" y2="168.79"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st13" points="174.9,171.39 176.5,168.79 174.9,166.29 174.8,166.29 174.8,171.39 "/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polygon class="st13" points="133.8,171.29 133.8,166.39 131.4,168.79 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="563.7" y1="168.79" x2="197.49" y2="168.79"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st12" points="563.4,171.29 563.4,166.39 565.8,168.79 "/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polygon class="st12" points="197.79,171.29 197.79,166.39 195.29,168.79 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 615.4679 160.3837)" class="st12 st9 st10">non-allocated space</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="593.7" y1="168.79" x2="829.7" y2="168.79"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st12" points="829.4,166.39 829.4,171.29 831.8,168.79 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<line class="st15" x1="678.7" y1="168.79" x2="589.7" y2="168.79"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st12" points="590.01,171.29 590.01,166.39 587.6,168.79 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 133.1212 200.1532)" class="st16 st9 st10">h</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 165.1565 200.1532)" class="st16 st9 st10">o</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 1.811981e-04 240.995)" class="st1 st9 st10">0</text>
|
||||||
|
</g>
|
||||||
|
<line class="st4" x1="5.5" y1="161.59" x2="5.5" y2="222.79"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,384 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 858.5 424.3" style="enable-background:new 0 0 858.5 424.3;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#9E9B83;}
|
||||||
|
.st1{fill:#9E8900;}
|
||||||
|
.st2{font-family:Arial;font-weight:bold;}
|
||||||
|
.st3{font-size:11px;}
|
||||||
|
.st4{display:none;fill:#576174;}
|
||||||
|
.st5{fill:#1F5F75;}
|
||||||
|
.st6{clip-path:url(#SVGID_3_);fill:#FFFFFF;fill-opacity:0.1;}
|
||||||
|
.st7{fill:#277691;}
|
||||||
|
.st8{fill:#FFFFFF;}
|
||||||
|
.st9{font-family:Arial;font-weight:bold;}
|
||||||
|
.st10{fill:#004219;}
|
||||||
|
.st11{clip-path:url(#SVGID_6_);fill:#FFFFFF;fill-opacity:0.1;}
|
||||||
|
.st12{fill:#005721;}
|
||||||
|
.st13{fill:url(#SVGID_7_);}
|
||||||
|
.st14{clip-path:url(#XMLID_13_);fill:#FFFFFF;fill-opacity:0.1;}
|
||||||
|
.st15{fill:#752E06;}
|
||||||
|
.st16{fill:#963B08;}
|
||||||
|
.st17{fill:#59008F;}
|
||||||
|
.st18{clip-path:url(#XMLID_15_);fill:#FFFFFF;fill-opacity:0.1;}
|
||||||
|
.st19{fill:#7300B8;}
|
||||||
|
.st20{fill:#FFDE00;}
|
||||||
|
.st21{fill:#576174;}
|
||||||
|
.st22{font-size:13px;}
|
||||||
|
.st23{fill:#8A8C8E;}
|
||||||
|
.st24{font-size:12px;}
|
||||||
|
.st25{opacity:0.69;}
|
||||||
|
.st26{fill:#B3B1B3;}
|
||||||
|
.st27{clip-path:url(#XMLID_17_);fill:#FFFFFF;fill-opacity:0.1;}
|
||||||
|
.st28{fill:#919091;}
|
||||||
|
.st29{fill:#BFBEBF;}
|
||||||
|
.st30{fill:#454445;}
|
||||||
|
.st31{font-size:9px;}
|
||||||
|
.st32{clip-path:url(#XMLID_19_);fill:#FFFFFF;fill-opacity:0.1;}
|
||||||
|
.st33{fill:#8695B3;}
|
||||||
|
.st34{fill:url(#SVGID_8_);}
|
||||||
|
.st35{font-size:18px;}
|
||||||
|
.st36{clip-path:url(#XMLID_20_);fill:#610041;fill-opacity:0.1;}
|
||||||
|
.st37{fill:#610041;}
|
||||||
|
.st38{clip-path:url(#XMLID_22_);fill:url(#SVGID_9_);fill-opacity:0.1;}
|
||||||
|
.st39{fill:none;stroke:#86005B;stroke-width:0.5;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
.st40{fill:none;stroke:#86005B;stroke-width:0.75;stroke-linecap:round;stroke-miterlimit:10;}
|
||||||
|
.st41{fill:url(#SVGID_10_);}
|
||||||
|
.st42{fill:url(#SVGID_11_);}
|
||||||
|
.st43{fill:#610042;}
|
||||||
|
.st44{fill:url(#SVGID_12_);}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M457.9,111.2l0.1,87.3c0,1.4-1.1,2.6-2.5,2.6h-76.3c-4.4-0.2-7.3-3.9-7.3-5.3l4.7-101.8
|
||||||
|
c0-1.4,0.8-7.4,2.2-7.4l54-0.1L457.9,111.2z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 383.2604 149.9447)" class="st1 st2 st3">packed_fs.c</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<rect x="84" y="25" class="st4" width="727.7" height="429.8"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st5" d="M207.5,104.6v87.4c0,1.4-1.1,2.6-2.5,2.6h-78.3c-1.4,0-2.5-1.1-2.5-2.6V84.5c0-1.4,1.1-2.5,2.5-2.5h58
|
||||||
|
L207.5,104.6z"/>
|
||||||
|
<g>
|
||||||
|
<defs>
|
||||||
|
<path id="SVGID_1_" d="M205.5,102.6v87.4c0,1.4-1.1,2.6-2.5,2.6h-78.3c-1.4,0-2.5-1.1-2.5-2.6v-57.5V82.5c0-1.4,1.1-2.5,2.5-2.5
|
||||||
|
h58L205.5,102.6z"/>
|
||||||
|
</defs>
|
||||||
|
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="122.1091" y1="136.2824" x2="205.5191" y2="136.2824">
|
||||||
|
<stop offset="0" style="stop-color:#45CFFF"/>
|
||||||
|
<stop offset="1" style="stop-color:#85DDFA"/>
|
||||||
|
</linearGradient>
|
||||||
|
<use xlink:href="#SVGID_1_" style="overflow:visible;fill:url(#SVGID_2_);"/>
|
||||||
|
<clipPath id="SVGID_3_">
|
||||||
|
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
|
||||||
|
</clipPath>
|
||||||
|
<path class="st6" d="M121.4,101.4c9.4,0.7,36.6,1.5,53.2,14.7c25,20,31.2,47.1,31.2,47.1l0.4,35.3l-84.8,0.2V101.4z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st5" d="M205.9,104.4l-20.6,0c-1.4,0-2.6-1.2-2.6-2.6l0-21.8l22.9,22.7L205.9,104.4z"/>
|
||||||
|
<path class="st7" d="M205.7,102.8h-20.5c-1.4,0-2.5-1.1-2.5-2.5V79.9l0,0l22.9,22.7L205.7,102.8z"/>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 129.0689 92.8531)" class="st8 st9 st3">filex.y</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st10" d="M191.4,121.6V209c0,1.4-1.1,2.6-2.5,2.6h-78.3c-1.4,0-2.5-1.1-2.5-2.6V101.4c0-1.4,1.1-2.5,2.5-2.5h58
|
||||||
|
L191.4,121.6z"/>
|
||||||
|
<g>
|
||||||
|
<defs>
|
||||||
|
<path id="SVGID_4_" d="M189.4,119.6V207c0,1.4-1.1,2.6-2.5,2.6h-78.3c-1.4,0-2.5-1.1-2.5-2.6v-57.5V99.4c0-1.4,1.1-2.5,2.5-2.5
|
||||||
|
h58L189.4,119.6z"/>
|
||||||
|
</defs>
|
||||||
|
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="106.0386" y1="153.2384" x2="189.4486" y2="153.2384">
|
||||||
|
<stop offset="0" style="stop-color:#00983A"/>
|
||||||
|
<stop offset="1" style="stop-color:#3A985D"/>
|
||||||
|
</linearGradient>
|
||||||
|
<use xlink:href="#SVGID_4_" style="overflow:visible;fill:url(#SVGID_5_);"/>
|
||||||
|
<clipPath id="SVGID_6_">
|
||||||
|
<use xlink:href="#SVGID_4_" style="overflow:visible;"/>
|
||||||
|
</clipPath>
|
||||||
|
<path class="st11" d="M105.3,118.3c9.4,0.7,36.6,1.5,53.2,14.7c25,20,31.2,47.1,31.2,47.1l0.4,35.3l-84.8,0.2V118.3z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st10" d="M189.8,121.3l-20.6,0c-1.4,0-2.6-1.2-2.6-2.6l0-21.8l22.9,22.7L189.8,121.3z"/>
|
||||||
|
<path class="st12" d="M189.6,119.7h-20.5c-1.4,0-2.5-1.1-2.5-2.5V96.9l0,0l22.9,22.7L189.6,119.7z"/>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 113.9984 109.809)" class="st8 st9 st3">main.js</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="93.2369" y1="173.2516" x2="176.6469" y2="173.2516">
|
||||||
|
<stop offset="0" style="stop-color:#EA5B0C"/>
|
||||||
|
<stop offset="1" style="stop-color:#EA671E"/>
|
||||||
|
</linearGradient>
|
||||||
|
<path class="st13" d="M176.6,139.6V227c0,1.4-1.1,2.6-2.5,2.6H95.8c-1.4,0-2.5-1.1-2.5-2.6V119.5c0-1.4,1.1-2.5,2.5-2.5h58
|
||||||
|
L176.6,139.6z"/>
|
||||||
|
<g>
|
||||||
|
<defs>
|
||||||
|
<path id="XMLID_2_" d="M174.6,137.6V225c0,1.4-1.1,2.6-2.5,2.6H93.8c-1.4,0-2.5-1.1-2.5-2.6v-57.5v-50.1c0-1.4,1.1-2.5,2.5-2.5
|
||||||
|
h58L174.6,137.6z"/>
|
||||||
|
</defs>
|
||||||
|
<linearGradient id="XMLID_12_" gradientUnits="userSpaceOnUse" x1="91.2369" y1="171.2516" x2="174.6469" y2="171.2516">
|
||||||
|
<stop offset="0" style="stop-color:#EA5B0C"/>
|
||||||
|
<stop offset="1" style="stop-color:#EA7B3D"/>
|
||||||
|
</linearGradient>
|
||||||
|
<use xlink:href="#XMLID_2_" style="overflow:visible;fill:url(#XMLID_12_);"/>
|
||||||
|
<clipPath id="XMLID_13_">
|
||||||
|
<use xlink:href="#XMLID_2_" style="overflow:visible;"/>
|
||||||
|
</clipPath>
|
||||||
|
<path class="st14" d="M90.5,136.3c9.4,0.7,36.6,1.5,53.2,14.7c25,20,31.2,47.1,31.2,47.1l0.4,35.3l-84.8,0.2V136.3z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st15" d="M175,139.3l-20.6,0c-1.4,0-2.6-1.2-2.6-2.6l0-21.8l22.9,22.7L175,139.3z"/>
|
||||||
|
<path class="st16" d="M174.8,137.7h-20.5c-1.4,0-2.5-1.1-2.5-2.5v-20.3l0,0l22.9,22.7L174.8,137.7z"/>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 97.1967 127.8222)" class="st8 st9 st3">style.css</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st17" d="M164.6,157.7v87.4c0,1.4-1.1,2.6-2.5,2.6H83.8c-1.4,0-2.5-1.1-2.5-2.6V137.5c0-1.4,1.1-2.5,2.5-2.5h58
|
||||||
|
L164.6,157.7z"/>
|
||||||
|
<g>
|
||||||
|
<defs>
|
||||||
|
<path id="XMLID_1_" d="M162.6,155.7v87.4c0,1.4-1.1,2.6-2.5,2.6H81.8c-1.4,0-2.5-1.1-2.5-2.6v-57.5v-50.1c0-1.4,1.1-2.5,2.5-2.5
|
||||||
|
h58L162.6,155.7z"/>
|
||||||
|
</defs>
|
||||||
|
<linearGradient id="XMLID_14_" gradientUnits="userSpaceOnUse" x1="79.2009" y1="189.3131" x2="162.6108" y2="189.3131">
|
||||||
|
<stop offset="0" style="stop-color:#A000FF"/>
|
||||||
|
<stop offset="1" style="stop-color:#C96EFF"/>
|
||||||
|
</linearGradient>
|
||||||
|
<use xlink:href="#XMLID_1_" style="overflow:visible;fill:url(#XMLID_14_);"/>
|
||||||
|
<clipPath id="XMLID_15_">
|
||||||
|
<use xlink:href="#XMLID_1_" style="overflow:visible;"/>
|
||||||
|
</clipPath>
|
||||||
|
<path class="st18" d="M78.5,154.4c9.4,0.7,36.6,1.5,53.2,14.7c25,20,31.2,47.1,31.2,47.1l0.4,35.3l-84.8,0.2V154.4z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st17" d="M163,157.4l-20.6,0c-1.4,0-2.6-1.2-2.6-2.6l0-21.8l22.9,22.7L163,157.4z"/>
|
||||||
|
<path class="st19" d="M162.8,155.8h-20.5c-1.4,0-2.5-1.1-2.5-2.5V133l0,0l22.9,22.7L162.8,155.8z"/>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 83.1607 145.8838)" class="st8 st9 st3">index.html</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st20" d="M455.2,108.2v87.4c0,1.4-1.1,2.6-2.5,2.6h-78.3c-1.4,0-2.5-1.1-2.5-2.6V88.1c0-1.4,1.1-2.5,2.5-2.5h58
|
||||||
|
L455.2,108.2z"/>
|
||||||
|
<path class="st0" d="M455.3,110.3l-18.4,0c-1.4,0-2.5-1.1-2.5-2.5V87.5l0,0l20.9,20.7L455.3,110.3z"/>
|
||||||
|
<polygon class="st0" points="435.2,109.7 432.9,107.3 436.3,106.9 "/>
|
||||||
|
<path class="st1" d="M455.4,108.3h-20.5c-1.4,0-2.5-1.1-2.5-2.5V85.5l0,0l22.9,22.7L455.4,108.3z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 379.2604 144.9447)" class="st21 st9 st22">packed_fs.c</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polygon class="st23" points="483.6,195.4 397.3,195.4 394.3,193.4 397.3,162.8 481.5,159.7 483.6,162.8 "/>
|
||||||
|
<rect x="394.3" y="159.8" class="st21" width="87.3" height="33.7"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 400.0845 171.7588)" class="st8 st9 st24">mg_unpack()</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 400.0845 186.935)" class="st8 st9 st24">mg_unlist()</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g class="st25">
|
||||||
|
<g>
|
||||||
|
<path class="st26" d="M467.7,256.6V344c0,1.4-1.1,2.6-2.5,2.6h-78.3c-1.4,0-2.5-1.1-2.5-2.6V236.4c0-1.4,1.1-2.5,2.5-2.5h58
|
||||||
|
L467.7,256.6z"/>
|
||||||
|
<g>
|
||||||
|
<defs>
|
||||||
|
<path id="XMLID_9_" d="M465.7,254.6V342c0,1.4-1.1,2.6-2.5,2.6h-78.3c-1.4,0-2.5-1.1-2.5-2.6v-57.5v-50.1c0-1.4,1.1-2.5,2.5-2.5
|
||||||
|
h58L465.7,254.6z"/>
|
||||||
|
</defs>
|
||||||
|
<linearGradient id="XMLID_16_" gradientUnits="userSpaceOnUse" x1="382.3115" y1="288.2073" x2="465.7215" y2="288.2073">
|
||||||
|
<stop offset="0" style="stop-color:#D6D4D6"/>
|
||||||
|
<stop offset="1" style="stop-color:#FFFDFE"/>
|
||||||
|
</linearGradient>
|
||||||
|
<use xlink:href="#XMLID_9_" style="overflow:visible;fill:url(#XMLID_16_);"/>
|
||||||
|
<clipPath id="XMLID_17_">
|
||||||
|
<use xlink:href="#XMLID_9_" style="overflow:visible;"/>
|
||||||
|
</clipPath>
|
||||||
|
<path class="st27" d="M381.6,253.3c9.4,0.7,36.6,1.5,53.2,14.7c25,20,31.2,47.1,31.2,47.1l0.4,35.3l-84.8,0.2V253.3z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st28" d="M466.1,256.3l-20.6,0c-1.4,0-2.6-1.2-2.6-2.6l0-21.8l22.9,22.7L466.1,256.3z"/>
|
||||||
|
<path class="st29" d="M465.9,254.7h-20.5c-1.4,0-2.5-1.1-2.5-2.5v-20.3l0,0l22.9,22.7L465.9,254.7z"/>
|
||||||
|
<text transform="matrix(1 0 0 1 386.2713 244.7779)" class="st30 st9 st31">mongoose.c</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st26" d="M455.7,276.6v87.4c0,1.4-1.1,2.6-2.5,2.6h-78.3c-1.4,0-2.5-1.1-2.5-2.6V256.5c0-1.4,1.1-2.5,2.5-2.5h58
|
||||||
|
L455.7,276.6z"/>
|
||||||
|
<g>
|
||||||
|
<defs>
|
||||||
|
<path id="XMLID_8_" d="M453.7,274.6v87.4c0,1.4-1.1,2.6-2.5,2.6h-78.3c-1.4,0-2.5-1.1-2.5-2.6v-57.5v-50.1
|
||||||
|
c0-1.4,1.1-2.5,2.5-2.5h58L453.7,274.6z"/>
|
||||||
|
</defs>
|
||||||
|
<linearGradient id="XMLID_18_" gradientUnits="userSpaceOnUse" x1="370.2755" y1="308.2689" x2="453.6855" y2="308.2689">
|
||||||
|
<stop offset="0" style="stop-color:#D6D4D6"/>
|
||||||
|
<stop offset="1" style="stop-color:#FFFDFE"/>
|
||||||
|
</linearGradient>
|
||||||
|
<use xlink:href="#XMLID_8_" style="overflow:visible;fill:url(#XMLID_18_);"/>
|
||||||
|
<clipPath id="XMLID_19_">
|
||||||
|
<use xlink:href="#XMLID_8_" style="overflow:visible;"/>
|
||||||
|
</clipPath>
|
||||||
|
<path class="st32" d="M369.6,273.3c9.4,0.7,36.6,1.5,53.2,14.7c25,20,31.2,47.1,31.2,47.1l0.4,35.3l-84.8,0.2V273.3z"/>
|
||||||
|
<use xlink:href="#XMLID_8_" style="overflow:visible;fill:none;stroke:#FFFDFE;stroke-width:0.25;stroke-miterlimit:10;"/>
|
||||||
|
</g>
|
||||||
|
<path class="st28" d="M454,276.4l-20.6,0c-1.4,0-2.6-1.2-2.6-2.6l0-21.8l22.9,22.7L454,276.4z"/>
|
||||||
|
<path class="st29" d="M453.8,274.8h-20.5c-1.4,0-2.5-1.1-2.5-2.5v-20.3l0,0l22.9,22.7L453.8,274.8z"/>
|
||||||
|
<text transform="matrix(1 0 0 1 379.2353 264.8395)" class="st30 st9 st31">app.c</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polygon class="st33" points="342.3,123.3 351.1,141.7 342.3,160.7 234.8,160.9 234.8,122.6 "/>
|
||||||
|
<path class="st23" d="M347.4,145.4"/>
|
||||||
|
<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="233.6667" y1="139.4088" x2="350.0318" y2="139.4088">
|
||||||
|
<stop offset="0" style="stop-color:#C1D5FF"/>
|
||||||
|
<stop offset="0.1995" style="stop-color:#BACDF6"/>
|
||||||
|
<stop offset="0.6001" style="stop-color:#B0C3EA"/>
|
||||||
|
<stop offset="1" style="stop-color:#ADC0E6"/>
|
||||||
|
</linearGradient>
|
||||||
|
<polygon class="st34" points="341.2,120.9 350,139.3 341.2,158.3 233.7,158.5 233.7,120.3 "/>
|
||||||
|
<text transform="matrix(1 0 0 1 262.9869 146.2633)" class="st8 st9 st35">PACK</text>
|
||||||
|
<polygon class="st33" points="234.8,160.9 233.7,158.5 238,158.7 "/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st23" d="M634.9,144.6L634.9,144.6z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polygon class="st33" points="619.6,139.3 619.6,139.3 618.7,137.4 618.7,137.4 "/>
|
||||||
|
<path class="st23" d="M623.1,168.2L623.1,168.2z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polygon class="st33" points="619.6,288.1 618.7,290 618.7,290 619.6,288.1 "/>
|
||||||
|
<path class="st23" d="M623.1,259.2L623.1,259.2z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st23" d="M617,282L617,282z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<defs>
|
||||||
|
<path id="XMLID_10_" d="M722.7,276.5h-71.2c-11.6,0-21.1-9.4-21.1-21.1v-71.2c0-11.6,9.4-21.1,21.1-21.1h71.2
|
||||||
|
c11.6,0,21.1,9.4,21.1,21.1v71.2C743.8,267,734.4,276.5,722.7,276.5z"/>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#XMLID_10_" style="overflow:visible;fill:#610041;"/>
|
||||||
|
<clipPath id="XMLID_20_">
|
||||||
|
<use xlink:href="#XMLID_10_" style="overflow:visible;"/>
|
||||||
|
</clipPath>
|
||||||
|
<path class="st36" d="M754.3,172.5c-2.7,8-17.3,30.9-65.8,30.8c-24.5,0-48.4-7.4-65.7-24.7l-12.1-12l69.3-64.6L754.3,172.5z"/>
|
||||||
|
</g>
|
||||||
|
<polygon class="st37" points="637.6,271.2 632.5,266.1 640.3,269.4 "/>
|
||||||
|
<polygon class="st37" points="738.3,169.9 734.6,166.2 736.4,171 "/>
|
||||||
|
<g>
|
||||||
|
<defs>
|
||||||
|
<path id="XMLID_5_" d="M719.7,273.5h-71.2c-11.6,0-21.1-9.4-21.1-21.1v-71.2c0-11.6,9.4-21.1,21.1-21.1h71.2
|
||||||
|
c11.6,0,21.1,9.4,21.1,21.1v71.2C740.8,264,731.4,273.5,719.7,273.5z"/>
|
||||||
|
</defs>
|
||||||
|
<radialGradient id="XMLID_21_" cx="684.119" cy="216.7761" r="56.6929" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" style="stop-color:#8B005D;stop-opacity:0.7"/>
|
||||||
|
<stop offset="1" style="stop-color:#8B005D"/>
|
||||||
|
</radialGradient>
|
||||||
|
<use xlink:href="#XMLID_5_" style="overflow:visible;fill:url(#XMLID_21_);"/>
|
||||||
|
<clipPath id="XMLID_22_">
|
||||||
|
<use xlink:href="#XMLID_5_" style="overflow:visible;"/>
|
||||||
|
</clipPath>
|
||||||
|
<radialGradient id="SVGID_9_" cx="678.486" cy="148.5836" r="90.2195" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" style="stop-color:#8B005D"/>
|
||||||
|
<stop offset="1" style="stop-color:#FFFFFF"/>
|
||||||
|
</radialGradient>
|
||||||
|
<path class="st38" d="M750.3,168.5c-2.7,8-17.3,30.9-65.8,30.8c-24.5,0-48.4-7.4-65.7-24.7l-12.1-12L676,97.9L750.3,168.5z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<text transform="matrix(1 0 0 1 646.3768 216.49)" class="st8 st9 st35">APP.EXE</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st8" d="M732,240.1c-0.3-1.4-0.8-2.7-1.4-3.9l1.7-2.3c-0.3-0.5-0.6-0.9-1-1.4c-0.3-0.5-0.7-0.9-1.1-1.3l-2.7,1
|
||||||
|
c-1-1-2.2-1.8-3.4-2.4l0-2.9c-1-0.4-2.1-0.8-3.2-1l-1.5,2.4c-1.4-0.2-2.8-0.2-4.1,0l-1.7-2.4c-1.1,0.2-2.1,0.6-3.1,1.1l0.2,2.9
|
||||||
|
c-0.6,0.3-1.2,0.7-1.7,1.1c-0.6,0.4-1.1,0.9-1.5,1.4l-2.7-0.9l-1.9,2.7l1.8,2.3c-0.6,1.3-1,2.6-1.1,4l-2.7,0.9
|
||||||
|
c-0.1,1.1,0,2.2,0.1,3.3l2.8,0.8c0.3,1.3,0.8,2.7,1.4,3.9l-1.7,2.3c0.3,0.5,0.6,0.9,1,1.4c0.3,0.4,0.7,0.9,1.1,1.3l2.7-1
|
||||||
|
c1,1,2.2,1.8,3.4,2.4l0,2.9c1,0.4,2.1,0.8,3.2,1l1.5-2.4c1.4,0.2,2.8,0.2,4.1,0l1.7,2.4c1.1-0.3,2.1-0.6,3.1-1.1l-0.2-2.9
|
||||||
|
c0.6-0.3,1.2-0.7,1.7-1.1c0.6-0.4,1.1-0.9,1.5-1.4l2.8,0.9c0.7-0.8,1.3-1.8,1.9-2.7L731,249c0.6-1.3,1-2.6,1.1-4l2.7-0.9
|
||||||
|
c0.1-1.1,0-2.2-0.1-3.3L732,240.1z M724,251c-4.6,3.5-11.1,2.6-14.6-2c-3.5-4.6-2.6-11.1,2-14.6s11.1-2.6,14.6,2
|
||||||
|
C729.5,241,728.6,247.5,724,251z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st8" d="M700.5,249.3c-0.2-0.8-0.5-1.6-0.8-2.4l1-1.4c-0.2-0.3-0.4-0.6-0.6-0.8c-0.2-0.3-0.4-0.5-0.7-0.8l-1.6,0.6
|
||||||
|
c-0.6-0.6-1.3-1.1-2.1-1.4l0-1.7c-0.6-0.3-1.3-0.5-1.9-0.6l-0.9,1.4c-0.8-0.1-1.7-0.1-2.5,0l-1-1.4c-0.6,0.1-1.3,0.4-1.9,0.6
|
||||||
|
l0.1,1.7c-0.4,0.2-0.7,0.4-1,0.7c-0.3,0.3-0.6,0.5-0.9,0.8l-1.6-0.6l-1.1,1.6l1.1,1.4c-0.3,0.8-0.6,1.6-0.7,2.4l-1.6,0.5
|
||||||
|
c0,0.7,0,1.3,0.1,2l1.7,0.5c0.2,0.8,0.4,1.6,0.9,2.4l-1,1.4c0.2,0.3,0.4,0.6,0.6,0.8c0.2,0.3,0.4,0.5,0.7,0.8l1.6-0.6
|
||||||
|
c0.6,0.6,1.3,1.1,2.1,1.4l0,1.7c0.6,0.3,1.3,0.5,1.9,0.6l0.9-1.4c0.8,0.1,1.7,0.1,2.5,0l1,1.4c0.6-0.2,1.3-0.4,1.9-0.6l-0.1-1.7
|
||||||
|
c0.4-0.2,0.7-0.4,1-0.7c0.3-0.3,0.6-0.5,0.9-0.8l1.6,0.6c0.4-0.5,0.8-1.1,1.1-1.6l-1.1-1.4c0.3-0.8,0.6-1.6,0.7-2.4l1.6-0.5
|
||||||
|
c0-0.7,0-1.3-0.1-2L700.5,249.3z M694.5,254.2c-1.8,1.4-4.5,1-5.9-0.8c-1.4-1.8-1-4.5,0.8-5.9c1.8-1.4,4.5-1,5.9,0.8
|
||||||
|
C696.8,250.2,696.4,252.8,694.5,254.2z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st39" d="M708.9,234.5"/>
|
||||||
|
<path class="st39" d="M706.7,237.9"/>
|
||||||
|
<path class="st40" d="M705.7,242.7c0-1.7,0.4-3.4,1-4.9"/>
|
||||||
|
<path class="st40" d="M708.9,234.5c2.2-2.4,5.3-3.8,8.8-3.8"/>
|
||||||
|
<path class="st40" d="M687.4,247.3c1.1-1.4,2.7-2.2,4.6-2.2"/>
|
||||||
|
<path class="st39" d="M686.4,249.5"/>
|
||||||
|
<path class="st40" d="M686.2,250.9c0-0.5,0.1-1,0.2-1.4"/>
|
||||||
|
<path class="st39" d="M686.7,253.2"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st33" d="M611.7,174.2l-23,3.3l-13.1-9.6c-6.3-4.6-13.9-7.1-21.7-7l-55.5,0.1l-1.1-2.4h1.1l64.2-0.2h0.1l3.9,0h0.1
|
||||||
|
l3.2,2.4l19.2,14l0.2,0.2l23.4-3.3L611.7,174.2z"/>
|
||||||
|
<path class="st23" d="M595.9,145.5L595.9,145.5z"/>
|
||||||
|
<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="497.2089" y1="147.636" x2="612.739" y2="147.636">
|
||||||
|
<stop offset="0" style="stop-color:#C1D5FF"/>
|
||||||
|
<stop offset="0.1995" style="stop-color:#BACDF6"/>
|
||||||
|
<stop offset="0.6001" style="stop-color:#B0C3EA"/>
|
||||||
|
<stop offset="1" style="stop-color:#ADC0E6"/>
|
||||||
|
</linearGradient>
|
||||||
|
<path class="st41" d="M612.7,171.7l-3,0.4l-20.3,2.8l-0.2-0.1l-13.6-9.9l0,0c-3.7-2.7-8.7-4.4-12.9-5.4c-2.9-0.7-6-1-9-1l-35.5,0.1
|
||||||
|
l-19.8,0.1h-1.1v-38.3l13.5,0.2l4,0l42.6,0.2l7.9,0c7,0,14,1.6,20.2,4.9c0,0,0,0,0,0c0.8,0.5,1.6,0.9,2.4,1.5l7.5,5.6l0.1,0.1l0,0
|
||||||
|
l15.4,11.3v0l0.1,3.2L612.7,171.7z"/>
|
||||||
|
<linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="597.6228" y1="138.4046" x2="598.5728" y2="138.4046">
|
||||||
|
<stop offset="0" style="stop-color:#C1D5FF"/>
|
||||||
|
<stop offset="0.1995" style="stop-color:#BACDF6"/>
|
||||||
|
<stop offset="0.6001" style="stop-color:#B0C3EA"/>
|
||||||
|
<stop offset="1" style="stop-color:#ADC0E6"/>
|
||||||
|
</linearGradient>
|
||||||
|
<polygon class="st42" points="598.6,139.3 598.6,139.4 597.6,137.4 597.7,137.5 "/>
|
||||||
|
<text transform="matrix(1 0 0 1 513.7678 146.3103)" class="st8 st9 st35">BUILD</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st33" d="M612.7,258.8l-0.1,2.2l-1.8-0.2l-20.3-2.8l-0.2,0.1l-13.6,9.9l0-0.1c-3.7,2.7-8.7,4.4-12.9,5.4
|
||||||
|
c-2.9,0.7-6,1-9,1l-35.5-0.1l-19.8-0.1h-1.1v35.4l-1.1,0.4l1.1,2.4l13.5-0.2l4,0l42.6-0.2l7.9-0.1c7,0,14-1.6,20.2-4.9c0,0,0,0,0,0
|
||||||
|
c0.8-0.5,1.6-0.9,2.4-1.5l7.5-5.6l0.1-0.1l0,0l15.4-11.3l1-0.9l1.9-26.7L612.7,258.8z"/>
|
||||||
|
<path class="st43" d="M593.5,272.5"/>
|
||||||
|
<g>
|
||||||
|
<path class="st23" d="M598.5,285L598.5,285z"/>
|
||||||
|
|
||||||
|
<linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="497.2089" y1="451.0054" x2="612.739" y2="451.0054" gradientTransform="matrix(1 0 0 -1 0 733.8616)">
|
||||||
|
<stop offset="0" style="stop-color:#C1D5FF"/>
|
||||||
|
<stop offset="0.1995" style="stop-color:#BACDF6"/>
|
||||||
|
<stop offset="0.6001" style="stop-color:#B0C3EA"/>
|
||||||
|
<stop offset="1" style="stop-color:#ADC0E6"/>
|
||||||
|
</linearGradient>
|
||||||
|
<path class="st44" d="M612.7,258.8l-3-0.4l-20.3-2.8l-0.2,0.1l-13.6,9.9l0,0c-3.7,2.7-8.7,4.4-12.9,5.4c-2.9,0.7-6,1-9,1
|
||||||
|
l-35.5-0.1l-19.8-0.1h-1.1v38.3l13.5-0.2l4,0l42.6-0.2l7.9,0c7,0,14-1.6,20.2-4.9c0,0,0,0,0,0c0.8-0.5,1.6-0.9,2.4-1.5l7.5-5.6
|
||||||
|
l0.1-0.1l0,0l15.4-11.3v0l0.1-3.2L612.7,258.8z"/>
|
||||||
|
<text transform="matrix(1 0 0 1 513.7682 298.0848)" class="st8 st9 st35">BUILD</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st33" d="M619.9,149.1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1 @@
|
|||||||
|
See detailed tutorial at https://mongoose.ws/tutorials/captive-dns-portal/
|
||||||
@@ -21,11 +21,14 @@ uint8_t answer[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||||
if (ev == MG_EV_READ) {
|
if (ev == MG_EV_OPEN) {
|
||||||
|
c->is_hexdumping = 1;
|
||||||
|
} else if (ev == MG_EV_READ) {
|
||||||
struct mg_dns_rr rr; // Parse first question, offset 12 is header size
|
struct mg_dns_rr rr; // Parse first question, offset 12 is header size
|
||||||
size_t n = mg_dns_parse_rr(c->recv.buf, c->recv.len, 12, true, &rr);
|
size_t n = mg_dns_parse_rr(c->recv.buf, c->recv.len, 12, true, &rr);
|
||||||
|
MG_INFO(("DNS request parsed, result=%d", (int) n));
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
char buf[12 + n + sizeof(answer)];
|
char buf[512];
|
||||||
struct mg_dns_header *h = (struct mg_dns_header *) buf;
|
struct mg_dns_header *h = (struct mg_dns_header *) buf;
|
||||||
memset(buf, 0, sizeof(buf)); // Clear the whole datagram
|
memset(buf, 0, sizeof(buf)); // Clear the whole datagram
|
||||||
h->txnid = ((struct mg_dns_header *) c->recv.buf)->txnid; // Copy tnxid
|
h->txnid = ((struct mg_dns_header *) c->recv.buf)->txnid; // Copy tnxid
|
||||||
@@ -34,9 +37,9 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
h->flags = mg_htons(0x8400); // Authoritative response
|
h->flags = mg_htons(0x8400); // Authoritative response
|
||||||
memcpy(buf + sizeof(*h), c->recv.buf + sizeof(*h), n); // Copy question
|
memcpy(buf + sizeof(*h), c->recv.buf + sizeof(*h), n); // Copy question
|
||||||
memcpy(buf + sizeof(*h) + n, answer, sizeof(answer)); // And answer
|
memcpy(buf + sizeof(*h) + n, answer, sizeof(answer)); // And answer
|
||||||
mg_send(c, buf, sizeof(buf)); // And send it!
|
mg_send(c, buf, 12 + n + sizeof(answer)); // And send it!
|
||||||
}
|
}
|
||||||
mg_iobuf_delete(&c->recv, c->recv.len);
|
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
(void) fn_data;
|
||||||
(void) ev_data;
|
(void) ev_data;
|
||||||
@@ -45,7 +48,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_mgr mgr;
|
struct mg_mgr mgr;
|
||||||
|
|
||||||
mg_log_set("3"); // Set log level
|
mg_log_set(MG_LL_DEBUG); // Set log level
|
||||||
mg_mgr_init(&mgr); // Initialise event manager
|
mg_mgr_init(&mgr); // Initialise event manager
|
||||||
mg_listen(&mgr, s_listen_url, fn, NULL); // Start DNS server
|
mg_listen(&mgr, s_listen_url, fn, NULL); // Start DNS server
|
||||||
for (;;) mg_mgr_poll(&mgr, 1000); // Event loop
|
for (;;) mg_mgr_poll(&mgr, 1000); // Event loop
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
# A complete device dashboard
|
|
||||||
|
|
||||||
This example is a demonstration of how Mongoose Library could be integrated
|
|
||||||
into an embedded device and provide a complete device dashbord with the
|
|
||||||
following features:
|
|
||||||
|
|
||||||
- Multiple logins with different permissions (admin and user)
|
|
||||||
- Login screen for non-authenticated connections
|
|
||||||
- A [preact](https://preactjs.com/)-based dashboard with multiple pages
|
|
||||||
- Internal variables
|
|
||||||
- A form that changes those variables
|
|
||||||
- All changes are propagates to all connected clients
|
|
||||||
- Live log stream
|
|
||||||
- Live video stream
|
|
||||||
|
|
||||||
|
|
||||||
In essense, this example is a combination of several other examples
|
|
||||||
pulled together (multiple-logins, dashboard, live-log, video-stream).
|
|
||||||
|
|
||||||
# Screenshots
|
|
||||||
|
|
||||||
This is a login screen that prompts for user/password
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
# Main dashboard
|
|
||||||
|
|
||||||
A main dashboard page shows internal data, live video stream, and a form
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
A live log page shows live log stream coming from device
|
|
||||||
|
|
||||||

|
|
||||||
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 10 KiB |
@@ -1,204 +0,0 @@
|
|||||||
// Copyright (c) 2020 Cesanta Software Limited
|
|
||||||
// All rights reserved
|
|
||||||
|
|
||||||
#include "mjson.h"
|
|
||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
// Authenticated user.
|
|
||||||
// A user can be authenticated by:
|
|
||||||
// - a name:pass pair
|
|
||||||
// - a token
|
|
||||||
// When a user is shown a login screen, she enters a user:pass. If successful,
|
|
||||||
// a server returns user info which includes token. From that point on,
|
|
||||||
// client can use token for authentication. Tokens could be refreshed/changed
|
|
||||||
// on a server side, forcing clients to re-login.
|
|
||||||
struct user {
|
|
||||||
const char *name, *pass, *token;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is a configuration structure we're going to show on a dashboard
|
|
||||||
static struct config {
|
|
||||||
int value1;
|
|
||||||
char *value2;
|
|
||||||
} s_config = {123, NULL};
|
|
||||||
|
|
||||||
// Stringifies the config. A caller must free() it.
|
|
||||||
static char *stringify_config(struct config *cfg) {
|
|
||||||
char *s = NULL;
|
|
||||||
mjson_printf(mjson_print_dynamic_buf, &s, "{%Q:%d,%Q:%Q}", "value1",
|
|
||||||
cfg->value1, "value2", cfg->value2);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update config structure. Return true if changed, false otherwise
|
|
||||||
static bool update_config(struct mg_http_message *hm, struct config *cfg) {
|
|
||||||
bool changed = false;
|
|
||||||
char buf[256];
|
|
||||||
double dv;
|
|
||||||
if (mjson_get_number(hm->body.ptr, hm->body.len, "$.value1", &dv)) {
|
|
||||||
s_config.value1 = dv;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (mjson_get_string(hm->body.ptr, hm->body.len, "$.value2", buf,
|
|
||||||
sizeof(buf)) > 0) {
|
|
||||||
free(s_config.value2);
|
|
||||||
s_config.value2 = strdup(buf);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse HTTP requests, return authenticated user or NULL
|
|
||||||
static struct user *getuser(struct mg_http_message *hm) {
|
|
||||||
// In production, make passwords strong and tokens randomly generated
|
|
||||||
// In this example, user list is kept in RAM. In production, it can
|
|
||||||
// be backed by file, database, or some other method.
|
|
||||||
static struct user users[] = {
|
|
||||||
{"admin", "admin", "admin_token"},
|
|
||||||
{"user1", "pass1", "user1_token"},
|
|
||||||
{"user2", "pass2", "user2_token"},
|
|
||||||
{NULL, NULL, NULL},
|
|
||||||
};
|
|
||||||
char user[256], pass[256];
|
|
||||||
struct user *u;
|
|
||||||
mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass));
|
|
||||||
if (user[0] != '\0' && pass[0] != '\0') {
|
|
||||||
// Both user and password is set, search by user/password
|
|
||||||
for (u = users; u->name != NULL; u++)
|
|
||||||
if (strcmp(user, u->name) == 0 && strcmp(pass, u->pass) == 0) return u;
|
|
||||||
} else if (user[0] == '\0') {
|
|
||||||
// Only password is set, search by token
|
|
||||||
for (u = users; u->name != NULL; u++)
|
|
||||||
if (strcmp(pass, u->token) == 0) return u;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify all config watchers about the config change
|
|
||||||
static void notify_config_change(struct mg_mgr *mgr) {
|
|
||||||
struct mg_connection *c;
|
|
||||||
char *s = stringify_config(&s_config);
|
|
||||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
|
||||||
if (c->label[0] == 'W') mg_http_printf_chunk(c, "%s\n", s);
|
|
||||||
}
|
|
||||||
free(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP request handler function
|
|
||||||
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
|
||||||
struct user *u = getuser(hm);
|
|
||||||
// LOG(LL_INFO, ("%p [%.*s] auth %s", c->fd, (int) hm->uri.len, hm->uri.ptr,
|
|
||||||
// u ? u->name : "NULL"));
|
|
||||||
if (u == NULL && mg_http_match_uri(hm, "/api/#")) {
|
|
||||||
// All URIs starting with /api/ must be authenticated
|
|
||||||
mg_printf(c, "%s", "HTTP/1.1 403 Denied\r\nContent-Length: 0\r\n\r\n");
|
|
||||||
} else if (mg_http_match_uri(hm, "/api/config/get")) {
|
|
||||||
char *s = stringify_config(&s_config);
|
|
||||||
mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s\n",
|
|
||||||
(int) strlen(s) + 1, s);
|
|
||||||
free(s);
|
|
||||||
} else if (mg_http_match_uri(hm, "/api/config/set")) {
|
|
||||||
// Admins only
|
|
||||||
if (strcmp(u->name, "admin") == 0) {
|
|
||||||
if (update_config(hm, &s_config)) notify_config_change(fn_data);
|
|
||||||
mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
|
|
||||||
} else {
|
|
||||||
mg_printf(c, "%s", "HTTP/1.1 403 Denied\r\nContent-Length: 0\r\n\r\n");
|
|
||||||
}
|
|
||||||
} else if (mg_http_match_uri(hm, "/api/config/watch")) {
|
|
||||||
c->label[0] = 'W'; // Mark ourselves as a config watcher
|
|
||||||
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
|
||||||
} else if (mg_http_match_uri(hm, "/api/video1")) {
|
|
||||||
c->label[0] = 'S'; // Mark that connection as live streamer
|
|
||||||
mg_printf(
|
|
||||||
c, "%s",
|
|
||||||
"HTTP/1.0 200 OK\r\n"
|
|
||||||
"Cache-Control: no-cache\r\n"
|
|
||||||
"Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
|
|
||||||
"Content-Type: multipart/x-mixed-replace; boundary=--foo\r\n\r\n");
|
|
||||||
} else if (mg_http_match_uri(hm, "/api/log/static")) {
|
|
||||||
mg_http_serve_file(c, hm, "log.txt", "text/plain", "");
|
|
||||||
} else if (mg_http_match_uri(hm, "/api/log/live")) {
|
|
||||||
c->label[0] = 'L'; // Mark that connection as live log listener
|
|
||||||
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
|
||||||
} else if (mg_http_match_uri(hm, "/api/login")) {
|
|
||||||
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
|
||||||
mg_http_printf_chunk(c, "{\"user\":\"%s\",\"token\":\"%s\"}\n", u->name,
|
|
||||||
u->token);
|
|
||||||
mg_http_printf_chunk(c, "");
|
|
||||||
} else {
|
|
||||||
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
|
||||||
mg_http_serve_dir(c, ev_data, &opts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The image stream is simulated by sending MJPEG frames specified by the
|
|
||||||
// "files" array of file names.
|
|
||||||
static void broadcast_mjpeg_frame(struct mg_mgr *mgr) {
|
|
||||||
const char *files[] = {"images/1.jpg", "images/2.jpg", "images/3.jpg",
|
|
||||||
"images/4.jpg", "images/5.jpg", "images/6.jpg"};
|
|
||||||
size_t nfiles = sizeof(files) / sizeof(files[0]);
|
|
||||||
static size_t i;
|
|
||||||
const char *path = files[i++ % nfiles];
|
|
||||||
size_t size = mg_file_size(path);
|
|
||||||
char *data = mg_file_read(path); // Read next file
|
|
||||||
struct mg_connection *c;
|
|
||||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
|
||||||
if (c->label[0] != 'S') continue; // Skip non-stream connections
|
|
||||||
if (data == NULL || size == 0) continue; // Skip on file read error
|
|
||||||
mg_printf(c,
|
|
||||||
"--foo\r\nContent-Type: image/jpeg\r\n"
|
|
||||||
"Content-Length: %lu\r\n\r\n",
|
|
||||||
(unsigned long) size);
|
|
||||||
mg_send(c, data, size);
|
|
||||||
mg_send(c, "\r\n", 2);
|
|
||||||
}
|
|
||||||
free(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mjpeg_cb(void *arg) {
|
|
||||||
broadcast_mjpeg_frame(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void log_message(const char *filename, const char *message) {
|
|
||||||
FILE *fp = fopen(filename, "a");
|
|
||||||
if (fp != NULL) {
|
|
||||||
fprintf(fp, "%s", message);
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void broadcast_message(struct mg_mgr *mgr, const char *message) {
|
|
||||||
struct mg_connection *c;
|
|
||||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
|
||||||
if (c->label[0] == 'L') mg_http_printf_chunk(c, "%s", message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Timer function - called periodically.
|
|
||||||
// Prepare log message. Save it to a file, and broadcast.
|
|
||||||
static void log_cb(void *arg) {
|
|
||||||
char buf[64];
|
|
||||||
snprintf(buf, sizeof(buf), "Time is: %lu\n", (unsigned long) time(NULL));
|
|
||||||
log_message("log.txt", buf);
|
|
||||||
broadcast_message(arg, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_mgr mgr;
|
|
||||||
struct mg_timer t1, t2;
|
|
||||||
|
|
||||||
mg_mgr_init(&mgr);
|
|
||||||
mg_http_listen(&mgr, "http://localhost:8000", cb, &mgr);
|
|
||||||
mg_timer_init(&t1, 500, MG_TIMER_REPEAT, mjpeg_cb, &mgr);
|
|
||||||
mg_timer_init(&t2, 1000, MG_TIMER_REPEAT, log_cb, &mgr);
|
|
||||||
for (;;) mg_mgr_poll(&mgr, 50);
|
|
||||||
mg_timer_free(&t1);
|
|
||||||
mg_timer_free(&t2);
|
|
||||||
mg_mgr_free(&mgr);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,981 +0,0 @@
|
|||||||
// Copyright (c) 2018-2020 Cesanta Software Limited
|
|
||||||
// All rights reserved
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
#include "mjson.h"
|
|
||||||
|
|
||||||
#if !defined(_MSC_VER) || _MSC_VER >= 1700
|
|
||||||
#else
|
|
||||||
#define va_copy(x, y) (x) = (y)
|
|
||||||
#define snprintf _snprintf
|
|
||||||
#define alloca _alloca
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int mjson_esc(int c, int esc) {
|
|
||||||
const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\"";
|
|
||||||
for (p = esc ? esc1 : esc2; *p != '\0'; p++) {
|
|
||||||
if (*p == c) return esc ? esc2[p - esc1] : esc1[p - esc2];
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mjson_pass_string(const char *s, int len) {
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
if (s[i] == '\\' && i + 1 < len && mjson_esc(s[i + 1], 1)) {
|
|
||||||
i++;
|
|
||||||
} else if (s[i] == '\0') {
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
} else if (s[i] == '"') {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson(const char *s, int len, mjson_cb_t cb, void *ud) {
|
|
||||||
enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE;
|
|
||||||
unsigned char nesting[MJSON_MAX_DEPTH];
|
|
||||||
int i, depth = 0;
|
|
||||||
#define MJSONCALL(ev) \
|
|
||||||
if (cb != NULL && cb(ev, s, start, i - start + 1, ud)) return i + 1;
|
|
||||||
|
|
||||||
// In the ascii table, the distance between `[` and `]` is 2.
|
|
||||||
// Ditto for `{` and `}`. Hence +2 in the code below.
|
|
||||||
#define MJSONEOO() \
|
|
||||||
do { \
|
|
||||||
if (c != nesting[depth - 1] + 2) return MJSON_ERROR_INVALID_INPUT; \
|
|
||||||
depth--; \
|
|
||||||
if (depth == 0) { \
|
|
||||||
MJSONCALL(tok); \
|
|
||||||
return i + 1; \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
int start = i;
|
|
||||||
unsigned char c = ((unsigned char *) s)[i];
|
|
||||||
int tok = c;
|
|
||||||
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue;
|
|
||||||
// printf("- %c [%.*s] %d %d\n", c, i, s, depth, expecting);
|
|
||||||
switch (expecting) {
|
|
||||||
case S_VALUE:
|
|
||||||
if (c == '{') {
|
|
||||||
if (depth >= (int) sizeof(nesting)) return MJSON_ERROR_TOO_DEEP;
|
|
||||||
nesting[depth++] = c;
|
|
||||||
expecting = S_KEY;
|
|
||||||
break;
|
|
||||||
} else if (c == '[') {
|
|
||||||
if (depth >= (int) sizeof(nesting)) return MJSON_ERROR_TOO_DEEP;
|
|
||||||
nesting[depth++] = c;
|
|
||||||
break;
|
|
||||||
} else if (c == ']' && depth > 0) { // Empty array
|
|
||||||
MJSONEOO();
|
|
||||||
} else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) {
|
|
||||||
i += 3;
|
|
||||||
tok = MJSON_TOK_TRUE;
|
|
||||||
} else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) {
|
|
||||||
i += 3;
|
|
||||||
tok = MJSON_TOK_NULL;
|
|
||||||
} else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) {
|
|
||||||
i += 4;
|
|
||||||
tok = MJSON_TOK_FALSE;
|
|
||||||
} else if (c == '-' || ((c >= '0' && c <= '9'))) {
|
|
||||||
char *end = NULL;
|
|
||||||
strtod(&s[i], &end);
|
|
||||||
if (end != NULL) i += end - &s[i] - 1;
|
|
||||||
tok = MJSON_TOK_NUMBER;
|
|
||||||
} else if (c == '"') {
|
|
||||||
int n = mjson_pass_string(&s[i + 1], len - i - 1);
|
|
||||||
if (n < 0) return n;
|
|
||||||
i += n + 1;
|
|
||||||
tok = MJSON_TOK_STRING;
|
|
||||||
} else {
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
}
|
|
||||||
if (depth == 0) {
|
|
||||||
MJSONCALL(tok);
|
|
||||||
return i + 1;
|
|
||||||
}
|
|
||||||
expecting = S_COMMA_OR_EOO;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case S_KEY:
|
|
||||||
if (c == '"') {
|
|
||||||
int n = mjson_pass_string(&s[i + 1], len - i - 1);
|
|
||||||
if (n < 0) return n;
|
|
||||||
i += n + 1;
|
|
||||||
tok = MJSON_TOK_KEY;
|
|
||||||
expecting = S_COLON;
|
|
||||||
} else if (c == '}') { // Empty object
|
|
||||||
MJSONEOO();
|
|
||||||
expecting = S_COMMA_OR_EOO;
|
|
||||||
} else {
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case S_COLON:
|
|
||||||
if (c == ':') {
|
|
||||||
expecting = S_VALUE;
|
|
||||||
} else {
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case S_COMMA_OR_EOO:
|
|
||||||
if (depth <= 0) return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
if (c == ',') {
|
|
||||||
expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE;
|
|
||||||
} else if (c == ']' || c == '}') {
|
|
||||||
MJSONEOO();
|
|
||||||
} else {
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
MJSONCALL(tok);
|
|
||||||
}
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct msjon_get_data {
|
|
||||||
const char *path; // Lookup json path
|
|
||||||
int pos; // Current path index
|
|
||||||
int d1; // Current depth of traversal
|
|
||||||
int d2; // Expected depth of traversal
|
|
||||||
int i1; // Index in an array
|
|
||||||
int i2; // Expected index in an array
|
|
||||||
int obj; // If the value is array/object, offset where it starts
|
|
||||||
const char **tokptr; // Destination
|
|
||||||
int *toklen; // Destination length
|
|
||||||
int tok; // Returned token
|
|
||||||
};
|
|
||||||
|
|
||||||
static int mjson_plen(const char *s) {
|
|
||||||
int i = 0;
|
|
||||||
while (s[i] != '\0' && s[i] != '.' && s[i] != '[') i++;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mjson_get_cb(int tok, const char *s, int off, int len, void *ud) {
|
|
||||||
struct msjon_get_data *data = (struct msjon_get_data *) ud;
|
|
||||||
// printf("--> %2x %2d %2d %2d %2d\t'%s'\t'%.*s'\t\t'%.*s'\n", tok, data->d1,
|
|
||||||
// data->d2, data->i1, data->i2, data->path + data->pos, off, s, len,
|
|
||||||
// s + off);
|
|
||||||
if (data->tok != MJSON_TOK_INVALID) return 1; // Found
|
|
||||||
|
|
||||||
if (tok == '{') {
|
|
||||||
if (!data->path[data->pos] && data->d1 == data->d2) data->obj = off;
|
|
||||||
data->d1++;
|
|
||||||
} else if (tok == '[') {
|
|
||||||
if (data->d1 == data->d2 && data->path[data->pos] == '[') {
|
|
||||||
data->i1 = 0;
|
|
||||||
data->i2 = (int) strtod(&data->path[data->pos + 1], NULL);
|
|
||||||
if (data->i1 == data->i2) {
|
|
||||||
data->d2++;
|
|
||||||
data->pos += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!data->path[data->pos] && data->d1 == data->d2) data->obj = off;
|
|
||||||
data->d1++;
|
|
||||||
} else if (tok == ',') {
|
|
||||||
if (data->d1 == data->d2 + 1) {
|
|
||||||
data->i1++;
|
|
||||||
if (data->i1 == data->i2) {
|
|
||||||
while (data->path[data->pos] != ']') data->pos++;
|
|
||||||
data->pos++;
|
|
||||||
data->d2++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (tok == MJSON_TOK_KEY && data->d1 == data->d2 + 1 &&
|
|
||||||
data->path[data->pos] == '.' && s[off] == '"' &&
|
|
||||||
s[off + len - 1] == '"' &&
|
|
||||||
mjson_plen(&data->path[data->pos + 1]) == len - 2 &&
|
|
||||||
!memcmp(s + off + 1, &data->path[data->pos + 1], len - 2)) {
|
|
||||||
data->d2++;
|
|
||||||
data->pos += len - 1;
|
|
||||||
} else if (tok == MJSON_TOK_KEY && data->d1 == data->d2) {
|
|
||||||
return 1; // Exhausted path, not found
|
|
||||||
} else if (tok == '}' || tok == ']') {
|
|
||||||
data->d1--;
|
|
||||||
// data->d2--;
|
|
||||||
if (!data->path[data->pos] && data->d1 == data->d2 && data->obj != -1) {
|
|
||||||
data->tok = tok - 2;
|
|
||||||
if (data->tokptr) *data->tokptr = s + data->obj;
|
|
||||||
if (data->toklen) *data->toklen = off - data->obj + 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else if (MJSON_TOK_IS_VALUE(tok)) {
|
|
||||||
// printf("TOK --> %d\n", tok);
|
|
||||||
if (data->d1 == data->d2 && !data->path[data->pos]) {
|
|
||||||
data->tok = tok;
|
|
||||||
if (data->tokptr) *data->tokptr = s + off;
|
|
||||||
if (data->toklen) *data->toklen = len;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum mjson_tok mjson_find(const char *s, int len, const char *jp,
|
|
||||||
const char **tokptr, int *toklen) {
|
|
||||||
struct msjon_get_data data = {jp, 1, 0, 0, 0,
|
|
||||||
0, -1, tokptr, toklen, MJSON_TOK_INVALID};
|
|
||||||
if (jp[0] != '$') return MJSON_TOK_INVALID;
|
|
||||||
if (mjson(s, len, mjson_get_cb, &data) < 0) return MJSON_TOK_INVALID;
|
|
||||||
return (enum mjson_tok) data.tok;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_get_number(const char *s, int len, const char *path, double *v) {
|
|
||||||
const char *p;
|
|
||||||
int tok, n;
|
|
||||||
if ((tok = mjson_find(s, len, path, &p, &n)) == MJSON_TOK_NUMBER) {
|
|
||||||
if (v != NULL) *v = strtod(p, NULL);
|
|
||||||
}
|
|
||||||
return tok == MJSON_TOK_NUMBER ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_get_bool(const char *s, int len, const char *path, int *v) {
|
|
||||||
int tok = mjson_find(s, len, path, NULL, NULL);
|
|
||||||
if (tok == MJSON_TOK_TRUE && v != NULL) *v = 1;
|
|
||||||
if (tok == MJSON_TOK_FALSE && v != NULL) *v = 0;
|
|
||||||
return tok == MJSON_TOK_TRUE || tok == MJSON_TOK_FALSE ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mjson_unescape(const char *s, int len, char *to, int n) {
|
|
||||||
int i, j;
|
|
||||||
for (i = 0, j = 0; i < len && j < n; i++, j++) {
|
|
||||||
if (s[i] == '\\' && i + 1 < len) {
|
|
||||||
int c = mjson_esc(s[i + 1], 0);
|
|
||||||
if (c == 0) return -1;
|
|
||||||
to[j] = c;
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
to[j] = s[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (j >= n) return -1;
|
|
||||||
if (n > 0) to[j] = '\0';
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_get_string(const char *s, int len, const char *path, char *to,
|
|
||||||
int n) {
|
|
||||||
const char *p;
|
|
||||||
int sz;
|
|
||||||
if (mjson_find(s, len, path, &p, &sz) != MJSON_TOK_STRING) return -1;
|
|
||||||
return mjson_unescape(p + 1, sz - 2, to, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_get_hex(const char *s, int len, const char *x, char *to, int n) {
|
|
||||||
const char *p;
|
|
||||||
int i, j, sz;
|
|
||||||
if (mjson_find(s, len, x, &p, &sz) != MJSON_TOK_STRING) return -1;
|
|
||||||
for (i = j = 0; i < sz - 3 && j < n; i += 2, j++) {
|
|
||||||
#define HEXTOI(x) (x >= '0' && x <= '9' ? x - '0' : x - 'W')
|
|
||||||
unsigned char a = *(const unsigned char *) (p + i + 1);
|
|
||||||
unsigned char b = *(const unsigned char *) (p + i + 2);
|
|
||||||
((unsigned char *) to)[j] = (HEXTOI(a) << 4) | HEXTOI(b);
|
|
||||||
}
|
|
||||||
if (j < n) to[j] = '\0';
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_BASE64
|
|
||||||
static int mjson_base64rev(int c) {
|
|
||||||
if (c >= 'A' && c <= 'Z') {
|
|
||||||
return c - 'A';
|
|
||||||
} else if (c >= 'a' && c <= 'z') {
|
|
||||||
return c + 26 - 'a';
|
|
||||||
} else if (c >= '0' && c <= '9') {
|
|
||||||
return c + 52 - '0';
|
|
||||||
} else if (c == '+') {
|
|
||||||
return 62;
|
|
||||||
} else if (c == '/') {
|
|
||||||
return 63;
|
|
||||||
} else {
|
|
||||||
return 64;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_base64_dec(const char *src, int n, char *dst, int dlen) {
|
|
||||||
const char *end = src + n;
|
|
||||||
int len = 0;
|
|
||||||
while (src + 3 < end && len < dlen) {
|
|
||||||
int a = mjson_base64rev(src[0]), b = mjson_base64rev(src[1]),
|
|
||||||
c = mjson_base64rev(src[2]), d = mjson_base64rev(src[3]);
|
|
||||||
dst[len++] = (a << 2) | (b >> 4);
|
|
||||||
if (src[2] != '=' && len < dlen) {
|
|
||||||
dst[len++] = (b << 4) | (c >> 2);
|
|
||||||
if (src[3] != '=' && len < dlen) {
|
|
||||||
dst[len++] = (c << 6) | d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
src += 4;
|
|
||||||
}
|
|
||||||
if (len < dlen) dst[len] = '\0';
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_get_base64(const char *s, int len, const char *path, char *to,
|
|
||||||
int n) {
|
|
||||||
const char *p;
|
|
||||||
int sz;
|
|
||||||
if (mjson_find(s, len, path, &p, &sz) != MJSON_TOK_STRING) return 0;
|
|
||||||
return mjson_base64_dec(p + 1, sz - 2, to, n);
|
|
||||||
}
|
|
||||||
#endif // MJSON_ENABLE_BASE64
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_NEXT
|
|
||||||
struct nextdata {
|
|
||||||
int off, len, depth, t, vo, arrayindex;
|
|
||||||
int *koff, *klen, *voff, *vlen, *vtype;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int next_cb(int tok, const char *s, int off, int len, void *ud) {
|
|
||||||
struct nextdata *d = (struct nextdata *) ud;
|
|
||||||
// int i;
|
|
||||||
switch (tok) {
|
|
||||||
case '{':
|
|
||||||
case '[':
|
|
||||||
if (d->depth == 0 && tok == '[') d->arrayindex = 0;
|
|
||||||
if (d->depth == 1 && off > d->off) {
|
|
||||||
d->vo = off;
|
|
||||||
d->t = tok == '{' ? MJSON_TOK_OBJECT : MJSON_TOK_ARRAY;
|
|
||||||
if (d->voff) *d->voff = off;
|
|
||||||
if (d->vtype) *d->vtype = d->t;
|
|
||||||
}
|
|
||||||
d->depth++;
|
|
||||||
break;
|
|
||||||
case '}':
|
|
||||||
case ']':
|
|
||||||
d->depth--;
|
|
||||||
if (d->depth == 1 && d->vo) {
|
|
||||||
d->len = off + len;
|
|
||||||
if (d->vlen) *d->vlen = d->len - d->vo;
|
|
||||||
if (d->arrayindex >= 0) {
|
|
||||||
if (d->koff) *d->koff = d->arrayindex; // koff holds array index
|
|
||||||
if (d->klen) *d->klen = 0; // klen holds 0
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (d->depth == 1 && d->arrayindex >= 0) d->arrayindex++;
|
|
||||||
break;
|
|
||||||
case ',':
|
|
||||||
case ':':
|
|
||||||
break;
|
|
||||||
case MJSON_TOK_KEY:
|
|
||||||
if (d->depth == 1 && d->off < off) {
|
|
||||||
if (d->koff) *d->koff = off; // And report back to the user
|
|
||||||
if (d->klen) *d->klen = len; // If we have to
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (d->depth != 1) break;
|
|
||||||
// If we're iterating over the array
|
|
||||||
if (off > d->off) {
|
|
||||||
d->len = off + len;
|
|
||||||
if (d->vlen) *d->vlen = len; // value length
|
|
||||||
if (d->voff) *d->voff = off; // value offset
|
|
||||||
if (d->vtype) *d->vtype = tok; // value type
|
|
||||||
if (d->arrayindex >= 0) {
|
|
||||||
if (d->koff) *d->koff = d->arrayindex; // koff holds array index
|
|
||||||
if (d->klen) *d->klen = 0; // klen holds 0
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (d->arrayindex >= 0) d->arrayindex++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
(void) s;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_next(const char *s, int n, int off, int *koff, int *klen, int *voff,
|
|
||||||
int *vlen, int *vtype) {
|
|
||||||
struct nextdata d = {off, 0, 0, 0, 0, -1, koff, klen, voff, vlen, vtype};
|
|
||||||
mjson(s, n, next_cb, &d);
|
|
||||||
return d.len;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_PRINT
|
|
||||||
int mjson_print_fixed_buf(const char *ptr, int len, void *fndata) {
|
|
||||||
struct mjson_fixedbuf *fb = (struct mjson_fixedbuf *) fndata;
|
|
||||||
int i, left = fb->size - 1 - fb->len;
|
|
||||||
if (left < len) len = left;
|
|
||||||
for (i = 0; i < len; i++) fb->ptr[fb->len + i] = ptr[i];
|
|
||||||
fb->len += len;
|
|
||||||
fb->ptr[fb->len] = '\0';
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_dynamic_buf(const char *ptr, int len, void *fndata) {
|
|
||||||
char *s, *buf = *(char **) fndata;
|
|
||||||
int curlen = buf == NULL ? 0 : strlen(buf);
|
|
||||||
if ((s = (char *) realloc(buf, curlen + len + 1)) == NULL) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
memcpy(s + curlen, ptr, len);
|
|
||||||
s[curlen + len] = '\0';
|
|
||||||
*(char **) fndata = s;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_null(const char *ptr, int len, void *userdata) {
|
|
||||||
(void) ptr;
|
|
||||||
(void) userdata;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_file(const char *ptr, int len, void *userdata) {
|
|
||||||
return fwrite(ptr, 1, len, (FILE *) userdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_buf(mjson_print_fn_t fn, void *fndata, const char *buf,
|
|
||||||
int len) {
|
|
||||||
return fn(buf, len, fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_int(mjson_print_fn_t fn, void *fndata, int value,
|
|
||||||
int is_signed) {
|
|
||||||
char buf[20];
|
|
||||||
int len = snprintf(buf, sizeof(buf), is_signed ? "%d" : "%u", value);
|
|
||||||
return fn(buf, len, fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_long(mjson_print_fn_t fn, void *fndata, long value,
|
|
||||||
int is_signed) {
|
|
||||||
char buf[20];
|
|
||||||
const char *fmt = (is_signed ? "%ld" : "%lu");
|
|
||||||
int len = snprintf(buf, sizeof(buf), fmt, value);
|
|
||||||
return fn(buf, len, fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_dbl(mjson_print_fn_t fn, void *fndata, double d,
|
|
||||||
const char *fmt) {
|
|
||||||
char buf[40];
|
|
||||||
int n = snprintf(buf, sizeof(buf), fmt, d);
|
|
||||||
return fn(buf, n, fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_str(mjson_print_fn_t fn, void *fndata, const char *s, int len) {
|
|
||||||
int i, n = fn("\"", 1, fndata);
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
char c = mjson_esc(s[i], 1);
|
|
||||||
if (c) {
|
|
||||||
n += fn("\\", 1, fndata);
|
|
||||||
n += fn(&c, 1, fndata);
|
|
||||||
} else {
|
|
||||||
n += fn(&s[i], 1, fndata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n + fn("\"", 1, fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_BASE64
|
|
||||||
int mjson_print_b64(mjson_print_fn_t fn, void *fndata, const unsigned char *s,
|
|
||||||
int n) {
|
|
||||||
const char *t =
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
||||||
int i, len = fn("\"", 1, fndata);
|
|
||||||
for (i = 0; i < n; i += 3) {
|
|
||||||
int a = s[i], b = i + 1 < n ? s[i + 1] : 0, c = i + 2 < n ? s[i + 2] : 0;
|
|
||||||
char buf[4] = {t[a >> 2], t[(a & 3) << 4 | (b >> 4)], '=', '='};
|
|
||||||
if (i + 1 < n) buf[2] = t[(b & 15) << 2 | (c >> 6)];
|
|
||||||
if (i + 2 < n) buf[3] = t[c & 63];
|
|
||||||
len += fn(buf, sizeof(buf), fndata);
|
|
||||||
}
|
|
||||||
return len + fn("\"", 1, fndata);
|
|
||||||
}
|
|
||||||
#endif /* MJSON_ENABLE_BASE64 */
|
|
||||||
|
|
||||||
int mjson_vprintf(mjson_print_fn_t fn, void *fndata, const char *fmt,
|
|
||||||
va_list xap) {
|
|
||||||
int i = 0, n = 0;
|
|
||||||
va_list ap;
|
|
||||||
va_copy(ap, xap);
|
|
||||||
while (fmt[i] != '\0') {
|
|
||||||
if (fmt[i] == '%') {
|
|
||||||
char fc = fmt[++i];
|
|
||||||
int is_long = 0;
|
|
||||||
if (fc == 'l') {
|
|
||||||
is_long = 1;
|
|
||||||
fc = fmt[i + 1];
|
|
||||||
}
|
|
||||||
if (fc == 'Q') {
|
|
||||||
char *buf = va_arg(ap, char *);
|
|
||||||
n += mjson_print_str(fn, fndata, buf ? buf : "", buf ? strlen(buf) : 0);
|
|
||||||
} else if (strncmp(&fmt[i], ".*Q", 3) == 0) {
|
|
||||||
int len = va_arg(ap, int);
|
|
||||||
char *buf = va_arg(ap, char *);
|
|
||||||
n += mjson_print_str(fn, fndata, buf, len);
|
|
||||||
i += 2;
|
|
||||||
} else if (fc == 'd' || fc == 'u') {
|
|
||||||
int is_signed = (fc == 'd');
|
|
||||||
if (is_long) {
|
|
||||||
long val = va_arg(ap, long);
|
|
||||||
n += mjson_print_long(fn, fndata, val, is_signed);
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
int val = va_arg(ap, int);
|
|
||||||
n += mjson_print_int(fn, fndata, val, is_signed);
|
|
||||||
}
|
|
||||||
} else if (fc == 'B') {
|
|
||||||
const char *s = va_arg(ap, int) ? "true" : "false";
|
|
||||||
n += mjson_print_buf(fn, fndata, s, strlen(s));
|
|
||||||
} else if (fc == 's') {
|
|
||||||
char *buf = va_arg(ap, char *);
|
|
||||||
n += mjson_print_buf(fn, fndata, buf, strlen(buf));
|
|
||||||
} else if (strncmp(&fmt[i], ".*s", 3) == 0) {
|
|
||||||
int len = va_arg(ap, int);
|
|
||||||
char *buf = va_arg(ap, char *);
|
|
||||||
n += mjson_print_buf(fn, fndata, buf, len);
|
|
||||||
i += 2;
|
|
||||||
} else if (fc == 'g') {
|
|
||||||
n += mjson_print_dbl(fn, fndata, va_arg(ap, double), "%g");
|
|
||||||
} else if (fc == 'f') {
|
|
||||||
n += mjson_print_dbl(fn, fndata, va_arg(ap, double), "%f");
|
|
||||||
#if MJSON_ENABLE_BASE64
|
|
||||||
} else if (fc == 'V') {
|
|
||||||
int len = va_arg(ap, int);
|
|
||||||
const char *buf = va_arg(ap, const char *);
|
|
||||||
n += mjson_print_b64(fn, fndata, (unsigned char *) buf, len);
|
|
||||||
#endif
|
|
||||||
} else if (fc == 'H') {
|
|
||||||
const char *hex = "0123456789abcdef";
|
|
||||||
int i, len = va_arg(ap, int);
|
|
||||||
const unsigned char *p = va_arg(ap, const unsigned char *);
|
|
||||||
n += fn("\"", 1, fndata);
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
n += fn(&hex[(p[i] >> 4) & 15], 1, fndata);
|
|
||||||
n += fn(&hex[p[i] & 15], 1, fndata);
|
|
||||||
}
|
|
||||||
n += fn("\"", 1, fndata);
|
|
||||||
} else if (fc == 'M') {
|
|
||||||
mjson_vprint_fn_t vfn = va_arg(ap, mjson_vprint_fn_t);
|
|
||||||
n += vfn(fn, fndata, &ap);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
n += mjson_print_buf(fn, fndata, &fmt[i++], 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
va_end(xap);
|
|
||||||
va_end(ap);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_printf(mjson_print_fn_t fn, void *fndata, const char *fmt, ...) {
|
|
||||||
va_list ap;
|
|
||||||
int len;
|
|
||||||
va_start(ap, fmt);
|
|
||||||
len = mjson_vprintf(fn, fndata, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
#endif /* MJSON_ENABLE_PRINT */
|
|
||||||
|
|
||||||
#if MJSON_IMPLEMENT_STRTOD
|
|
||||||
static inline int is_digit(int c) {
|
|
||||||
return c >= '0' && c <= '9';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NOTE: strtod() implementation by Yasuhiro Matsumoto. */
|
|
||||||
double strtod(const char *str, char **end) {
|
|
||||||
double d = 0.0;
|
|
||||||
int sign = 1, n = 0;
|
|
||||||
const char *p = str, *a = str;
|
|
||||||
|
|
||||||
/* decimal part */
|
|
||||||
if (*p == '-') {
|
|
||||||
sign = -1;
|
|
||||||
++p;
|
|
||||||
} else if (*p == '+')
|
|
||||||
++p;
|
|
||||||
if (is_digit(*p)) {
|
|
||||||
d = (double) (*p++ - '0');
|
|
||||||
while (*p && is_digit(*p)) {
|
|
||||||
d = d * 10.0 + (double) (*p - '0');
|
|
||||||
++p;
|
|
||||||
++n;
|
|
||||||
}
|
|
||||||
a = p;
|
|
||||||
} else if (*p != '.')
|
|
||||||
goto done;
|
|
||||||
d *= sign;
|
|
||||||
|
|
||||||
/* fraction part */
|
|
||||||
if (*p == '.') {
|
|
||||||
double f = 0.0;
|
|
||||||
double base = 0.1;
|
|
||||||
++p;
|
|
||||||
|
|
||||||
if (is_digit(*p)) {
|
|
||||||
while (*p && is_digit(*p)) {
|
|
||||||
f += base * (*p - '0');
|
|
||||||
base /= 10.0;
|
|
||||||
++p;
|
|
||||||
++n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d += f * sign;
|
|
||||||
a = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* exponential part */
|
|
||||||
if ((*p == 'E') || (*p == 'e')) {
|
|
||||||
int e = 0;
|
|
||||||
++p;
|
|
||||||
|
|
||||||
sign = 1;
|
|
||||||
if (*p == '-') {
|
|
||||||
sign = -1;
|
|
||||||
++p;
|
|
||||||
} else if (*p == '+')
|
|
||||||
++p;
|
|
||||||
|
|
||||||
if (is_digit(*p)) {
|
|
||||||
while (*p == '0') ++p;
|
|
||||||
e = (int) (*p++ - '0');
|
|
||||||
while (*p && is_digit(*p)) {
|
|
||||||
e = e * 10 + (int) (*p - '0');
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
e *= sign;
|
|
||||||
} else if (!is_digit(*(a - 1))) {
|
|
||||||
a = str;
|
|
||||||
goto done;
|
|
||||||
} else if (*p == 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
if (d == 2.2250738585072011 && e == -308) {
|
|
||||||
d = 0.0;
|
|
||||||
a = p;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (d == 2.2250738585072012 && e <= -308) {
|
|
||||||
d *= 1.0e-308;
|
|
||||||
a = p;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 10; i++) d *= 10;
|
|
||||||
}
|
|
||||||
a = p;
|
|
||||||
} else if (p > str && !is_digit(*(p - 1))) {
|
|
||||||
a = str;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
if (end) *end = (char *) a;
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_MERGE
|
|
||||||
int mjson_merge(const char *s, int n, const char *s2, int n2,
|
|
||||||
mjson_print_fn_t fn, void *userdata) {
|
|
||||||
int koff, klen, voff, vlen, t, t2, k, off = 0, len = 0, comma = 0;
|
|
||||||
if (n < 2) return len;
|
|
||||||
len += fn("{", 1, userdata);
|
|
||||||
while ((off = mjson_next(s, n, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
|
|
||||||
#if !defined(_MSC_VER) || _MSC_VER >= 1700
|
|
||||||
char path[klen + 1];
|
|
||||||
#else
|
|
||||||
char *path = (char *) alloca(klen + 1);
|
|
||||||
#endif
|
|
||||||
const char *val;
|
|
||||||
memcpy(path, "$.", 2);
|
|
||||||
memcpy(path + 2, s + koff + 1, klen - 2);
|
|
||||||
path[klen] = '\0';
|
|
||||||
if ((t2 = mjson_find(s2, n2, path, &val, &k)) != MJSON_TOK_INVALID) {
|
|
||||||
if (t2 == MJSON_TOK_NULL) continue; // null deletes the key
|
|
||||||
} else {
|
|
||||||
val = s + voff; // Key is not found in the update. Copy the old value.
|
|
||||||
}
|
|
||||||
if (comma) len += fn(",", 1, userdata);
|
|
||||||
len += fn(s + koff, klen, userdata);
|
|
||||||
len += fn(":", 1, userdata);
|
|
||||||
if (t == MJSON_TOK_OBJECT && t2 == MJSON_TOK_OBJECT) {
|
|
||||||
len += mjson_merge(s + voff, vlen, val, k, fn, userdata);
|
|
||||||
} else {
|
|
||||||
if (t2 != MJSON_TOK_INVALID) vlen = k;
|
|
||||||
len += fn(val, vlen, userdata);
|
|
||||||
}
|
|
||||||
comma = 1;
|
|
||||||
}
|
|
||||||
// Add missing keys
|
|
||||||
off = 0;
|
|
||||||
while ((off = mjson_next(s2, n2, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
|
|
||||||
#if !defined(_MSC_VER) || _MSC_VER >= 1700
|
|
||||||
char path[klen + 1];
|
|
||||||
#else
|
|
||||||
char *path = (char *) alloca(klen + 1);
|
|
||||||
#endif
|
|
||||||
const char *val;
|
|
||||||
if (t == MJSON_TOK_NULL) continue;
|
|
||||||
memcpy(path, "$.", 2);
|
|
||||||
memcpy(path + 2, s2 + koff + 1, klen - 2);
|
|
||||||
path[klen] = '\0';
|
|
||||||
if (mjson_find(s, n, path, &val, &vlen) != MJSON_TOK_INVALID) continue;
|
|
||||||
if (comma) len += fn(",", 1, userdata);
|
|
||||||
len += fn(s2 + koff, klen, userdata);
|
|
||||||
len += fn(":", 1, userdata);
|
|
||||||
len += fn(s2 + voff, vlen, userdata);
|
|
||||||
comma = 1;
|
|
||||||
}
|
|
||||||
len += fn("}", 1, userdata);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
#endif // MJSON_ENABLE_MERGE
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_PRETTY
|
|
||||||
struct prettydata {
|
|
||||||
int level;
|
|
||||||
int len;
|
|
||||||
int prev;
|
|
||||||
const char *pad;
|
|
||||||
int padlen;
|
|
||||||
mjson_print_fn_t fn;
|
|
||||||
void *userdata;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int pretty_cb(int ev, const char *s, int off, int len, void *ud) {
|
|
||||||
struct prettydata *d = (struct prettydata *) ud;
|
|
||||||
int i;
|
|
||||||
switch (ev) {
|
|
||||||
case '{':
|
|
||||||
case '[':
|
|
||||||
d->level++;
|
|
||||||
d->len += d->fn(s + off, len, d->userdata);
|
|
||||||
break;
|
|
||||||
case '}':
|
|
||||||
case ']':
|
|
||||||
d->level--;
|
|
||||||
if (d->prev != '[' && d->prev != '{' && d->padlen > 0) {
|
|
||||||
d->len += d->fn("\n", 1, d->userdata);
|
|
||||||
for (i = 0; i < d->level; i++)
|
|
||||||
d->len += d->fn(d->pad, d->padlen, d->userdata);
|
|
||||||
}
|
|
||||||
d->len += d->fn(s + off, len, d->userdata);
|
|
||||||
break;
|
|
||||||
case ',':
|
|
||||||
d->len += d->fn(s + off, len, d->userdata);
|
|
||||||
if (d->padlen > 0) {
|
|
||||||
d->len += d->fn("\n", 1, d->userdata);
|
|
||||||
for (i = 0; i < d->level; i++)
|
|
||||||
d->len += d->fn(d->pad, d->padlen, d->userdata);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ':':
|
|
||||||
d->len += d->fn(s + off, len, d->userdata);
|
|
||||||
if (d->padlen > 0) d->len += d->fn(" ", 1, d->userdata);
|
|
||||||
break;
|
|
||||||
case MJSON_TOK_KEY:
|
|
||||||
if (d->prev == '{' && d->padlen > 0) {
|
|
||||||
d->len += d->fn("\n", 1, d->userdata);
|
|
||||||
for (i = 0; i < d->level; i++)
|
|
||||||
d->len += d->fn(d->pad, d->padlen, d->userdata);
|
|
||||||
}
|
|
||||||
d->len += d->fn(s + off, len, d->userdata);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (d->prev == '[' && d->padlen > 0) {
|
|
||||||
d->len += d->fn("\n", 1, d->userdata);
|
|
||||||
for (i = 0; i < d->level; i++)
|
|
||||||
d->len += d->fn(d->pad, d->padlen, d->userdata);
|
|
||||||
}
|
|
||||||
d->len += d->fn(s + off, len, d->userdata);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
d->prev = ev;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_pretty(const char *s, int n, const char *pad, mjson_print_fn_t fn,
|
|
||||||
void *userdata) {
|
|
||||||
struct prettydata d = {0, 0, 0, pad, strlen(pad), fn, userdata};
|
|
||||||
if (mjson(s, n, pretty_cb, &d) < 0) return -1;
|
|
||||||
return d.len;
|
|
||||||
}
|
|
||||||
#endif // MJSON_ENABLE_PRETTY
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_RPC
|
|
||||||
struct jsonrpc_ctx jsonrpc_default_context;
|
|
||||||
struct jsonrpc_userdata {
|
|
||||||
mjson_print_fn_t fn;
|
|
||||||
void *fndata;
|
|
||||||
};
|
|
||||||
|
|
||||||
int mjson_globmatch(const char *s1, int n1, const char *s2, int n2) {
|
|
||||||
int i = 0, j = 0, ni = 0, nj = 0;
|
|
||||||
while (i < n1 || j < n2) {
|
|
||||||
if (i < n1 && j < n2 && (s1[i] == '?' || s2[j] == s1[i])) {
|
|
||||||
i++, j++;
|
|
||||||
} else if (i < n1 && (s1[i] == '*' || s1[i] == '#')) {
|
|
||||||
ni = i, nj = j + 1, i++;
|
|
||||||
} else if (nj > 0 && nj <= n2 && (s1[i - 1] == '#' || s2[j] != '/')) {
|
|
||||||
i = ni, j = nj;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int jsonrpc_printer(const char *buf, int len, void *userdata) {
|
|
||||||
struct jsonrpc_userdata *u = (struct jsonrpc_userdata *) userdata;
|
|
||||||
return u->fn(buf, len, u->fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_return_errorv(struct jsonrpc_request *r, int code,
|
|
||||||
const char *message, const char *data_fmt,
|
|
||||||
va_list ap) {
|
|
||||||
if (r->id_len == 0) return;
|
|
||||||
mjson_printf(r->fn, r->fndata,
|
|
||||||
"{\"id\":%.*s,\"error\":{\"code\":%d,\"message\":%Q", r->id_len,
|
|
||||||
r->id, code, message == NULL ? "" : message);
|
|
||||||
if (data_fmt != NULL) {
|
|
||||||
mjson_printf(r->fn, r->fndata, ",\"data\":");
|
|
||||||
mjson_vprintf(r->fn, r->fndata, data_fmt, ap);
|
|
||||||
}
|
|
||||||
mjson_printf(r->fn, r->fndata, "}}\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_return_error(struct jsonrpc_request *r, int code,
|
|
||||||
const char *message, const char *data_fmt, ...) {
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, data_fmt);
|
|
||||||
jsonrpc_return_errorv(r, code, message, data_fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_return_successv(struct jsonrpc_request *r, const char *result_fmt,
|
|
||||||
va_list ap) {
|
|
||||||
if (r->id_len == 0) return;
|
|
||||||
mjson_printf(r->fn, r->fndata, "{\"id\":%.*s,\"result\":", r->id_len, r->id);
|
|
||||||
if (result_fmt != NULL) {
|
|
||||||
mjson_vprintf(r->fn, r->fndata, result_fmt, ap);
|
|
||||||
} else {
|
|
||||||
mjson_printf(r->fn, r->fndata, "%s", "null");
|
|
||||||
}
|
|
||||||
mjson_printf(r->fn, r->fndata, "}\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
|
|
||||||
...) {
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, result_fmt);
|
|
||||||
jsonrpc_return_successv(r, result_fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *req, int req_sz,
|
|
||||||
mjson_print_fn_t fn, void *fndata) {
|
|
||||||
const char *result = NULL, *error = NULL;
|
|
||||||
int result_sz = 0, error_sz = 0;
|
|
||||||
struct jsonrpc_method *m = NULL;
|
|
||||||
struct jsonrpc_userdata d;
|
|
||||||
struct jsonrpc_request r = {req, req_sz, 0, 0, 0, 0, 0,
|
|
||||||
0, &jsonrpc_printer, NULL, NULL};
|
|
||||||
|
|
||||||
d.fn = fn;
|
|
||||||
d.fndata = fndata;
|
|
||||||
r.fndata = &d;
|
|
||||||
|
|
||||||
// Is is a response frame?
|
|
||||||
mjson_find(req, req_sz, "$.result", &result, &result_sz);
|
|
||||||
if (result == NULL) mjson_find(req, req_sz, "$.error", &error, &error_sz);
|
|
||||||
if (result_sz > 0 || error_sz > 0) {
|
|
||||||
if (ctx->response_cb != NULL) ctx->response_cb(req, req_sz, ctx->userdata);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method must exist and must be a string
|
|
||||||
if (mjson_find(req, req_sz, "$.method", &r.method, &r.method_len) !=
|
|
||||||
MJSON_TOK_STRING) {
|
|
||||||
mjson_printf(fn, fndata, "{\"error\":{\"code\":-32700,\"message\":%.*Q}}\n",
|
|
||||||
req_sz, req);
|
|
||||||
ctx->in_len = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// id and params are optional
|
|
||||||
mjson_find(req, req_sz, "$.id", &r.id, &r.id_len);
|
|
||||||
mjson_find(req, req_sz, "$.params", &r.params, &r.params_len);
|
|
||||||
|
|
||||||
for (m = ctx->methods; m != NULL; m = m->next) {
|
|
||||||
if (mjson_globmatch(m->method, m->method_sz, r.method + 1,
|
|
||||||
r.method_len - 2) > 0) {
|
|
||||||
if (r.params == NULL) r.params = "";
|
|
||||||
r.userdata = m->cbdata;
|
|
||||||
m->cb(&r);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m == NULL) {
|
|
||||||
jsonrpc_return_error(&r, JSONRPC_ERROR_NOT_FOUND, "method not found", NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int jsonrpc_print_methods(mjson_print_fn_t fn, void *fndata,
|
|
||||||
va_list *ap) {
|
|
||||||
struct jsonrpc_ctx *ctx = va_arg(*ap, struct jsonrpc_ctx *);
|
|
||||||
struct jsonrpc_method *m;
|
|
||||||
int len = 0;
|
|
||||||
for (m = ctx->methods; m != NULL; m = m->next) {
|
|
||||||
if (m != ctx->methods) len += mjson_print_buf(fn, fndata, ",", 1);
|
|
||||||
len += mjson_print_str(fn, fndata, m->method, strlen(m->method));
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rpclist(struct jsonrpc_request *r) {
|
|
||||||
jsonrpc_return_success(r, "[%M]", jsonrpc_print_methods, r->userdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t response_cb,
|
|
||||||
void *userdata) {
|
|
||||||
ctx->response_cb = response_cb;
|
|
||||||
ctx->userdata = userdata;
|
|
||||||
jsonrpc_ctx_export(ctx, MJSON_RPC_LIST_NAME, rpclist, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_ctx_process_byte(struct jsonrpc_ctx *ctx, unsigned char ch,
|
|
||||||
mjson_print_fn_t fn, void *p) {
|
|
||||||
if (ctx->in_len >= (int) sizeof(ctx->in)) ctx->in_len = 0; // Overflow
|
|
||||||
if (ch == '\n') { // If new line, parse frame
|
|
||||||
if (ctx->in_len > 1) jsonrpc_ctx_process(ctx, ctx->in, ctx->in_len, fn, p);
|
|
||||||
ctx->in_len = 0;
|
|
||||||
} else {
|
|
||||||
ctx->in[ctx->in_len] = ch; // Append to the buffer
|
|
||||||
ctx->in_len++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_init(mjson_print_fn_t response_cb, void *userdata) {
|
|
||||||
jsonrpc_ctx_init(&jsonrpc_default_context, response_cb, userdata);
|
|
||||||
}
|
|
||||||
#endif // MJSON_ENABLE_RPC
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
// Copyright (c) 2018-2020 Cesanta Software Limited
|
|
||||||
// All rights reserved
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
#ifndef MJSON_H
|
|
||||||
#define MJSON_H
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#ifndef MJSON_ENABLE_PRINT
|
|
||||||
#define MJSON_ENABLE_PRINT 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MJSON_ENABLE_RPC
|
|
||||||
#define MJSON_ENABLE_RPC 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MJSON_ENABLE_BASE64
|
|
||||||
#define MJSON_ENABLE_BASE64 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MJSON_ENABLE_MERGE
|
|
||||||
#define MJSON_ENABLE_MERGE 0
|
|
||||||
#elif MJSON_ENABLE_MERGE
|
|
||||||
#define MJSON_ENABLE_NEXT 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MJSON_ENABLE_PRETTY
|
|
||||||
#define MJSON_ENABLE_PRETTY 0
|
|
||||||
#elif MJSON_ENABLE_PRETTY
|
|
||||||
#define MJSON_ENABLE_NEXT 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MJSON_ENABLE_NEXT
|
|
||||||
#define MJSON_ENABLE_NEXT 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef MJSON_RPC_IN_BUF_SIZE
|
|
||||||
#define MJSON_RPC_IN_BUF_SIZE 256
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MJSON_RPC_LIST_NAME
|
|
||||||
#define MJSON_RPC_LIST_NAME "rpc.list"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum {
|
|
||||||
MJSON_ERROR_INVALID_INPUT = -1,
|
|
||||||
MJSON_ERROR_TOO_DEEP = -2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mjson_tok {
|
|
||||||
MJSON_TOK_INVALID = 0,
|
|
||||||
MJSON_TOK_KEY = 1,
|
|
||||||
MJSON_TOK_STRING = 11,
|
|
||||||
MJSON_TOK_NUMBER = 12,
|
|
||||||
MJSON_TOK_TRUE = 13,
|
|
||||||
MJSON_TOK_FALSE = 14,
|
|
||||||
MJSON_TOK_NULL = 15,
|
|
||||||
MJSON_TOK_ARRAY = 91,
|
|
||||||
MJSON_TOK_OBJECT = 123,
|
|
||||||
};
|
|
||||||
#define MJSON_TOK_IS_VALUE(t) ((t) > 10 && (t) < 20)
|
|
||||||
|
|
||||||
typedef int (*mjson_cb_t)(int ev, const char *s, int off, int len, void *ud);
|
|
||||||
|
|
||||||
#ifndef MJSON_MAX_DEPTH
|
|
||||||
#define MJSON_MAX_DEPTH 20
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int mjson(const char *s, int len, mjson_cb_t cb, void *ud);
|
|
||||||
enum mjson_tok mjson_find(const char *s, int len, const char *jp,
|
|
||||||
const char **tokptr, int *toklen);
|
|
||||||
int mjson_get_number(const char *s, int len, const char *path, double *v);
|
|
||||||
int mjson_get_bool(const char *s, int len, const char *path, int *v);
|
|
||||||
int mjson_get_string(const char *s, int len, const char *path, char *to, int n);
|
|
||||||
int mjson_get_hex(const char *s, int len, const char *path, char *to, int n);
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_NEXT
|
|
||||||
int mjson_next(const char *s, int n, int off, int *koff, int *klen, int *voff,
|
|
||||||
int *vlen, int *vtype);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_BASE64
|
|
||||||
int mjson_get_base64(const char *s, int len, const char *path, char *to, int n);
|
|
||||||
int mjson_base64_dec(const char *src, int n, char *dst, int dlen);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_PRINT
|
|
||||||
typedef int (*mjson_print_fn_t)(const char *buf, int len, void *userdata);
|
|
||||||
typedef int (*mjson_vprint_fn_t)(mjson_print_fn_t, void *, va_list *);
|
|
||||||
|
|
||||||
struct mjson_fixedbuf {
|
|
||||||
char *ptr;
|
|
||||||
int size, len;
|
|
||||||
};
|
|
||||||
|
|
||||||
int mjson_printf(mjson_print_fn_t, void *, const char *fmt, ...);
|
|
||||||
int mjson_vprintf(mjson_print_fn_t, void *, const char *fmt, va_list ap);
|
|
||||||
int mjson_print_str(mjson_print_fn_t, void *, const char *s, int len);
|
|
||||||
int mjson_print_int(mjson_print_fn_t, void *, int value, int is_signed);
|
|
||||||
int mjson_print_long(mjson_print_fn_t, void *, long value, int is_signed);
|
|
||||||
int mjson_print_buf(mjson_print_fn_t fn, void *, const char *buf, int len);
|
|
||||||
|
|
||||||
int mjson_print_null(const char *ptr, int len, void *userdata);
|
|
||||||
int mjson_print_file(const char *ptr, int len, void *userdata);
|
|
||||||
int mjson_print_fixed_buf(const char *ptr, int len, void *userdata);
|
|
||||||
int mjson_print_dynamic_buf(const char *ptr, int len, void *userdata);
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_PRETTY
|
|
||||||
int mjson_pretty(const char *, int, const char *, mjson_print_fn_t, void *);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_MERGE
|
|
||||||
int mjson_merge(const char *, int, const char *, int, mjson_print_fn_t, void *);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // MJSON_ENABLE_PRINT
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_RPC
|
|
||||||
|
|
||||||
void jsonrpc_init(mjson_print_fn_t, void *userdata);
|
|
||||||
int mjson_globmatch(const char *s1, int n1, const char *s2, int n2);
|
|
||||||
|
|
||||||
struct jsonrpc_request {
|
|
||||||
const char *frame; // Points to the whole frame
|
|
||||||
int frame_len; // Frame length
|
|
||||||
const char *params; // Points to the "params" in the request frame
|
|
||||||
int params_len; // Length of the "params"
|
|
||||||
const char *id; // Points to the "id" in the request frame
|
|
||||||
int id_len; // Length of the "id"
|
|
||||||
const char *method; // Points to the "method" in the request frame
|
|
||||||
int method_len; // Length of the "method"
|
|
||||||
mjson_print_fn_t fn; // Printer function
|
|
||||||
void *fndata; // Printer function data
|
|
||||||
void *userdata; // Callback's user data as specified at export time
|
|
||||||
};
|
|
||||||
|
|
||||||
struct jsonrpc_method {
|
|
||||||
const char *method;
|
|
||||||
int method_sz;
|
|
||||||
void (*cb)(struct jsonrpc_request *);
|
|
||||||
void *cbdata;
|
|
||||||
struct jsonrpc_method *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Main RPC context, stores current request information and a list of
|
|
||||||
// exported RPC methods.
|
|
||||||
struct jsonrpc_ctx {
|
|
||||||
struct jsonrpc_method *methods;
|
|
||||||
void *userdata;
|
|
||||||
mjson_print_fn_t response_cb;
|
|
||||||
int in_len;
|
|
||||||
char in[MJSON_RPC_IN_BUF_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Registers function fn under the given name within the given RPC context
|
|
||||||
#define jsonrpc_ctx_export(ctx, name, fn, ud) \
|
|
||||||
do { \
|
|
||||||
static struct jsonrpc_method m = {(name), sizeof(name) - 1, (fn), 0, 0}; \
|
|
||||||
m.cbdata = (ud); \
|
|
||||||
m.next = (ctx)->methods; \
|
|
||||||
(ctx)->methods = &m; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t, void *);
|
|
||||||
void jsonrpc_return_error(struct jsonrpc_request *r, int code,
|
|
||||||
const char *message, const char *data_fmt, ...);
|
|
||||||
void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
|
|
||||||
...);
|
|
||||||
void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *req, int req_sz,
|
|
||||||
mjson_print_fn_t fn, void *fndata);
|
|
||||||
void jsonrpc_ctx_process_byte(struct jsonrpc_ctx *ctx, unsigned char ch,
|
|
||||||
mjson_print_fn_t fn, void *fndata);
|
|
||||||
|
|
||||||
extern struct jsonrpc_ctx jsonrpc_default_context;
|
|
||||||
|
|
||||||
#define jsonrpc_export(name, fn, ud) \
|
|
||||||
jsonrpc_ctx_export(&jsonrpc_default_context, (name), (fn), (ud))
|
|
||||||
|
|
||||||
#define jsonrpc_process(buf, len, fn, data) \
|
|
||||||
jsonrpc_ctx_process(&jsonrpc_default_context, (buf), (len), (fn), (data))
|
|
||||||
|
|
||||||
#define jsonrpc_process_byte(x, fn, data) \
|
|
||||||
jsonrpc_ctx_process_byte(&jsonrpc_default_context, (x), (fn), (data))
|
|
||||||
|
|
||||||
#define JSONRPC_ERROR_INVALID -32700 /* Invalid JSON was received */
|
|
||||||
#define JSONRPC_ERROR_NOT_FOUND -32601 /* The method does not exist */
|
|
||||||
#define JSONRPC_ERROR_BAD_PARAMS -32602 /* Invalid params passed */
|
|
||||||
#define JSONRPC_ERROR_INTERNAL -32603 /* Internal JSON-RPC error */
|
|
||||||
|
|
||||||
#endif // MJSON_ENABLE_RPC
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif // MJSON_H
|
|
||||||
|
Before Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 122 KiB |
@@ -1,312 +0,0 @@
|
|||||||
const {h, render, Component} = preact;
|
|
||||||
const html = htm.bind(h);
|
|
||||||
|
|
||||||
class Login extends Component {
|
|
||||||
state = {user: '', pass: ''};
|
|
||||||
onclick = (app) => {
|
|
||||||
const authhdr = 'Basic ' + btoa(this.state.user + ':' + this.state.pass);
|
|
||||||
const headers = {Authorization: authhdr};
|
|
||||||
return axios.get('/api/login', {headers})
|
|
||||||
.then(res => app.setUser(res.data.token))
|
|
||||||
.catch(err => alert('Login failed'));
|
|
||||||
};
|
|
||||||
onpassinput = (ev) => this.setState({pass: ev.target.value});
|
|
||||||
onuserinput = (ev) => this.setState({user: ev.target.value});
|
|
||||||
render({app}, {user, pass, signup}) {
|
|
||||||
return html`
|
|
||||||
<div class='mx-auto bg-light rounded border my-5' style='max-width: 480px;'>
|
|
||||||
<div class='form p-5 rounded form-sm'>
|
|
||||||
<h4 class="text-muted mb-4">Login </h4>
|
|
||||||
<input type='email' placeholder='Email' class='my-2 form-control'
|
|
||||||
oninput=${this.onuserinput} value=${user} />
|
|
||||||
<input type="password" placeholder="Password" class="my-2 form-control"
|
|
||||||
oninput=${this.onpassinput} value=${pass}
|
|
||||||
onchange=${ev => this.onclick(app)} />
|
|
||||||
<div class="mb-4">
|
|
||||||
<button class="btn btn-info btn-block"
|
|
||||||
disabled=${!user || !pass} onclick="${ev => this.onclick(app)}"
|
|
||||||
> Login </button>
|
|
||||||
</div>
|
|
||||||
<div class="text-muted small">
|
|
||||||
Valid logins: admin:admin, user1:pass1, user2:pass2
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Dropdown extends Component {
|
|
||||||
state = {show: false};
|
|
||||||
render(props, state) {
|
|
||||||
const onclick = x => this.setState({show: x});
|
|
||||||
const show = state.show ? 'show' : '';
|
|
||||||
return html`
|
|
||||||
<div class="dropdown autoexpand ${props.cls}">
|
|
||||||
<div type="buttonx" onclick=${() => onclick(!state.show)}
|
|
||||||
class="dropdown-toggle my-0 ${props.bcls}"
|
|
||||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
${props.title}
|
|
||||||
</div>
|
|
||||||
<div onclick=${() => onclick(false)} style=${props.dstyle}
|
|
||||||
class="dropdown-menu ${props.dcls} ${show}">
|
|
||||||
${props.children}
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const NavLink = ({href, title, url}) => html`<a class="nav-item nav-link
|
|
||||||
${url == href ? 'active' : ''}"
|
|
||||||
target=${href.match(/^http/) ? '_blank' : ''}
|
|
||||||
href="${href.match(/^http/) ? '' : '#'}${href}">${title}</a>`;
|
|
||||||
|
|
||||||
class Header extends Component {
|
|
||||||
state = {expanded: false};
|
|
||||||
ontoggle = () => this.setState({expanded: !this.state.expanded});
|
|
||||||
render(props, state) {
|
|
||||||
const u = props.app.state.user || {};
|
|
||||||
return html`
|
|
||||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
|
|
||||||
<a class="navbar-brand" href="#">
|
|
||||||
<img src="images/logo.png" width="26" height="26" alt="" class="mr-2" />
|
|
||||||
MyProduct
|
|
||||||
</a>
|
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"
|
|
||||||
onclick=${() => this.ontoggle()} >
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="navbar-collapse ${state.expanded ? '' : 'collapse'}"
|
|
||||||
id="navbarNav">
|
|
||||||
<div class="nav navbar-nav mr-auto">
|
|
||||||
<${NavLink} href="/" title="Dashboard" url=${props.url} />
|
|
||||||
<${NavLink} href="/logs" title="Logs" url=${props.url} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<form class="form-inline">
|
|
||||||
<${Dropdown} title="${u.user}" cls="mr-2"
|
|
||||||
bcls="btn btn-sm btn-outline-light pointer" dcls="m-0 dropdown-menu-right">
|
|
||||||
<div onclick=${() => props.app.setUser('')}
|
|
||||||
class="dropdown-item small pointer text-center">logout</div>
|
|
||||||
</${Dropdown}>
|
|
||||||
<img src="images/user.png" class="rounded-circle nav-item mr-2" width="30" />
|
|
||||||
</form>
|
|
||||||
</nav>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Info = () => html`<div class="alert alert-secondary my-3">
|
|
||||||
This dashboard shows values kept in server memory. Many clients
|
|
||||||
can open this page. As soon as any client changes any value,
|
|
||||||
all clients update automatically.
|
|
||||||
The JS code that watches state changes, reconnects on network failures.
|
|
||||||
That means if server restarts, dashboard on all connected clients
|
|
||||||
refresh automatically.
|
|
||||||
You can use <code>curl</code> command-line utility:
|
|
||||||
<br/><code>curl localhost:8000/api/config/get</code>
|
|
||||||
<br/><code>curl localhost:8000/api/config/watch</code>
|
|
||||||
<br/><code>curl localhost:8000/api/config/set -d '{"a":123}'</code>
|
|
||||||
<br/><span class="badge badge-danger">NOTE:</span> administrators
|
|
||||||
can change settings values, whilst users cannot.
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
class DashVideo extends Component {
|
|
||||||
render(props, state) {
|
|
||||||
const onclick = ev => axios.post(
|
|
||||||
'/api/config/set', {value1: +state.value1, value2: state.value2});
|
|
||||||
// alert(JSON.stringify(state));
|
|
||||||
return html`<div id="form">
|
|
||||||
<div><b>Change values</b></div>
|
|
||||||
<table>
|
|
||||||
<tr><td>Value 1 (number):</td><td><input value=${state.value1}
|
|
||||||
onInput=${linkState(this, 'value1')} /></td></tr>
|
|
||||||
<tr><td>Value 2 (string):</td><td><input value=${state.value2}
|
|
||||||
onInput=${linkState(this, 'value2')}/></td></tr>
|
|
||||||
<tr><td></td>
|
|
||||||
<td><button onClick=${onclick}>Save values</button></td></tr>
|
|
||||||
</table>
|
|
||||||
</div>`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class DashForm extends Component {
|
|
||||||
render(props, state) {
|
|
||||||
const onclick = ev => axios.post(
|
|
||||||
'/api/config/set', {value1: +state.value1, value2: state.value2});
|
|
||||||
// alert(JSON.stringify(state));
|
|
||||||
return html`<div id="form">
|
|
||||||
<div><b>Change values</b></div>
|
|
||||||
<table>
|
|
||||||
<tr><td>Value 1 (number):</td><td><input class="form-control form-control-sm"
|
|
||||||
value=${state.value1}
|
|
||||||
onInput=${linkState(this, 'value1')} /></td></tr>
|
|
||||||
<tr><td>Value 2 (string):</td><td><input class="form-control form-control-sm"
|
|
||||||
value=${state.value2}
|
|
||||||
onInput=${linkState(this, 'value2')}/></td></tr>
|
|
||||||
<tr><td></td>
|
|
||||||
<td><button class="btn btn-primary btn-block"
|
|
||||||
onClick=${onclick}>Save values</button></td></tr>
|
|
||||||
</table>
|
|
||||||
</div>`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class DashSettings extends Component {
|
|
||||||
state = {value1: null, value2: null};
|
|
||||||
componentDidMount() {
|
|
||||||
axios.get('/api/config/get')
|
|
||||||
.then(r => this.setState(r.data))
|
|
||||||
.catch(e => console.log(e));
|
|
||||||
var self = this;
|
|
||||||
var f = function(reader) {
|
|
||||||
return reader.read().then(function(result) {
|
|
||||||
var data = String.fromCharCode.apply(null, result.value);
|
|
||||||
self.setState(JSON.parse(data));
|
|
||||||
// console.log(JSON.parse(data));
|
|
||||||
if (!result.done) return f(reader);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
fetch('/api/config/watch')
|
|
||||||
.then(r => r.body.getReader())
|
|
||||||
.then(f)
|
|
||||||
.catch(e => setTimeout(x => self.componentDidMount(), 1000));
|
|
||||||
}
|
|
||||||
render(props, state) {
|
|
||||||
return html
|
|
||||||
`<div id="dashboard" style="display: flex; justify-content: flex-evenly;">
|
|
||||||
<div style="width:50%;text-align:center;">Value 1 (number):
|
|
||||||
<div style="font-size:140%;"><b>${state.value1}</b></div></div>
|
|
||||||
<div style="width:50%;text-align:center;">Value 2 (string):
|
|
||||||
<div style="font-size:140%;"><b>${state.value2}</b></div></div>
|
|
||||||
</div>`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class AdminDashboard extends Component {
|
|
||||||
render(props, state) {
|
|
||||||
return html`<div class="container-fluid">
|
|
||||||
<${Info} />
|
|
||||||
<div class="card-deck">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Settings</div>
|
|
||||||
<div class="card-body"><${DashSettings} /></div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Video stream</div>
|
|
||||||
<div class="card-body"><img src="/api/video1" class="mw-100" /></div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Change settings</div>
|
|
||||||
<div class="card-body"><${DashForm} /></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UserDashboard extends Component {
|
|
||||||
render(props, state) {
|
|
||||||
return html`<div class="container-fluid">
|
|
||||||
<${Info} />
|
|
||||||
<div class="card-deck">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Settings</div>
|
|
||||||
<div class="card-body"><${DashSettings} /></div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Video stream</div>
|
|
||||||
<div class="card-body"><img src="/api/video1" class="mw-100" /></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Logs extends Component {
|
|
||||||
state = {live: '', static: ''};
|
|
||||||
componentDidMount() {
|
|
||||||
var self = this;
|
|
||||||
var f = function(r) {
|
|
||||||
return r.read().then(function(result) {
|
|
||||||
var data = String.fromCharCode.apply(null, result.value);
|
|
||||||
var live = self.state.live + data;
|
|
||||||
self.setState({live}); // Append live log
|
|
||||||
if (!result.done) f(r); // Read next chunk
|
|
||||||
});
|
|
||||||
};
|
|
||||||
fetch('/api/log/static')
|
|
||||||
.then(r => r.text())
|
|
||||||
.then(r => {
|
|
||||||
self.setState({static: r});
|
|
||||||
})
|
|
||||||
.then(r => fetch('/api/log/live'))
|
|
||||||
.then(r => r.body.getReader())
|
|
||||||
.then(f);
|
|
||||||
}
|
|
||||||
render(props, state) {
|
|
||||||
return html`<div class="container-fluid">
|
|
||||||
<div class="alert alert-secondary my-3">
|
|
||||||
A div below shows file log.txt, which is produced by the server.
|
|
||||||
The server appends a new log message to that file every second,
|
|
||||||
and a div below also shows that, implementing a "live log" feature.
|
|
||||||
JS code on this page first fetches /api/log/static that returns
|
|
||||||
log.txt contents, then fetches /api/log/live that does not return,
|
|
||||||
but feeds chunks of data as they arrive (live log).
|
|
||||||
<br/><br/>
|
|
||||||
You can also use <code>curl</code> command-line utility to see
|
|
||||||
live logs:
|
|
||||||
<br/><code>curl localhost:8000/api/log/live</code>
|
|
||||||
</div>
|
|
||||||
<div class="card-deck">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Static</div>
|
|
||||||
<div class="card-body"><pre class="log">${state.static}</pre></div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Live</div>
|
|
||||||
<div class="card-body"><pre class="log">${state.live}</pre></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class App extends Component {
|
|
||||||
state = {user: null, url: '/'};
|
|
||||||
setUser(token) {
|
|
||||||
const maxAge = token ? 86400 : 0;
|
|
||||||
document.cookie = `access_token=${token};path=/;max-age=${maxAge}`;
|
|
||||||
// this.setState({token});
|
|
||||||
return axios.get('/api/login')
|
|
||||||
.then(res => this.setState({user: res.data}))
|
|
||||||
.catch(err => this.setState({user: {}}));
|
|
||||||
}
|
|
||||||
componentDidMount() {
|
|
||||||
const getCookie = name => {
|
|
||||||
var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
|
|
||||||
return v ? v[2] : '';
|
|
||||||
};
|
|
||||||
this.setUser(getCookie('access_token')); // Move from state1 to 2 or 3
|
|
||||||
}
|
|
||||||
render(props, state) {
|
|
||||||
// We have three states:
|
|
||||||
// 1. Beginning. We don't know if we have a valid auth token or not
|
|
||||||
// 2. We sent an /api/login request, and failed to login
|
|
||||||
// 3. We sent an /api/login request, and succeeded to login
|
|
||||||
|
|
||||||
if (!state.user) return ''; // State 1
|
|
||||||
if (!state.user.user) return h(Login, {app: this}); // State 2
|
|
||||||
return h( // State 3
|
|
||||||
'div', {}, h(Header, {url: state.url, app: this}),
|
|
||||||
h(preactRouter.Router, {
|
|
||||||
history: History.createHashHistory(),
|
|
||||||
onChange: ev => this.setState({url: ev.url}),
|
|
||||||
},
|
|
||||||
h(state.user.user == 'admin' ? AdminDashboard : UserDashboard,
|
|
||||||
{default: true, app: this}),
|
|
||||||
h(Logs, {path: 'logs', app: this})));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onload = () => render(h(App), document.body);
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
/* axios v0.21.1 | (c) 2020 by Matt Zabriskie */
|
|
||||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(4),a=n(22),u=n(10),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(9),c.all=function(e){return Promise.all(e)},c.spread=n(25),c.isAxiosError=n(26),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===R.call(e)}function o(e){return"undefined"==typeof e}function s(e){return null!==e&&!o(e)&&null!==e.constructor&&!o(e.constructor)&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}function i(e){return"[object ArrayBuffer]"===R.call(e)}function a(e){return"undefined"!=typeof FormData&&e instanceof FormData}function u(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function c(e){return"string"==typeof e}function f(e){return"number"==typeof e}function p(e){return null!==e&&"object"==typeof e}function d(e){if("[object Object]"!==R.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function l(e){return"[object Date]"===R.call(e)}function h(e){return"[object File]"===R.call(e)}function m(e){return"[object Blob]"===R.call(e)}function y(e){return"[object Function]"===R.call(e)}function g(e){return p(e)&&y(e.pipe)}function v(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function x(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function w(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function b(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.call(null,e[s],s,e)}function E(){function e(e,n){d(t[n])&&d(e)?t[n]=E(t[n],e):d(e)?t[n]=E({},e):r(e)?t[n]=e.slice():t[n]=e}for(var t={},n=0,o=arguments.length;n<o;n++)b(arguments[n],e);return t}function j(e,t,n){return b(t,function(t,r){n&&"function"==typeof t?e[r]=S(t,n):e[r]=t}),e}function C(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e}var S=n(3),R=Object.prototype.toString;e.exports={isArray:r,isArrayBuffer:i,isBuffer:s,isFormData:a,isArrayBufferView:u,isString:c,isNumber:f,isObject:p,isPlainObject:d,isUndefined:o,isDate:l,isFile:h,isBlob:m,isFunction:y,isStream:g,isURLSearchParams:v,isStandardBrowserEnv:w,forEach:b,merge:E,extend:j,trim:x,stripBOM:C}},function(e,t){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return e.apply(t,n)}}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(5),i=n(6),a=n(7),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(u(n||{},{method:e,url:t,data:(n||{}).data}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(u(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}if(s){var a=e.indexOf("#");a!==-1&&(e=e.slice(0,a)),e+=(e.indexOf("?")===-1?"?":"&")+s}return e}},function(e,t,n){"use stricLine truncated
|
|
||||||
//# sourceMappingURL=axios.min.map
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Bootstrap v4.3.1 (https://getbootstrap.com/)
|
|
||||||
* Copyright 2011-2019 The Bootstrap Authors
|
|
||||||
* Copyright 2011-2019 Twitter, Inc.
|
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|
||||||
*/:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,Line truncated
|
|
||||||
/*# sourceMappingURL=bootstrap.min.css.map */
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.History=n():t.History=n()}(this,function(){return function(t){function n(o){if(e[o])return e[o].exports;var r=e[o]={exports:{},id:o,loaded:!1};return t[o].call(r.exports,r,r.exports,n),r.loaded=!0,r.exports}var e={};return n.m=t,n.c=e,n.p="",n(0)}([function(t,n,e){"use strict";function o(t){return t&&t.__esModule?t:{default:t}}n.__esModule=!0,n.createPath=n.parsePath=n.locationsAreEqual=n.createLocation=n.createMemoryHistory=n.createHashHistory=n.createBrowserHistory=void 0;var r=e(2);Object.defineProperty(n,"createLocation",{enumerable:!0,get:function(){return r.createLocation}}),Object.defineProperty(n,"locationsAreEqual",{enumerable:!0,get:function(){return r.locationsAreEqual}});var i=e(1);Object.defineProperty(n,"parsePath",{enumerable:!0,get:function(){return i.parsePath}}),Object.defineProperty(n,"createPath",{enumerable:!0,get:function(){return i.createPath}});var a=e(7),c=o(a),u=e(8),s=o(u),f=e(9),l=o(f);n.createBrowserHistory=c.default,n.createHashHistory=s.default,n.createMemoryHistory=l.default},function(t,n){"use strict";n.__esModule=!0;n.addLeadingSlash=function(t){return"/"===t.charAt(0)?t:"/"+t},n.stripLeadingSlash=function(t){return"/"===t.charAt(0)?t.substr(1):t},n.stripPrefix=function(t,n){return 0===t.indexOf(n)?t.substr(n.length):t},n.stripTrailingSlash=function(t){return"/"===t.charAt(t.length-1)?t.slice(0,-1):t},n.parsePath=function(t){var n=t||"/",e="",o="",r=n.indexOf("#");r!==-1&&(o=n.substr(r),n=n.substr(0,r));var i=n.indexOf("?");return i!==-1&&(e=n.substr(i),n=n.substr(0,i)),n=decodeURI(n),{pathname:n,search:"?"===e?"":e,hash:"#"===o?"":o}},n.createPath=function(t){var n=t.pathname,e=t.search,o=t.hash,r=encodeURI(n||"/");return e&&"?"!==e&&(r+="?"===e.charAt(0)?e:"?"+e),o&&"#"!==o&&(r+="#"===o.charAt(0)?o:"#"+o),r}},function(t,n,e){"use strict";function o(t){return t&&t.__esModule?t:{default:t}}n.__esModule=!0,n.locationsAreEqual=n.createLocation=void 0;var r=Object.assign||function(t){for(var n=1;n<arguments.length;n++){var e=arguments[n];for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=e[o])}return t},i=e(10),a=o(i),c=e(11),u=o(c),s=e(1);n.createLocation=function(t,n,e,o){var i=void 0;return"string"==typeof t?(i=(0,s.parsePath)(t),i.state=n):(i=r({},t),void 0===i.pathname&&(i.pathname=""),i.search?"?"!==i.search.charAt(0)&&(i.search="?"+i.search):i.search="",i.hash?"#"!==i.hash.charAt(0)&&(i.hash="#"+i.hash):i.hash="",void 0!==n&&void 0===i.state&&(i.state=n)),i.key=e,o&&(i.pathname?"/"!==i.pathname.charAt(0)&&(i.pathname=(0,a.default)(i.pathname,o.pathname)):i.pathname=o.pathname),i},n.locationsAreEqual=function(t,n){return t.pathname===n.pathname&&t.search===n.search&&t.hash===n.hash&&t.key===n.key&&(0,u.default)(t.state,n.state)}},function(t,n,e){"use strict";var o=function(){};t.exports=o},function(t,n,e){"use strict";function o(t){return t&&t.__esModule?t:{default:t}}n.__esModule=!0;var r=e(3),i=(o(r),function(){var t=null,n=function(n){return t=n,function(){t===n&&(t=null)}},e=function(n,e,o,r){if(null!=t){var i="function"==typeof t?t(n,e):t;"string"==typeof i?"function"==typeof o?o(i,r):r(!0):r(i!==!1)}else r(!0)},o=[],r=function(t){var n=!0,e=function(){n&&t.apply(void 0,arguments)};return o.push(e),function(){n=!1,o=o.filter(function(t){return t!==e})}},i=function(){for(var t=arguments.length,n=Array(t),e=0;e<t;e++)n[e]=arguments[e];o.forEach(function(t){return t.apply(void 0,n)})};return{setPrompt:n,confirmTransitionTo:e,appendListener:r,notifyListeners:i}});n.default=i},function(t,n){"use strict";n.__esModule=!0;n.canUseDOM=!("undefined"==typeof window||!window.document||!window.document.createElement),n.addEventListener=function(t,n,e){return t.addEventListener?t.addEventListener(n,e,!1):t.attachEvent("on"+n,e)},n.removeEventListener=function(t,n,e){return t.removeEventListener?t.removeEventListener(n,e,!1):t.detachEvent("on"+n,e)},n.getConfirmation=function(t,n){return n(window.confirm(t))},n.supportsHistory=function(){var t=window.navigator.userAgent;return(t.indexOf("Android 2.")===-1&&t.indexOf("Android 4.0")===-1||t.indexOf("Mobile Safari")===-1||t.indexOf("Chrome")!==-1||t.indexOf("Windows Phone")!==-1)&&(window.history&&"pushState"in window.history)},n.supportsPopStateOnHashChange=function(){return window.navigator.userAgent.indexOf("Trident")===-1},n.supportsGoWithoutReloadUsingHash=function(){return window.navigator.userAgent.indexOf("Firefox")===-1},n.isExtraneousPopstateEvent=function(t){return void 0===t.state&&navigator.userAgent.indexOf("CriOS")===-1}},function(t,n,e){"use strict";var o=function(t,n,e,o,r,i,a,c){if(!t){var u;if(void 0===n)u=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var s=[e,o,r,i,a,c],f=0;u=new Error(n.replace(/%s/g,function(){return s[f++]})),u.name="InLine truncated
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(){function n(n){var t=e.get(this);return t||(t=new Map,e.set(this,t)),1<(t=a(this,t.get(n)||(t.set(n,t=function(n){function t(n){1===u&&(n||(r=r.replace(/^\s*\n\s*|\s*\n\s*$/g,"")))?p.push(0,n,r):3===u&&(n||r)?(p.push(3,n,r),u=2):2===u&&"..."===r&&n?p.push(4,n,0):2===u&&r&&!n?p.push(5,0,!0,r):5<=u&&((r||!n&&5===u)&&(p.push(u,0,r,s),u=6),n&&(p.push(u,n,0,s),u=6)),r=""}for(var e,s,u=1,r="",h="",p=[0],a=0;a<n.length;a++){a&&(1===u&&t(),t(a));for(var o=0;o<n[a].length;o++)e=n[a][o],1===u?"<"===e?(t(),p=[p],u=3):r+=e:4===u?r="--"===r&&">"===e?(u=1,""):e+r[0]:h?e===h?h="":r+=e:'"'===e||"'"===e?h=e:">"===e?(t(),u=1):u&&("="===e?(u=5,s=r,r=""):"/"===e&&(u<5||">"===n[a][o+1])?(t(),3===u&&(p=p[0]),(p=(u=p)[0]).push(2,0,u),u=0):" "===e||"\t"===e||"\n"===e||"\r"===e?(t(),u=2):r+=e),3===u&&"!--"===r&&(u=4,p=p[0])}return t(),p}(n)),t),arguments,[])).length?t:t[0]}var a=function(n,t,e,s){var u;t[0]=0;for(var r=1;r<t.length;r++){var h=t[r++],p=t[r]?(t[0]|=h?1:2,e[t[r++]]):t[++r];3===h?s[0]=p:4===h?s[1]=Object.assign(s[1]||{},p):5===h?(s[1]=s[1]||{})[t[++r]]=p:6===h?s[1][t[++r]]+=p+"":h?(u=n.apply(p,a(n,p,e,["",null])),s.push(u),p[0]?t[0]|=2:(t[r-2]=0,t[r]=u)):s.push(p)}return s},e=new Map;"undefined"!=typeof module?module.exports=n:self.htm=n}();
|
|
||||||
|
Before Width: | Height: | Size: 3.2 KiB |
@@ -1,19 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>example</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<link rel="stylesheet" href="bootstrap.min.css" />
|
|
||||||
<link rel="stylesheet" href="style.css" />
|
|
||||||
<script src="preact.min.js"></script>
|
|
||||||
<script src="preact-router.min.js"></script>
|
|
||||||
<script src="htm.min.js"></script>
|
|
||||||
<script src="history.min.js"></script>
|
|
||||||
<script src="linkstate.min.js"></script>
|
|
||||||
<script src="axios.min.js"></script>
|
|
||||||
<script src="app.js"></script>
|
|
||||||
</head>
|
|
||||||
<body></body>
|
|
||||||
</html>
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
function linkState(component, key, eventPath) {
|
|
||||||
let path = key.split('.'), cache = component.__lsc || (component.__lsc = {});
|
|
||||||
|
|
||||||
return cache[key + eventPath] || (cache[key + eventPath] = function(e) {
|
|
||||||
let t = e && e.target || this, state = {}, obj = state,
|
|
||||||
v = typeof eventPath === 'string' ?
|
|
||||||
delve(e, eventPath) :
|
|
||||||
t.nodeName ? (t.type.match(/^che|rad/) ? t.checked : t.value) :
|
|
||||||
e,
|
|
||||||
i = 0;
|
|
||||||
for (; i < path.length - 1; i++) {
|
|
||||||
obj = obj[path[i]] ||
|
|
||||||
(obj[path[i]] = !i && component.state[path[i]] || {});
|
|
||||||
}
|
|
||||||
obj[path[i]] = v;
|
|
||||||
component.setState(state);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("preact")):"function"==typeof define&&define.amd?define(["preact"],e):t.preactRouter=e(t.preact)}(this,function(p){function i(t,e){for(var n in e)t[n]=e[n];return t}function u(t,e,n){var r,o=/(?:\?([^#]*))?(#.*)?$/,i=t.match(o),u={};if(i&&i[1])for(var a=i[1].split("&"),p=0;p<a.length;p++){var c=a[p].split("=");u[decodeURIComponent(c[0])]=decodeURIComponent(c.slice(1).join("="))}t=y(t.replace(o,"")),e=y(e||"");for(var f=Math.max(t.length,e.length),l=0;l<f;l++)if(e[l]&&":"===e[l].charAt(0)){var s=e[l].replace(/(^:|[+*?]+$)/g,""),h=(e[l].match(/[+*?]+$/)||v)[0]||"",d=~h.indexOf("+"),g=~h.indexOf("*"),m=t[l]||"";if(!m&&!g&&(h.indexOf("?")<0||d)){r=!1;break}if(u[s]=decodeURIComponent(m),d||g){u[s]=t.slice(l).map(decodeURIComponent).join("/");break}}else if(e[l]!==t[l]){r=!1;break}return(!0===n.default||!1!==r)&&u}function e(t,e){return t.rank<e.rank?1:t.rank>e.rank?-1:t.index-e.index}function n(t,e){return t.index=e,t.rank=(n=t).props.default?0:function(t){return y(t).map(r).join("")}(n.props.path),t.props;var n}function y(t){return t.replace(/(^\/+|\/+$)/g,"").split("/")}function r(t){return":"==t.charAt(0)?1+"*+?".indexOf(t.charAt(t.length-1))||4:5}function o(){var t;return""+((t=g&&g.location?g.location:g&&g.getCurrentLocation?g.getCurrentLocation():"undefined"!=typeof location?location:b).pathname||"")+(t.search||"")}function a(t,e){return void 0===e&&(e=!1),"string"!=typeof t&&t.url&&(e=t.replace,t=t.url),function(t){for(var e=m.length;e--;)if(m[e].canRoute(t))return 1;return}(t)&&(n=t,void 0===(r=e?"replace":"push")&&(r="push"),g&&g[r]?g[r](n):"undefined"!=typeof history&&history[r+"State"]&&history[r+"State"](null,null,n)),c(t);var n,r}function c(t){for(var e=!1,n=0;n<m.length;n++)!0===m[n].routeTo(t)&&(e=!0);for(var r=C.length;r--;)C[r](t);return e}function f(t){if(t&&t.getAttribute){var e=t.getAttribute("href"),n=t.getAttribute("target");if(e&&e.match(/^\//g)&&(!n||n.match(/^_?self$/i)))return a(e)}}function l(t){if(!(t.ctrlKey||t.metaKey||t.altKey||t.shiftKey||0!==t.button))return f(t.currentTarget||t.target||this),s(t)}function s(t){return t&&(t.stopImmediatePropagation&&t.stopImmediatePropagation(),t.stopPropagation&&t.stopPropagation(),t.preventDefault()),!1}function h(t){if(!(t.ctrlKey||t.metaKey||t.altKey||t.shiftKey||0!==t.button)){var e=t.target;do{if("A"===(e.nodeName+"").toUpperCase()&&e.getAttribute("href")){if(e.hasAttribute("native"))return;if(f(e))return s(t)}}while(e=e.parentNode)}}var d,v={},g=null,m=[],C=[],b={},U=!1,t=((d=p.Component)&&(k.__proto__=d),((k.prototype=Object.create(d&&d.prototype)).constructor=k).prototype.shouldComponentUpdate=function(t){return!0!==t.static||t.url!==this.props.url||t.onChange!==this.props.onChange},k.prototype.canRoute=function(t){return 0<this.getMatchingChildren(p.toChildArray(this.props.children),t,!1).length},k.prototype.routeTo=function(t){this.setState({url:t});var e=this.canRoute(t);return this.updating||this.forceUpdate(),e},k.prototype.componentWillMount=function(){m.push(this),this.updating=!0},k.prototype.componentDidMount=function(){var e=this;g&&(this.unlisten=g.listen(function(t){e.routeTo(""+(t.pathname||"")+(t.search||""))})),this.updating=!1},k.prototype.componentWillUnmount=function(){"function"==typeof this.unlisten&&this.unlisten(),m.splice(m.indexOf(this),1)},k.prototype.componentWillUpdate=function(){this.updating=!0},k.prototype.componentDidUpdate=function(){this.updating=!1},k.prototype.getMatchingChildren=function(t,r,o){return t.filter(n).sort(e).map(function(t){var e=u(r,t.props.path,t.props);if(e){if(!1===o)return t;var n={url:r,matches:e};return i(n,e),delete n.ref,delete n.key,p.cloneElement(t,n)}}).filter(Boolean)},k.prototype.render=function(t,e){var n=t.children,r=t.onChange,o=e.url,i=this.getMatchingChildren(p.toChildArray(n),o,!0),u=i[0]||null,a=this.previousUrl;return o!==a&&(this.previousUrl=o,"function"==typeof r&&r({router:this,url:o,previous:a,active:i,current:u})),u},k);function k(t){d.call(this,t),t.history&&(g=t.history),this.state={url:t.url||o()},U||("function"==typeof addEventListener&&(g||addEventListener("popstate",function(){c(o())}),addEventListener("click",h)),U=!0)}return t.subscribers=C,t.getCurrentUrl=o,t.route=a,(t.Router=t).Route=function(t){return p.createElement(t.component,t)},t.Link=function(t){return p.createElement("a",i({onClick:l},t))},t.exec=u,t});
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
!function(){var n,l,u,t,i,o,r,e,f={},c=[],a=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function s(n,l){for(var u in l)n[u]=l[u];return n}function v(n){var l=n.parentNode;l&&l.removeChild(n)}function h(n,l,u){var t,i=arguments,o={};for(t in l)"key"!==t&&"ref"!==t&&(o[t]=l[t]);if(arguments.length>3)for(u=[u],t=3;t<arguments.length;t++)u.push(i[t]);if(null!=u&&(o.children=u),"function"==typeof n&&null!=n.defaultProps)for(t in n.defaultProps)void 0===o[t]&&(o[t]=n.defaultProps[t]);return y(n,o,l&&l.key,l&&l.ref,null)}function y(l,u,t,i,o){var r={type:l,props:u,key:t,ref:i,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,constructor:void 0,__v:o};return null==o&&(r.__v=r),n.vnode&&n.vnode(r),r}function p(n){return n.children}function d(n,l){this.props=n,this.context=l}function _(n,l){if(null==l)return n.__?_(n.__,n.__.__k.indexOf(n)+1):null;for(var u;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e)return u.__e;return"function"==typeof n.type?_(n):null}function m(n){var l,u;if(null!=(n=n.__)&&null!=n.__c){for(n.__e=n.__c.base=null,l=0;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e){n.__e=n.__c.base=u.__e;break}return m(n)}}function k(l){(!l.__d&&(l.__d=!0)&&u.push(l)&&!w.__r++||i!==n.debounceRendering)&&((i=n.debounceRendering)||t)(w)}function w(){for(var n;w.__r=u.length;)n=u.sort(function(n,l){return n.__v.__b-l.__v.__b}),u=[],n.some(function(n){var l,u,t,i,o,r,e;n.__d&&(r=(o=(l=n).__v).__e,(e=l.__P)&&(u=[],(t=s({},o)).__v=t,i=N(e,o,t,l.__n,void 0!==e.ownerSVGElement,null,u,null==r?_(o):r),z(u,o),i!=r&&m(o)))})}function g(n,l,u,t,i,o,r,e,a,s){var h,d,m,k,w,g,x,C=t&&t.__k||c,A=C.length;for(a==f&&(a=null!=r?r[0]:A?_(t,0):null),u.__k=[],h=0;h<l.length;h++)if(null!=(k=u.__k[h]=null==(k=l[h])||"boolean"==typeof k?null:"string"==typeof k||"number"==typeof k?y(null,k,null,null,k):Array.isArray(k)?y(p,{children:k},null,null,null):null!=k.__e||null!=k.__c?y(k.type,k.props,k.key,null,k.__v):k)){if(k.__=u,k.__b=u.__b+1,null===(m=C[h])||m&&k.key==m.key&&k.type===m.type)C[h]=void 0;else for(d=0;d<A;d++){if((m=C[d])&&k.key==m.key&&k.type===m.type){C[d]=void 0;break}m=null}w=N(n,k,m=m||f,i,o,r,e,a,s),(d=k.ref)&&m.ref!=d&&(x||(x=[]),m.ref&&x.push(m.ref,null,k),x.push(d,k.__c||w,k)),null!=w?(null==g&&(g=w),a=b(n,k,m,C,r,w,a),"option"==u.type?n.value="":"function"==typeof u.type&&(u.__d=a)):a&&m.__e==a&&a.parentNode!=n&&(a=_(m))}if(u.__e=g,null!=r&&"function"!=typeof u.type)for(h=r.length;h--;)null!=r[h]&&v(r[h]);for(h=A;h--;)null!=C[h]&&j(C[h],C[h]);if(x)for(h=0;h<x.length;h++)$(x[h],x[++h],x[++h])}function b(n,l,u,t,i,o,r){var e,f,c;if(void 0!==l.__d)e=l.__d,l.__d=void 0;else if(i==u||o!=r||null==o.parentNode)n:if(null==r||r.parentNode!==n)n.appendChild(o),e=null;else{for(f=r,c=0;(f=f.nextSibling)&&c<t.length;c+=2)if(f==o)break n;n.insertBefore(o,r),e=r}return void 0!==e?e:o.nextSibling}function x(n,l,u,t,i){var o;for(o in u)"children"===o||"key"===o||o in l||A(n,o,null,u[o],t);for(o in l)i&&"function"!=typeof l[o]||"children"===o||"key"===o||"value"===o||"checked"===o||u[o]===l[o]||A(n,o,l[o],u[o],t)}function C(n,l,u){"-"===l[0]?n.setProperty(l,u):n[l]="number"==typeof u&&!1===a.test(l)?u+"px":null==u?"":u}function A(n,l,u,t,i){var o,r,e,f,c;if(i?"className"===l&&(l="class"):"class"===l&&(l="className"),"style"===l)if(o=n.style,"string"==typeof u)o.cssText=u;else{if("string"==typeof t&&(o.cssText="",t=null),t)for(f in t)u&&f in u||C(o,f,"");if(u)for(c in u)t&&u[c]===t[c]||C(o,c,u[c])}else"o"===l[0]&&"n"===l[1]?(r=l!==(l=l.replace(/Capture$/,"")),e=l.toLowerCase(),l=(e in n?e:l).slice(2),u?(t||n.addEventListener(l,P,r),(n.l||(n.l={}))[l]=u):n.removeEventListener(l,P,r)):"list"!==l&&"tagName"!==l&&"form"!==l&&"type"!==l&&"size"!==l&&!i&&l in n?n[l]=null==u?"":u:"function"!=typeof u&&"dangerouslySetInnerHTML"!==l&&(l!==(l=l.replace(/^xlink:?/,""))?null==u||!1===u?n.removeAttributeNS("http://www.w3.org/1999/xlink",l.toLowerCase()):n.setAttributeNS("http://www.w3.org/1999/xlink",l.toLowerCase(),u):null==u||!1===u&&!/^ar/.test(l)?n.removeAttribute(l):n.setAttribute(l,u))}function P(l){this.l[l.type](n.event?n.event(l):l)}function E(n,l,u){var t,i;for(t=0;t<n.__k.length;t++)(i=n.__k[t])&&(i.__=n,i.__e&&("function"==typeof i.type&&i.__k.length>1&&E(i,l,u),l=b(u,i,i,n.__k,null,i.__e,l),"function"==typeof n.type&&(n.__d=l)))}function N(l,u,t,i,o,r,e,f,c){var a,v,h,y,_,m,k,w,b,x,C,A=u.type;if(void 0!==u.constructor)return null;(a=n.__b)&&a(u);try{n:if("function"==typeof A){if(w=u.props,b=(a=A.contextType)&&i[a.__c],x=a?b?b.props.value:a.__:i,t.__c?k=(v=u.__c=t.__c).__=v.__E:("prototype"in A&&A.prototype.render?u.__c=v=new A(w,x):(u.__c=v=new d(w,x),v.constructor=A,v.render=F),b&&b.sub(v),v.props=w,v.state||(v.state={}),v.context=x,v.__n=i,h=v.__d=!0,v.__h=[]),null==v.__s&&(v.__s=v.state),null!=A.getDerivedStateFromProps&&(v.__s==v.state&&(v.__s=s({},v.__s)),s(v.__s,A.getDerivedStateFromProps(w,v.__s))),y=v.props,_=v.state,h)null==A.getDerivedStateFromProps&&null!=v.componentWillMount&&v.componenLine truncated
|
|
||||||
//# sourceMappingURL=preact.min.js.map
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
html, body { margin: 0; padding: 0; height: 100%; }
|
|
||||||
select, input, label::before, textarea { outline: none; box-shadow:none !important; border: 1px solid #ccc !important; }
|
|
||||||
.btn:focus,.btn:active:focus,.btn.active:focus,
|
|
||||||
.btn.focus,.btn:active.focus,.btn.active.focus { outline: none; box-shadow:none !important; }
|
|
||||||
.dropdown.autoexpand:hover>.dropdown-menu { display: block; }
|
|
||||||
.pointer { cursor: pointer; }
|
|
||||||
.log { max-height: 10em; overflow: auto; height: 100%; }
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
// Copyright (c) 2020 Cesanta Software Limited
|
|
||||||
// All rights reserved
|
|
||||||
|
|
||||||
#include "mjson.h" // JSON parsing and printing
|
|
||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
// This is a configuration structure we're going to show on a dashboard
|
|
||||||
static struct config {
|
|
||||||
int value1;
|
|
||||||
char *value2;
|
|
||||||
} s_config = {123, NULL};
|
|
||||||
|
|
||||||
// Stringifies the config. A caller must free() it.
|
|
||||||
static char *stringify_config(struct config *cfg) {
|
|
||||||
char *s = NULL;
|
|
||||||
mjson_printf(mjson_print_dynamic_buf, &s, "{%Q:%d,%Q:%Q}", "value1",
|
|
||||||
cfg->value1, "value2", cfg->value2);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update config structure. Return true if changed, false otherwise
|
|
||||||
static bool update_config(struct mg_http_message *hm, struct config *cfg) {
|
|
||||||
bool changed = false;
|
|
||||||
char buf[256];
|
|
||||||
double dv;
|
|
||||||
if (mjson_get_number(hm->body.ptr, hm->body.len, "$.value1", &dv)) {
|
|
||||||
s_config.value1 = dv;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (mjson_get_string(hm->body.ptr, hm->body.len, "$.value2", buf,
|
|
||||||
sizeof(buf)) > 0) {
|
|
||||||
free(s_config.value2);
|
|
||||||
s_config.value2 = strdup(buf);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify all config watchers about the config change
|
|
||||||
static void notify_config_change(struct mg_mgr *mgr) {
|
|
||||||
struct mg_connection *c;
|
|
||||||
char *s = stringify_config(&s_config);
|
|
||||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
|
||||||
if (c->label[0] == 'W') mg_http_printf_chunk(c, "%s\n", s);
|
|
||||||
}
|
|
||||||
free(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP request handler function. It implements the following endpoints:
|
|
||||||
// /api/config/get - returns current config
|
|
||||||
// /api/config/set - updates current config
|
|
||||||
// /api/config/watch - does not return. Sends config as it changes in
|
|
||||||
// chunks all other URI - serves web_root/ directory
|
|
||||||
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
|
||||||
if (mg_http_match_uri(hm, "/api/config/get")) {
|
|
||||||
char *s = stringify_config(&s_config);
|
|
||||||
mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s\n",
|
|
||||||
(int) strlen(s) + 1, s);
|
|
||||||
free(s);
|
|
||||||
} else if (mg_http_match_uri(hm, "/api/config/set")) {
|
|
||||||
if (update_config(hm, &s_config)) notify_config_change(fn_data);
|
|
||||||
mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
|
|
||||||
} else if (mg_http_match_uri(hm, "/api/config/watch")) {
|
|
||||||
c->label[0] = 'W'; // Mark ourselves as a config watcher
|
|
||||||
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
|
||||||
} else {
|
|
||||||
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
|
||||||
mg_http_serve_dir(c, ev_data, &opts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_mgr mgr;
|
|
||||||
mg_mgr_init(&mgr);
|
|
||||||
mg_http_listen(&mgr, "http://localhost:8000", cb, &mgr);
|
|
||||||
for (;;) mg_mgr_poll(&mgr, 1000);
|
|
||||||
mg_mgr_free(&mgr);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,981 +0,0 @@
|
|||||||
// Copyright (c) 2018-2020 Cesanta Software Limited
|
|
||||||
// All rights reserved
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
#include "mjson.h"
|
|
||||||
|
|
||||||
#if !defined(_MSC_VER) || _MSC_VER >= 1700
|
|
||||||
#else
|
|
||||||
#define va_copy(x, y) (x) = (y)
|
|
||||||
#define snprintf _snprintf
|
|
||||||
#define alloca _alloca
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int mjson_esc(int c, int esc) {
|
|
||||||
const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\"";
|
|
||||||
for (p = esc ? esc1 : esc2; *p != '\0'; p++) {
|
|
||||||
if (*p == c) return esc ? esc2[p - esc1] : esc1[p - esc2];
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mjson_pass_string(const char *s, int len) {
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
if (s[i] == '\\' && i + 1 < len && mjson_esc(s[i + 1], 1)) {
|
|
||||||
i++;
|
|
||||||
} else if (s[i] == '\0') {
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
} else if (s[i] == '"') {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson(const char *s, int len, mjson_cb_t cb, void *ud) {
|
|
||||||
enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE;
|
|
||||||
unsigned char nesting[MJSON_MAX_DEPTH];
|
|
||||||
int i, depth = 0;
|
|
||||||
#define MJSONCALL(ev) \
|
|
||||||
if (cb != NULL && cb(ev, s, start, i - start + 1, ud)) return i + 1;
|
|
||||||
|
|
||||||
// In the ascii table, the distance between `[` and `]` is 2.
|
|
||||||
// Ditto for `{` and `}`. Hence +2 in the code below.
|
|
||||||
#define MJSONEOO() \
|
|
||||||
do { \
|
|
||||||
if (c != nesting[depth - 1] + 2) return MJSON_ERROR_INVALID_INPUT; \
|
|
||||||
depth--; \
|
|
||||||
if (depth == 0) { \
|
|
||||||
MJSONCALL(tok); \
|
|
||||||
return i + 1; \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
int start = i;
|
|
||||||
unsigned char c = ((unsigned char *) s)[i];
|
|
||||||
int tok = c;
|
|
||||||
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue;
|
|
||||||
// printf("- %c [%.*s] %d %d\n", c, i, s, depth, expecting);
|
|
||||||
switch (expecting) {
|
|
||||||
case S_VALUE:
|
|
||||||
if (c == '{') {
|
|
||||||
if (depth >= (int) sizeof(nesting)) return MJSON_ERROR_TOO_DEEP;
|
|
||||||
nesting[depth++] = c;
|
|
||||||
expecting = S_KEY;
|
|
||||||
break;
|
|
||||||
} else if (c == '[') {
|
|
||||||
if (depth >= (int) sizeof(nesting)) return MJSON_ERROR_TOO_DEEP;
|
|
||||||
nesting[depth++] = c;
|
|
||||||
break;
|
|
||||||
} else if (c == ']' && depth > 0) { // Empty array
|
|
||||||
MJSONEOO();
|
|
||||||
} else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) {
|
|
||||||
i += 3;
|
|
||||||
tok = MJSON_TOK_TRUE;
|
|
||||||
} else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) {
|
|
||||||
i += 3;
|
|
||||||
tok = MJSON_TOK_NULL;
|
|
||||||
} else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) {
|
|
||||||
i += 4;
|
|
||||||
tok = MJSON_TOK_FALSE;
|
|
||||||
} else if (c == '-' || ((c >= '0' && c <= '9'))) {
|
|
||||||
char *end = NULL;
|
|
||||||
strtod(&s[i], &end);
|
|
||||||
if (end != NULL) i += end - &s[i] - 1;
|
|
||||||
tok = MJSON_TOK_NUMBER;
|
|
||||||
} else if (c == '"') {
|
|
||||||
int n = mjson_pass_string(&s[i + 1], len - i - 1);
|
|
||||||
if (n < 0) return n;
|
|
||||||
i += n + 1;
|
|
||||||
tok = MJSON_TOK_STRING;
|
|
||||||
} else {
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
}
|
|
||||||
if (depth == 0) {
|
|
||||||
MJSONCALL(tok);
|
|
||||||
return i + 1;
|
|
||||||
}
|
|
||||||
expecting = S_COMMA_OR_EOO;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case S_KEY:
|
|
||||||
if (c == '"') {
|
|
||||||
int n = mjson_pass_string(&s[i + 1], len - i - 1);
|
|
||||||
if (n < 0) return n;
|
|
||||||
i += n + 1;
|
|
||||||
tok = MJSON_TOK_KEY;
|
|
||||||
expecting = S_COLON;
|
|
||||||
} else if (c == '}') { // Empty object
|
|
||||||
MJSONEOO();
|
|
||||||
expecting = S_COMMA_OR_EOO;
|
|
||||||
} else {
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case S_COLON:
|
|
||||||
if (c == ':') {
|
|
||||||
expecting = S_VALUE;
|
|
||||||
} else {
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case S_COMMA_OR_EOO:
|
|
||||||
if (depth <= 0) return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
if (c == ',') {
|
|
||||||
expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE;
|
|
||||||
} else if (c == ']' || c == '}') {
|
|
||||||
MJSONEOO();
|
|
||||||
} else {
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
MJSONCALL(tok);
|
|
||||||
}
|
|
||||||
return MJSON_ERROR_INVALID_INPUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct msjon_get_data {
|
|
||||||
const char *path; // Lookup json path
|
|
||||||
int pos; // Current path index
|
|
||||||
int d1; // Current depth of traversal
|
|
||||||
int d2; // Expected depth of traversal
|
|
||||||
int i1; // Index in an array
|
|
||||||
int i2; // Expected index in an array
|
|
||||||
int obj; // If the value is array/object, offset where it starts
|
|
||||||
const char **tokptr; // Destination
|
|
||||||
int *toklen; // Destination length
|
|
||||||
int tok; // Returned token
|
|
||||||
};
|
|
||||||
|
|
||||||
static int mjson_plen(const char *s) {
|
|
||||||
int i = 0;
|
|
||||||
while (s[i] != '\0' && s[i] != '.' && s[i] != '[') i++;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mjson_get_cb(int tok, const char *s, int off, int len, void *ud) {
|
|
||||||
struct msjon_get_data *data = (struct msjon_get_data *) ud;
|
|
||||||
// printf("--> %2x %2d %2d %2d %2d\t'%s'\t'%.*s'\t\t'%.*s'\n", tok, data->d1,
|
|
||||||
// data->d2, data->i1, data->i2, data->path + data->pos, off, s, len,
|
|
||||||
// s + off);
|
|
||||||
if (data->tok != MJSON_TOK_INVALID) return 1; // Found
|
|
||||||
|
|
||||||
if (tok == '{') {
|
|
||||||
if (!data->path[data->pos] && data->d1 == data->d2) data->obj = off;
|
|
||||||
data->d1++;
|
|
||||||
} else if (tok == '[') {
|
|
||||||
if (data->d1 == data->d2 && data->path[data->pos] == '[') {
|
|
||||||
data->i1 = 0;
|
|
||||||
data->i2 = (int) strtod(&data->path[data->pos + 1], NULL);
|
|
||||||
if (data->i1 == data->i2) {
|
|
||||||
data->d2++;
|
|
||||||
data->pos += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!data->path[data->pos] && data->d1 == data->d2) data->obj = off;
|
|
||||||
data->d1++;
|
|
||||||
} else if (tok == ',') {
|
|
||||||
if (data->d1 == data->d2 + 1) {
|
|
||||||
data->i1++;
|
|
||||||
if (data->i1 == data->i2) {
|
|
||||||
while (data->path[data->pos] != ']') data->pos++;
|
|
||||||
data->pos++;
|
|
||||||
data->d2++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (tok == MJSON_TOK_KEY && data->d1 == data->d2 + 1 &&
|
|
||||||
data->path[data->pos] == '.' && s[off] == '"' &&
|
|
||||||
s[off + len - 1] == '"' &&
|
|
||||||
mjson_plen(&data->path[data->pos + 1]) == len - 2 &&
|
|
||||||
!memcmp(s + off + 1, &data->path[data->pos + 1], len - 2)) {
|
|
||||||
data->d2++;
|
|
||||||
data->pos += len - 1;
|
|
||||||
} else if (tok == MJSON_TOK_KEY && data->d1 == data->d2) {
|
|
||||||
return 1; // Exhausted path, not found
|
|
||||||
} else if (tok == '}' || tok == ']') {
|
|
||||||
data->d1--;
|
|
||||||
// data->d2--;
|
|
||||||
if (!data->path[data->pos] && data->d1 == data->d2 && data->obj != -1) {
|
|
||||||
data->tok = tok - 2;
|
|
||||||
if (data->tokptr) *data->tokptr = s + data->obj;
|
|
||||||
if (data->toklen) *data->toklen = off - data->obj + 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else if (MJSON_TOK_IS_VALUE(tok)) {
|
|
||||||
// printf("TOK --> %d\n", tok);
|
|
||||||
if (data->d1 == data->d2 && !data->path[data->pos]) {
|
|
||||||
data->tok = tok;
|
|
||||||
if (data->tokptr) *data->tokptr = s + off;
|
|
||||||
if (data->toklen) *data->toklen = len;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum mjson_tok mjson_find(const char *s, int len, const char *jp,
|
|
||||||
const char **tokptr, int *toklen) {
|
|
||||||
struct msjon_get_data data = {jp, 1, 0, 0, 0,
|
|
||||||
0, -1, tokptr, toklen, MJSON_TOK_INVALID};
|
|
||||||
if (jp[0] != '$') return MJSON_TOK_INVALID;
|
|
||||||
if (mjson(s, len, mjson_get_cb, &data) < 0) return MJSON_TOK_INVALID;
|
|
||||||
return (enum mjson_tok) data.tok;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_get_number(const char *s, int len, const char *path, double *v) {
|
|
||||||
const char *p;
|
|
||||||
int tok, n;
|
|
||||||
if ((tok = mjson_find(s, len, path, &p, &n)) == MJSON_TOK_NUMBER) {
|
|
||||||
if (v != NULL) *v = strtod(p, NULL);
|
|
||||||
}
|
|
||||||
return tok == MJSON_TOK_NUMBER ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_get_bool(const char *s, int len, const char *path, int *v) {
|
|
||||||
int tok = mjson_find(s, len, path, NULL, NULL);
|
|
||||||
if (tok == MJSON_TOK_TRUE && v != NULL) *v = 1;
|
|
||||||
if (tok == MJSON_TOK_FALSE && v != NULL) *v = 0;
|
|
||||||
return tok == MJSON_TOK_TRUE || tok == MJSON_TOK_FALSE ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mjson_unescape(const char *s, int len, char *to, int n) {
|
|
||||||
int i, j;
|
|
||||||
for (i = 0, j = 0; i < len && j < n; i++, j++) {
|
|
||||||
if (s[i] == '\\' && i + 1 < len) {
|
|
||||||
int c = mjson_esc(s[i + 1], 0);
|
|
||||||
if (c == 0) return -1;
|
|
||||||
to[j] = c;
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
to[j] = s[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (j >= n) return -1;
|
|
||||||
if (n > 0) to[j] = '\0';
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_get_string(const char *s, int len, const char *path, char *to,
|
|
||||||
int n) {
|
|
||||||
const char *p;
|
|
||||||
int sz;
|
|
||||||
if (mjson_find(s, len, path, &p, &sz) != MJSON_TOK_STRING) return -1;
|
|
||||||
return mjson_unescape(p + 1, sz - 2, to, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_get_hex(const char *s, int len, const char *x, char *to, int n) {
|
|
||||||
const char *p;
|
|
||||||
int i, j, sz;
|
|
||||||
if (mjson_find(s, len, x, &p, &sz) != MJSON_TOK_STRING) return -1;
|
|
||||||
for (i = j = 0; i < sz - 3 && j < n; i += 2, j++) {
|
|
||||||
#define HEXTOI(x) (x >= '0' && x <= '9' ? x - '0' : x - 'W')
|
|
||||||
unsigned char a = *(const unsigned char *) (p + i + 1);
|
|
||||||
unsigned char b = *(const unsigned char *) (p + i + 2);
|
|
||||||
((unsigned char *) to)[j] = (HEXTOI(a) << 4) | HEXTOI(b);
|
|
||||||
}
|
|
||||||
if (j < n) to[j] = '\0';
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_BASE64
|
|
||||||
static int mjson_base64rev(int c) {
|
|
||||||
if (c >= 'A' && c <= 'Z') {
|
|
||||||
return c - 'A';
|
|
||||||
} else if (c >= 'a' && c <= 'z') {
|
|
||||||
return c + 26 - 'a';
|
|
||||||
} else if (c >= '0' && c <= '9') {
|
|
||||||
return c + 52 - '0';
|
|
||||||
} else if (c == '+') {
|
|
||||||
return 62;
|
|
||||||
} else if (c == '/') {
|
|
||||||
return 63;
|
|
||||||
} else {
|
|
||||||
return 64;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_base64_dec(const char *src, int n, char *dst, int dlen) {
|
|
||||||
const char *end = src + n;
|
|
||||||
int len = 0;
|
|
||||||
while (src + 3 < end && len < dlen) {
|
|
||||||
int a = mjson_base64rev(src[0]), b = mjson_base64rev(src[1]),
|
|
||||||
c = mjson_base64rev(src[2]), d = mjson_base64rev(src[3]);
|
|
||||||
dst[len++] = (a << 2) | (b >> 4);
|
|
||||||
if (src[2] != '=' && len < dlen) {
|
|
||||||
dst[len++] = (b << 4) | (c >> 2);
|
|
||||||
if (src[3] != '=' && len < dlen) {
|
|
||||||
dst[len++] = (c << 6) | d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
src += 4;
|
|
||||||
}
|
|
||||||
if (len < dlen) dst[len] = '\0';
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_get_base64(const char *s, int len, const char *path, char *to,
|
|
||||||
int n) {
|
|
||||||
const char *p;
|
|
||||||
int sz;
|
|
||||||
if (mjson_find(s, len, path, &p, &sz) != MJSON_TOK_STRING) return 0;
|
|
||||||
return mjson_base64_dec(p + 1, sz - 2, to, n);
|
|
||||||
}
|
|
||||||
#endif // MJSON_ENABLE_BASE64
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_NEXT
|
|
||||||
struct nextdata {
|
|
||||||
int off, len, depth, t, vo, arrayindex;
|
|
||||||
int *koff, *klen, *voff, *vlen, *vtype;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int next_cb(int tok, const char *s, int off, int len, void *ud) {
|
|
||||||
struct nextdata *d = (struct nextdata *) ud;
|
|
||||||
// int i;
|
|
||||||
switch (tok) {
|
|
||||||
case '{':
|
|
||||||
case '[':
|
|
||||||
if (d->depth == 0 && tok == '[') d->arrayindex = 0;
|
|
||||||
if (d->depth == 1 && off > d->off) {
|
|
||||||
d->vo = off;
|
|
||||||
d->t = tok == '{' ? MJSON_TOK_OBJECT : MJSON_TOK_ARRAY;
|
|
||||||
if (d->voff) *d->voff = off;
|
|
||||||
if (d->vtype) *d->vtype = d->t;
|
|
||||||
}
|
|
||||||
d->depth++;
|
|
||||||
break;
|
|
||||||
case '}':
|
|
||||||
case ']':
|
|
||||||
d->depth--;
|
|
||||||
if (d->depth == 1 && d->vo) {
|
|
||||||
d->len = off + len;
|
|
||||||
if (d->vlen) *d->vlen = d->len - d->vo;
|
|
||||||
if (d->arrayindex >= 0) {
|
|
||||||
if (d->koff) *d->koff = d->arrayindex; // koff holds array index
|
|
||||||
if (d->klen) *d->klen = 0; // klen holds 0
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (d->depth == 1 && d->arrayindex >= 0) d->arrayindex++;
|
|
||||||
break;
|
|
||||||
case ',':
|
|
||||||
case ':':
|
|
||||||
break;
|
|
||||||
case MJSON_TOK_KEY:
|
|
||||||
if (d->depth == 1 && d->off < off) {
|
|
||||||
if (d->koff) *d->koff = off; // And report back to the user
|
|
||||||
if (d->klen) *d->klen = len; // If we have to
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (d->depth != 1) break;
|
|
||||||
// If we're iterating over the array
|
|
||||||
if (off > d->off) {
|
|
||||||
d->len = off + len;
|
|
||||||
if (d->vlen) *d->vlen = len; // value length
|
|
||||||
if (d->voff) *d->voff = off; // value offset
|
|
||||||
if (d->vtype) *d->vtype = tok; // value type
|
|
||||||
if (d->arrayindex >= 0) {
|
|
||||||
if (d->koff) *d->koff = d->arrayindex; // koff holds array index
|
|
||||||
if (d->klen) *d->klen = 0; // klen holds 0
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (d->arrayindex >= 0) d->arrayindex++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
(void) s;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_next(const char *s, int n, int off, int *koff, int *klen, int *voff,
|
|
||||||
int *vlen, int *vtype) {
|
|
||||||
struct nextdata d = {off, 0, 0, 0, 0, -1, koff, klen, voff, vlen, vtype};
|
|
||||||
mjson(s, n, next_cb, &d);
|
|
||||||
return d.len;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_PRINT
|
|
||||||
int mjson_print_fixed_buf(const char *ptr, int len, void *fndata) {
|
|
||||||
struct mjson_fixedbuf *fb = (struct mjson_fixedbuf *) fndata;
|
|
||||||
int i, left = fb->size - 1 - fb->len;
|
|
||||||
if (left < len) len = left;
|
|
||||||
for (i = 0; i < len; i++) fb->ptr[fb->len + i] = ptr[i];
|
|
||||||
fb->len += len;
|
|
||||||
fb->ptr[fb->len] = '\0';
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_dynamic_buf(const char *ptr, int len, void *fndata) {
|
|
||||||
char *s, *buf = *(char **) fndata;
|
|
||||||
int curlen = buf == NULL ? 0 : strlen(buf);
|
|
||||||
if ((s = (char *) realloc(buf, curlen + len + 1)) == NULL) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
memcpy(s + curlen, ptr, len);
|
|
||||||
s[curlen + len] = '\0';
|
|
||||||
*(char **) fndata = s;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_null(const char *ptr, int len, void *userdata) {
|
|
||||||
(void) ptr;
|
|
||||||
(void) userdata;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_file(const char *ptr, int len, void *userdata) {
|
|
||||||
return fwrite(ptr, 1, len, (FILE *) userdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_buf(mjson_print_fn_t fn, void *fndata, const char *buf,
|
|
||||||
int len) {
|
|
||||||
return fn(buf, len, fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_int(mjson_print_fn_t fn, void *fndata, int value,
|
|
||||||
int is_signed) {
|
|
||||||
char buf[20];
|
|
||||||
int len = snprintf(buf, sizeof(buf), is_signed ? "%d" : "%u", value);
|
|
||||||
return fn(buf, len, fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_long(mjson_print_fn_t fn, void *fndata, long value,
|
|
||||||
int is_signed) {
|
|
||||||
char buf[20];
|
|
||||||
const char *fmt = (is_signed ? "%ld" : "%lu");
|
|
||||||
int len = snprintf(buf, sizeof(buf), fmt, value);
|
|
||||||
return fn(buf, len, fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_dbl(mjson_print_fn_t fn, void *fndata, double d,
|
|
||||||
const char *fmt) {
|
|
||||||
char buf[40];
|
|
||||||
int n = snprintf(buf, sizeof(buf), fmt, d);
|
|
||||||
return fn(buf, n, fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_print_str(mjson_print_fn_t fn, void *fndata, const char *s, int len) {
|
|
||||||
int i, n = fn("\"", 1, fndata);
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
char c = mjson_esc(s[i], 1);
|
|
||||||
if (c) {
|
|
||||||
n += fn("\\", 1, fndata);
|
|
||||||
n += fn(&c, 1, fndata);
|
|
||||||
} else {
|
|
||||||
n += fn(&s[i], 1, fndata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n + fn("\"", 1, fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_BASE64
|
|
||||||
int mjson_print_b64(mjson_print_fn_t fn, void *fndata, const unsigned char *s,
|
|
||||||
int n) {
|
|
||||||
const char *t =
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
||||||
int i, len = fn("\"", 1, fndata);
|
|
||||||
for (i = 0; i < n; i += 3) {
|
|
||||||
int a = s[i], b = i + 1 < n ? s[i + 1] : 0, c = i + 2 < n ? s[i + 2] : 0;
|
|
||||||
char buf[4] = {t[a >> 2], t[(a & 3) << 4 | (b >> 4)], '=', '='};
|
|
||||||
if (i + 1 < n) buf[2] = t[(b & 15) << 2 | (c >> 6)];
|
|
||||||
if (i + 2 < n) buf[3] = t[c & 63];
|
|
||||||
len += fn(buf, sizeof(buf), fndata);
|
|
||||||
}
|
|
||||||
return len + fn("\"", 1, fndata);
|
|
||||||
}
|
|
||||||
#endif /* MJSON_ENABLE_BASE64 */
|
|
||||||
|
|
||||||
int mjson_vprintf(mjson_print_fn_t fn, void *fndata, const char *fmt,
|
|
||||||
va_list xap) {
|
|
||||||
int i = 0, n = 0;
|
|
||||||
va_list ap;
|
|
||||||
va_copy(ap, xap);
|
|
||||||
while (fmt[i] != '\0') {
|
|
||||||
if (fmt[i] == '%') {
|
|
||||||
char fc = fmt[++i];
|
|
||||||
int is_long = 0;
|
|
||||||
if (fc == 'l') {
|
|
||||||
is_long = 1;
|
|
||||||
fc = fmt[i + 1];
|
|
||||||
}
|
|
||||||
if (fc == 'Q') {
|
|
||||||
char *buf = va_arg(ap, char *);
|
|
||||||
n += mjson_print_str(fn, fndata, buf ? buf : "", buf ? strlen(buf) : 0);
|
|
||||||
} else if (strncmp(&fmt[i], ".*Q", 3) == 0) {
|
|
||||||
int len = va_arg(ap, int);
|
|
||||||
char *buf = va_arg(ap, char *);
|
|
||||||
n += mjson_print_str(fn, fndata, buf, len);
|
|
||||||
i += 2;
|
|
||||||
} else if (fc == 'd' || fc == 'u') {
|
|
||||||
int is_signed = (fc == 'd');
|
|
||||||
if (is_long) {
|
|
||||||
long val = va_arg(ap, long);
|
|
||||||
n += mjson_print_long(fn, fndata, val, is_signed);
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
int val = va_arg(ap, int);
|
|
||||||
n += mjson_print_int(fn, fndata, val, is_signed);
|
|
||||||
}
|
|
||||||
} else if (fc == 'B') {
|
|
||||||
const char *s = va_arg(ap, int) ? "true" : "false";
|
|
||||||
n += mjson_print_buf(fn, fndata, s, strlen(s));
|
|
||||||
} else if (fc == 's') {
|
|
||||||
char *buf = va_arg(ap, char *);
|
|
||||||
n += mjson_print_buf(fn, fndata, buf, strlen(buf));
|
|
||||||
} else if (strncmp(&fmt[i], ".*s", 3) == 0) {
|
|
||||||
int len = va_arg(ap, int);
|
|
||||||
char *buf = va_arg(ap, char *);
|
|
||||||
n += mjson_print_buf(fn, fndata, buf, len);
|
|
||||||
i += 2;
|
|
||||||
} else if (fc == 'g') {
|
|
||||||
n += mjson_print_dbl(fn, fndata, va_arg(ap, double), "%g");
|
|
||||||
} else if (fc == 'f') {
|
|
||||||
n += mjson_print_dbl(fn, fndata, va_arg(ap, double), "%f");
|
|
||||||
#if MJSON_ENABLE_BASE64
|
|
||||||
} else if (fc == 'V') {
|
|
||||||
int len = va_arg(ap, int);
|
|
||||||
const char *buf = va_arg(ap, const char *);
|
|
||||||
n += mjson_print_b64(fn, fndata, (unsigned char *) buf, len);
|
|
||||||
#endif
|
|
||||||
} else if (fc == 'H') {
|
|
||||||
const char *hex = "0123456789abcdef";
|
|
||||||
int i, len = va_arg(ap, int);
|
|
||||||
const unsigned char *p = va_arg(ap, const unsigned char *);
|
|
||||||
n += fn("\"", 1, fndata);
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
n += fn(&hex[(p[i] >> 4) & 15], 1, fndata);
|
|
||||||
n += fn(&hex[p[i] & 15], 1, fndata);
|
|
||||||
}
|
|
||||||
n += fn("\"", 1, fndata);
|
|
||||||
} else if (fc == 'M') {
|
|
||||||
mjson_vprint_fn_t vfn = va_arg(ap, mjson_vprint_fn_t);
|
|
||||||
n += vfn(fn, fndata, &ap);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
n += mjson_print_buf(fn, fndata, &fmt[i++], 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
va_end(xap);
|
|
||||||
va_end(ap);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_printf(mjson_print_fn_t fn, void *fndata, const char *fmt, ...) {
|
|
||||||
va_list ap;
|
|
||||||
int len;
|
|
||||||
va_start(ap, fmt);
|
|
||||||
len = mjson_vprintf(fn, fndata, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
#endif /* MJSON_ENABLE_PRINT */
|
|
||||||
|
|
||||||
#if MJSON_IMPLEMENT_STRTOD
|
|
||||||
static inline int is_digit(int c) {
|
|
||||||
return c >= '0' && c <= '9';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NOTE: strtod() implementation by Yasuhiro Matsumoto. */
|
|
||||||
double strtod(const char *str, char **end) {
|
|
||||||
double d = 0.0;
|
|
||||||
int sign = 1, n = 0;
|
|
||||||
const char *p = str, *a = str;
|
|
||||||
|
|
||||||
/* decimal part */
|
|
||||||
if (*p == '-') {
|
|
||||||
sign = -1;
|
|
||||||
++p;
|
|
||||||
} else if (*p == '+')
|
|
||||||
++p;
|
|
||||||
if (is_digit(*p)) {
|
|
||||||
d = (double) (*p++ - '0');
|
|
||||||
while (*p && is_digit(*p)) {
|
|
||||||
d = d * 10.0 + (double) (*p - '0');
|
|
||||||
++p;
|
|
||||||
++n;
|
|
||||||
}
|
|
||||||
a = p;
|
|
||||||
} else if (*p != '.')
|
|
||||||
goto done;
|
|
||||||
d *= sign;
|
|
||||||
|
|
||||||
/* fraction part */
|
|
||||||
if (*p == '.') {
|
|
||||||
double f = 0.0;
|
|
||||||
double base = 0.1;
|
|
||||||
++p;
|
|
||||||
|
|
||||||
if (is_digit(*p)) {
|
|
||||||
while (*p && is_digit(*p)) {
|
|
||||||
f += base * (*p - '0');
|
|
||||||
base /= 10.0;
|
|
||||||
++p;
|
|
||||||
++n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d += f * sign;
|
|
||||||
a = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* exponential part */
|
|
||||||
if ((*p == 'E') || (*p == 'e')) {
|
|
||||||
int e = 0;
|
|
||||||
++p;
|
|
||||||
|
|
||||||
sign = 1;
|
|
||||||
if (*p == '-') {
|
|
||||||
sign = -1;
|
|
||||||
++p;
|
|
||||||
} else if (*p == '+')
|
|
||||||
++p;
|
|
||||||
|
|
||||||
if (is_digit(*p)) {
|
|
||||||
while (*p == '0') ++p;
|
|
||||||
e = (int) (*p++ - '0');
|
|
||||||
while (*p && is_digit(*p)) {
|
|
||||||
e = e * 10 + (int) (*p - '0');
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
e *= sign;
|
|
||||||
} else if (!is_digit(*(a - 1))) {
|
|
||||||
a = str;
|
|
||||||
goto done;
|
|
||||||
} else if (*p == 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
if (d == 2.2250738585072011 && e == -308) {
|
|
||||||
d = 0.0;
|
|
||||||
a = p;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (d == 2.2250738585072012 && e <= -308) {
|
|
||||||
d *= 1.0e-308;
|
|
||||||
a = p;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 10; i++) d *= 10;
|
|
||||||
}
|
|
||||||
a = p;
|
|
||||||
} else if (p > str && !is_digit(*(p - 1))) {
|
|
||||||
a = str;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
if (end) *end = (char *) a;
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_MERGE
|
|
||||||
int mjson_merge(const char *s, int n, const char *s2, int n2,
|
|
||||||
mjson_print_fn_t fn, void *userdata) {
|
|
||||||
int koff, klen, voff, vlen, t, t2, k, off = 0, len = 0, comma = 0;
|
|
||||||
if (n < 2) return len;
|
|
||||||
len += fn("{", 1, userdata);
|
|
||||||
while ((off = mjson_next(s, n, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
|
|
||||||
#if !defined(_MSC_VER) || _MSC_VER >= 1700
|
|
||||||
char path[klen + 1];
|
|
||||||
#else
|
|
||||||
char *path = (char *) alloca(klen + 1);
|
|
||||||
#endif
|
|
||||||
const char *val;
|
|
||||||
memcpy(path, "$.", 2);
|
|
||||||
memcpy(path + 2, s + koff + 1, klen - 2);
|
|
||||||
path[klen] = '\0';
|
|
||||||
if ((t2 = mjson_find(s2, n2, path, &val, &k)) != MJSON_TOK_INVALID) {
|
|
||||||
if (t2 == MJSON_TOK_NULL) continue; // null deletes the key
|
|
||||||
} else {
|
|
||||||
val = s + voff; // Key is not found in the update. Copy the old value.
|
|
||||||
}
|
|
||||||
if (comma) len += fn(",", 1, userdata);
|
|
||||||
len += fn(s + koff, klen, userdata);
|
|
||||||
len += fn(":", 1, userdata);
|
|
||||||
if (t == MJSON_TOK_OBJECT && t2 == MJSON_TOK_OBJECT) {
|
|
||||||
len += mjson_merge(s + voff, vlen, val, k, fn, userdata);
|
|
||||||
} else {
|
|
||||||
if (t2 != MJSON_TOK_INVALID) vlen = k;
|
|
||||||
len += fn(val, vlen, userdata);
|
|
||||||
}
|
|
||||||
comma = 1;
|
|
||||||
}
|
|
||||||
// Add missing keys
|
|
||||||
off = 0;
|
|
||||||
while ((off = mjson_next(s2, n2, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
|
|
||||||
#if !defined(_MSC_VER) || _MSC_VER >= 1700
|
|
||||||
char path[klen + 1];
|
|
||||||
#else
|
|
||||||
char *path = (char *) alloca(klen + 1);
|
|
||||||
#endif
|
|
||||||
const char *val;
|
|
||||||
if (t == MJSON_TOK_NULL) continue;
|
|
||||||
memcpy(path, "$.", 2);
|
|
||||||
memcpy(path + 2, s2 + koff + 1, klen - 2);
|
|
||||||
path[klen] = '\0';
|
|
||||||
if (mjson_find(s, n, path, &val, &vlen) != MJSON_TOK_INVALID) continue;
|
|
||||||
if (comma) len += fn(",", 1, userdata);
|
|
||||||
len += fn(s2 + koff, klen, userdata);
|
|
||||||
len += fn(":", 1, userdata);
|
|
||||||
len += fn(s2 + voff, vlen, userdata);
|
|
||||||
comma = 1;
|
|
||||||
}
|
|
||||||
len += fn("}", 1, userdata);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
#endif // MJSON_ENABLE_MERGE
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_PRETTY
|
|
||||||
struct prettydata {
|
|
||||||
int level;
|
|
||||||
int len;
|
|
||||||
int prev;
|
|
||||||
const char *pad;
|
|
||||||
int padlen;
|
|
||||||
mjson_print_fn_t fn;
|
|
||||||
void *userdata;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int pretty_cb(int ev, const char *s, int off, int len, void *ud) {
|
|
||||||
struct prettydata *d = (struct prettydata *) ud;
|
|
||||||
int i;
|
|
||||||
switch (ev) {
|
|
||||||
case '{':
|
|
||||||
case '[':
|
|
||||||
d->level++;
|
|
||||||
d->len += d->fn(s + off, len, d->userdata);
|
|
||||||
break;
|
|
||||||
case '}':
|
|
||||||
case ']':
|
|
||||||
d->level--;
|
|
||||||
if (d->prev != '[' && d->prev != '{' && d->padlen > 0) {
|
|
||||||
d->len += d->fn("\n", 1, d->userdata);
|
|
||||||
for (i = 0; i < d->level; i++)
|
|
||||||
d->len += d->fn(d->pad, d->padlen, d->userdata);
|
|
||||||
}
|
|
||||||
d->len += d->fn(s + off, len, d->userdata);
|
|
||||||
break;
|
|
||||||
case ',':
|
|
||||||
d->len += d->fn(s + off, len, d->userdata);
|
|
||||||
if (d->padlen > 0) {
|
|
||||||
d->len += d->fn("\n", 1, d->userdata);
|
|
||||||
for (i = 0; i < d->level; i++)
|
|
||||||
d->len += d->fn(d->pad, d->padlen, d->userdata);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ':':
|
|
||||||
d->len += d->fn(s + off, len, d->userdata);
|
|
||||||
if (d->padlen > 0) d->len += d->fn(" ", 1, d->userdata);
|
|
||||||
break;
|
|
||||||
case MJSON_TOK_KEY:
|
|
||||||
if (d->prev == '{' && d->padlen > 0) {
|
|
||||||
d->len += d->fn("\n", 1, d->userdata);
|
|
||||||
for (i = 0; i < d->level; i++)
|
|
||||||
d->len += d->fn(d->pad, d->padlen, d->userdata);
|
|
||||||
}
|
|
||||||
d->len += d->fn(s + off, len, d->userdata);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (d->prev == '[' && d->padlen > 0) {
|
|
||||||
d->len += d->fn("\n", 1, d->userdata);
|
|
||||||
for (i = 0; i < d->level; i++)
|
|
||||||
d->len += d->fn(d->pad, d->padlen, d->userdata);
|
|
||||||
}
|
|
||||||
d->len += d->fn(s + off, len, d->userdata);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
d->prev = ev;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mjson_pretty(const char *s, int n, const char *pad, mjson_print_fn_t fn,
|
|
||||||
void *userdata) {
|
|
||||||
struct prettydata d = {0, 0, 0, pad, strlen(pad), fn, userdata};
|
|
||||||
if (mjson(s, n, pretty_cb, &d) < 0) return -1;
|
|
||||||
return d.len;
|
|
||||||
}
|
|
||||||
#endif // MJSON_ENABLE_PRETTY
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_RPC
|
|
||||||
struct jsonrpc_ctx jsonrpc_default_context;
|
|
||||||
struct jsonrpc_userdata {
|
|
||||||
mjson_print_fn_t fn;
|
|
||||||
void *fndata;
|
|
||||||
};
|
|
||||||
|
|
||||||
int mjson_globmatch(const char *s1, int n1, const char *s2, int n2) {
|
|
||||||
int i = 0, j = 0, ni = 0, nj = 0;
|
|
||||||
while (i < n1 || j < n2) {
|
|
||||||
if (i < n1 && j < n2 && (s1[i] == '?' || s2[j] == s1[i])) {
|
|
||||||
i++, j++;
|
|
||||||
} else if (i < n1 && (s1[i] == '*' || s1[i] == '#')) {
|
|
||||||
ni = i, nj = j + 1, i++;
|
|
||||||
} else if (nj > 0 && nj <= n2 && (s1[i - 1] == '#' || s2[j] != '/')) {
|
|
||||||
i = ni, j = nj;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int jsonrpc_printer(const char *buf, int len, void *userdata) {
|
|
||||||
struct jsonrpc_userdata *u = (struct jsonrpc_userdata *) userdata;
|
|
||||||
return u->fn(buf, len, u->fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_return_errorv(struct jsonrpc_request *r, int code,
|
|
||||||
const char *message, const char *data_fmt,
|
|
||||||
va_list ap) {
|
|
||||||
if (r->id_len == 0) return;
|
|
||||||
mjson_printf(r->fn, r->fndata,
|
|
||||||
"{\"id\":%.*s,\"error\":{\"code\":%d,\"message\":%Q", r->id_len,
|
|
||||||
r->id, code, message == NULL ? "" : message);
|
|
||||||
if (data_fmt != NULL) {
|
|
||||||
mjson_printf(r->fn, r->fndata, ",\"data\":");
|
|
||||||
mjson_vprintf(r->fn, r->fndata, data_fmt, ap);
|
|
||||||
}
|
|
||||||
mjson_printf(r->fn, r->fndata, "}}\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_return_error(struct jsonrpc_request *r, int code,
|
|
||||||
const char *message, const char *data_fmt, ...) {
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, data_fmt);
|
|
||||||
jsonrpc_return_errorv(r, code, message, data_fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_return_successv(struct jsonrpc_request *r, const char *result_fmt,
|
|
||||||
va_list ap) {
|
|
||||||
if (r->id_len == 0) return;
|
|
||||||
mjson_printf(r->fn, r->fndata, "{\"id\":%.*s,\"result\":", r->id_len, r->id);
|
|
||||||
if (result_fmt != NULL) {
|
|
||||||
mjson_vprintf(r->fn, r->fndata, result_fmt, ap);
|
|
||||||
} else {
|
|
||||||
mjson_printf(r->fn, r->fndata, "%s", "null");
|
|
||||||
}
|
|
||||||
mjson_printf(r->fn, r->fndata, "}\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
|
|
||||||
...) {
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, result_fmt);
|
|
||||||
jsonrpc_return_successv(r, result_fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *req, int req_sz,
|
|
||||||
mjson_print_fn_t fn, void *fndata) {
|
|
||||||
const char *result = NULL, *error = NULL;
|
|
||||||
int result_sz = 0, error_sz = 0;
|
|
||||||
struct jsonrpc_method *m = NULL;
|
|
||||||
struct jsonrpc_userdata d;
|
|
||||||
struct jsonrpc_request r = {req, req_sz, 0, 0, 0, 0, 0,
|
|
||||||
0, &jsonrpc_printer, NULL, NULL};
|
|
||||||
|
|
||||||
d.fn = fn;
|
|
||||||
d.fndata = fndata;
|
|
||||||
r.fndata = &d;
|
|
||||||
|
|
||||||
// Is is a response frame?
|
|
||||||
mjson_find(req, req_sz, "$.result", &result, &result_sz);
|
|
||||||
if (result == NULL) mjson_find(req, req_sz, "$.error", &error, &error_sz);
|
|
||||||
if (result_sz > 0 || error_sz > 0) {
|
|
||||||
if (ctx->response_cb != NULL) ctx->response_cb(req, req_sz, ctx->userdata);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method must exist and must be a string
|
|
||||||
if (mjson_find(req, req_sz, "$.method", &r.method, &r.method_len) !=
|
|
||||||
MJSON_TOK_STRING) {
|
|
||||||
mjson_printf(fn, fndata, "{\"error\":{\"code\":-32700,\"message\":%.*Q}}\n",
|
|
||||||
req_sz, req);
|
|
||||||
ctx->in_len = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// id and params are optional
|
|
||||||
mjson_find(req, req_sz, "$.id", &r.id, &r.id_len);
|
|
||||||
mjson_find(req, req_sz, "$.params", &r.params, &r.params_len);
|
|
||||||
|
|
||||||
for (m = ctx->methods; m != NULL; m = m->next) {
|
|
||||||
if (mjson_globmatch(m->method, m->method_sz, r.method + 1,
|
|
||||||
r.method_len - 2) > 0) {
|
|
||||||
if (r.params == NULL) r.params = "";
|
|
||||||
r.userdata = m->cbdata;
|
|
||||||
m->cb(&r);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m == NULL) {
|
|
||||||
jsonrpc_return_error(&r, JSONRPC_ERROR_NOT_FOUND, "method not found", NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int jsonrpc_print_methods(mjson_print_fn_t fn, void *fndata,
|
|
||||||
va_list *ap) {
|
|
||||||
struct jsonrpc_ctx *ctx = va_arg(*ap, struct jsonrpc_ctx *);
|
|
||||||
struct jsonrpc_method *m;
|
|
||||||
int len = 0;
|
|
||||||
for (m = ctx->methods; m != NULL; m = m->next) {
|
|
||||||
if (m != ctx->methods) len += mjson_print_buf(fn, fndata, ",", 1);
|
|
||||||
len += mjson_print_str(fn, fndata, m->method, strlen(m->method));
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rpclist(struct jsonrpc_request *r) {
|
|
||||||
jsonrpc_return_success(r, "[%M]", jsonrpc_print_methods, r->userdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t response_cb,
|
|
||||||
void *userdata) {
|
|
||||||
ctx->response_cb = response_cb;
|
|
||||||
ctx->userdata = userdata;
|
|
||||||
jsonrpc_ctx_export(ctx, MJSON_RPC_LIST_NAME, rpclist, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_ctx_process_byte(struct jsonrpc_ctx *ctx, unsigned char ch,
|
|
||||||
mjson_print_fn_t fn, void *p) {
|
|
||||||
if (ctx->in_len >= (int) sizeof(ctx->in)) ctx->in_len = 0; // Overflow
|
|
||||||
if (ch == '\n') { // If new line, parse frame
|
|
||||||
if (ctx->in_len > 1) jsonrpc_ctx_process(ctx, ctx->in, ctx->in_len, fn, p);
|
|
||||||
ctx->in_len = 0;
|
|
||||||
} else {
|
|
||||||
ctx->in[ctx->in_len] = ch; // Append to the buffer
|
|
||||||
ctx->in_len++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void jsonrpc_init(mjson_print_fn_t response_cb, void *userdata) {
|
|
||||||
jsonrpc_ctx_init(&jsonrpc_default_context, response_cb, userdata);
|
|
||||||
}
|
|
||||||
#endif // MJSON_ENABLE_RPC
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
// Copyright (c) 2018-2020 Cesanta Software Limited
|
|
||||||
// All rights reserved
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
#ifndef MJSON_H
|
|
||||||
#define MJSON_H
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#ifndef MJSON_ENABLE_PRINT
|
|
||||||
#define MJSON_ENABLE_PRINT 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MJSON_ENABLE_RPC
|
|
||||||
#define MJSON_ENABLE_RPC 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MJSON_ENABLE_BASE64
|
|
||||||
#define MJSON_ENABLE_BASE64 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MJSON_ENABLE_MERGE
|
|
||||||
#define MJSON_ENABLE_MERGE 0
|
|
||||||
#elif MJSON_ENABLE_MERGE
|
|
||||||
#define MJSON_ENABLE_NEXT 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MJSON_ENABLE_PRETTY
|
|
||||||
#define MJSON_ENABLE_PRETTY 0
|
|
||||||
#elif MJSON_ENABLE_PRETTY
|
|
||||||
#define MJSON_ENABLE_NEXT 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MJSON_ENABLE_NEXT
|
|
||||||
#define MJSON_ENABLE_NEXT 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef MJSON_RPC_IN_BUF_SIZE
|
|
||||||
#define MJSON_RPC_IN_BUF_SIZE 256
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MJSON_RPC_LIST_NAME
|
|
||||||
#define MJSON_RPC_LIST_NAME "rpc.list"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum {
|
|
||||||
MJSON_ERROR_INVALID_INPUT = -1,
|
|
||||||
MJSON_ERROR_TOO_DEEP = -2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mjson_tok {
|
|
||||||
MJSON_TOK_INVALID = 0,
|
|
||||||
MJSON_TOK_KEY = 1,
|
|
||||||
MJSON_TOK_STRING = 11,
|
|
||||||
MJSON_TOK_NUMBER = 12,
|
|
||||||
MJSON_TOK_TRUE = 13,
|
|
||||||
MJSON_TOK_FALSE = 14,
|
|
||||||
MJSON_TOK_NULL = 15,
|
|
||||||
MJSON_TOK_ARRAY = 91,
|
|
||||||
MJSON_TOK_OBJECT = 123,
|
|
||||||
};
|
|
||||||
#define MJSON_TOK_IS_VALUE(t) ((t) > 10 && (t) < 20)
|
|
||||||
|
|
||||||
typedef int (*mjson_cb_t)(int ev, const char *s, int off, int len, void *ud);
|
|
||||||
|
|
||||||
#ifndef MJSON_MAX_DEPTH
|
|
||||||
#define MJSON_MAX_DEPTH 20
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int mjson(const char *s, int len, mjson_cb_t cb, void *ud);
|
|
||||||
enum mjson_tok mjson_find(const char *s, int len, const char *jp,
|
|
||||||
const char **tokptr, int *toklen);
|
|
||||||
int mjson_get_number(const char *s, int len, const char *path, double *v);
|
|
||||||
int mjson_get_bool(const char *s, int len, const char *path, int *v);
|
|
||||||
int mjson_get_string(const char *s, int len, const char *path, char *to, int n);
|
|
||||||
int mjson_get_hex(const char *s, int len, const char *path, char *to, int n);
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_NEXT
|
|
||||||
int mjson_next(const char *s, int n, int off, int *koff, int *klen, int *voff,
|
|
||||||
int *vlen, int *vtype);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_BASE64
|
|
||||||
int mjson_get_base64(const char *s, int len, const char *path, char *to, int n);
|
|
||||||
int mjson_base64_dec(const char *src, int n, char *dst, int dlen);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_PRINT
|
|
||||||
typedef int (*mjson_print_fn_t)(const char *buf, int len, void *userdata);
|
|
||||||
typedef int (*mjson_vprint_fn_t)(mjson_print_fn_t, void *, va_list *);
|
|
||||||
|
|
||||||
struct mjson_fixedbuf {
|
|
||||||
char *ptr;
|
|
||||||
int size, len;
|
|
||||||
};
|
|
||||||
|
|
||||||
int mjson_printf(mjson_print_fn_t, void *, const char *fmt, ...);
|
|
||||||
int mjson_vprintf(mjson_print_fn_t, void *, const char *fmt, va_list ap);
|
|
||||||
int mjson_print_str(mjson_print_fn_t, void *, const char *s, int len);
|
|
||||||
int mjson_print_int(mjson_print_fn_t, void *, int value, int is_signed);
|
|
||||||
int mjson_print_long(mjson_print_fn_t, void *, long value, int is_signed);
|
|
||||||
int mjson_print_buf(mjson_print_fn_t fn, void *, const char *buf, int len);
|
|
||||||
|
|
||||||
int mjson_print_null(const char *ptr, int len, void *userdata);
|
|
||||||
int mjson_print_file(const char *ptr, int len, void *userdata);
|
|
||||||
int mjson_print_fixed_buf(const char *ptr, int len, void *userdata);
|
|
||||||
int mjson_print_dynamic_buf(const char *ptr, int len, void *userdata);
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_PRETTY
|
|
||||||
int mjson_pretty(const char *, int, const char *, mjson_print_fn_t, void *);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_MERGE
|
|
||||||
int mjson_merge(const char *, int, const char *, int, mjson_print_fn_t, void *);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // MJSON_ENABLE_PRINT
|
|
||||||
|
|
||||||
#if MJSON_ENABLE_RPC
|
|
||||||
|
|
||||||
void jsonrpc_init(mjson_print_fn_t, void *userdata);
|
|
||||||
int mjson_globmatch(const char *s1, int n1, const char *s2, int n2);
|
|
||||||
|
|
||||||
struct jsonrpc_request {
|
|
||||||
const char *frame; // Points to the whole frame
|
|
||||||
int frame_len; // Frame length
|
|
||||||
const char *params; // Points to the "params" in the request frame
|
|
||||||
int params_len; // Length of the "params"
|
|
||||||
const char *id; // Points to the "id" in the request frame
|
|
||||||
int id_len; // Length of the "id"
|
|
||||||
const char *method; // Points to the "method" in the request frame
|
|
||||||
int method_len; // Length of the "method"
|
|
||||||
mjson_print_fn_t fn; // Printer function
|
|
||||||
void *fndata; // Printer function data
|
|
||||||
void *userdata; // Callback's user data as specified at export time
|
|
||||||
};
|
|
||||||
|
|
||||||
struct jsonrpc_method {
|
|
||||||
const char *method;
|
|
||||||
int method_sz;
|
|
||||||
void (*cb)(struct jsonrpc_request *);
|
|
||||||
void *cbdata;
|
|
||||||
struct jsonrpc_method *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Main RPC context, stores current request information and a list of
|
|
||||||
// exported RPC methods.
|
|
||||||
struct jsonrpc_ctx {
|
|
||||||
struct jsonrpc_method *methods;
|
|
||||||
void *userdata;
|
|
||||||
mjson_print_fn_t response_cb;
|
|
||||||
int in_len;
|
|
||||||
char in[MJSON_RPC_IN_BUF_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Registers function fn under the given name within the given RPC context
|
|
||||||
#define jsonrpc_ctx_export(ctx, name, fn, ud) \
|
|
||||||
do { \
|
|
||||||
static struct jsonrpc_method m = {(name), sizeof(name) - 1, (fn), 0, 0}; \
|
|
||||||
m.cbdata = (ud); \
|
|
||||||
m.next = (ctx)->methods; \
|
|
||||||
(ctx)->methods = &m; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t, void *);
|
|
||||||
void jsonrpc_return_error(struct jsonrpc_request *r, int code,
|
|
||||||
const char *message, const char *data_fmt, ...);
|
|
||||||
void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
|
|
||||||
...);
|
|
||||||
void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *req, int req_sz,
|
|
||||||
mjson_print_fn_t fn, void *fndata);
|
|
||||||
void jsonrpc_ctx_process_byte(struct jsonrpc_ctx *ctx, unsigned char ch,
|
|
||||||
mjson_print_fn_t fn, void *fndata);
|
|
||||||
|
|
||||||
extern struct jsonrpc_ctx jsonrpc_default_context;
|
|
||||||
|
|
||||||
#define jsonrpc_export(name, fn, ud) \
|
|
||||||
jsonrpc_ctx_export(&jsonrpc_default_context, (name), (fn), (ud))
|
|
||||||
|
|
||||||
#define jsonrpc_process(buf, len, fn, data) \
|
|
||||||
jsonrpc_ctx_process(&jsonrpc_default_context, (buf), (len), (fn), (data))
|
|
||||||
|
|
||||||
#define jsonrpc_process_byte(x, fn, data) \
|
|
||||||
jsonrpc_ctx_process_byte(&jsonrpc_default_context, (x), (fn), (data))
|
|
||||||
|
|
||||||
#define JSONRPC_ERROR_INVALID -32700 /* Invalid JSON was received */
|
|
||||||
#define JSONRPC_ERROR_NOT_FOUND -32601 /* The method does not exist */
|
|
||||||
#define JSONRPC_ERROR_BAD_PARAMS -32602 /* Invalid params passed */
|
|
||||||
#define JSONRPC_ERROR_INTERNAL -32603 /* Internal JSON-RPC error */
|
|
||||||
|
|
||||||
#endif // MJSON_ENABLE_RPC
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif // MJSON_H
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
const {h, render, Component} = preact;
|
|
||||||
const html = htm.bind(h);
|
|
||||||
|
|
||||||
const Info = () => html`<div id="info">
|
|
||||||
This dashboard shows values kept in server memory. Many clients
|
|
||||||
can open this page. As soon as any client changes any value,
|
|
||||||
all clients update automatically.
|
|
||||||
<br/><br/>
|
|
||||||
The JS code that watches state changes, reconnects on network failures.
|
|
||||||
That means if server restarts, dashboard on all connected clients
|
|
||||||
refresh automatically.
|
|
||||||
<br/><br/>
|
|
||||||
|
|
||||||
You can use <code>curl</code> command-line utility:
|
|
||||||
<br/><code>curl localhost:8000/api/config/get</code>
|
|
||||||
<br/><code>curl localhost:8000/api/config/watch</code>
|
|
||||||
<br/><code>curl localhost:8000/api/config/set -d '{"a":123}'</code>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
class Dashboard extends Component {
|
|
||||||
state = {value1: null, value2: null};
|
|
||||||
componentDidMount() {
|
|
||||||
axios.get('/api/config/get')
|
|
||||||
.then(r => this.setState(r.data))
|
|
||||||
.catch(e => console.log(e));
|
|
||||||
var self = this;
|
|
||||||
var f = function(reader) {
|
|
||||||
return reader.read().then(function(result) {
|
|
||||||
var data = String.fromCharCode.apply(null, result.value);
|
|
||||||
self.setState(JSON.parse(data));
|
|
||||||
// console.log(JSON.parse(data));
|
|
||||||
if (!result.done) return f(reader);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
fetch('/api/config/watch')
|
|
||||||
.then(r => r.body.getReader())
|
|
||||||
.then(f)
|
|
||||||
.catch(e => setTimeout(x => self.componentDidMount(), 1000));
|
|
||||||
}
|
|
||||||
render(props, state) {
|
|
||||||
return html
|
|
||||||
`<div id="dashboard" style="display: flex; justify-content: flex-evenly;">
|
|
||||||
<div style="width:50%;text-align:center;">Value 1 (number):
|
|
||||||
<div style="font-size:140%;"><b>${state.value1}</b></div></div>
|
|
||||||
<div style="width:50%;text-align:center;">Value 2 (string):
|
|
||||||
<div style="font-size:140%;"><b>${state.value2}</b></div></div>
|
|
||||||
</div>`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Form extends Component {
|
|
||||||
render(props, state) {
|
|
||||||
const onclick = ev => axios.post(
|
|
||||||
'/api/config/set', {value1: +state.value1, value2: state.value2});
|
|
||||||
// alert(JSON.stringify(state));
|
|
||||||
return html`<div id="form">
|
|
||||||
<div><b>Change values</b></div>
|
|
||||||
<table>
|
|
||||||
<tr><td>Value 1 (number):</td><td><input value=${state.value1}
|
|
||||||
onInput=${linkState(this, 'value1')} /></td></tr>
|
|
||||||
<tr><td>Value 2 (string):</td><td><input value=${state.value2}
|
|
||||||
onInput=${linkState(this, 'value2')}/></td></tr>
|
|
||||||
<tr><td></td>
|
|
||||||
<td><button onClick=${onclick}>Save values</button></td></tr>
|
|
||||||
</table>
|
|
||||||
</div>`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const App = () => html`<div id="container">
|
|
||||||
<${Info} />
|
|
||||||
<${Dashboard} />
|
|
||||||
<${Form} />
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
window.onload = () => render(h(App), document.body);
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
/* axios v0.21.1 | (c) 2020 by Matt Zabriskie */
|
|
||||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(4),a=n(22),u=n(10),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(9),c.all=function(e){return Promise.all(e)},c.spread=n(25),c.isAxiosError=n(26),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===R.call(e)}function o(e){return"undefined"==typeof e}function s(e){return null!==e&&!o(e)&&null!==e.constructor&&!o(e.constructor)&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}function i(e){return"[object ArrayBuffer]"===R.call(e)}function a(e){return"undefined"!=typeof FormData&&e instanceof FormData}function u(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function c(e){return"string"==typeof e}function f(e){return"number"==typeof e}function p(e){return null!==e&&"object"==typeof e}function d(e){if("[object Object]"!==R.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function l(e){return"[object Date]"===R.call(e)}function h(e){return"[object File]"===R.call(e)}function m(e){return"[object Blob]"===R.call(e)}function y(e){return"[object Function]"===R.call(e)}function g(e){return p(e)&&y(e.pipe)}function v(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function x(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function w(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function b(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.call(null,e[s],s,e)}function E(){function e(e,n){d(t[n])&&d(e)?t[n]=E(t[n],e):d(e)?t[n]=E({},e):r(e)?t[n]=e.slice():t[n]=e}for(var t={},n=0,o=arguments.length;n<o;n++)b(arguments[n],e);return t}function j(e,t,n){return b(t,function(t,r){n&&"function"==typeof t?e[r]=S(t,n):e[r]=t}),e}function C(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e}var S=n(3),R=Object.prototype.toString;e.exports={isArray:r,isArrayBuffer:i,isBuffer:s,isFormData:a,isArrayBufferView:u,isString:c,isNumber:f,isObject:p,isPlainObject:d,isUndefined:o,isDate:l,isFile:h,isBlob:m,isFunction:y,isStream:g,isURLSearchParams:v,isStandardBrowserEnv:w,forEach:b,merge:E,extend:j,trim:x,stripBOM:C}},function(e,t){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return e.apply(t,n)}}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(5),i=n(6),a=n(7),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(u(n||{},{method:e,url:t,data:(n||{}).data}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(u(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}if(s){var a=e.indexOf("#");a!==-1&&(e=e.slice(0,a)),e+=(e.indexOf("?")===-1?"?":"&")+s}return e}},function(e,t,n){"use stricLine truncated
|
|
||||||
//# sourceMappingURL=axios.min.map
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(){function n(n){var t=e.get(this);return t||(t=new Map,e.set(this,t)),1<(t=a(this,t.get(n)||(t.set(n,t=function(n){function t(n){1===u&&(n||(r=r.replace(/^\s*\n\s*|\s*\n\s*$/g,"")))?p.push(0,n,r):3===u&&(n||r)?(p.push(3,n,r),u=2):2===u&&"..."===r&&n?p.push(4,n,0):2===u&&r&&!n?p.push(5,0,!0,r):5<=u&&((r||!n&&5===u)&&(p.push(u,0,r,s),u=6),n&&(p.push(u,n,0,s),u=6)),r=""}for(var e,s,u=1,r="",h="",p=[0],a=0;a<n.length;a++){a&&(1===u&&t(),t(a));for(var o=0;o<n[a].length;o++)e=n[a][o],1===u?"<"===e?(t(),p=[p],u=3):r+=e:4===u?r="--"===r&&">"===e?(u=1,""):e+r[0]:h?e===h?h="":r+=e:'"'===e||"'"===e?h=e:">"===e?(t(),u=1):u&&("="===e?(u=5,s=r,r=""):"/"===e&&(u<5||">"===n[a][o+1])?(t(),3===u&&(p=p[0]),(p=(u=p)[0]).push(2,0,u),u=0):" "===e||"\t"===e||"\n"===e||"\r"===e?(t(),u=2):r+=e),3===u&&"!--"===r&&(u=4,p=p[0])}return t(),p}(n)),t),arguments,[])).length?t:t[0]}var a=function(n,t,e,s){var u;t[0]=0;for(var r=1;r<t.length;r++){var h=t[r++],p=t[r]?(t[0]|=h?1:2,e[t[r++]]):t[++r];3===h?s[0]=p:4===h?s[1]=Object.assign(s[1]||{},p):5===h?(s[1]=s[1]||{})[t[++r]]=p:6===h?s[1][t[++r]]+=p+"":h?(u=n.apply(p,a(n,p,e,["",null])),s.push(u),p[0]?t[0]|=2:(t[r-2]=0,t[r]=u)):s.push(p)}return s},e=new Map;"undefined"!=typeof module?module.exports=n:self.htm=n}();
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
function linkState(component, key, eventPath) {
|
|
||||||
let path = key.split('.'), cache = component.__lsc || (component.__lsc = {});
|
|
||||||
|
|
||||||
return cache[key + eventPath] || (cache[key + eventPath] = function(e) {
|
|
||||||
let t = e && e.target || this, state = {}, obj = state,
|
|
||||||
v = typeof eventPath === 'string' ?
|
|
||||||
delve(e, eventPath) :
|
|
||||||
t.nodeName ? (t.type.match(/^che|rad/) ? t.checked : t.value) :
|
|
||||||
e,
|
|
||||||
i = 0;
|
|
||||||
for (; i < path.length - 1; i++) {
|
|
||||||
obj = obj[path[i]] ||
|
|
||||||
(obj[path[i]] = !i && component.state[path[i]] || {});
|
|
||||||
}
|
|
||||||
obj[path[i]] = v;
|
|
||||||
component.setState(state);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
!function(){var n,l,u,t,i,o,r,e,f={},c=[],a=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function s(n,l){for(var u in l)n[u]=l[u];return n}function v(n){var l=n.parentNode;l&&l.removeChild(n)}function h(n,l,u){var t,i=arguments,o={};for(t in l)"key"!==t&&"ref"!==t&&(o[t]=l[t]);if(arguments.length>3)for(u=[u],t=3;t<arguments.length;t++)u.push(i[t]);if(null!=u&&(o.children=u),"function"==typeof n&&null!=n.defaultProps)for(t in n.defaultProps)void 0===o[t]&&(o[t]=n.defaultProps[t]);return y(n,o,l&&l.key,l&&l.ref,null)}function y(l,u,t,i,o){var r={type:l,props:u,key:t,ref:i,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,constructor:void 0,__v:o};return null==o&&(r.__v=r),n.vnode&&n.vnode(r),r}function p(n){return n.children}function d(n,l){this.props=n,this.context=l}function _(n,l){if(null==l)return n.__?_(n.__,n.__.__k.indexOf(n)+1):null;for(var u;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e)return u.__e;return"function"==typeof n.type?_(n):null}function m(n){var l,u;if(null!=(n=n.__)&&null!=n.__c){for(n.__e=n.__c.base=null,l=0;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e){n.__e=n.__c.base=u.__e;break}return m(n)}}function k(l){(!l.__d&&(l.__d=!0)&&u.push(l)&&!w.__r++||i!==n.debounceRendering)&&((i=n.debounceRendering)||t)(w)}function w(){for(var n;w.__r=u.length;)n=u.sort(function(n,l){return n.__v.__b-l.__v.__b}),u=[],n.some(function(n){var l,u,t,i,o,r,e;n.__d&&(r=(o=(l=n).__v).__e,(e=l.__P)&&(u=[],(t=s({},o)).__v=t,i=N(e,o,t,l.__n,void 0!==e.ownerSVGElement,null,u,null==r?_(o):r),z(u,o),i!=r&&m(o)))})}function g(n,l,u,t,i,o,r,e,a,s){var h,d,m,k,w,g,x,C=t&&t.__k||c,A=C.length;for(a==f&&(a=null!=r?r[0]:A?_(t,0):null),u.__k=[],h=0;h<l.length;h++)if(null!=(k=u.__k[h]=null==(k=l[h])||"boolean"==typeof k?null:"string"==typeof k||"number"==typeof k?y(null,k,null,null,k):Array.isArray(k)?y(p,{children:k},null,null,null):null!=k.__e||null!=k.__c?y(k.type,k.props,k.key,null,k.__v):k)){if(k.__=u,k.__b=u.__b+1,null===(m=C[h])||m&&k.key==m.key&&k.type===m.type)C[h]=void 0;else for(d=0;d<A;d++){if((m=C[d])&&k.key==m.key&&k.type===m.type){C[d]=void 0;break}m=null}w=N(n,k,m=m||f,i,o,r,e,a,s),(d=k.ref)&&m.ref!=d&&(x||(x=[]),m.ref&&x.push(m.ref,null,k),x.push(d,k.__c||w,k)),null!=w?(null==g&&(g=w),a=b(n,k,m,C,r,w,a),"option"==u.type?n.value="":"function"==typeof u.type&&(u.__d=a)):a&&m.__e==a&&a.parentNode!=n&&(a=_(m))}if(u.__e=g,null!=r&&"function"!=typeof u.type)for(h=r.length;h--;)null!=r[h]&&v(r[h]);for(h=A;h--;)null!=C[h]&&j(C[h],C[h]);if(x)for(h=0;h<x.length;h++)$(x[h],x[++h],x[++h])}function b(n,l,u,t,i,o,r){var e,f,c;if(void 0!==l.__d)e=l.__d,l.__d=void 0;else if(i==u||o!=r||null==o.parentNode)n:if(null==r||r.parentNode!==n)n.appendChild(o),e=null;else{for(f=r,c=0;(f=f.nextSibling)&&c<t.length;c+=2)if(f==o)break n;n.insertBefore(o,r),e=r}return void 0!==e?e:o.nextSibling}function x(n,l,u,t,i){var o;for(o in u)"children"===o||"key"===o||o in l||A(n,o,null,u[o],t);for(o in l)i&&"function"!=typeof l[o]||"children"===o||"key"===o||"value"===o||"checked"===o||u[o]===l[o]||A(n,o,l[o],u[o],t)}function C(n,l,u){"-"===l[0]?n.setProperty(l,u):n[l]="number"==typeof u&&!1===a.test(l)?u+"px":null==u?"":u}function A(n,l,u,t,i){var o,r,e,f,c;if(i?"className"===l&&(l="class"):"class"===l&&(l="className"),"style"===l)if(o=n.style,"string"==typeof u)o.cssText=u;else{if("string"==typeof t&&(o.cssText="",t=null),t)for(f in t)u&&f in u||C(o,f,"");if(u)for(c in u)t&&u[c]===t[c]||C(o,c,u[c])}else"o"===l[0]&&"n"===l[1]?(r=l!==(l=l.replace(/Capture$/,"")),e=l.toLowerCase(),l=(e in n?e:l).slice(2),u?(t||n.addEventListener(l,P,r),(n.l||(n.l={}))[l]=u):n.removeEventListener(l,P,r)):"list"!==l&&"tagName"!==l&&"form"!==l&&"type"!==l&&"size"!==l&&!i&&l in n?n[l]=null==u?"":u:"function"!=typeof u&&"dangerouslySetInnerHTML"!==l&&(l!==(l=l.replace(/^xlink:?/,""))?null==u||!1===u?n.removeAttributeNS("http://www.w3.org/1999/xlink",l.toLowerCase()):n.setAttributeNS("http://www.w3.org/1999/xlink",l.toLowerCase(),u):null==u||!1===u&&!/^ar/.test(l)?n.removeAttribute(l):n.setAttribute(l,u))}function P(l){this.l[l.type](n.event?n.event(l):l)}function E(n,l,u){var t,i;for(t=0;t<n.__k.length;t++)(i=n.__k[t])&&(i.__=n,i.__e&&("function"==typeof i.type&&i.__k.length>1&&E(i,l,u),l=b(u,i,i,n.__k,null,i.__e,l),"function"==typeof n.type&&(n.__d=l)))}function N(l,u,t,i,o,r,e,f,c){var a,v,h,y,_,m,k,w,b,x,C,A=u.type;if(void 0!==u.constructor)return null;(a=n.__b)&&a(u);try{n:if("function"==typeof A){if(w=u.props,b=(a=A.contextType)&&i[a.__c],x=a?b?b.props.value:a.__:i,t.__c?k=(v=u.__c=t.__c).__=v.__E:("prototype"in A&&A.prototype.render?u.__c=v=new A(w,x):(u.__c=v=new d(w,x),v.constructor=A,v.render=F),b&&b.sub(v),v.props=w,v.state||(v.state={}),v.context=x,v.__n=i,h=v.__d=!0,v.__h=[]),null==v.__s&&(v.__s=v.state),null!=A.getDerivedStateFromProps&&(v.__s==v.state&&(v.__s=s({},v.__s)),s(v.__s,A.getDerivedStateFromProps(w,v.__s))),y=v.props,_=v.state,h)null==A.getDerivedStateFromProps&&null!=v.componentWillMount&&v.componenLine truncated
|
|
||||||
//# sourceMappingURL=preact.min.js.map
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#container { margin-right: auto; margin-left: auto; max-width: 480px; }
|
|
||||||
#info { background: #d0f0f0; border-radius: .5em; padding: 2em; }
|
|
||||||
#dashboard { background: #f0f0f0; border-radius: .5em; padding: 0.5em; max-height: 10em; overflow: auto; height: 100%; margin-top: 1em; }
|
|
||||||
#form { background: #d0c0f0; border-radius: .5em; padding: 0.5em; max-height: 10em; overflow: auto; height: 100%; margin-top: 1em; }
|
|
||||||
button { width: 100%; }
|
|
||||||
button, select, input, label::before, textarea { outline: none; box-shadow:none !important; border: 1px solid #ccc !important; }
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
PROG ?= example
|
||||||
|
SOURCES ?= ../../mongoose.c main.c net.c packed_fs.c
|
||||||
|
CFLAGS ?= -I../.. -DMG_ENABLE_PACKED_FS=1 -DMG_ENABLE_LINES=1 $(EXTRA)
|
||||||
|
FILES_TO_EMBED ?= $(wildcard web_root/*)
|
||||||
|
ROOT ?= $(realpath $(CURDIR)/../../..)
|
||||||
|
DOCKER ?= docker run --rm -e Tmp=. -e WINEDEBUG=-all -v $(ROOT):$(ROOT) -w $(CURDIR)
|
||||||
|
VC98 ?= $(DOCKER) mdashnet/vc98 wine
|
||||||
|
MINGW ?= $(DOCKER) mdashnet/mingw
|
||||||
|
|
||||||
|
ifeq "$(SSL)" "MBEDTLS"
|
||||||
|
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS)/include -L$(MBEDTLS)/lib -lmbedtls -lmbedcrypto -lmbedx509
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq "$(SSL)" "OPENSSL"
|
||||||
|
CFLAGS += -DMG_ENABLE_OPENSSL=1 -I$(OPENSSL)/include -L$(OPENSSL)/lib -lssl -lcrypto
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
$(RUN) ./$(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES) $(FILES_TO_EMBED)
|
||||||
|
$(CC) ../../test/pack.c -o pack
|
||||||
|
./pack $(FILES_TO_EMBED) > packed_fs.c
|
||||||
|
$(CC) -W -Wall -Wextra -O0 -g3 $(CFLAGS) -o $(PROG) $(SOURCES)
|
||||||
|
|
||||||
|
|
||||||
|
mingw: $(SOURCES) $(FILES_TO_EMBED)
|
||||||
|
$(MINGW) i686-w64-mingw32-gcc $(CFLAGS) ../../test/pack.c -o pack.exe
|
||||||
|
$(MINGW) wine cmd /c 'pack.exe $(FILES_TO_EMBED) > packed_fs.c'
|
||||||
|
$(MINGW) i686-w64-mingw32-gcc $(CFLAGS) $(SOURCES) -lws2_32 -o $(PROG).exe
|
||||||
|
$(MINGW) wine $(PROG).exe
|
||||||
|
|
||||||
|
vc98: $(SOURCES) $(FILES_TO_EMBED)
|
||||||
|
$(VC98) cl.exe /nologo $(CFLAGS) ../../test/pack.c /Fepack.exe
|
||||||
|
$(VC98) cmd /c 'pack.exe $(FILES_TO_EMBED) > packed_fs.c'
|
||||||
|
$(VC98) cl.exe /nologo /O2 $(CFLAGS) $(SOURCES) /Fe$(PROG).exe
|
||||||
|
$(VC98) $(PROG).exe
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb log.txt pack
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# A complete device dashboard
|
||||||
|
|
||||||
|
This example is a demonstration of how Mongoose Library could be integrated
|
||||||
|
into an embedded device and provide a complete device dashboard with the
|
||||||
|
following features:
|
||||||
|
|
||||||
|
- Authentication: login-protected dashboard
|
||||||
|
- Multiple logins with different permissions (admin and user)
|
||||||
|
- Web UI is fully embedded into the server/firmware binary, and does not
|
||||||
|
need a filesystem to serve it. UI is resilient to FS problems
|
||||||
|
- Administrators can change server settings
|
||||||
|
- All changes are propagates to all connected clients
|
||||||
|
- A device is connected to the external MQTT server
|
||||||
|
- Logged in clients can send/receive messages to a device which get
|
||||||
|
forwarded to MQTT
|
||||||
|
|
||||||
|
# Screenshots
|
||||||
|
|
||||||
|
This is a login screen that prompts for user/password
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Main dashboard
|
||||||
|
|
||||||
|
A main dashboard page shows and interactive MQTT console
|
||||||
|
|
||||||
|

|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../test/data/ca.pem
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (c) 2020-2022 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
const char *s_listening_url = "http://0.0.0.0:8000";
|
||||||
|
|
||||||
|
void device_dashboard_fn(struct mg_connection *, int, void *, void *);
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
mg_log_set(MG_LL_DEBUG); // Set debug log level
|
||||||
|
mg_mgr_init(&mgr);
|
||||||
|
mg_http_listen(&mgr, s_listening_url, device_dashboard_fn, &mgr);
|
||||||
|
MG_INFO(("Listening on %s", s_listening_url));
|
||||||
|
while (mgr.conns != NULL) mg_mgr_poll(&mgr, 500);
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
// Copyright (c) 2020-2022 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
#define MQTT_SERVER "mqtt://broker.hivemq.com:1883"
|
||||||
|
#define MQTT_PUBLISH_TOPIC "mg/my_device"
|
||||||
|
#define MQTT_SUBSCRIBE_TOPIC "mg/#"
|
||||||
|
|
||||||
|
static time_t s_boot_timestamp = 0; // Updated by SNTP
|
||||||
|
static struct mg_connection *s_sntp_conn = NULL; // SNTP connection
|
||||||
|
|
||||||
|
// Define system time()
|
||||||
|
time_t time(time_t *tp) {
|
||||||
|
time_t t = s_boot_timestamp + (time_t) (mg_millis() / 1000);
|
||||||
|
if (tp != NULL) *tp = t;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticated user.
|
||||||
|
// A user can be authenticated by:
|
||||||
|
// - a name:pass pair
|
||||||
|
// - a token
|
||||||
|
// When a user is shown a login screen, she enters a user:pass. If successful,
|
||||||
|
// a server returns user info which includes token. From that point on,
|
||||||
|
// client can use token for authentication. Tokens could be refreshed/changed
|
||||||
|
// on a server side, forcing clients to re-login.
|
||||||
|
struct user {
|
||||||
|
const char *name, *pass, *token;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is a configuration structure we're going to show on a dashboard
|
||||||
|
static struct config {
|
||||||
|
char *url, *pub, *sub; // MQTT settings
|
||||||
|
} s_config;
|
||||||
|
|
||||||
|
static struct mg_connection *s_mqtt = NULL; // MQTT connection
|
||||||
|
static bool s_connected = false; // MQTT connection established
|
||||||
|
|
||||||
|
// Try to update a single configuration value
|
||||||
|
static void update_config(struct mg_str *body, const char *name, char **value) {
|
||||||
|
char buf[256];
|
||||||
|
if (mg_http_get_var(body, name, buf, sizeof(buf)) > 0) {
|
||||||
|
free(*value);
|
||||||
|
*value = strdup(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse HTTP requests, return authenticated user or NULL
|
||||||
|
static struct user *getuser(struct mg_http_message *hm) {
|
||||||
|
// In production, make passwords strong and tokens randomly generated
|
||||||
|
// In this example, user list is kept in RAM. In production, it can
|
||||||
|
// be backed by file, database, or some other method.
|
||||||
|
static struct user users[] = {
|
||||||
|
{"admin", "pass0", "admin_token"},
|
||||||
|
{"user1", "pass1", "user1_token"},
|
||||||
|
{"user2", "pass2", "user2_token"},
|
||||||
|
{NULL, NULL, NULL},
|
||||||
|
};
|
||||||
|
char user[256], pass[256];
|
||||||
|
struct user *u;
|
||||||
|
mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass));
|
||||||
|
if (user[0] != '\0' && pass[0] != '\0') {
|
||||||
|
// Both user and password is set, search by user/password
|
||||||
|
for (u = users; u->name != NULL; u++)
|
||||||
|
if (strcmp(user, u->name) == 0 && strcmp(pass, u->pass) == 0) return u;
|
||||||
|
} else if (user[0] == '\0') {
|
||||||
|
// Only password is set, search by token
|
||||||
|
for (u = users; u->name != NULL; u++)
|
||||||
|
if (strcmp(pass, u->token) == 0) return u;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify all config watchers about the config change
|
||||||
|
static void send_notification(struct mg_mgr *mgr, const char *fmt, ...) {
|
||||||
|
struct mg_connection *c;
|
||||||
|
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||||
|
if (c->label[0] == 'W') {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
mg_ws_vprintf(c, WEBSOCKET_OP_TEXT, fmt, &ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send simulated metrics data to the dashboard, for chart rendering
|
||||||
|
static void timer_metrics_fn(void *param) {
|
||||||
|
send_notification(param, "{%Q:%Q,%Q:[%lu, %d]}", "name", "metrics", "data",
|
||||||
|
(unsigned long) time(NULL),
|
||||||
|
10 + (int) ((double) rand() * 10 / RAND_MAX));
|
||||||
|
}
|
||||||
|
|
||||||
|
// MQTT event handler function
|
||||||
|
static void mqtt_fn(struct mg_connection *c, int ev, void *ev_data, void *fnd) {
|
||||||
|
if (ev == MG_EV_CONNECT && mg_url_is_ssl(s_config.url)) {
|
||||||
|
struct mg_tls_opts opts = {.ca = "ca.pem",
|
||||||
|
.srvname = mg_url_host(s_config.url)};
|
||||||
|
mg_tls_init(c, &opts);
|
||||||
|
} else if (ev == MG_EV_MQTT_OPEN) {
|
||||||
|
s_connected = true;
|
||||||
|
c->is_hexdumping = 1;
|
||||||
|
mg_mqtt_sub(s_mqtt, mg_str(s_config.sub), 2);
|
||||||
|
send_notification(c->mgr, "{%Q:%Q,%Q:null}", "name", "config", "data");
|
||||||
|
} else if (ev == MG_EV_MQTT_MSG) {
|
||||||
|
struct mg_mqtt_message *mm = ev_data;
|
||||||
|
send_notification(c->mgr, "{%Q:%Q,%Q:{%Q: %.*Q, %Q: %.*Q, %Q: %d}}", "name",
|
||||||
|
"message", "data", "topic", (int) mm->topic.len,
|
||||||
|
mm->topic.ptr, "data", (int) mm->data.len, mm->data.ptr,
|
||||||
|
"qos", (int) mm->qos);
|
||||||
|
} else if (ev == MG_EV_MQTT_CMD) {
|
||||||
|
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
|
||||||
|
MG_DEBUG(("cmd %d qos %d", mm->cmd, mm->qos));
|
||||||
|
} else if (ev == MG_EV_CLOSE) {
|
||||||
|
s_mqtt = NULL;
|
||||||
|
if (s_connected) {
|
||||||
|
s_connected = false;
|
||||||
|
send_notification(c->mgr, "{%Q:%Q,%Q:null}", "name", "config", "data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void) fnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep MQTT connection open - reconnect if closed
|
||||||
|
static void timer_mqtt_fn(void *param) {
|
||||||
|
struct mg_mgr *mgr = (struct mg_mgr *) param;
|
||||||
|
if (s_mqtt == NULL) {
|
||||||
|
struct mg_mqtt_opts opts = {0};
|
||||||
|
s_mqtt = mg_mqtt_connect(mgr, s_config.url, &opts, mqtt_fn, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SNTP connection event handler. When we get a response from an SNTP server,
|
||||||
|
// adjust s_boot_timestamp. We'll get a valid time from that point on
|
||||||
|
static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||||
|
if (ev == MG_EV_SNTP_TIME) {
|
||||||
|
uint64_t t = *(uint64_t *) ev_data;
|
||||||
|
MG_INFO(("%lu SNTP: %lld ms from epoch", c->id, t));
|
||||||
|
s_boot_timestamp = (time_t) ((t - mg_millis()) / 1000);
|
||||||
|
c->is_closing = 1;
|
||||||
|
} else if (ev == MG_EV_CLOSE) {
|
||||||
|
s_sntp_conn = NULL;
|
||||||
|
}
|
||||||
|
(void) fn_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timer_sntp_fn(void *param) { // SNTP timer function. Sync up time
|
||||||
|
struct mg_mgr *mgr = (struct mg_mgr *) param;
|
||||||
|
if (s_sntp_conn == NULL && s_boot_timestamp == 0) {
|
||||||
|
s_sntp_conn = mg_sntp_connect(mgr, NULL, sfn, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP request handler function
|
||||||
|
void device_dashboard_fn(struct mg_connection *c, int ev, void *ev_data,
|
||||||
|
void *fn_data) {
|
||||||
|
if (ev == MG_EV_OPEN && c->is_listening) {
|
||||||
|
mg_timer_add(c->mgr, 1000, MG_TIMER_REPEAT, timer_metrics_fn, c->mgr);
|
||||||
|
mg_timer_add(c->mgr, 1000, MG_TIMER_REPEAT, timer_mqtt_fn, c->mgr);
|
||||||
|
mg_timer_add(c->mgr, 1000, MG_TIMER_REPEAT, timer_sntp_fn, c->mgr);
|
||||||
|
s_config.url = strdup(MQTT_SERVER);
|
||||||
|
s_config.pub = strdup(MQTT_PUBLISH_TOPIC);
|
||||||
|
s_config.sub = strdup(MQTT_SUBSCRIBE_TOPIC);
|
||||||
|
} else if (ev == MG_EV_HTTP_MSG) {
|
||||||
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
|
struct user *u = getuser(hm);
|
||||||
|
// MG_INFO(("%p [%.*s] auth %s", c->fd, (int) hm->uri.len, hm->uri.ptr,
|
||||||
|
// u ? u->name : "NULL"));
|
||||||
|
if (mg_http_match_uri(hm, "/api/hi")) {
|
||||||
|
mg_http_reply(c, 200, "", "hi\n"); // Testing endpoint
|
||||||
|
} else if (u == NULL && mg_http_match_uri(hm, "/api/#")) {
|
||||||
|
// All URIs starting with /api/ must be authenticated
|
||||||
|
mg_http_reply(c, 403, "", "Denied\n");
|
||||||
|
} else if (mg_http_match_uri(hm, "/api/config/get")) {
|
||||||
|
mg_http_reply(c, 200, NULL, "{%Q:%Q,%Q:%Q,%Q:%Q,%Q:%s}\n", "url",
|
||||||
|
s_config.url, "pub", s_config.pub, "sub", s_config.sub,
|
||||||
|
"connected", s_connected ? "true" : "false");
|
||||||
|
} else if (mg_http_match_uri(hm, "/api/config/set")) {
|
||||||
|
// Admins only
|
||||||
|
if (strcmp(u->name, "admin") == 0) {
|
||||||
|
update_config(&hm->body, "url", &s_config.url);
|
||||||
|
update_config(&hm->body, "pub", &s_config.pub);
|
||||||
|
update_config(&hm->body, "sub", &s_config.sub);
|
||||||
|
if (s_mqtt) s_mqtt->is_closing = 1; // Ask to disconnect from MQTT
|
||||||
|
send_notification(fn_data, "{%Q:%Q,%Q:null}", "name", "config", "data");
|
||||||
|
mg_http_reply(c, 200, "", "ok\n");
|
||||||
|
} else {
|
||||||
|
mg_http_reply(c, 403, "", "Denied\n");
|
||||||
|
}
|
||||||
|
} else if (mg_http_match_uri(hm, "/api/message/send")) {
|
||||||
|
char buf[256];
|
||||||
|
if (s_connected &&
|
||||||
|
mg_http_get_var(&hm->body, "message", buf, sizeof(buf)) > 0) {
|
||||||
|
mg_mqtt_pub(s_mqtt, mg_str(s_config.pub), mg_str(buf), 1, false);
|
||||||
|
}
|
||||||
|
mg_http_reply(c, 200, "", "ok\n");
|
||||||
|
} else if (mg_http_match_uri(hm, "/api/watch")) {
|
||||||
|
c->label[0] = 'W'; // Mark ourselves as a event listener
|
||||||
|
mg_ws_upgrade(c, hm, NULL);
|
||||||
|
} else if (mg_http_match_uri(hm, "/api/login")) {
|
||||||
|
mg_http_reply(c, 200, NULL, "{%Q:%Q,%Q:%Q}\n", "user", u->name, "token",
|
||||||
|
u->token);
|
||||||
|
} else {
|
||||||
|
struct mg_http_serve_opts opts = {0};
|
||||||
|
#if 1
|
||||||
|
opts.root_dir = "/web_root";
|
||||||
|
opts.fs = &mg_fs_packed;
|
||||||
|
#else
|
||||||
|
opts.root_dir = "web_root";
|
||||||
|
#endif
|
||||||
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
|
}
|
||||||
|
MG_DEBUG(("%.*s %.*s -> %.*s", (int) hm->method.len, hm->method.ptr,
|
||||||
|
(int) hm->uri.len, hm->uri.ptr, (int) 3, &c->send.buf[9]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 65 KiB |
@@ -1,16 +1,12 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>example</title>
|
<title>Device Dashboard</title>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script src="preact.min.js"></script>
|
|
||||||
<script src="htm.min.js"></script>
|
|
||||||
<script src="linkstate.min.js"></script>
|
|
||||||
<script src="axios.min.js"></script>
|
|
||||||
<script src="app.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body></body>
|
<body></body>
|
||||||
|
<script type="module" src="main.js"></script>
|
||||||
</html>
|
</html>
|
||||||
@@ -0,0 +1,378 @@
|
|||||||
|
'use strict';
|
||||||
|
import {Component, h, html, render, useEffect, useState, useRef} from './preact.min.js';
|
||||||
|
|
||||||
|
const MaxMetricsDataPoints = 50;
|
||||||
|
|
||||||
|
// This simple publish/subscribe is used to pass notifications that were
|
||||||
|
// received from the server, to all child components of the app.
|
||||||
|
var PubSub = (function() {
|
||||||
|
var handlers = {}, id = 0;
|
||||||
|
return {
|
||||||
|
subscribe: function(fn) {
|
||||||
|
handlers[id++] = fn;
|
||||||
|
},
|
||||||
|
unsubscribe: function(id) {
|
||||||
|
delete handlers[id];
|
||||||
|
},
|
||||||
|
publish: function(data) {
|
||||||
|
for (var k in handlers) handlers[k](data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
const Nav = props => html`
|
||||||
|
<div style="background: #333; padding: 0.5em; color: #fff;">
|
||||||
|
<div class="container d-flex">
|
||||||
|
<div style="flex: 1 1 auto; display: flex; align-items: center;">
|
||||||
|
<b>Your Product</b>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; align-items: center; flex: 0 0 auto; ">
|
||||||
|
<span>Logged in as:</span>
|
||||||
|
<span style="padding: 0 0.5em;"><img src="user.png" height="22" /></span>
|
||||||
|
<span>${props.user}</span>
|
||||||
|
<a class="btn" onclick=${props.logout}
|
||||||
|
style="margin-left: 1em; font-size: 0.8em; background: #8aa;">logout</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
|
||||||
|
const Hero = props => html`
|
||||||
|
<div class="section">
|
||||||
|
<div style="margin-top: 1em; background: #eee; padding: 1em; border-radius: 0.5em; color: #777; ">
|
||||||
|
<h1 style="margin: 0.2em 0;">Interactive Device Dashboard</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This device dashboard is developed using the modern and compact Preact framework,
|
||||||
|
in order to fit on very small devices. This is
|
||||||
|
a <a href="https://mongoose.ws/tutorials/http-server/">hybrid server</a> which
|
||||||
|
provides both static and dynamic content. Static files, like CSS/JS/HTML
|
||||||
|
or images, are compiled into the server binary.
|
||||||
|
|
||||||
|
This UI uses the REST API implemented by the device, which you can examine
|
||||||
|
using <code>curl</code> command-line utility:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div><code>curl -u admin:pass0 localhost:8000/api/config/get</code> </div>
|
||||||
|
<div><code>curl -u admin:pass0 localhost:8000/api/config/set -d 'pub=mg/topic'</code> </div>
|
||||||
|
<div><code>curl -u admin:pass0 localhost:8000/api/message/send -d 'message=hello'</code> </div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The device can send notifications to this dashboard at anytime. Notifications
|
||||||
|
are sent over WebSocket at URI <code>/api/watch</code> as JSON strings: <code>{"name": "..", "data": ...}</code>
|
||||||
|
<div>Try <code>wscat --auth user1:pass1 --connect ws://localhost:8000/api/watch</code></div>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
const Login = function(props) {
|
||||||
|
const [user, setUser] = useState('');
|
||||||
|
const [pass, setPass] = useState('');
|
||||||
|
const login = ev =>
|
||||||
|
fetch(
|
||||||
|
'/api/login',
|
||||||
|
{headers: {Authorization: 'Basic ' + btoa(user + ':' + pass)}})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(r => r && props.login(r))
|
||||||
|
.catch(err => err);
|
||||||
|
return html`
|
||||||
|
<div class="rounded border" style="max-width: 480px; margin: 0 auto; margin-top: 5em; background: #eee; ">
|
||||||
|
<div style="padding: 2em; ">
|
||||||
|
<h1 style="color: #666;">Device Dashboard Login </h1>
|
||||||
|
<div style="margin: 0.5em 0;">
|
||||||
|
<input type='text' placeholder='Name' style="width: 100%;"
|
||||||
|
oninput=${ev => setUser(ev.target.value)} value=${user} />
|
||||||
|
</div>
|
||||||
|
<div style="margin: 0.5em 0;">
|
||||||
|
<input type="password" placeholder="Password" style="width: 100%;"
|
||||||
|
oninput=${ev => setPass(ev.target.value)} value=${pass}
|
||||||
|
onchange=${login} />
|
||||||
|
</div>
|
||||||
|
<div style="margin: 1em 0;">
|
||||||
|
<button class="btn" style="width: 100%; background: #8aa;"
|
||||||
|
disabled=${!user || !pass} onclick=${login}> Login </button>
|
||||||
|
</div>
|
||||||
|
<div style="color: #777; margin-top: 2em;">
|
||||||
|
Valid logins: admin:pass0, user1:pass1, user2:pass2
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const Configuration = function(props) {
|
||||||
|
const [url, setUrl] = useState(props.config.url || '');
|
||||||
|
const [pub, setPub] = useState(props.config.pub || '');
|
||||||
|
const [sub, setSub] = useState(props.config.sub || '');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setUrl(props.config.url);
|
||||||
|
setPub(props.config.pub);
|
||||||
|
setSub(props.config.sub);
|
||||||
|
}, [props.config]);
|
||||||
|
|
||||||
|
const update = (name, val) => fetch('/api/config/set', {
|
||||||
|
method: 'post',
|
||||||
|
body: `${name}=${encodeURIComponent(val)}`
|
||||||
|
}).catch(err => err);
|
||||||
|
const updateurl = ev => update('url', url);
|
||||||
|
const updatepub = ev => update('pub', pub);
|
||||||
|
const updatesub = ev => update('sub', sub);
|
||||||
|
|
||||||
|
// console.log(props, [url, pub, sub]);
|
||||||
|
return html`
|
||||||
|
<div class="section">
|
||||||
|
<h3 style="background: #c03434; color: #fff; padding: 0.4em;">
|
||||||
|
Device Configuration</h3>
|
||||||
|
<div style="margin: 0.5em 0; display: flex;">
|
||||||
|
<span class="addon nowrap">MQTT server:</span>
|
||||||
|
<input type="text" style="flex: 1 100%;"
|
||||||
|
value=${url} onchange=${updateurl}
|
||||||
|
oninput=${ev => setUrl(ev.target.value)} />
|
||||||
|
<button class="btn" disabled=${!url} onclick=${updateurl}
|
||||||
|
style="margin-left: 1em; background: #8aa;">Update</button>
|
||||||
|
</div>
|
||||||
|
<div style="margin: 0.5em 0; display: flex; ">
|
||||||
|
<span class="addon nowrap">Subscribe topic:</span>
|
||||||
|
<input type="text" style="flex: 1 100%;"
|
||||||
|
value=${sub} onchange=${updatesub}
|
||||||
|
oninput=${ev => setSub(ev.target.value)} />
|
||||||
|
<button class="btn" disabled=${!sub} onclick=${updatesub}
|
||||||
|
style="margin-left: 1em; background: #8aa;">Update</button>
|
||||||
|
</div>
|
||||||
|
<div style="margin: 0.5em 0; display: flex;">
|
||||||
|
<span class="addon nowrap">Publish topic:</span>
|
||||||
|
<input type="text" style="flex: 1 100%;"
|
||||||
|
value=${pub} onchange=${updatepub}
|
||||||
|
oninput=${ev => setPub(ev.target.value)} />
|
||||||
|
<button class="btn" disabled=${!pub} onclick=${updatepub}
|
||||||
|
style="margin-left: 1em; background: #8aa;">Update</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
You can use <a href="http://www.hivemq.com/demos/websocket-client/">
|
||||||
|
HiveMQ Websocket web client</a> to send messages to this console.
|
||||||
|
</div>
|
||||||
|
<div class="msg">
|
||||||
|
The device keeps a persistent connection to the configured MQTT server.
|
||||||
|
Changes to this configuration are propagated to all dashboards: try
|
||||||
|
changing them in this dashboard and observe changes in other opened
|
||||||
|
dashboards.
|
||||||
|
</div><div class="msg">
|
||||||
|
Note: administrators can see this section and can change device
|
||||||
|
configuration, whilst users cannot.
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const Message = m => html`<div style="margin: 0.5em 0;">
|
||||||
|
<span class="qos">qos: ${m.message.qos} </span>
|
||||||
|
<span class="topic">topic: ${m.message.topic} </span>
|
||||||
|
<span class="data">data: ${m.message.data}</span>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
const Messages = function(props) {
|
||||||
|
const [messages, setMessages] = useState([]);
|
||||||
|
const [txt, setTxt] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const id = PubSub.subscribe(function(msg) {
|
||||||
|
if (msg.name == 'message') setMessages(x => x.concat([msg.data]));
|
||||||
|
});
|
||||||
|
return PubSub.unsubscribe(id);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const sendmessage = ev => fetch('/api/message/send', {
|
||||||
|
method: 'post',
|
||||||
|
body: `message=${encodeURIComponent(txt)}`
|
||||||
|
}).then(r => setTxt(''));
|
||||||
|
const connstatus = props.config.connected ? 'connected' : 'disconnected';
|
||||||
|
return html`
|
||||||
|
<div class="section">
|
||||||
|
<h3 style="background: #30c040; color: #fff; padding: 0.4em;">MQTT messages</h3>
|
||||||
|
<div>
|
||||||
|
MQTT server status: <b>${connstatus}</b>
|
||||||
|
</div>
|
||||||
|
<div style="height: 10em; overflow: auto; padding: 0.5em; " class="border">
|
||||||
|
${messages.map(message => h(Message, {message}))}
|
||||||
|
</div>
|
||||||
|
<div style="margin: 0.5em 0; display: flex">
|
||||||
|
<span class="addon nowrap">Publish message:</span>
|
||||||
|
<input placeholder="type and press enter..." style="flex: 1 100%;"
|
||||||
|
value=${txt} onchange=${sendmessage}
|
||||||
|
oninput=${ev => setTxt(ev.target.value)} />
|
||||||
|
</div>
|
||||||
|
<div class="msg">
|
||||||
|
The message gets passed to the device via REST. Then the device sends it to
|
||||||
|
the MQTT server over MQTT. All MQTT messages on a subscribed topic
|
||||||
|
received by the device, are propagated to this dashboard via /api/watch.
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Expected arguments:
|
||||||
|
// data: timeseries, e.g. [ [1654361352, 19], [1654361353, 18], ... ]
|
||||||
|
// width, height, yticks, xticks, ymin, ymax, xmin, xmax
|
||||||
|
const SVG = function(props) {
|
||||||
|
// w
|
||||||
|
// +---------------------+
|
||||||
|
// | h1 |
|
||||||
|
// | +-----------+ |
|
||||||
|
// | | | | h
|
||||||
|
// | w1 | | w2 |
|
||||||
|
// | +-----------+ |
|
||||||
|
// | h2 |
|
||||||
|
// +---------------------+
|
||||||
|
//
|
||||||
|
let w = props.width, h = props.height, w1 = 30, w2 = 0, h1 = 8, h2 = 18;
|
||||||
|
let yticks = props.yticks || 4, xticks = props.xticks || 5;
|
||||||
|
let data = props.data || [];
|
||||||
|
let ymin = props.ymin || 0;
|
||||||
|
let ymax = props.ymax || Math.max.apply(null, data.map(p => p[1]));
|
||||||
|
let xmin = props.xmin || Math.min.apply(null, data.map(p => p[0]));
|
||||||
|
let xmax = props.xmax || Math.max.apply(null, data.map(p => p[0]));
|
||||||
|
|
||||||
|
// Y-axis tick lines and labels
|
||||||
|
let yta = (new Array(yticks + 1)).fill(0).map((_, i) => i); // indices
|
||||||
|
let yti = i => h - h2 - (h - h1 - h2) * i / yticks; // index's Y
|
||||||
|
let ytv = i => (ymax - ymin) * i / yticks;
|
||||||
|
let ytl = y => html`<line x1=${w1} y1=${y} x2=${w} y2=${y} class="tick"/>`;
|
||||||
|
let ytt = (y, v) => html`<text x=0 y=${y + 5} class="label">${v}</text>`;
|
||||||
|
|
||||||
|
// X-axis tick lines and labels
|
||||||
|
let datefmt = unix => (new Date(unix * 1000)).toISOString().substr(14, 5);
|
||||||
|
let xta = (new Array(xticks + 1)).fill(0).map((_, i) => i); // indices
|
||||||
|
let xti = i => w1 + (w - w1 - w2) * i / xticks; // index's X
|
||||||
|
let xtv = i => datefmt(xmin + (xmax - xmin) * i / xticks);
|
||||||
|
let xtl = x => html`<path d="M ${x},${h1} L ${x},${h - h2}" class="tick"/>`;
|
||||||
|
let xtt = (x, v) =>
|
||||||
|
html`<text x=${x - 15} y=${h - 2} class="label">${v}</text>`;
|
||||||
|
|
||||||
|
// Transform data points array into coordinate
|
||||||
|
let dx = v => w1 + (v - xmin) / ((xmax - xmin) || 1) * (w - w1 - w2);
|
||||||
|
let dy = v => h - h2 - (v - ymin) / ((ymax - ymin) || 1) * (h - h1 - h2);
|
||||||
|
let dd = data.map(p => [Math.round(dx(p[0])), Math.round(dy(p[1]))]);
|
||||||
|
let ddl = dd.length;
|
||||||
|
// And plot the data as <path> element
|
||||||
|
let begin0 = ddl ? `M ${dd[0][0]},${dd[0][1]}` : `M 0,0`;
|
||||||
|
let begin = `M ${w1},${h - h2}`; // Initial point
|
||||||
|
let end = ddl ? `L ${dd[ddl - 1][0]},${h - h2}` : `L ${w1},${h - h2}`;
|
||||||
|
let series = ddl ? dd.map(p => `L ${p[0]} ${p[1]}`) : [];
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<svg viewBox="0 0 ${w} ${h}">
|
||||||
|
<style>
|
||||||
|
.axis { stroke: #aaa; fill: none; }
|
||||||
|
.label { stroke: #aaa; font-size: 13px; }
|
||||||
|
.tick { stroke: #ccc; fill: none; stroke-dasharray: 5; }
|
||||||
|
.seriesbg { stroke: none; fill: rgba(200,225,255, 0.25)}
|
||||||
|
.series { stroke: #25a; fill: none; }
|
||||||
|
</style>
|
||||||
|
${yta.map(i => ytl(yti(i)))}
|
||||||
|
${yta.map(i => ytt(yti(i), ytv(i)))}
|
||||||
|
${xta.map(i => xtl(xti(i)))}
|
||||||
|
${data.length ? xta.map(i => xtt(xti(i), xtv(i))) : ''}
|
||||||
|
<path d="${begin} ${series.join(' ')} ${end}" class="seriesbg" />
|
||||||
|
<path d="${begin0} ${series.join(' ')}" class="series" />
|
||||||
|
</svg>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const Chart = function(props) {
|
||||||
|
const [data, setData] = useState([]);
|
||||||
|
useEffect(() => {
|
||||||
|
const id = PubSub.subscribe(function(msg) {
|
||||||
|
if (msg.name != 'metrics') return;
|
||||||
|
setData(x => x.concat([msg.data]).splice(-MaxMetricsDataPoints));
|
||||||
|
});
|
||||||
|
return PubSub.unsubscribe(id);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
let xmax = 0, missing = MaxMetricsDataPoints - data.length;
|
||||||
|
if (missing > 0) xmax = Math.round(Date.now() / 1000) + missing;
|
||||||
|
return html`
|
||||||
|
<div class="section">
|
||||||
|
<h3 style="background: #ec3; color: #fff; padding: 0.4em;">Data Chart</h3>
|
||||||
|
<div style="overflow: auto; padding: 0.5em;" class="">
|
||||||
|
<${SVG} height=240 width=600 ymin=0 ymax=20 xmax=${xmax} data=${data} />
|
||||||
|
</div>
|
||||||
|
<div class="msg">
|
||||||
|
This chart plots live sensor data, sent by the device via /api/watch.
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = function(props) {
|
||||||
|
const [user, setUser] = useState('');
|
||||||
|
const [config, setConfig] = useState({});
|
||||||
|
|
||||||
|
const getconfig = () =>
|
||||||
|
fetch('/api/config/get', {headers: {Authorization: ''}})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(r => setConfig(r))
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
|
||||||
|
// Watch for notifications. As soon as a notification arrives, pass it on
|
||||||
|
// to all subscribed components
|
||||||
|
const watch = function() {
|
||||||
|
var l = window.location, proto = l.protocol.replace('http', 'ws');
|
||||||
|
var tid, wsURI = proto + '//' + l.host + '/api/watch'
|
||||||
|
var reconnect = function() {
|
||||||
|
var ws = new WebSocket(wsURI);
|
||||||
|
// ws.onopen = () => console.log('ws connected');
|
||||||
|
ws.onmessage = function(ev) {
|
||||||
|
try {
|
||||||
|
var msg = JSON.parse(ev.data);
|
||||||
|
PubSub.publish(msg);
|
||||||
|
// if (msg.name != 'metrics') console.log('ws->', msg);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Invalid ws frame:', ev.data); // eslint-disable-line
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ws.onclose = function() {
|
||||||
|
clearTimeout(tid);
|
||||||
|
tid = setTimeout(reconnect, 1000);
|
||||||
|
console.log('ws disconnected');
|
||||||
|
};
|
||||||
|
};
|
||||||
|
reconnect();
|
||||||
|
};
|
||||||
|
|
||||||
|
const login = function(u) {
|
||||||
|
document.cookie = `access_token=${u.token}; Secure, HttpOnly; SameSite=Lax; path=/; max-age=3600`;
|
||||||
|
setUser(u.user);
|
||||||
|
watch();
|
||||||
|
return getconfig();
|
||||||
|
};
|
||||||
|
|
||||||
|
const logout = ev => {
|
||||||
|
document.cookie = `access_token=; Secure, HttpOnly; SameSite=Lax; path=/; max-age=0`;
|
||||||
|
setUser('');
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Called once at init time
|
||||||
|
PubSub.subscribe(msg => msg.name == 'config' && getconfig());
|
||||||
|
fetch('/api/login', {headers: {Authorization: ''}})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(r => login(r))
|
||||||
|
.catch(err => setUser(''));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!user) return html`<${Login} login=${login} />`;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<${Nav} user=${user} logout=${logout} />
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col col-6"><${Hero} /></div>
|
||||||
|
<div class="col col-6"><${Chart} /></div>
|
||||||
|
<div class="col col-6">
|
||||||
|
${user == 'admin' && h(Configuration, {config})}
|
||||||
|
</div>
|
||||||
|
<div class="col col-6"><${Messages} config=${config} /></div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onload = () => render(h(App), document.body);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
var e,n,_,t,o,r,u,l={},i=[],c=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function s(e,n){for(var _ in n)e[_]=n[_];return e}function f(e){var n=e.parentNode;n&&n.removeChild(e)}function a(n,_,t){var o,r,u,l={};for(u in _)"key"==u?o=_[u]:"ref"==u?r=_[u]:l[u]=_[u];if(arguments.length>2&&(l.children=arguments.length>3?e.call(arguments,2):t),"function"==typeof n&&null!=n.defaultProps)for(u in n.defaultProps)void 0===l[u]&&(l[u]=n.defaultProps[u]);return p(n,l,o,r,null)}function p(e,t,o,r,u){var l={type:e,props:t,key:o,ref:r,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==u?++_:u};return null!=n.vnode&&n.vnode(l),l}function h(e){return e.children}function d(e,n){this.props=e,this.context=n}function v(e,n){if(null==n)return e.__?v(e.__,e.__.__k.indexOf(e)+1):null;for(var _;n<e.__k.length;n++)if(null!=(_=e.__k[n])&&null!=_.__e)return _.__e;return"function"==typeof e.type?v(e):null}function y(e){var n,_;if(null!=(e=e.__)&&null!=e.__c){for(e.__e=e.__c.base=null,n=0;n<e.__k.length;n++)if(null!=(_=e.__k[n])&&null!=_.__e){e.__e=e.__c.base=_.__e;break}return y(e)}}function m(e){(!e.__d&&(e.__d=!0)&&t.push(e)&&!g.__r++||r!==n.debounceRendering)&&((r=n.debounceRendering)||o)(g)}function g(){for(var e;g.__r=t.length;)e=t.sort(function(e,n){return e.__v.__b-n.__v.__b}),t=[],e.some(function(e){var n,_,t,o,r,u;e.__d&&(r=(o=(n=e).__v).__e,(u=n.__P)&&(_=[],(t=s({},o)).__v=o.__v+1,P(u,o,t,n.__n,void 0!==u.ownerSVGElement,null!=o.__h?[r]:null,_,null==r?v(o):r,o.__h),D(_,o),o.__e!=r&&y(o)))})}function k(e,n,_,t,o,r,u,c,s,f){var a,d,y,m,g,k,x,H=t&&t.__k||i,E=H.length;for(_.__k=[],a=0;a<n.length;a++)if(null!=(m=_.__k[a]=null==(m=n[a])||"boolean"==typeof m?null:"string"==typeof m||"number"==typeof m||"bigint"==typeof m?p(null,m,null,null,m):Array.isArray(m)?p(h,{children:m},null,null,null):m.__b>0?p(m.type,m.props,m.key,null,m.__v):m)){if(m.__=_,m.__b=_.__b+1,null===(y=H[a])||y&&m.key==y.key&&m.type===y.type)H[a]=void 0;else for(d=0;d<E;d++){if((y=H[d])&&m.key==y.key&&m.type===y.type){H[d]=void 0;break}y=null}P(e,m,y=y||l,o,r,u,c,s,f),g=m.__e,(d=m.ref)&&y.ref!=d&&(x||(x=[]),y.ref&&x.push(y.ref,null,m),x.push(d,m.__c||g,m)),null!=g?(null==k&&(k=g),"function"==typeof m.type&&null!=m.__k&&m.__k===y.__k?m.__d=s=b(m,s,e):s=C(e,m,y,H,g,s),f||"option"!==_.type?"function"==typeof _.type&&(_.__d=s):e.value=""):s&&y.__e==s&&s.parentNode!=e&&(s=v(y))}for(_.__e=k,a=E;a--;)null!=H[a]&&("function"==typeof _.type&&null!=H[a].__e&&H[a].__e==_.__d&&(_.__d=v(t,a+1)),U(H[a],H[a]));if(x)for(a=0;a<x.length;a++)T(x[a],x[++a],x[++a])}function b(e,n,_){var t,o;for(t=0;t<e.__k.length;t++)(o=e.__k[t])&&(o.__=e,n="function"==typeof o.type?b(o,n,_):C(_,o,o,e.__k,o.__e,n));return n}function C(e,n,_,t,o,r){var u,l,i;if(void 0!==n.__d)u=n.__d,n.__d=void 0;else if(null==_||o!=r||null==o.parentNode)e:if(null==r||r.parentNode!==e)e.appendChild(o),u=null;else{for(l=r,i=0;(l=l.nextSibling)&&i<t.length;i+=2)if(l==o)break e;e.insertBefore(o,r),u=r}return void 0!==u?u:o.nextSibling}function x(e,n,_){"-"===n[0]?e.setProperty(n,_):e[n]=null==_?"":"number"!=typeof _||c.test(n)?_:_+"px"}function H(e,n,_,t,o){var r;e:if("style"===n)if("string"==typeof _)e.style.cssText=_;else{if("string"==typeof t&&(e.style.cssText=t=""),t)for(n in t)_&&n in _||x(e.style,n,"");if(_)for(n in _)t&&_[n]===t[n]||x(e.style,n,_[n])}else if("o"===n[0]&&"n"===n[1])r=n!==(n=n.replace(/Capture$/,"")),n=n.toLowerCase()in e?n.toLowerCase().slice(2):n.slice(2),e.l||(e.l={}),e.l[n+r]=_,_?t||e.addEventListener(n,r?S:E,r):e.removeEventListener(n,r?S:E,r);else if("dangerouslySetInnerHTML"!==n){if(o)n=n.replace(/xlink[H:h]/,"h").replace(/sName$/,"s");else if("href"!==n&&"list"!==n&&"form"!==n&&"tabIndex"!==n&&"download"!==n&&n in e)try{e[n]=null==_?"":_;break e}catch(e){}"function"==typeof _||(null!=_&&(!1!==_||"a"===n[0]&&"r"===n[1])?e.setAttribute(n,_):e.removeAttribute(n))}}function E(e){this.l[e.type+!1](n.event?n.event(e):e)}function S(e){this.l[e.type+!0](n.event?n.event(e):e)}function P(e,_,t,o,r,u,l,i,c){var f,a,p,v,y,m,g,b,C,x,H,E=_.type;if(void 0!==_.constructor)return null;null!=t.__h&&(c=t.__h,i=_.__e=t.__e,_.__h=null,u=[i]),(f=n.__b)&&f(_);try{e:if("function"==typeof E){if(b=_.props,C=(f=E.contextType)&&o[f.__c],x=f?C?C.props.value:f.__:o,t.__c?g=(a=_.__c=t.__c).__=a.__E:("prototype"in E&&E.prototype.render?_.__c=a=new E(b,x):(_.__c=a=new d(b,x),a.constructor=E,a.render=A),C&&C.sub(a),a.props=b,a.state||(a.state={}),a.context=x,a.__n=o,p=a.__d=!0,a.__h=[]),null==a.__s&&(a.__s=a.state),null!=E.getDerivedStateFromProps&&(a.__s==a.state&&(a.__s=s({},a.__s)),s(a.__s,E.getDerivedStateFromProps(b,a.__s))),v=a.props,y=a.state,p)null==E.getDerivedStateFromProps&&null!=a.componentWillMount&&a.componentWillMount(),null!=a.componentDidMount&&a.__h.push(a.componentDidMount);else{if(null==E.getDerivedStateFromProps&&b!==v&&null!=a.componentWillReceiveProps&&a.componentWillReceiveProps(b,x),!a.__e&&null!=a.shouldComponentUpdate&&!1===a.shouldCompLine truncated
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
* { box-sizing: border-box; }
|
||||||
|
html, body { margin: 0; padding: 0; height: 100%; font: 16px sans-serif; }
|
||||||
|
select, input, label::before, textarea { outline: none; box-shadow:none !important; border: 1px solid #ccc !important; }
|
||||||
|
code, pre { color: #373; font-family: monospace; font-weight: bolder; font-size: smaller; background: #ddd; padding: 0.1em 0.3em; border-radius: 0.2em; }
|
||||||
|
textarea, input, .addon { font-size: 15px; border: 1px solid #ccc; padding: 0.5em; }
|
||||||
|
a, a:visited, a:active { color: #55f; }
|
||||||
|
.addon { background: #eee; min-width: 9em;}
|
||||||
|
.btn {
|
||||||
|
background: #ccc; border-radius: 0.3em; border: 0; color: #fff; cursor: pointer;
|
||||||
|
display: inline-block; padding: 0.6em 2em; font-weight: bolder;
|
||||||
|
}
|
||||||
|
.btn[disabled] { opacity: 0.5; cursor: auto;}
|
||||||
|
.smooth { transition: all .2s; }
|
||||||
|
.container { margin: 0 20px; width: auto; }
|
||||||
|
.d-flex { display: flex; }
|
||||||
|
.d-none { display: none; }
|
||||||
|
.border { border: 1px solid #ddd; }
|
||||||
|
.rounded { border-radius: 0.5em; }
|
||||||
|
.nowrap { white-space: nowrap; }
|
||||||
|
.msg { background: #def; border-left: 5px solid #59d; padding: 0.5em; font-size: 90%; margin: 1em 0; }
|
||||||
|
.section { margin: 0 1em; }
|
||||||
|
.topic, .data, .qos { padding: 0.2em 0.5em; border-radius: 0.4em; margin-right: 0.5em; }
|
||||||
|
.qos { background: #efa; }
|
||||||
|
.topic { background: #fea; }
|
||||||
|
.data { background: #aef; }
|
||||||
|
|
||||||
|
/* Grid */
|
||||||
|
.row { display: flex; flex-wrap: wrap; }
|
||||||
|
.col { margin: 0; padding: 0; overflow: auto; }
|
||||||
|
.col-12 { width: 100%; }
|
||||||
|
.col-11 { width: 91.66%; }
|
||||||
|
.col-10 { width: 83.33%; }
|
||||||
|
.col-9 { width: 75%; }
|
||||||
|
.col-8 { width: 66.66%; }
|
||||||
|
.col-7 { width: 58.33%; }
|
||||||
|
.col-6 { width: 50%; }
|
||||||
|
.col-5 { width: 41.66%; }
|
||||||
|
.col-4 { width: 33.33%; }
|
||||||
|
.col-3 { width: 25%; }
|
||||||
|
.col-2 { width: 16.66%; }
|
||||||
|
.col-1 { width: 8.33%; }
|
||||||
|
@media (min-width: 1310px) { .container { margin: auto; width: 1270px; } }
|
||||||
|
@media (max-width: 920px) { .row .col { width: 100%; } }
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
PROG ?= example
|
||||||
|
SOURCES ?= ../../mongoose.c main.c packed_fs.c
|
||||||
|
CFLAGS ?= -I../.. -DMG_ENABLE_PACKED_FS=1 -DMG_ENABLE_LINES=1 $(EXTRA)
|
||||||
|
FILES_TO_EMBED ?= $(wildcard web_root/*)
|
||||||
|
ROOT ?= $(realpath $(CURDIR)/../../..)
|
||||||
|
DOCKER ?= docker run --rm -e Tmp=. -e WINEDEBUG=-all -v $(ROOT):$(ROOT) -w $(CURDIR)
|
||||||
|
VC98 ?= $(DOCKER) mdashnet/vc98 wine
|
||||||
|
MINGW ?= $(DOCKER) mdashnet/mingw
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
$(RUN) ./$(PROG)
|
||||||
|
|
||||||
|
$(PROG): $(SOURCES) $(FILES_TO_EMBED)
|
||||||
|
$(CC) ../../test/pack.c -o pack
|
||||||
|
./pack $(FILES_TO_EMBED) > packed_fs.c
|
||||||
|
$(CC) -W -Wall -Wextra -O0 -g3 $(CFLAGS) -o $(PROG) $(SOURCES)
|
||||||
|
|
||||||
|
|
||||||
|
mingw: $(SOURCES) $(FILES_TO_EMBED)
|
||||||
|
$(MINGW) i686-w64-mingw32-gcc $(CFLAGS) ../../test/pack.c -o pack.exe
|
||||||
|
$(MINGW) wine cmd /c 'pack.exe $(FILES_TO_EMBED) > packed_fs.c'
|
||||||
|
$(MINGW) i686-w64-mingw32-gcc $(CFLAGS) $(SOURCES) -lws2_32 -o $(PROG).exe
|
||||||
|
$(MINGW) wine $(PROG).exe
|
||||||
|
|
||||||
|
vc98: $(SOURCES) $(FILES_TO_EMBED)
|
||||||
|
$(VC98) cl.exe /nologo $(CFLAGS) ../../test/pack.c /Fepack.exe
|
||||||
|
$(VC98) cmd /c 'pack.exe $(FILES_TO_EMBED) > packed_fs.c'
|
||||||
|
$(VC98) cl.exe /nologo /O2 $(CFLAGS) $(SOURCES) /Fe$(PROG).exe
|
||||||
|
$(VC98) $(PROG).exe
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb log.txt pack
|
||||||
|
|
||||||
|
.PHONY: compress expand gzipped plain all
|
||||||
|
|
||||||
|
gzipped: compress
|
||||||
|
$(MAKE) all
|
||||||
|
|
||||||
|
plain: expand
|
||||||
|
$(MAKE) all
|
||||||
|
|
||||||
|
compress:
|
||||||
|
for file in $(FILES_TO_EMBED) ; do \
|
||||||
|
gzip -q $$file ; \
|
||||||
|
done
|
||||||
|
|
||||||
|
expand:
|
||||||
|
for file in $(FILES_TO_EMBED) ; do \
|
||||||
|
gunzip -q $$file ; \
|
||||||
|
done
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (c) 2022 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
const char *s_listening_url = "http://0.0.0.0:8000";
|
||||||
|
|
||||||
|
// HTTP request handler function
|
||||||
|
void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||||
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
|
struct mg_http_serve_opts opts = {
|
||||||
|
.root_dir = "/web_root",
|
||||||
|
.fs = &mg_fs_packed
|
||||||
|
};
|
||||||
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
|
}
|
||||||
|
(void) fn_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
mg_log_set(MG_LL_INFO);
|
||||||
|
mg_mgr_init(&mgr);
|
||||||
|
mg_http_listen(&mgr, s_listening_url, fn, NULL);
|
||||||
|
while (true) mg_mgr_poll(&mgr, 500);
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta name="description" content="Mongoose Embedded Filesytem example" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Mongoose Embedded Filesytem example</title>
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
<script type="module" src="main.js"></script>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
'use strict';
|
||||||
|
import { h, html, render } from './preact.min.js';
|
||||||
|
|
||||||
|
|
||||||
|
const X = function () {
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="section">ABOUT US</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-7">
|
||||||
|
<p>
|
||||||
|
Cesanta Software Ltd. is headquartered in Dublin, Republic of Ireland.
|
||||||
|
</p>
|
||||||
|
<p></p>
|
||||||
|
Our story roots back to 2004, when Mongoose Web Server Library development started. <br>
|
||||||
|
|
||||||
|
As Mongoose Web Server grew in popularity and matured over the following years, in 2013 Cesanta was established to continue its development and
|
||||||
|
provide support to our valued customers.
|
||||||
|
</p>
|
||||||
|
<p>We are proud to have among our customers many <i>Fortune 500</i> companies as well as medium and small size businesses.
|
||||||
|
Security and quality of our solutions is a paramount for us and the fact that Mongoose Web Server is used by NASA aboard the International Space Station is
|
||||||
|
the best confirmation to it.</p>
|
||||||
|
<p>Since 2013, Cesanta has expanded its product portfolio. We develop and distribute embedded software and hardware with focus on connected products and the Internet of Things.</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Y = function () {
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="container">
|
||||||
|
<h3 class="section">Among our products are:</h3>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-7">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://mongoose.ws/">Mongoose Web Server</a>
|
||||||
|
- an embedded web server and networking library
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://vcon.io/">VCON.io</a>
|
||||||
|
- Arduino-compatible boards with built-in firmware OTA updates
|
||||||
|
and management dashboard
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://mdash.net/">mDash.net</a>
|
||||||
|
- an all-in-one IoT Platform
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://mongoose-os.com">Mongoose OS</a>
|
||||||
|
- an operating system for low-power microcontrollers
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="https://github.com/cesanta/mjs">mJS</a>
|
||||||
|
- an embedded JavaScript engine for C/C++
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<p><b>Our solutions are:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>integrated into thousands of commercial products</li>
|
||||||
|
<li>deployed to hundreds of millions devices in production environments</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = function (props) {
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<h1>Basic Embedded Filesystem demo</h1>
|
||||||
|
<div>
|
||||||
|
${h(X)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
${h(Y)}
|
||||||
|
</div>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onload = () => render(h(App), document.body);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
var e,n,_,t,o,r,u,l={},i=[],c=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function s(e,n){for(var _ in n)e[_]=n[_];return e}function f(e){var n=e.parentNode;n&&n.removeChild(e)}function a(n,_,t){var o,r,u,l={};for(u in _)"key"==u?o=_[u]:"ref"==u?r=_[u]:l[u]=_[u];if(arguments.length>2&&(l.children=arguments.length>3?e.call(arguments,2):t),"function"==typeof n&&null!=n.defaultProps)for(u in n.defaultProps)void 0===l[u]&&(l[u]=n.defaultProps[u]);return p(n,l,o,r,null)}function p(e,t,o,r,u){var l={type:e,props:t,key:o,ref:r,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==u?++_:u};return null!=n.vnode&&n.vnode(l),l}function h(e){return e.children}function d(e,n){this.props=e,this.context=n}function v(e,n){if(null==n)return e.__?v(e.__,e.__.__k.indexOf(e)+1):null;for(var _;n<e.__k.length;n++)if(null!=(_=e.__k[n])&&null!=_.__e)return _.__e;return"function"==typeof e.type?v(e):null}function y(e){var n,_;if(null!=(e=e.__)&&null!=e.__c){for(e.__e=e.__c.base=null,n=0;n<e.__k.length;n++)if(null!=(_=e.__k[n])&&null!=_.__e){e.__e=e.__c.base=_.__e;break}return y(e)}}function m(e){(!e.__d&&(e.__d=!0)&&t.push(e)&&!g.__r++||r!==n.debounceRendering)&&((r=n.debounceRendering)||o)(g)}function g(){for(var e;g.__r=t.length;)e=t.sort(function(e,n){return e.__v.__b-n.__v.__b}),t=[],e.some(function(e){var n,_,t,o,r,u;e.__d&&(r=(o=(n=e).__v).__e,(u=n.__P)&&(_=[],(t=s({},o)).__v=o.__v+1,P(u,o,t,n.__n,void 0!==u.ownerSVGElement,null!=o.__h?[r]:null,_,null==r?v(o):r,o.__h),D(_,o),o.__e!=r&&y(o)))})}function k(e,n,_,t,o,r,u,c,s,f){var a,d,y,m,g,k,x,H=t&&t.__k||i,E=H.length;for(_.__k=[],a=0;a<n.length;a++)if(null!=(m=_.__k[a]=null==(m=n[a])||"boolean"==typeof m?null:"string"==typeof m||"number"==typeof m||"bigint"==typeof m?p(null,m,null,null,m):Array.isArray(m)?p(h,{children:m},null,null,null):m.__b>0?p(m.type,m.props,m.key,null,m.__v):m)){if(m.__=_,m.__b=_.__b+1,null===(y=H[a])||y&&m.key==y.key&&m.type===y.type)H[a]=void 0;else for(d=0;d<E;d++){if((y=H[d])&&m.key==y.key&&m.type===y.type){H[d]=void 0;break}y=null}P(e,m,y=y||l,o,r,u,c,s,f),g=m.__e,(d=m.ref)&&y.ref!=d&&(x||(x=[]),y.ref&&x.push(y.ref,null,m),x.push(d,m.__c||g,m)),null!=g?(null==k&&(k=g),"function"==typeof m.type&&null!=m.__k&&m.__k===y.__k?m.__d=s=b(m,s,e):s=C(e,m,y,H,g,s),f||"option"!==_.type?"function"==typeof _.type&&(_.__d=s):e.value=""):s&&y.__e==s&&s.parentNode!=e&&(s=v(y))}for(_.__e=k,a=E;a--;)null!=H[a]&&("function"==typeof _.type&&null!=H[a].__e&&H[a].__e==_.__d&&(_.__d=v(t,a+1)),U(H[a],H[a]));if(x)for(a=0;a<x.length;a++)T(x[a],x[++a],x[++a])}function b(e,n,_){var t,o;for(t=0;t<e.__k.length;t++)(o=e.__k[t])&&(o.__=e,n="function"==typeof o.type?b(o,n,_):C(_,o,o,e.__k,o.__e,n));return n}function C(e,n,_,t,o,r){var u,l,i;if(void 0!==n.__d)u=n.__d,n.__d=void 0;else if(null==_||o!=r||null==o.parentNode)e:if(null==r||r.parentNode!==e)e.appendChild(o),u=null;else{for(l=r,i=0;(l=l.nextSibling)&&i<t.length;i+=2)if(l==o)break e;e.insertBefore(o,r),u=r}return void 0!==u?u:o.nextSibling}function x(e,n,_){"-"===n[0]?e.setProperty(n,_):e[n]=null==_?"":"number"!=typeof _||c.test(n)?_:_+"px"}function H(e,n,_,t,o){var r;e:if("style"===n)if("string"==typeof _)e.style.cssText=_;else{if("string"==typeof t&&(e.style.cssText=t=""),t)for(n in t)_&&n in _||x(e.style,n,"");if(_)for(n in _)t&&_[n]===t[n]||x(e.style,n,_[n])}else if("o"===n[0]&&"n"===n[1])r=n!==(n=n.replace(/Capture$/,"")),n=n.toLowerCase()in e?n.toLowerCase().slice(2):n.slice(2),e.l||(e.l={}),e.l[n+r]=_,_?t||e.addEventListener(n,r?S:E,r):e.removeEventListener(n,r?S:E,r);else if("dangerouslySetInnerHTML"!==n){if(o)n=n.replace(/xlink[H:h]/,"h").replace(/sName$/,"s");else if("href"!==n&&"list"!==n&&"form"!==n&&"tabIndex"!==n&&"download"!==n&&n in e)try{e[n]=null==_?"":_;break e}catch(e){}"function"==typeof _||(null!=_&&(!1!==_||"a"===n[0]&&"r"===n[1])?e.setAttribute(n,_):e.removeAttribute(n))}}function E(e){this.l[e.type+!1](n.event?n.event(e):e)}function S(e){this.l[e.type+!0](n.event?n.event(e):e)}function P(e,_,t,o,r,u,l,i,c){var f,a,p,v,y,m,g,b,C,x,H,E=_.type;if(void 0!==_.constructor)return null;null!=t.__h&&(c=t.__h,i=_.__e=t.__e,_.__h=null,u=[i]),(f=n.__b)&&f(_);try{e:if("function"==typeof E){if(b=_.props,C=(f=E.contextType)&&o[f.__c],x=f?C?C.props.value:f.__:o,t.__c?g=(a=_.__c=t.__c).__=a.__E:("prototype"in E&&E.prototype.render?_.__c=a=new E(b,x):(_.__c=a=new d(b,x),a.constructor=E,a.render=A),C&&C.sub(a),a.props=b,a.state||(a.state={}),a.context=x,a.__n=o,p=a.__d=!0,a.__h=[]),null==a.__s&&(a.__s=a.state),null!=E.getDerivedStateFromProps&&(a.__s==a.state&&(a.__s=s({},a.__s)),s(a.__s,E.getDerivedStateFromProps(b,a.__s))),v=a.props,y=a.state,p)null==E.getDerivedStateFromProps&&null!=a.componentWillMount&&a.componentWillMount(),null!=a.componentDidMount&&a.__h.push(a.componentDidMount);else{if(null==E.getDerivedStateFromProps&&b!==v&&null!=a.componentWillReceiveProps&&a.componentWillReceiveProps(b,x),!a.__e&&null!=a.shouldComponentUpdate&&!1===a.shouldCompLine truncated
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
* { box-sizing: border-box; }
|
||||||
|
html, body { margin: 0; padding: 0; height: 100%; font: 16px sans-serif; }
|
||||||
|
select, input, label::before, textarea { outline: none; box-shadow:none !important; border: 1px solid #ccc !important; }
|
||||||
|
code, pre { color: #373; font-family: monospace; font-weight: bolder; font-size: smaller; background: #ddd; padding: 0.1em 0.3em; border-radius: 0.2em; }
|
||||||
|
textarea, input, .addon { font-size: 15px; border: 1px solid #ccc; padding: 0.5em; }
|
||||||
|
a, a:visited, a:active { color: #55f; }
|
||||||
|
.addon { background: #eee; min-width: 9em;}
|
||||||
|
.btn {
|
||||||
|
background: #ccc; border-radius: 0.3em; border: 0; color: #fff; cursor: pointer;
|
||||||
|
display: inline-block; padding: 0.6em 2em; font-weight: bolder;
|
||||||
|
}
|
||||||
|
.btn[disabled] { opacity: 0.5; cursor: auto;}
|
||||||
|
.smooth { transition: all .2s; }
|
||||||
|
.container { margin: 0 20px; width: auto; }
|
||||||
|
.d-flex { display: flex; }
|
||||||
|
.d-none { display: none; }
|
||||||
|
.border { border: 1px solid #ddd; }
|
||||||
|
.rounded { border-radius: 0.5em; }
|
||||||
|
.nowrap { white-space: nowrap; }
|
||||||
|
.msg { background: #def; border-left: 5px solid #59d; padding: 0.5em; font-size: 90%; margin: 1em 0; }
|
||||||
|
.section { margin: 0 1em; }
|
||||||
|
.topic, .data, .qos { padding: 0.2em 0.5em; border-radius: 0.4em; margin-right: 0.5em; }
|
||||||
|
.qos { background: #efa; }
|
||||||
|
.topic { background: #fea; }
|
||||||
|
.data { background: #aef; }
|
||||||
|
|
||||||
|
/* Grid */
|
||||||
|
.row { display: flex; flex-wrap: wrap; }
|
||||||
|
.col { margin: 0; padding: 0; overflow: auto; }
|
||||||
|
.col-12 { width: 100%; }
|
||||||
|
.col-11 { width: 91.66%; }
|
||||||
|
.col-10 { width: 83.33%; }
|
||||||
|
.col-9 { width: 75%; }
|
||||||
|
.col-8 { width: 66.66%; }
|
||||||
|
.col-7 { width: 58.33%; }
|
||||||
|
.col-6 { width: 50%; }
|
||||||
|
.col-5 { width: 41.66%; }
|
||||||
|
.col-4 { width: 33.33%; }
|
||||||
|
.col-3 { width: 25%; }
|
||||||
|
.col-2 { width: 16.66%; }
|
||||||
|
.col-1 { width: 8.33%; }
|
||||||
|
@media (min-width: 1310px) { .container { margin: auto; width: 1270px; } }
|
||||||
|
@media (max-width: 920px) { .row .col { width: 100%; } }
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
THISDIR = $(realpath $(CURDIR))
|
|
||||||
ROOTDIR = $(realpath $(CURDIR)/../..)
|
|
||||||
|
|
||||||
all: example
|
|
||||||
|
|
||||||
example: main/main.c Makefile
|
|
||||||
docker run --rm -v $(ROOTDIR):$(ROOTDIR) -w $(THISDIR) espressif/idf idf.py build
|
|
||||||
|
|
||||||
COMPORT ?= /dev/cu.SLAB_USBtoUART
|
|
||||||
ESPTOOL ?= esptool.py
|
|
||||||
flash:
|
|
||||||
cd build && $(ESPTOOL) --chip esp32 -p $(COMPORT) -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x8000 partition_table/partition-table.bin 0x1000 bootloader/bootloader.bin 0x100000 mongoose-esp32-example.bin
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb mongoose mongoose_* mongoose.* build sdkconfig
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# An example ESP32 application
|
|
||||||
|
|
||||||
To build this application, follow these steps:
|
|
||||||
|
|
||||||
1. Make sure you have Docker installed
|
|
||||||
2. Clone whole mongoose repository on your machine:
|
|
||||||
```sh
|
|
||||||
git clone --depth 1 https://github.com/cesanta/mongoose.git
|
|
||||||
```
|
|
||||||
3. Start command line or terminal, and go into the example's directory:
|
|
||||||
```sh
|
|
||||||
cd mongoose/examples/esp32
|
|
||||||
```
|
|
||||||
4. Run `make`. This invokes a Docker-based build. A firmware will be built
|
|
||||||
in the `build/` directory:
|
|
||||||
```sh
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
# Flashing built firmware
|
|
||||||
|
|
||||||
If you have a real board to flash to, you can flash a built firmware.
|
|
||||||
First, make sure to have `esptool.py` script installed on your workstation.
|
|
||||||
Then, connect the board to your workstation and run the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make flash COMPORT=YOUR_USB_PORT
|
|
||||||
```
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
THISDIR = $(realpath $(CURDIR))
|
||||||
|
ROOTDIR = $(realpath $(CURDIR)/../../..)
|
||||||
|
DOCKER ?= docker run --rm $(DA) -v $(ROOTDIR):$(ROOTDIR) -w $(THISDIR) espressif/idf
|
||||||
|
CMD ?= idf.py build
|
||||||
|
PORT ?= /dev/ttyUSB0
|
||||||
|
|
||||||
|
all: example
|
||||||
|
|
||||||
|
example:
|
||||||
|
true
|
||||||
|
|
||||||
|
build: Makefile $(wildcard main/*)
|
||||||
|
$(DOCKER) $(CMD)
|
||||||
|
|
||||||
|
flash:
|
||||||
|
flash: CMD = idf.py flash
|
||||||
|
flash: DA = --device $(PORT)
|
||||||
|
flash: build
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
|
||||||
|
dashboard.hex: build
|
||||||
|
esputil mkhex \
|
||||||
|
0x8000 build/partition_table/partition-table.bin \
|
||||||
|
0x1000 build/bootloader/bootloader.bin \
|
||||||
|
0x100000 build/mongoose-esp32-example.bin > $@
|
||||||
|
|
||||||
|
flash2: dashboard.hex
|
||||||
|
esputil -p $(PORT) -b 921600 -fp 0x220 flash dashboard.hex
|
||||||
|
esputil -p $(PORT) monitor
|
||||||
|
|
||||||
|
ESPTOOL ?= esptool.py
|
||||||
|
|
||||||
|
flash3:
|
||||||
|
cd build && $(ESPTOOL) --chip esp32 -p $(PORT) -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x8000 partition_table/partition-table.bin 0x1000 bootloader/bootloader.bin 0x100000 mongoose-esp32-example.bin
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(DOCKER) rm -rf build sdkconfig
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
See detailed tutorial at https://mongoose.ws/tutorials/esp32/device-dashboard
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
idf_component_register(SRCS "main.c"
|
||||||
|
"wifi.c"
|
||||||
|
"net.c"
|
||||||
|
"packed_fs.c"
|
||||||
|
"mongoose.c")
|
||||||
|
component_compile_options(-DMG_ENABLE_LINES)
|
||||||
|
component_compile_options(-DMG_ENABLE_PACKED_FS)
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (c) 2020 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "esp_spiffs.h"
|
||||||
|
//#include "freertos/FreeRTOS.h"
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
const char *s_listening_url = "http://0.0.0.0:80";
|
||||||
|
void device_dashboard_fn(struct mg_connection *, int, void *, void *);
|
||||||
|
|
||||||
|
#define WIFI_SSID "YOUR_WIFI_NETWORK_NAME" // SET THIS!
|
||||||
|
#define WIFI_PASS "YOUR_WIFI_PASSWORD" // SET THIS!
|
||||||
|
#define FS_ROOT "/spiffs"
|
||||||
|
|
||||||
|
void app_main(void) {
|
||||||
|
// Mount filesystem
|
||||||
|
esp_vfs_spiffs_conf_t conf = {
|
||||||
|
.base_path = FS_ROOT, .max_files = 20, .format_if_mount_failed = true};
|
||||||
|
int res = esp_vfs_spiffs_register(&conf);
|
||||||
|
MG_INFO(("FS %s, %d", conf.base_path, res));
|
||||||
|
mg_file_printf(&mg_fs_posix, FS_ROOT "/hello.txt", "%s", "hello from ESP");
|
||||||
|
|
||||||
|
// Setup wifi. This function is implemented in wifi.c
|
||||||
|
// It blocks until connected to the configured WiFi network
|
||||||
|
void wifi_init(const char *ssid, const char *pass);
|
||||||
|
wifi_init(WIFI_SSID, WIFI_PASS);
|
||||||
|
|
||||||
|
// Connected to WiFi, now start HTTP server
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
mg_log_set(MG_LL_DEBUG); // Set log level
|
||||||
|
mg_mgr_init(&mgr);
|
||||||
|
MG_INFO(("Mongoose v%s on %s", MG_VERSION, s_listening_url));
|
||||||
|
mg_http_listen(&mgr, s_listening_url, device_dashboard_fn, &mgr);
|
||||||
|
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../mongoose.c
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../mongoose.h
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../device-dashboard/net.c
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../device-dashboard/packed_fs.c
|
||||||
@@ -35,14 +35,14 @@ static void event_handler(void *arg, esp_event_base_t event_base,
|
|||||||
if (s_retry_num < 3) {
|
if (s_retry_num < 3) {
|
||||||
esp_wifi_connect();
|
esp_wifi_connect();
|
||||||
s_retry_num++;
|
s_retry_num++;
|
||||||
LOG(LL_INFO, ("retry to connect to the AP"));
|
MG_INFO(("retry to connect to the AP"));
|
||||||
} else {
|
} else {
|
||||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||||
}
|
}
|
||||||
LOG(LL_ERROR, ("connect to the AP fail"));
|
MG_ERROR(("connect to the AP fail"));
|
||||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
|
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
|
||||||
LOG(LL_INFO, ("got ip:" IPSTR, IP2STR(&event->ip_info.ip)));
|
MG_INFO(("IP ADDRESS:" IPSTR, IP2STR(&event->ip_info.ip)));
|
||||||
s_retry_num = 0;
|
s_retry_num = 0;
|
||||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||||
}
|
}
|
||||||
@@ -81,18 +81,18 @@ void wifi_init(const char *ssid, const char *pass) {
|
|||||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &c));
|
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &c));
|
||||||
ESP_ERROR_CHECK(esp_wifi_start());
|
ESP_ERROR_CHECK(esp_wifi_start());
|
||||||
LOG(LL_DEBUG, ("wifi_init_sta finished."));
|
MG_DEBUG(("wifi_init_sta finished."));
|
||||||
|
|
||||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||||
pdFALSE, pdFALSE, portMAX_DELAY);
|
pdFALSE, pdFALSE, portMAX_DELAY);
|
||||||
|
|
||||||
if (bits & WIFI_CONNECTED_BIT) {
|
if (bits & WIFI_CONNECTED_BIT) {
|
||||||
LOG(LL_INFO, ("connected to ap SSID:%s password:%s", ssid, pass));
|
MG_INFO(("connected to ap SSID:%s password:%s", ssid, pass));
|
||||||
} else if (bits & WIFI_FAIL_BIT) {
|
} else if (bits & WIFI_FAIL_BIT) {
|
||||||
LOG(LL_ERROR, ("Failed to connect to SSID:%s, password:%s", ssid, pass));
|
MG_ERROR(("Failed to connect to SSID:%s, password:%s", ssid, pass));
|
||||||
} else {
|
} else {
|
||||||
LOG(LL_ERROR, ("UNEXPECTED EVENT"));
|
MG_ERROR(("UNEXPECTED EVENT"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The event will not be processed after unregister */
|
/* The event will not be processed after unregister */
|
||||||
|
500 Internal Server Error
An error occurred: An error occurred Gitea Version: 28.2.0 |