mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22f8cb14d5 | ||
|
|
e28b3ca91d | ||
|
|
73d2b395d1 | ||
|
|
e556a487e0 | ||
|
|
5728fa1cea | ||
|
|
b461edabd9 | ||
|
|
c9e666babc | ||
|
|
18e64397fc | ||
|
|
3c8418d324 | ||
|
|
784a1580ce | ||
|
|
6a0ac69761 | ||
|
|
7c06279a8c | ||
|
|
aec6f85f55 | ||
|
|
52e3be5c58 | ||
|
|
01a664ad06 | ||
|
|
94933047a7 | ||
|
|
cd6005c86f | ||
|
|
6d14ed3a18 | ||
|
|
0fb5db59d3 | ||
|
|
20b274bb2b | ||
|
|
00b289a0f4 | ||
|
|
9818f9849b | ||
|
|
df1c23222e | ||
|
|
f18ac3e094 | ||
|
|
d8596fae27 | ||
|
|
85209ac4fa | ||
|
|
a58e82b301 | ||
|
|
35f485558b | ||
|
|
93365fcbd2 | ||
|
|
e40a44ba70 | ||
|
|
1eed770238 | ||
|
|
511ef4137f | ||
|
|
479164129b | ||
|
|
08746115e2 | ||
|
|
587aad75c1 | ||
|
|
218428662e | ||
|
|
04fc209644 | ||
|
|
53bedd1eb5 | ||
|
|
a0b43ae8e0 | ||
|
|
866a7e2823 | ||
|
|
2055d41a9f | ||
|
|
1ef29c1b3b | ||
|
|
e95dbb90a7 | ||
|
|
cf18c932a4 | ||
|
|
a730afc44d | ||
|
|
94296c9697 | ||
|
|
667707df50 | ||
|
|
90b6fab3bb | ||
|
|
e47dbcbdc5 | ||
|
|
2a15f8d567 | ||
|
|
b5d397c74f | ||
|
|
3d6b373c8f | ||
|
|
ed236dddeb | ||
|
|
596fb1e88a | ||
|
|
ca0cd5040d | ||
|
|
5b84acbc14 | ||
|
|
c12838e2c3 | ||
|
|
fa3e6ce271 | ||
|
|
743f48b8e0 | ||
|
|
421c16b2bb | ||
|
|
3054a7f09e | ||
|
|
2de96bd54e | ||
|
|
f815c315e7 | ||
|
|
e59ee00154 | ||
|
|
b07745ab50 | ||
|
|
2468a8fb38 | ||
|
|
ed38a4102e | ||
|
|
b1c66f6544 | ||
|
|
8f79a0564f | ||
|
|
2e93cde929 | ||
|
|
e53d05637a | ||
|
|
9d75d35365 | ||
|
|
2ea8d85cff | ||
|
|
ea81a22529 | ||
|
|
526f3aa31c | ||
|
|
62d66201c9 | ||
|
|
f32e14478f | ||
|
|
ca019799b9 | ||
|
|
c6fb9ac3d0 | ||
|
|
c80a88a407 | ||
|
|
f2e19ca3ff | ||
|
|
16c463d2e9 | ||
|
|
6a99518048 | ||
|
|
3b242ec68b | ||
|
|
7409166409 | ||
|
|
ca66b84f7d | ||
|
|
dc1c732f11 | ||
|
|
61148f1323 | ||
|
|
136e3e353d | ||
|
|
c70b80e4e1 | ||
|
|
cba4c9d85d | ||
|
|
3e588065dc | ||
|
|
35cb27dbe6 | ||
|
|
d6b9f76e9c | ||
|
|
a9d51c0e87 | ||
|
|
4b163232e3 | ||
|
|
e003236a03 | ||
|
|
de7745370c | ||
|
|
21786eec8e | ||
|
|
036b213b2b | ||
|
|
61f5f0b725 | ||
|
|
d864d72470 | ||
|
|
7ca5962d2d | ||
|
|
8ce1562f0d | ||
|
|
49d9016275 | ||
|
|
cdc7380d00 | ||
|
|
e645b33b6c | ||
|
|
30f71a8bc4 | ||
|
|
faa030385c | ||
|
|
feb3b5db96 | ||
|
|
508b67d6f5 | ||
|
|
133aec777d | ||
|
|
389ed4e099 | ||
|
|
bc40aa5442 | ||
|
|
5fb9aca2e1 | ||
|
|
c68df310b8 | ||
|
|
70ce6c5ad3 | ||
|
|
460738f11b | ||
|
|
a09ec8988e | ||
|
|
c46aa050ae | ||
|
|
348db16245 | ||
|
|
c0ca6a53be | ||
|
|
e58d622d5f | ||
|
|
bcb294620f | ||
|
|
15937a9c5c | ||
|
|
fa160cb9a3 | ||
|
|
137d6484e2 | ||
|
|
e7c34c26cb | ||
|
|
dfb7d928c2 | ||
|
|
4bb9faaf50 | ||
|
|
a856971b6d | ||
|
|
4a88ad5ca9 | ||
|
|
17ecb0a459 | ||
|
|
afd39abc6f | ||
|
|
4babcff8de | ||
|
|
17d48b1d04 | ||
|
|
6ab20ed9a3 | ||
|
|
a0e5494569 | ||
|
|
f62c733990 | ||
|
|
d283e3167f | ||
|
|
94050d44e4 | ||
|
|
dee97cbf22 | ||
|
|
be01471968 | ||
|
|
a74c44c463 | ||
|
|
81e53d8375 | ||
|
|
ca626abcac | ||
|
|
b5e7730ac7 | ||
|
|
33b920ed2c | ||
|
|
b5b234fab2 | ||
|
|
af89d2446b | ||
|
|
791ad49651 | ||
|
|
063b26fece | ||
|
|
add079a949 | ||
|
|
6e59ad8a0d | ||
|
|
f4862b646d | ||
|
|
91fa767efb | ||
|
|
5e78711467 | ||
|
|
b4a622cfd6 | ||
|
|
8c118bf4cc | ||
|
|
728ebdaafa | ||
|
|
3a186976d7 | ||
|
|
2e232b3304 | ||
|
|
d72645bf91 | ||
|
|
7d5bcec92d | ||
|
|
215ea28db0 | ||
|
|
9b76d60b38 | ||
|
|
63ccf8199f | ||
|
|
6bc6022aa9 | ||
|
|
b68db66e97 | ||
|
|
94e1c5a41b | ||
|
|
ab16220861 | ||
|
|
c0eb3e56b4 | ||
|
|
5591aa5a0d | ||
|
|
6f946f5eea | ||
|
|
39cd5b8323 | ||
|
|
ca96db714a | ||
|
|
a7f97bf2f2 | ||
|
|
e579355b01 | ||
|
|
cfbaf7f1ae | ||
|
|
ce26234ace | ||
|
|
c28dc45d4c | ||
|
|
98b6c5e16a | ||
|
|
1d9d1111e1 | ||
|
|
2a0b55b30b | ||
|
|
1e3b167208 | ||
|
|
f7821b757c | ||
|
|
c352486abb | ||
|
|
4120b9bcc0 | ||
|
|
f7725f2eeb | ||
|
|
be06c3620c | ||
|
|
272644e39f | ||
|
|
55ef3afea4 | ||
|
|
9e07c742ff | ||
|
|
cd2e50c2c4 | ||
|
|
fdef2b8643 | ||
|
|
ce55924a02 | ||
|
|
fbca54bcaf | ||
|
|
41c25d60d6 | ||
|
|
a9b5bb351f | ||
|
|
51ed3efcd1 | ||
|
|
67cffc8db5 | ||
|
|
72abaa043b | ||
|
|
b3a511f9c5 | ||
|
|
8d1f6377c4 | ||
|
|
22dddc2eeb | ||
|
|
daab527743 | ||
|
|
dd49c0791f | ||
|
|
4b539d4b95 | ||
|
|
5eabfe5da2 | ||
|
|
36b7c8d60d | ||
|
|
aa37b728ad | ||
|
|
62085e5215 | ||
|
|
d036f8a3f0 | ||
|
|
fb5e2b8c2f | ||
|
|
4505b00733 | ||
|
|
c485b8264e | ||
|
|
8edd710c00 | ||
|
|
3984e9f5cc | ||
|
|
d4fa794c83 | ||
|
|
3892e0199e | ||
|
|
67f5de624e | ||
|
|
10234b07c4 | ||
|
|
36a83e1452 | ||
|
|
34df4ec1fd | ||
|
|
69cb94f3fb | ||
|
|
7204d71136 | ||
|
|
283f590349 | ||
|
|
27735f293b | ||
|
|
a560334fbb | ||
|
|
cd1707034c | ||
|
|
5ce1fc3c2a | ||
|
|
c78fe425a0 | ||
|
|
a58bb71c19 | ||
|
|
fc9a9593d2 | ||
|
|
eb1fdf56e6 | ||
|
|
ab960df8a9 | ||
|
|
08048bd5bb | ||
|
|
800517ac16 | ||
|
|
690eff4fa2 | ||
|
|
e47b5b775a | ||
|
|
c15e2af897 | ||
|
|
d8536b011c | ||
|
|
a2239c2b4b | ||
|
|
d3c02331d4 | ||
|
|
d707841166 | ||
|
|
affd82a885 | ||
|
|
8322c82790 | ||
|
|
08708435a7 | ||
|
|
1c6036c3ac | ||
|
|
6c3cb6f3eb | ||
|
|
a123874ac5 | ||
|
|
7931929a42 | ||
|
|
cc4a4fb2ab | ||
|
|
3d211d89c5 | ||
|
|
fbf1ccee45 | ||
|
|
0bd43bcae3 | ||
|
|
b809665cad | ||
|
|
463b49f1bd | ||
|
|
a881aad5c6 | ||
|
|
aae0995cd2 | ||
|
|
26b3f227d9 | ||
|
|
665770d602 | ||
|
|
fcafaa73d6 | ||
|
|
079b9ec2c1 | ||
|
|
7bd9a85897 | ||
|
|
2efc5d9fce | ||
|
|
1cb094e059 | ||
|
|
27fe9c8f01 | ||
|
|
9178fbd153 | ||
|
|
b2b43ab0cd | ||
|
|
1415d187eb | ||
|
|
a65078b858 | ||
|
|
7107afae5c | ||
|
|
f6a807c5a4 | ||
|
|
47b11dd19e | ||
|
|
61cdc93405 | ||
|
|
7a52bd643c | ||
|
|
63e8052e4b | ||
|
|
782d0cda46 | ||
|
|
72803eb7f8 | ||
|
|
c7fd221ab7 | ||
|
|
492185e35f | ||
|
|
845d7b08e1 | ||
|
|
49bac31d2d | ||
|
|
9baf2d4ba4 | ||
|
|
1becd26a8f | ||
|
|
ffa983552f |
No files matched your search
@@ -1,19 +1,16 @@
|
|||||||
Copyright (c) 2004-2010 Sergey Lyubka
|
Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||||
|
Copyright (c) 2013 Cesanta Software Limited
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
This code is dual-licensed: you can redistribute it and/or modify
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
it under the terms of the GNU General Public License version 2 as
|
||||||
in the Software without restriction, including without limitation the rights
|
published by the Free Software Foundation. For the terms of this
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
license, see <http://www.gnu.org/licenses/>.
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
You are free to use this code under the terms of the GNU General
|
||||||
all copies or substantial portions of the Software.
|
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU General Public License for more details.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
Alternatively, you can license this code under a commercial
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
license, as set out in <http://cesanta.com/products.html>.
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
@@ -1,67 +1,57 @@
|
|||||||
Project Mission
|
# Project Mission
|
||||||
---------------
|
|
||||||
|
|
||||||
Project mission is to provide simple, functional, embeddable web server to
|
Project mission is to provide easy to use, powerful, embeddable web server.
|
||||||
make it easy for application and device developers to implement web interface
|
|
||||||
for their application and devices, and to offer a simple web development
|
|
||||||
environment.
|
|
||||||
|
|
||||||
Overview
|
# Downloads
|
||||||
--------
|
|
||||||
|
|
||||||
To accomplish it's mission, Mongoose keeps balance on functionality and
|
Sources, binaries and bundles are available for download at
|
||||||
|
[mongoose downloads](http://cesanta.com/downloads.html).
|
||||||
|
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
Mongoose keeps the balance between functionality and
|
||||||
simplicity by carefully selected list of features:
|
simplicity by carefully selected list of features:
|
||||||
|
|
||||||
- Liberal, commercial-friendly
|
|
||||||
[MIT license](http://en.wikipedia.org/wiki/MIT_License)
|
|
||||||
- Works on Windows, Mac, UNIX, iPhone, Android, and many other platforms
|
- Works on Windows, Mac, UNIX, iPhone, Android, and many other platforms
|
||||||
|
- Scripting and database support (Lua Server Pages + Sqlite, see
|
||||||
|
[page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp) ),
|
||||||
|
which provides ready to go, powerful web development platform in
|
||||||
|
one single-click executable with **no dependencies**: forget LAMP!
|
||||||
- Support for CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV
|
- Support for CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV
|
||||||
- Lua server pages (PHP-like functionality using Lua), see
|
- Resumed download, URL rewrite, file blacklist, IP-based ACL, Windows service
|
||||||
[page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp)
|
|
||||||
- Resumed download, URL rewrite, IP-based ACL, Windows service
|
|
||||||
- Excluding files from serving by URI pattern (file blacklist)
|
|
||||||
- Download speed limit based on client subnet or URI pattern
|
- Download speed limit based on client subnet or URI pattern
|
||||||
- Small footprint: executable size is 50 kB on Linux 2.6 i386 system
|
|
||||||
- 130 kilobytes Windows executable with all of the above and no dependencies
|
|
||||||
- Simple and clean embedding API,
|
- Simple and clean embedding API,
|
||||||
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h).
|
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h).
|
||||||
The source is in single
|
The source is in single
|
||||||
[mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file
|
[mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file
|
||||||
to make things easy
|
to make things easy. Embedding examples:
|
||||||
- Embedding examples:
|
|
||||||
[hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c),
|
[hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c),
|
||||||
[post.c](https://github.com/valenok/mongoose/blob/master/examples/post.c),
|
[post.c](https://github.com/valenok/mongoose/blob/master/examples/post.c),
|
||||||
[upload.c](https://github.com/valenok/mongoose/blob/master/examples/upload.c),
|
[upload.c](https://github.com/valenok/mongoose/blob/master/examples/upload.c),
|
||||||
[websocket.c](https://github.com/valenok/mongoose/blob/master/examples/websocket.c)
|
[websocket.c](https://github.com/valenok/mongoose/blob/master/examples/websocket.c)
|
||||||
- HTTP client functionality for embedded usage, capable of
|
- HTTP client capable of sending arbitrary HTTP/HTTPS requests
|
||||||
sending arbitrary HTTP/HTTPS requests
|
- [User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md)
|
||||||
- Extensive documentation in form of
|
|
||||||
[User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md)
|
Note that Windows and MacOS binaries have following 3rd party software
|
||||||
|
compiled in:
|
||||||
|
<a href="http://wolfssl.com">WolfSSL lightweight SSL library</a>,
|
||||||
|
<a href="http://sqlite.org">SQLite embedded database</a>,
|
||||||
|
<a href="http://lua.org">Lua embedded scripting engine</a>.
|
||||||
|
|
||||||
Questions can be asked at
|
Questions can be asked at
|
||||||
[mongoose-users@google.com](http://groups.google.com/group/mongoose-users)
|
[mongoose-users@googlegroups.com](http://groups.google.com/group/mongoose-users)
|
||||||
mailing list.
|
mailing list.
|
||||||
|
|
||||||
|
[](http://githalytics.com/valenok/mongoose)
|
||||||
Keep Sergey happy
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
I am Sergey Lyubka, a software engineer from Galway, Ireland. I started
|
|
||||||
working on Mongoose in 2004, and since then continuously improving it,
|
|
||||||
investing thousands of hours of work. My other project I'm contributing to the
|
|
||||||
community for free is
|
|
||||||
[Super Light Regular Expression library](http://code.google.com/p/slre).
|
|
||||||
|
|
||||||
|
|
||||||
If you feel grateful for the stuff I've done, you can buy me a book from my
|
# License
|
||||||
[Amazon wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority). Many thanks
|
|
||||||
to all who already did so: T.Barmann, D.Hughes, J.C.Sloan, R.Romeo,
|
|
||||||
L.E.Spencer, S.Kotay, B.Fjukstad and 6 others.
|
|
||||||
Appreciated guys, you keep my brains going!
|
|
||||||
|
|
||||||
Cash is also appreciated indeed. Press
|
Mongoose is dual-licensed. It is available under the terms of
|
||||||
[<img src="http://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif">](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DGZ2FMP95TAL6)
|
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) license
|
||||||
button to donate. Donation progress: 4/1000 €
|
license for free, or under the terms of standard commercial license provided
|
||||||
(thanks to O.M.Vilhunen, C.Radik)
|
by [Cesanta Software](http://cesanta.com).
|
||||||
|
|
||||||

|
[Super Light Regular Expression library](https://github.com/cesanta/slre) is
|
||||||
|
another project by Cesanta Software, developed with the same philosophy
|
||||||
|
of functionality and simplicity.
|
||||||
+451
-119
@@ -1,14 +1,15 @@
|
|||||||
|
# Overview
|
||||||
**NOTE: THIS MANUAL IS WORK IN PROGRESS**
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Mongoose is small and easy to use web server. It is self-contained, and does
|
Mongoose is small and easy to use web server. It is self-contained, and does
|
||||||
not require any external software to run.
|
not require any external software to run.
|
||||||
|
|
||||||
On Windows, mongoose iconifies itself to the system tray icon when started.
|
On Windows, mongoose iconifies itself to the system tray icon when started.
|
||||||
Right-click on the icon pops up a menu, where it is possible to stop
|
Right-click on the icon pops up a menu, where it is possible to stop
|
||||||
mongoose, or configure it, or install it as Windows service.
|
mongoose, or configure it, or install it as Windows service. The easiest way
|
||||||
|
to share a folder on Windows is to copy `mongoose.exe` to a folder,
|
||||||
|
double-click the exe, and launch a browser at
|
||||||
|
[http://localhost:8080](http://localhost:8080). Note that 'localhost' should
|
||||||
|
be changed to a machine's name if a folder is accessed from other computer.
|
||||||
|
|
||||||
On UNIX and Mac, mongoose is a command line utility. Running `mongoose` in
|
On UNIX and Mac, mongoose is a command line utility. Running `mongoose` in
|
||||||
terminal, optionally followed by configuration parameters
|
terminal, optionally followed by configuration parameters
|
||||||
@@ -21,12 +22,13 @@ When started, mongoose first searches for the configuration file.
|
|||||||
If configuration file is specified explicitly in the command line, i.e.
|
If configuration file is specified explicitly in the command line, i.e.
|
||||||
`mongoose path_to_config_file`, then specified configuration file is used.
|
`mongoose path_to_config_file`, then specified configuration file is used.
|
||||||
Otherwise, mongoose would search for file `mongoose.conf` in the same directory
|
Otherwise, mongoose would search for file `mongoose.conf` in the same directory
|
||||||
where binary is located, and use it.
|
where binary is located, and use it. Configuration file can be absent.
|
||||||
|
|
||||||
|
|
||||||
Configuration file is a sequence of lines, each line containing
|
Configuration file is a sequence of lines, each line containing
|
||||||
command line argument name and it's value. Empty lines, and lines beginning
|
command line argument name and it's value. Empty lines, and lines beginning
|
||||||
with `#`, are ignored. Here is the example of `mongoose.conf` file:
|
with `#`, are ignored. Here is the example of `mongoose.conf` file:
|
||||||
|
|
||||||
# mongoose.conf file
|
|
||||||
document_root c:\www
|
document_root c:\www
|
||||||
listening_ports 8080,8043s
|
listening_ports 8080,8043s
|
||||||
ssl_certificate c:\mongoose\ssl_cert.pem
|
ssl_certificate c:\mongoose\ssl_cert.pem
|
||||||
@@ -38,164 +40,409 @@ For example, if `mongoose.conf` has line
|
|||||||
`document_root /var/www`, and mongoose has been started as
|
`document_root /var/www`, and mongoose has been started as
|
||||||
`mongoose -document_root /etc`, then `/etc` directory will be served as
|
`mongoose -document_root /etc`, then `/etc` directory will be served as
|
||||||
document root, because command line options take priority over
|
document root, because command line options take priority over
|
||||||
configuration file.
|
configuration file. Configuration options section below provide a good
|
||||||
|
overview of Mongoose features.
|
||||||
|
|
||||||
|
Note that configuration options on the command line must start with `-`,
|
||||||
|
but their names are the same as in the config file. All option names are
|
||||||
|
listed in the next section. Thus, the following two setups are equivalent:
|
||||||
|
|
||||||
|
# Using command line arguments
|
||||||
|
$ mongoose -listening_ports 1234 -document_root /var/www
|
||||||
|
|
||||||
|
# Using config file
|
||||||
|
$ cat mongoose.conf
|
||||||
|
listening_ports 1234
|
||||||
|
document_root /var/www
|
||||||
|
$ mongoose
|
||||||
|
|
||||||
Mongoose can also be used to modify `.htpasswd` passwords file:
|
Mongoose can also be used to modify `.htpasswd` passwords file:
|
||||||
|
|
||||||
mongoose -A <htpasswd_file> <realm> <user> <passwd>
|
mongoose -A <htpasswd_file> <realm> <user> <passwd>
|
||||||
|
|
||||||
## Usage Examples
|
Unlike other web servers, mongoose does not require CGI scripts be located in
|
||||||
|
a special directory. CGI scripts can be anywhere. CGI (and SSI) files are
|
||||||
|
recognized by the file name pattern. Mongoose uses shell-like glob
|
||||||
|
patterns. Pattern match starts at the beginning of the string, so essentially
|
||||||
|
patterns are prefix patterns. Syntax is as follows:
|
||||||
|
|
||||||
- How to share a Windows folder: copy mongoose executable to a folder and
|
** Matches everything
|
||||||
double-click the executable. The folder should be accessible via
|
* Matches everything but slash character, '/'
|
||||||
[http://localhost:8080](http://localhost:8080) in any browser.
|
? Matches any character
|
||||||
|
$ Matches the end of the string
|
||||||
|
| Matches if pattern on the left side or the right side matches.
|
||||||
|
|
||||||
- How to start mongoose at UNIX startup time in daemon mode, serving
|
All other characters in the pattern match themselves. Examples:
|
||||||
directory `/var/www`: put this line in the system startup script:
|
|
||||||
`/path/to/mongoose -listening_ports 80 -document_root /var/www &`
|
|
||||||
|
|
||||||
- How to serve user home directories using URL rewrite:
|
**.cgi$ Any string that ends with .cgi
|
||||||
`mongoose -url_rewrite_patterns /~joe/=/home/joe/,/~bill=/home/bill/`
|
/foo Any string that begins with /foo
|
||||||
|
**a$|**b$ Any string that ends with a or b
|
||||||
|
|
||||||
## Configuration Options
|
# Configuration Options
|
||||||
```
|
|
||||||
-C cgi_pattern
|
|
||||||
All files that fully match cgi_pattern are treated as CGI.
|
|
||||||
Default pattern allows CGI files be anywhere. To restrict CGIs to
|
|
||||||
certain directory, use e.g. "-C /cgi-bin/**.cgi". Default:
|
|
||||||
"**.cgi$|**.pl$|**.php$"
|
|
||||||
|
|
||||||
-E cgi_environment
|
Below is a list of configuration options Mongoose understands. Every option
|
||||||
|
is followed by it's default value. If default value is not present, then
|
||||||
|
it is empty.
|
||||||
|
|
||||||
|
### cgi_pattern `**.cgi$|**.pl$|**.php$`
|
||||||
|
All files that match `cgi_pattern` are treated as CGI files. Default pattern
|
||||||
|
allows CGI files be anywhere. To restrict CGIs to a certain directory,
|
||||||
|
use `/path/to/cgi-bin/**.cgi` as pattern. Note that full file path is
|
||||||
|
matched against the pattern, not the URI.
|
||||||
|
|
||||||
|
### cgi_environment
|
||||||
Extra environment variables to be passed to the CGI script in
|
Extra environment variables to be passed to the CGI script in
|
||||||
addition to standard ones. The list must be comma-separated list
|
addition to standard ones. The list must be comma-separated list
|
||||||
of X=Y pairs, like this: "VARIABLE1=VALUE1,VARIABLE2=VALUE2".
|
of name=value pairs, like this: `VARIABLE1=VALUE1,VARIABLE2=VALUE2`.
|
||||||
Default: ""
|
|
||||||
|
|
||||||
-G put_delete_passwords_file
|
### put\_delete\_auth\_file
|
||||||
PUT and DELETE passwords file. This must be specified if PUT or
|
Passwords file for PUT and DELETE requests. Without it, PUT and DELETE requests
|
||||||
DELETE methods are used. Default: ""
|
will fail.
|
||||||
|
|
||||||
-I cgi_interpreter
|
### cgi_interpreter
|
||||||
Use cgi_interpreter as a CGI interpreter for all CGI scripts
|
Path to an executable to use as CGI interpreter for __all__ CGI scripts
|
||||||
regardless script extension. Mongoose decides which interpreter
|
regardless script extension. If this option is not set (which is a default),
|
||||||
to use by looking at the first line of a CGI script. Default:
|
Mongoose looks at first line of a CGI script,
|
||||||
"".
|
[shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\)), for an interpreter.
|
||||||
|
|
||||||
-P protect_uri
|
For example, if both PHP and perl CGIs are used, then
|
||||||
|
`#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the
|
||||||
|
respective CGI scripts. Note that paths should be either full file paths,
|
||||||
|
or file paths relative to the current working directory of mongoose server.
|
||||||
|
If mongoose is started by mouse double-click on Windows, current working
|
||||||
|
directory is a directory where mongoose executable is located.
|
||||||
|
|
||||||
|
If all CGIs use the same interpreter, for example they are all PHP, then
|
||||||
|
`cgi_interpreter` can be set to the path to `php-cgi.exe` executable and
|
||||||
|
shebang line in the CGI scripts can be omitted.
|
||||||
|
Note that PHP scripts must use `php-cgi.exe` executable, not `php.exe`.
|
||||||
|
|
||||||
|
### protect_uri
|
||||||
Comma separated list of URI=PATH pairs, specifying that given
|
Comma separated list of URI=PATH pairs, specifying that given
|
||||||
URIs must be protected with respected password files. Default: ""
|
URIs must be protected with respected password files. Paths must be full
|
||||||
|
file paths.
|
||||||
|
|
||||||
-R authentication_domain
|
### authentication_domain `mydomain.com`
|
||||||
Authorization realm. Default: "mydomain.com"
|
Authorization realm used in `.htpasswd` authorization.
|
||||||
|
|
||||||
-S ssi_pattern
|
### ssi_pattern `**.shtml$|**.shtm$`
|
||||||
All files that fully match ssi_pattern are treated as SSI.
|
All files that match `ssi_pattern` are treated as SSI.
|
||||||
Unknown SSI directives are silently ignored. Currently, two SSI
|
|
||||||
directives are supported, "include" and "exec". Default:
|
|
||||||
"**.shtml$|**.shtm$"
|
|
||||||
|
|
||||||
-T throttle
|
Server Side Includes (SSI) is a simple interpreted server-side scripting
|
||||||
Limit download speed for clients. throttle is a comma-separated
|
language which is most commonly used to include the contents of a file into
|
||||||
list of key=value pairs, where key could be a '*' character
|
a web page. It can be useful when it is desirable to include a common piece
|
||||||
(limit for all connections), a subnet in form x.x.x.x/mask (limit
|
of code throughout a website, for example, headers and footers.
|
||||||
for a given subnet, for example 10.0.0.0/8), or an URI prefix
|
|
||||||
pattern (limit for the set of URIs, for example /foo/**). The
|
In order for a webpage to recognize an SSI-enabled HTML file, the filename
|
||||||
value is a floating-point number of bytes per second, optionally
|
should end with a special extension, by default the extension should be
|
||||||
followed by a `k' or `m' character, meaning kilobytes and
|
either `.shtml` or `.shtm`.
|
||||||
|
|
||||||
|
Unknown SSI directives are silently ignored by mongoose. Currently, two SSI
|
||||||
|
directives are supported, `<!--#include ...>` and
|
||||||
|
`<!--#exec "command">`. Note that `<!--#include ...>` directive supports
|
||||||
|
three path specifications:
|
||||||
|
|
||||||
|
<!--#include virtual="path"> Path is relative to web server root
|
||||||
|
<!--#include abspath="path"> Path is absolute or relative to
|
||||||
|
web server working dir
|
||||||
|
<!--#include file="path">, Path is relative to current document
|
||||||
|
<!--#include "path">
|
||||||
|
|
||||||
|
The `include` directive may be used to include the contents of a file or the
|
||||||
|
result of running a CGI script. The `exec` directive is used to execute a
|
||||||
|
command on a server, and show command's output. Example:
|
||||||
|
|
||||||
|
<!--#exec "ls -l" -->
|
||||||
|
|
||||||
|
For more information on Server Side Includes, take a look at the Wikipedia:
|
||||||
|
[Server Side Includes](http://en.wikipedia.org/wiki/Server_Side_Includes)
|
||||||
|
|
||||||
|
### throttle
|
||||||
|
Limit download speed for clients. `throttle` is a comma-separated
|
||||||
|
list of key=value pairs, where key could be:
|
||||||
|
|
||||||
|
* limit speed for all connections
|
||||||
|
x.x.x.x/mask limit speed for specified subnet
|
||||||
|
uri_prefix_pattern limit speed for given URIs
|
||||||
|
|
||||||
|
The value is a floating-point number of bytes per second, optionally
|
||||||
|
followed by a `k` or `m` character, meaning kilobytes and
|
||||||
megabytes respectively. A limit of 0 means unlimited rate. The
|
megabytes respectively. A limit of 0 means unlimited rate. The
|
||||||
last matching rule wins. For example, "*=1k,10.0.0.0/8" means
|
last matching rule wins. Examples:
|
||||||
limit everybody to 1 kilobyte per second, but give people from
|
|
||||||
10/8 subnet unlimited speed. Default: ""
|
|
||||||
|
|
||||||
-a access_log_file
|
*=1k,10.0.0.0/8=0 limit all accesses to 1 kilobyte per second,
|
||||||
Access log file. Default: "", no logging is done.
|
but give connections from 10.0.0.0/8 subnet
|
||||||
|
unlimited speed
|
||||||
|
|
||||||
-d enable_directory_listing
|
/downloads/=5k limit accesses to all URIs in `/downloads/` to
|
||||||
Enable/disable directory listing. Default: "yes"
|
5 kilobytes per secods. All other accesses are unlimited
|
||||||
|
|
||||||
-e error_log_file
|
### access\_log\_file
|
||||||
Error log file. Default: "", no errors are logged.
|
Path to a file for access logs. Either full path, or relative to current
|
||||||
|
working directory. If absent (default), then accesses are not logged.
|
||||||
|
|
||||||
-g global_passwords_file
|
### error\_log\_file
|
||||||
Location of a global passwords file. If set, per-directory
|
Path to a file for error logs. Either full path, or relative to current
|
||||||
.htpasswd files are ignored, and all requests must be authorised
|
working directory. If absent (default), then errors are not logged.
|
||||||
against that file. Default: ""
|
|
||||||
|
|
||||||
-i index_files
|
### enable\_directory\_listing `yes`
|
||||||
|
Enable directory listing, either `yes` or `no`.
|
||||||
|
|
||||||
|
### enable\_keep\_alive `no`
|
||||||
|
Enable connection keep alive, either `yes` or `no`.
|
||||||
|
|
||||||
|
Experimental feature. Allows clients to reuse TCP connection for
|
||||||
|
subsequent HTTP requests, which improves performance.
|
||||||
|
For this to work when using request handlers it's important to add the correct
|
||||||
|
Content-Length HTTP header for each request. If this is forgotten the client
|
||||||
|
will time out.
|
||||||
|
|
||||||
|
|
||||||
|
### global\_auth\_file
|
||||||
|
Path to a global passwords file, either full path or relative to the current
|
||||||
|
working directory. If set, per-directory `.htpasswd` files are ignored,
|
||||||
|
and all requests are authorised against that file.
|
||||||
|
|
||||||
|
The file has to include the realm set through `authentication_domain` and the password in digest format:
|
||||||
|
|
||||||
|
user:realm:digest
|
||||||
|
test:test.com:ce0220efc2dd2fad6185e1f1af5a4327
|
||||||
|
|
||||||
|
(e.g. use [this generator](http://www.askapache.com/online-tools/htpasswd-generator))
|
||||||
|
|
||||||
|
### index_files `index.html,index.htm,index.cgi,index.shtml,index.php`
|
||||||
Comma-separated list of files to be treated as directory index
|
Comma-separated list of files to be treated as directory index
|
||||||
files. Default: "index.html,index.htm,index.cgi"
|
files.
|
||||||
|
|
||||||
-l access_control_list
|
### access\_control\_list
|
||||||
Specify access control list (ACL). ACL is a comma separated list
|
An Access Control List (ACL) allows restrictions to be put on the list of IP
|
||||||
of IP subnets, each subnet is prepended by '-' or '+' sign. Plus
|
addresses which have access to the web server. In the case of the Mongoose
|
||||||
means allow, minus means deny. If subnet mask is omitted, like
|
web server, the ACL is a comma separated list of IP subnets, where each
|
||||||
"-1.2.3.4", then it means single IP address. Mask may vary from 0
|
subnet is prepended by either a `-` or a `+` sign. A plus sign means allow,
|
||||||
to 32 inclusive. On each request, full list is traversed, and
|
where a minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`,
|
||||||
last match wins. Default setting is to allow all. For example, to
|
this means to deny only that single IP address.
|
||||||
allow only 192.168/16 subnet to connect, run "mongoose
|
|
||||||
-0.0.0.0/0,+192.168/16". Default: ""
|
|
||||||
|
|
||||||
-m extra_mime_types
|
Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow
|
||||||
Extra mime types to recognize, in form "extension1=type1,exten-
|
all accesses. On each request the full list is traversed, and
|
||||||
sion2=type2,...". Extension must include dot. Example: "mongoose
|
the last match wins. Examples:
|
||||||
-m .cpp=plain/text,.java=plain/text". Default: ""
|
|
||||||
|
|
||||||
-p listening_ports
|
-0.0.0.0/0,+192.168/16 deny all acccesses, only allow 192.168/16 subnet
|
||||||
|
|
||||||
|
To learn more about subnet masks, see the
|
||||||
|
[Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork)
|
||||||
|
|
||||||
|
### extra\_mime\_types
|
||||||
|
Extra mime types to recognize, in form `extension1=type1,exten-
|
||||||
|
sion2=type2,...`. Extension must include dot. Example:
|
||||||
|
`.cpp=plain/text,.java=plain/text`
|
||||||
|
|
||||||
|
### listening_ports `8080`
|
||||||
Comma-separated list of ports to listen on. If the port is SSL, a
|
Comma-separated list of ports to listen on. If the port is SSL, a
|
||||||
letter 's' must be appeneded, for example, "-p 80,443s" will open
|
letter `s` must be appeneded, for example, `80,443s` will open
|
||||||
port 80 and port 443, and connections on port 443 will be SSL-ed.
|
port 80 and port 443, and connections on port 443 will be SSL-ed.
|
||||||
|
For non-SSL ports, it is allowed to append letter `r`, meaning 'redirect'.
|
||||||
|
Redirect ports will redirect all their traffic to the first configured
|
||||||
|
SSL port. For example, if `listening_ports` is `80r,443s`, then all
|
||||||
|
HTTP traffic coming at port 80 will be redirected to HTTPS port 443.
|
||||||
|
|
||||||
It is possible to specify an IP address to bind to. In this case,
|
It is possible to specify an IP address to bind to. In this case,
|
||||||
an IP address and a colon must be prepended to the port number.
|
an IP address and a colon must be prepended to the port number.
|
||||||
For example, to bind to a loopback interface on port 80 and to
|
For example, to bind to a loopback interface on port 80 and to
|
||||||
all interfaces on HTTPS port 443, use "mongoose -p
|
all interfaces on HTTPS port 443, use `127.0.0.1:80,443s`.
|
||||||
127.0.0.1:80,443s". Default: "8080"
|
|
||||||
|
|
||||||
-r document_root
|
### document_root `.`
|
||||||
Location of the WWW root directory. Default: "."
|
A directory to serve. By default, currect directory is served. Current
|
||||||
|
directory is commonly referenced as dot (`.`).
|
||||||
|
|
||||||
-s ssl_certificate
|
### ssl_certificate
|
||||||
Location of SSL certificate file. Default: ""
|
Path to SSL certificate file. This option is only required when at least one
|
||||||
|
of the `listening_ports` is SSL. The file must be in PEM format,
|
||||||
|
and it must have both private key and certificate, see for example
|
||||||
|
[ssl_cert.pem](https://github.com/valenok/mongoose/blob/master/build/ssl_cert.pem)
|
||||||
|
|
||||||
-t num_threads
|
### num_threads `50`
|
||||||
Number of worker threads to start. Default: "10"
|
Number of worker threads. Mongoose handles each incoming connection in a
|
||||||
|
separate thread. Therefore, the value of this option is effectively a number
|
||||||
|
of concurrent HTTP connections Mongoose can handle.
|
||||||
|
|
||||||
-u run_as_user
|
### run\_as\_user
|
||||||
Switch to given user's credentials after startup. Default: ""
|
Switch to given user credentials after startup. Usually, this option is
|
||||||
|
required when mongoose needs to bind on privileged port on UNIX. To do
|
||||||
|
that, mongoose needs to be started as root. But running as root is a bad idea,
|
||||||
|
therefore this option can be used to drop privileges. Example:
|
||||||
|
|
||||||
-w url_rewrite_patterns
|
mongoose -listening_ports 80 -run_as_user nobody
|
||||||
Comma-separated list of URL rewrites in the form of "pattern=sub-
|
|
||||||
stitution,..." If the "pattern" matches some prefix of the
|
|
||||||
requested URL, then matched prefix gets substituted with "substi-
|
|
||||||
tution". For example, "-w /con-
|
|
||||||
fig=/etc,**.doc|**.rtf=/path/to/cgi-bin/handle_doc.cgi" will
|
|
||||||
serve all URLs that start with "/config" from the "/etc" direc-
|
|
||||||
tory, and call handle_doc.cgi script for .doc and .rtf file
|
|
||||||
requests. If some pattern matches, no further matching/substitu-
|
|
||||||
tion is performed (first matching pattern wins). Use full paths
|
|
||||||
in substitutions. Default: ""
|
|
||||||
|
|
||||||
-x hide_files_patterns
|
### request\_timeout\_ms `30000`
|
||||||
A prefix pattern for the files to hide. Files that match the pat-
|
Timeout for network read and network write operations, in milliseconds.
|
||||||
tern will not show up in directory listing and return 404 Not
|
If client intends to keep long-running connection, either increase this value
|
||||||
Found if requested. Default: ""
|
or use keep-alive messages.
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Problems
|
### url\_rewrite\_patterns
|
||||||
---------------
|
Comma-separated list of URL rewrites in the form of
|
||||||
|
`uri_pattern=file_or_directory_path`. When Mongoose receives the request,
|
||||||
|
it constructs the file name to show by combining `document_root` and the URI.
|
||||||
|
However, if the rewrite option is used and `uri_pattern` matches the
|
||||||
|
requested URI, then `document_root` is ignored. Insted,
|
||||||
|
`file_or_directory_path` is used, which should be a full path name or
|
||||||
|
a path relative to the web server's current working directory. Note that
|
||||||
|
`uri_pattern`, as all mongoose patterns, is a prefix pattern.
|
||||||
|
|
||||||
|
This makes it possible to serve many directories outside from `document_root`,
|
||||||
|
redirect all requests to scripts, and do other tricky things. For example,
|
||||||
|
to redirect all accesses to `.doc` files to a special script, do:
|
||||||
|
|
||||||
|
mongoose -url_rewrite_patterns **.doc$=/path/to/cgi-bin/handle_doc.cgi
|
||||||
|
|
||||||
|
Or, to imitate user home directories support, do:
|
||||||
|
|
||||||
|
mongoose -url_rewrite_patterns /~joe/=/home/joe/,/~bill=/home/bill/
|
||||||
|
|
||||||
|
### hide\_files\_patterns
|
||||||
|
A pattern for the files to hide. Files that match the pattern will not
|
||||||
|
show up in directory listing and return `404 Not Found` if requested. Pattern
|
||||||
|
must be for a file name only, not including directory name. Example:
|
||||||
|
|
||||||
|
mongoose -hide_files_patterns secret.txt|even_more_secret.txt
|
||||||
|
|
||||||
|
# Lua Server Pages
|
||||||
|
Pre-built Windows and Mac mongoose binaries have built-in Lua Server Pages
|
||||||
|
support. That means it is possible to write PHP-like scripts with mongoose,
|
||||||
|
using Lua programming language instead of PHP. Lua is known
|
||||||
|
for it's speed and small size. Mongoose uses Lua version 5.2.1, the
|
||||||
|
documentation for it can be found at
|
||||||
|
[Lua 5.2 reference manual](http://www.lua.org/manual/5.2/).
|
||||||
|
|
||||||
|
To create a Lua Page, make sure a file has `.lp` extension. For example,
|
||||||
|
let's say it is going to be `my_page.lp`. The contents of the file, just like
|
||||||
|
with PHP, is HTML with embedded Lua code. Lua code must be enclosed in
|
||||||
|
`<? ?>` blocks, and can appear anywhere on the page. For example, to
|
||||||
|
print current weekday name, one can write:
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<span>Today is:</span>
|
||||||
|
<? mg.write(os.date("%A")) ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
Note that this example uses function `mg.write()`, which prints data to the
|
||||||
|
web page. Using function `mg.write()` is the way to generate web content from
|
||||||
|
inside Lua code. In addition to `mg.write()`, all standard library functions
|
||||||
|
are accessible from the Lua code (please check reference manual for details),
|
||||||
|
and also information about the request is available in `mg.request_info` object,
|
||||||
|
like request method, all headers, etcetera. Please refer to
|
||||||
|
`struct mg_request_info` definition in
|
||||||
|
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)
|
||||||
|
to see what kind of information is present in `mg.request_info` object. Also,
|
||||||
|
[page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp) and
|
||||||
|
[prime_numbers.lp](https://github.com/valenok/mongoose/blob/master/examples/lua/prime_numbers.lp)
|
||||||
|
contains some example code that uses `request_info` and other functions(form submitting for example).
|
||||||
|
|
||||||
|
Mongoose exports the following to the Lua server page:
|
||||||
|
|
||||||
|
mg.read() -- reads a chunk from POST data, returns it as a string
|
||||||
|
mg.write(str) -- writes string to the client
|
||||||
|
mg.include(path) -- sources another Lua file
|
||||||
|
mg.redirect(uri) -- internal redirect to a given URI
|
||||||
|
mg.onerror(msg) -- error handler, can be overridden
|
||||||
|
mg.version -- a string that holds Mongoose version
|
||||||
|
mg.request_info -- a table with request information
|
||||||
|
|
||||||
|
-- Connect to the remote TCP server. This function is an implementation
|
||||||
|
-- of simple socket interface. It returns a socket object with three
|
||||||
|
-- methods: send, recv, close, which are synchronous (blocking).
|
||||||
|
-- connect() throws an exception on connection error.
|
||||||
|
connect(host, port, use_ssl)
|
||||||
|
|
||||||
|
-- Example of using connect() interface:
|
||||||
|
local host = 'code.google.com' -- IP address or domain name
|
||||||
|
local ok, sock = pcall(connect, host, 80, 1)
|
||||||
|
if ok then
|
||||||
|
sock:send('GET /p/mongoose/ HTTP/1.0\r\n' ..
|
||||||
|
'Host: ' .. host .. '\r\n\r\n')
|
||||||
|
local reply = sock:recv()
|
||||||
|
sock:close()
|
||||||
|
-- reply now contains the web page https://code.google.com/p/mongoose
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
**IMPORTANT: Mongoose does not send HTTP headers for Lua pages. Therefore,
|
||||||
|
every Lua Page must begin with HTTP reply line and headers**, like this:
|
||||||
|
|
||||||
|
<? print('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') ?>
|
||||||
|
<html><body>
|
||||||
|
... the rest of the web page ...
|
||||||
|
|
||||||
|
To serve Lua Page, mongoose creates Lua context. That context is used for
|
||||||
|
all Lua blocks within the page. That means, all Lua blocks on the same page
|
||||||
|
share the same context. If one block defines a variable, for example, that
|
||||||
|
variable is visible in the block that follows.
|
||||||
|
|
||||||
|
# Common Problems
|
||||||
- PHP doesn't work - getting empty page, or 'File not found' error. The
|
- PHP doesn't work - getting empty page, or 'File not found' error. The
|
||||||
reason for that is wrong paths to the interpreter. Remember that with PHP,
|
reason for that is wrong paths to the interpreter. Remember that with PHP,
|
||||||
correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify
|
correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify
|
||||||
full path to the PHP interpreter, e.g.
|
full path to the PHP interpreter, e.g.:
|
||||||
`mongoose -cgi_interpreter /full/path/to/php-cgi`
|
`mongoose -cgi_interpreter /full/path/to/php-cgi`
|
||||||
|
|
||||||
## Embedding
|
- Mongoose fails to start. If Mongoose exits immediately when run, this
|
||||||
Embedding Mongoose is easy. Somewhere in the application code, `mg_start()`
|
usually indicates a syntax error in the configuration file
|
||||||
function must be called. That starts the web server in a separate thread.
|
(named `mongoose.conf` by default) or the command-line arguments.
|
||||||
When it is not needed anymore, `mg_stop()` must be called. Application code
|
Syntax checking is omitted from Mongoose to keep its size low. However,
|
||||||
can pass configuration options to `mg_start()`, and also specify callback
|
the Manual should be of help. Note: the syntax changes from time to time,
|
||||||
functions that Mongoose should call at certain events.
|
so updating the config file might be necessary after executable update.
|
||||||
[hello.c](http://a.c) provides a minimalistic example.
|
|
||||||
|
- Embedding with OpenSSL on Windows might fail because of calling convention.
|
||||||
|
To force Mongoose to use `__stdcall` convention, add `/Gz` compilation
|
||||||
|
flag in Visual Studio compiler.
|
||||||
|
|
||||||
|
# Embedding
|
||||||
|
Embedding Mongoose is easy. Copy
|
||||||
|
[mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) and
|
||||||
|
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)
|
||||||
|
to your application's source tree and include them in the build. For
|
||||||
|
example, your application's code lives in C file `my_app.c`, then on UNIX
|
||||||
|
this command embeds Mongoose:
|
||||||
|
|
||||||
|
$ ls
|
||||||
|
my_app.c mongoose.c mongoose.h
|
||||||
|
$ gcc my_app.c mongoose.c -o my_app -ldl -pthread
|
||||||
|
|
||||||
|
Somewhere in the application code, call `mg_start()` to start the server.
|
||||||
|
Pass configuration options and event handlers to `mg_start()`.
|
||||||
|
Mongoose then calls handlers when certain events happen.
|
||||||
|
For example, when new request arrives, Mongoose calls `begin_request`
|
||||||
|
handler function to let user handle the request. In the handler, user code
|
||||||
|
can get all information about the request -- parsed headers, etcetera.
|
||||||
|
|
||||||
|
Mongoose API is logically divided in three categories: server setup/shutdown
|
||||||
|
functions, functions to be used by user-written event handlers, and
|
||||||
|
convenience utility functions.
|
||||||
|
|
||||||
|
### Starting and stopping embedded web server
|
||||||
|
To start the embedded web server, call `mg_start()`. To stop it, call
|
||||||
|
`mg_stop()`.
|
||||||
|
|
||||||
|
// This structure needs to be passed to mg_start(), to let mongoose know
|
||||||
|
// which callbacks to invoke. For detailed description, see
|
||||||
|
// https://github.com/valenok/mongoose/blob/master/UserManual.md
|
||||||
|
struct mg_callbacks {
|
||||||
|
int (*begin_request)(struct mg_connection *);
|
||||||
|
void (*end_request)(const struct mg_connection *, int reply_status_code);
|
||||||
|
int (*log_message)(const struct mg_connection *, const char *message);
|
||||||
|
int (*init_ssl)(void *ssl_context);
|
||||||
|
int (*websocket_connect)(const struct mg_connection *);
|
||||||
|
void (*websocket_ready)(struct mg_connection *);
|
||||||
|
int (*websocket_data)(struct mg_connection *);
|
||||||
|
const char * (*open_file)(const struct mg_connection *,
|
||||||
|
const char *path, size_t *data_len);
|
||||||
|
void (*init_lua)(struct mg_connection *, void *lua_context);
|
||||||
|
void (*upload)(struct mg_connection *, const char *file_name);
|
||||||
|
int (*http_error)(struct mg_connection *, int status);
|
||||||
|
};
|
||||||
|
|
||||||
|
[hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c)
|
||||||
|
provides a minimalistic example.
|
||||||
|
|
||||||
Common pattern is to implement `begin_request` callback, and serve static files
|
Common pattern is to implement `begin_request` callback, and serve static files
|
||||||
from memory, and/or construct dynamic replies on the fly. Here is
|
from memory, and/or construct dynamic replies on the fly. Here is
|
||||||
@@ -204,8 +451,93 @@ that shows how to easily any data can be embedded
|
|||||||
directly into the executable. If such data needs to be encrypted, then
|
directly into the executable. If such data needs to be encrypted, then
|
||||||
encrypted database or encryption dongles would be a better choice.
|
encrypted database or encryption dongles would be a better choice.
|
||||||
|
|
||||||
|
# Build on Android
|
||||||
|
|
||||||
## Other Resources
|
This is a small guide to help you run mongoose on Android. Currently it is
|
||||||
|
tested on the HTC Wildfire. If you have managed to run it on other devices
|
||||||
|
as well, please comment or drop an email in the mailing list.
|
||||||
|
Note : You dont need root access to run mongoose on Android.
|
||||||
|
|
||||||
|
- Download the source from the Downloads page.
|
||||||
|
- Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html)
|
||||||
|
- Run `/path-to-ndk/ndk-build -C /path-to-mongoose/build`
|
||||||
|
That should generate mongoose/lib/armeabi/mongoose
|
||||||
|
- Using the adb tool (you need to have Android SDK installed for that),
|
||||||
|
push the generated mongoose binary to `/data/local` folder on device.
|
||||||
|
- From adb shell, navigate to `/data/local` and execute `./mongoose`.
|
||||||
|
- To test if the server is running fine, visit your web-browser and
|
||||||
|
navigate to `http://127.0.0.1:8080` You should see the `Index of /` page.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- `jni` stands for Java Native Interface. Read up on Android NDK if you want
|
||||||
|
to know how to interact with the native C functions of mongoose in Android
|
||||||
|
Java applications.
|
||||||
|
- TODO: A Java application that interacts with the native binary or a
|
||||||
|
shared library.
|
||||||
|
|
||||||
|
# Mongoose internals
|
||||||
|
|
||||||
|
Mongoose is multithreaded web server. `mg_start()` function allocates
|
||||||
|
web server context (`struct mg_context`), which holds all information
|
||||||
|
about web server instance:
|
||||||
|
|
||||||
|
- configuration options. Note that mongoose makes internal copies of
|
||||||
|
passed options.
|
||||||
|
- SSL context, if any
|
||||||
|
- user-defined callbacks
|
||||||
|
- opened listening sockets
|
||||||
|
- a queue for accepted sockets
|
||||||
|
- mutexes and condition variables for inter-thread synchronization
|
||||||
|
|
||||||
|
When `mg_start()` returns, all initialization is quaranteed to be complete
|
||||||
|
(e.g. listening ports are opened, SSL is initialized, etc). `mg_start()` starts
|
||||||
|
two threads: a master thread, that accepts new connections, and several
|
||||||
|
worker threads, that process accepted connections. The number of worker threads
|
||||||
|
is configurable via `num_threads` configuration option. That number puts a
|
||||||
|
limit on number of simultaneous requests that can be handled by mongoose.
|
||||||
|
|
||||||
|
When master thread accepts new connection, a new accepted socket (described by
|
||||||
|
`struct socket`) it placed into the accepted sockets queue,
|
||||||
|
which has size of 20 (see [code](https://github.com/valenok/mongoose/blob/3892e0199e6ca9613b160535d9d107ede09daa43/mongoose.c#L486)). Any idle worker thread
|
||||||
|
can grab accepted sockets from that queue. If all worker threads are busy,
|
||||||
|
master thread can accept and queue up to 20 more TCP connections,
|
||||||
|
filling up the queue.
|
||||||
|
In the attempt to queue next accepted connection, master thread blocks
|
||||||
|
until there is space in a queue. When master thread is blocked on a
|
||||||
|
full queue, TCP layer in OS can also queue incoming connection.
|
||||||
|
The number is limited by the `listen()` call parameter on listening socket,
|
||||||
|
which is `SOMAXCONN` in case of Mongoose, and depends on a platform.
|
||||||
|
|
||||||
|
Worker threads are running in an infinite loop, which in simplified form
|
||||||
|
looks something like this:
|
||||||
|
|
||||||
|
static void *worker_thread() {
|
||||||
|
while (consume_socket()) {
|
||||||
|
process_new_connection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Function `consume_socket()` gets new accepted socket from the mongoose socket
|
||||||
|
queue, atomically removing it from the queue. If the queue is empty,
|
||||||
|
`consume_socket()` blocks and waits until new sockets are placed in a queue
|
||||||
|
by the master thread. `process_new_connection()` actually processes the
|
||||||
|
connection, i.e. reads the request, parses it, and performs appropriate action
|
||||||
|
depending on a parsed request.
|
||||||
|
|
||||||
|
Master thread uses `poll()` and `accept()` to accept new connections on
|
||||||
|
listening sockets. `poll()` is used to avoid `FD_SETSIZE` limitation of
|
||||||
|
`select()`. Since there are only a few listening sockets, there is no reason
|
||||||
|
to use hi-performance alternatives like `epoll()` or `kqueue()`. Worker
|
||||||
|
threads use blocking IO on accepted sockets for reading and writing data.
|
||||||
|
All accepted sockets have `SO_RCVTIMEO` and `SO_SNDTIMEO` socket options set
|
||||||
|
(controlled by `request_timeout_ms` mongoose option, 30 seconds default) which
|
||||||
|
specify read/write timeout on client connection.
|
||||||
|
|
||||||
|
# Other Resources
|
||||||
- Presentation made by Arnout Vandecappelle at FOSDEM 2011 on 2011-02-06
|
- Presentation made by Arnout Vandecappelle at FOSDEM 2011 on 2011-02-06
|
||||||
in Brussels, Belgium, called
|
in Brussels, Belgium, called
|
||||||
"Creating secure web based user interfaces for Embedded Devices"
|
"Creating secure web based user interfaces for Embedded Devices"
|
||||||
|
|||||||
+110
-53
@@ -1,6 +1,9 @@
|
|||||||
# This Makefile is part of Mongoose web server project,
|
# This Makefile is part of Mongoose web server project,
|
||||||
# https://github.com/valenok/mongoose
|
# https://github.com/valenok/mongoose
|
||||||
#
|
#
|
||||||
|
# This Makefile is GNU make compatible. You can get GNU Make from
|
||||||
|
# http://gnuwin32.sourceforge.net/packages/make.htm
|
||||||
|
#
|
||||||
# Example custom build:
|
# Example custom build:
|
||||||
# COPT="-g -O0 -DNO_SSL_DL -DUSE_LUA -llua -lcrypto -lssl" make linux
|
# COPT="-g -O0 -DNO_SSL_DL -DUSE_LUA -llua -lcrypto -lssl" make linux
|
||||||
#
|
#
|
||||||
@@ -17,11 +20,12 @@
|
|||||||
# -DUSE_LUA - embed Lua in Mongoose (+100kb)
|
# -DUSE_LUA - embed Lua in Mongoose (+100kb)
|
||||||
|
|
||||||
PROG = mongoose
|
PROG = mongoose
|
||||||
CFLAGS = -std=c99 -O2 -W -Wall -pedantic -pthread $(COPT)
|
CFLAGS = -std=c99 -O2 -W -Wall -pedantic -pthread -pipe -I.. $(COPT)
|
||||||
|
|
||||||
# To build with Lua, download and unzip Lua 5.2.1 source code into the
|
# To build with Lua, download and unzip Lua 5.2.1 source code into the
|
||||||
# mongoose directory, and then add $(LUA_SOURCES) to CFLAGS
|
# mongoose directory, and then add $(LUA_SOURCES) to CFLAGS
|
||||||
LUA = lua-5.2.1/src
|
LUA = ../lua-5.2.1/src
|
||||||
|
LUA_FLAGS = -DLUA_COMPAT_ALL
|
||||||
LUA_SOURCES = $(LUA)/lapi.c $(LUA)/lcode.c $(LUA)/lctype.c \
|
LUA_SOURCES = $(LUA)/lapi.c $(LUA)/lcode.c $(LUA)/lctype.c \
|
||||||
$(LUA)/ldebug.c $(LUA)/ldo.c $(LUA)/ldump.c \
|
$(LUA)/ldebug.c $(LUA)/ldo.c $(LUA)/ldump.c \
|
||||||
$(LUA)/lfunc.c $(LUA)/lgc.c $(LUA)/llex.c \
|
$(LUA)/lfunc.c $(LUA)/lgc.c $(LUA)/llex.c \
|
||||||
@@ -33,21 +37,32 @@ LUA_SOURCES = $(LUA)/lapi.c $(LUA)/lcode.c $(LUA)/lctype.c \
|
|||||||
$(LUA)/ldblib.c $(LUA)/liolib.c $(LUA)/lmathlib.c \
|
$(LUA)/ldblib.c $(LUA)/liolib.c $(LUA)/lmathlib.c \
|
||||||
$(LUA)/loslib.c $(LUA)/lstrlib.c $(LUA)/ltablib.c \
|
$(LUA)/loslib.c $(LUA)/lstrlib.c $(LUA)/ltablib.c \
|
||||||
$(LUA)/loadlib.c $(LUA)/linit.c
|
$(LUA)/loadlib.c $(LUA)/linit.c
|
||||||
LUA_OBJECTS = $(LUA_SOURCES:%.c=%.o)
|
LUA_HEADERS = $(LUA)/llimits.h $(LUA)/lobject.h $(LUA)/lapi.h $(LUA)/lzio.h \
|
||||||
|
$(LUA)/ldo.h $(LUA)/ltm.h $(LUA)/lstate.h $(LUA)/lgc.h \
|
||||||
|
$(LUA)/lvm.h $(LUA)/ltable.h $(LUA)/lstring.h $(LUA)/lfunc.h \
|
||||||
|
$(LUA)/lundump.h $(LUA)/ldebug.h $(LUA)/lopcodes.h $(LUA)/llex.h \
|
||||||
|
$(LUA)/lparser.h $(LUA)/lcode.h $(LUA)/lmem.h $(LUA)/lctype.h
|
||||||
|
LUA_WINOBJS = $(LUA_SOURCES:%.c=%.obj)
|
||||||
|
|
||||||
# Using Visual Studio 6.0. To build Mongoose:
|
ifneq ($(OS), Windows_NT)
|
||||||
# Set MSVC variable below to where VS 6.0 is installed on your system
|
LUA_FLAGS += -DLUA_USE_DLOPEN
|
||||||
# Run "PATH_TO_VC6\bin\nmake windows"
|
endif
|
||||||
MSVC = ../vc6
|
|
||||||
#DBG = /Zi /Od
|
lua_5.2.1.c: lua_5.2.1.h
|
||||||
DBG = /DNDEBUG /O1
|
(echo '#define LUA_CORE' ; \
|
||||||
CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /Gz /W3 \
|
echo '#define LUA_LIB' ; \
|
||||||
/I$(MSVC)/include /I$(LUA) /I. /I$(YASSL) /I$(YASSL)/cyassl /GA
|
echo '#define lobject_c' ; \
|
||||||
MSLIB = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
|
echo '#define lua_c' ; \
|
||||||
user32.lib shell32.lib comdlg32.lib ws2_32.lib advapi32.lib
|
echo '#define ltable_c' ; \
|
||||||
|
echo '#include "lua_5.2.1.h"' ; \
|
||||||
|
cat $(LUA_HEADERS) $(LUA_SOURCES) | sed '/^#include "/d') > $@
|
||||||
|
|
||||||
|
lua_5.2.1.h:
|
||||||
|
cat $(LUA)/luaconf.h $(LUA)/lua.h $(LUA)/lauxlib.h $(LUA)/lualib.h | \
|
||||||
|
sed '/^#include "/d' > $@
|
||||||
|
|
||||||
# Stock windows binary builds with Lua and YASSL library.
|
# Stock windows binary builds with Lua and YASSL library.
|
||||||
YASSL = ../cyassl-2.4.6
|
YASSL = ../../cyassl-2.4.6
|
||||||
YASSL_FLAGS = -I $(YASSL) -I $(YASSL)/cyassl \
|
YASSL_FLAGS = -I $(YASSL) -I $(YASSL)/cyassl \
|
||||||
-D _LIB -D OPENSSL_EXTRA -D HAVE_ERRNO_H \
|
-D _LIB -D OPENSSL_EXTRA -D HAVE_ERRNO_H \
|
||||||
-D HAVE_GETHOSTBYNAME -D HAVE_INET_NTOA -D HAVE_LIMITS_H \
|
-D HAVE_GETHOSTBYNAME -D HAVE_INET_NTOA -D HAVE_LIMITS_H \
|
||||||
@@ -71,67 +86,108 @@ YASSL_SOURCES = \
|
|||||||
$(YASSL)/ctaocrypt/src/ecc.c $(YASSL)/src/ocsp.c $(YASSL)/src/crl.c \
|
$(YASSL)/ctaocrypt/src/ecc.c $(YASSL)/src/ocsp.c $(YASSL)/src/crl.c \
|
||||||
$(YASSL)/ctaocrypt/src/hc128.c $(YASSL)/ctaocrypt/src/memory.c
|
$(YASSL)/ctaocrypt/src/hc128.c $(YASSL)/ctaocrypt/src/memory.c
|
||||||
|
|
||||||
|
ALL_SOURCES = ../mongoose.c main.c sqlite3.c lsqlite3.c lua_5.2.1.c \
|
||||||
|
$(YASSL_SOURCES)
|
||||||
|
ALL_OBJECTS = $(ALL_SOURCES:%.c=%.o)
|
||||||
|
ALL_WINOBJS = $(ALL_SOURCES:%.c=%.obj)
|
||||||
|
|
||||||
|
SQLITE_FLAGS = -DTHREADSAFE=1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS
|
||||||
|
MONGOOSE_FLAGS = -DNO_SSL_DL -DUSE_LUA -DUSE_LUA_SQLITE3 -I.. $(COPT)
|
||||||
|
|
||||||
|
FLAGS = $(MONGOOSE_FLAGS) $(SQLITE_FLAGS) $(YASSL_FLAGS) $(LUA_FLAGS)
|
||||||
|
|
||||||
|
# Using Visual Studio 6.0. To build Mongoose:
|
||||||
|
# Set MSVC variable below to where VS 6.0 is installed on your system
|
||||||
|
# Run "PATH_TO_VC6\bin\nmake windows"
|
||||||
|
MSVC = ../../vc6
|
||||||
|
#DBG = /Zi /Od
|
||||||
|
DBG = /DNDEBUG /O1
|
||||||
|
CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /W3 /GA /I$(MSVC)/include
|
||||||
|
LINK_FLAGS = /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
|
||||||
|
user32.lib shell32.lib comdlg32.lib ws2_32.lib advapi32.lib
|
||||||
|
LINK = $(MSVC)/bin/link $(LINK_FLAGS)
|
||||||
|
|
||||||
all:
|
all:
|
||||||
@echo "make (linux|bsd|solaris|mac|windows|mingw|cygwin)"
|
@echo "make (linux|bsd|solaris|mac|windows|mingw|cygwin)"
|
||||||
|
|
||||||
|
%.obj: %.c
|
||||||
|
$(CL) /c $(FLAGS) /Fo$@ $<
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -o $@ $< -c $(FLAGS) $(CFLAGS)
|
||||||
|
|
||||||
|
# Lua library for Windows
|
||||||
|
lua.lib: $(LUA_WINOBJS)
|
||||||
|
$(MSVC)/bin/lib /out:$@ $(LUA_WINOBJS)
|
||||||
|
|
||||||
|
# To build with lua, make sure you have Lua unpacked into lua-5.2.1 directory
|
||||||
|
linux_lua: $(ALL_OBJECTS)
|
||||||
|
$(CC) $(ALL_OBJECTS) -o $(PROG) -ldl
|
||||||
|
|
||||||
|
mongoose.o: mod_lua.c
|
||||||
|
|
||||||
# Make sure that the compiler flags come last in the compilation string.
|
# Make sure that the compiler flags come last in the compilation string.
|
||||||
# If not so, this can break some on some Linux distros which use
|
# If not so, this can break some on some Linux distros which use
|
||||||
# "-Wl,--as-needed" turned on by default in cc command.
|
# "-Wl,--as-needed" turned on by default in cc command.
|
||||||
# Also, this is turned in many other distros in static linkage builds.
|
# Also, this is turned in many other distros in static linkage builds.
|
||||||
linux:
|
linux:
|
||||||
$(CC) mongoose.c main.c -o $(PROG) -ldl $(CFLAGS)
|
$(CC) ../mongoose.c main.c -o $(PROG) -ldl $(CFLAGS)
|
||||||
|
|
||||||
mac: bsd
|
mac: bsd
|
||||||
bsd:
|
bsd:
|
||||||
$(CC) mongoose.c main.c -o $(PROG) $(CFLAGS)
|
$(CC) ../mongoose.c main.c -o $(PROG) $(CFLAGS)
|
||||||
|
|
||||||
bsd_yassl:
|
bsd_lua: $(ALL_OBJECTS)
|
||||||
$(CC) mongoose.c main.c build/lsqlite3.c build/sqlite3.c -o $(PROG) \
|
$(CC) $(ALL_OBJECTS) -o $@
|
||||||
$(CFLAGS) -I$(LUA) -Ibuild \
|
|
||||||
$(YASSL_SOURCES) $(YASSL_FLAGS) -DNO_SSL_DL \
|
|
||||||
$(LUA_SOURCES) -DUSE_LUA -DUSE_LUA_SQLITE3 -DLUA_COMPAT_ALL
|
|
||||||
|
|
||||||
solaris:
|
solaris:
|
||||||
$(CC) mongoose.c main.c -lnsl -lsocket -o $(PROG) $(CFLAGS)
|
$(CC) ../mongoose.c main.c -lnsl -lsocket -o $(PROG) $(CFLAGS)
|
||||||
|
|
||||||
|
lib$(PROG).a: $(ALL_OBJECTS)
|
||||||
|
ar cr $@ $(ALL_OBJECTS)
|
||||||
|
|
||||||
|
$(PROG).lib: $(ALL_WINOBJS)
|
||||||
|
$(MSVC)/bin/lib /out:$@ $(ALL_WINOBJS)
|
||||||
|
|
||||||
# For codesign to work in non-interactive mode, unlock login keychain:
|
# For codesign to work in non-interactive mode, unlock login keychain:
|
||||||
# security unlock ~/Library/Keychains/login.keychain
|
# security unlock ~/Library/Keychains/login.keychain
|
||||||
# See e.g. http://lists.apple.com/archives/apple-cdsa/2008/Jan/msg00027.html
|
# See e.g. http://lists.apple.com/archives/apple-cdsa/2008/Jan/msg00027.html
|
||||||
cocoa:
|
Mongoose: ../mongoose.c main.c
|
||||||
$(CC) mongoose.c main.c build/lsqlite3.c build/sqlite3.c \
|
$(CC) $(ALL_SOURCES) \
|
||||||
-DUSE_COCOA $(CFLAGS) -I$(LUA) -Ibuild \
|
-DUSE_COCOA $(CFLAGS) $(FLAGS) -mmacosx-version-min=10.4 \
|
||||||
$(YASSL_SOURCES) $(YASSL_FLAGS) -DNO_SSL_DL \
|
|
||||||
$(LUA_SOURCES) -DUSE_LUA -DUSE_LUA_SQLITE3 -DLUA_COMPAT_ALL \
|
|
||||||
-framework Cocoa -ObjC -arch i386 -arch x86_64 -o Mongoose
|
-framework Cocoa -ObjC -arch i386 -arch x86_64 -o Mongoose
|
||||||
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`; DIR=dmg/Mongoose.app && rm -rf $$DIR && mkdir -p $$DIR/Contents/{MacOS,Resources} && install -m 644 build/mongoose_*.png $$DIR/Contents/Resources/ && install -m 644 build/Info.plist $$DIR/Contents/ && install -m 755 Mongoose $$DIR/Contents/MacOS/ && ln -fs /Applications dmg/ ; hdiutil create Mongoose_$$V.dmg -volname "Mongoose $$V" -srcfolder dmg -ov #; rm -rf dmg
|
|
||||||
|
|
||||||
u:
|
cocoa: Mongoose
|
||||||
$(CC) test/unit_test.c -o unit_test -I. -I$(LUA) $(LUA_SOURCES) \
|
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' ../mongoose.c`; DIR=dmg/Mongoose.app && rm -rf $$DIR && mkdir -p $$DIR/Contents/{MacOS,Resources} && install -m 644 mongoose_*.png $$DIR/Contents/Resources/ && install -m 644 Info.plist $$DIR/Contents/ && install -m 755 Mongoose $$DIR/Contents/MacOS/ && ln -fs /Applications dmg/ ; hdiutil create Mongoose_$$V.dmg -volname "Mongoose $$V" -srcfolder dmg -ov #; rm -rf dmg
|
||||||
$(CFLAGS) -g -O0
|
|
||||||
|
un:
|
||||||
|
$(CC) ../test/unit_test.c -o unit_test lua_5.2.1.c $(CFLAGS) -g -O0
|
||||||
./unit_test
|
./unit_test
|
||||||
|
|
||||||
w:
|
wi:
|
||||||
$(CL) test/unit_test.c $(LUA_SOURCES) \
|
$(CL) test/unit_test.c $(LUA_SOURCES) $(LUA_FLAGS) \
|
||||||
$(YASSL_SOURCES) $(YASSL_FLAGS) /DNO_SSL_DL \
|
$(YASSL_SOURCES) $(YASSL_FLAGS) /I. /DNO_SSL_DL \
|
||||||
$(MSLIB) /out:unit_test.exe
|
/link /libpath:$(MSVC)/lib advapi32.lib /out:unit_test.exe
|
||||||
./unit_test.exe
|
./unit_test.exe
|
||||||
|
|
||||||
windows:
|
windows: $(ALL_WINOBJS)
|
||||||
$(MSVC)/bin/rc build\res.rc
|
$(MSVC)/bin/rc res.rc
|
||||||
$(CL) main.c mongoose.c build/lsqlite3.c build/sqlite3.c \
|
$(LINK) /nologo $(ALL_WINOBJS) res.res /out:$(PROG).exe
|
||||||
$(YASSL_SOURCES) $(YASSL_FLAGS) /DNO_SSL_DL \
|
|
||||||
$(LUA_SOURCES) /DUSE_LUA /DUSE_LUA_SQLITE3 /DLUA_COMPAT_ALL \
|
windows_tiny: $(ALL_SOURCES)
|
||||||
$(MSLIB) build\res.res /out:$(PROG).exe /subsystem:windows
|
$(MSVC)/bin/rc res.rc
|
||||||
|
$(CL) /nologo ../mongoose.c main.c /I.. \
|
||||||
|
/link $(LINK_FLAGS) res.res /out:$(PROG)-tiny.exe
|
||||||
|
|
||||||
# Build for Windows under MinGW
|
# Build for Windows under MinGW
|
||||||
#MINGWDBG= -DDEBUG -O0 -ggdb
|
#MINGWDBG= -DDEBUG -O0 -ggdb
|
||||||
MINGWDBG= -DNDEBUG -Os
|
MINGWDBG= -DNDEBUG -Os
|
||||||
MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,console $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
|
MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,console $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
|
||||||
mingw:
|
mingw:
|
||||||
windres build\res.rc build\res.o
|
windres res.rc res.o
|
||||||
$(CC) $(MINGWOPT) mongoose.c -lws2_32 \
|
$(CC) $(MINGWOPT) ../mongoose.c -lws2_32 \
|
||||||
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
|
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
|
||||||
$(CC) $(MINGWOPT) mongoose.c main.c build\res.o \
|
$(CC) $(MINGWOPT) ../mongoose.c main.c res.o \
|
||||||
-lws2_32 -ladvapi32 -lcomdlg32 -o $(PROG).exe
|
-lws2_32 -ladvapi32 -lcomdlg32 -o $(PROG).exe
|
||||||
|
|
||||||
# Build for Windows under Cygwin
|
# Build for Windows under Cygwin
|
||||||
@@ -139,23 +195,24 @@ mingw:
|
|||||||
CYGWINDBG= -DNDEBUG -Os
|
CYGWINDBG= -DNDEBUG -Os
|
||||||
CYGWINOPT= -W -Wall -mthreads -Wl,--subsystem,console $(CYGWINDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
|
CYGWINOPT= -W -Wall -mthreads -Wl,--subsystem,console $(CYGWINDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
|
||||||
cygwin:
|
cygwin:
|
||||||
windres ./build/res.rc ./build/res.o
|
windres res.rc res.o
|
||||||
$(CC) $(CYGWINOPT) mongoose.c -lws2_32 \
|
$(CC) $(CYGWINOPT) ../mongoose.c -lws2_32 \
|
||||||
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
|
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
|
||||||
$(CC) $(CYGWINOPT) -Ibuild mongoose.c main.c ./build/res.o \
|
$(CC) $(CYGWINOPT) -I.. ../mongoose.c main.c res.o \
|
||||||
-lws2_32 -ladvapi32 -o $(PROG).exe
|
-lws2_32 -ladvapi32 -o $(PROG).exe
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
perl test/test.pl $(TEST)
|
perl ../test/test.pl $(TEST)
|
||||||
|
|
||||||
tarball: clean
|
tarball: clean
|
||||||
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`.tgz ; cd .. && tar -czf x mongoose/{LICENSE,Makefile,examples,test,build,*.[ch],*.md} && mv x mongoose/$$F
|
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' ../mongoose.c`.tgz ; cd ../.. && tar -czf x mongoose/{LICENSE,*.[ch]} && mv x mongoose/build/$$F
|
||||||
|
|
||||||
release: tarball cocoa
|
release: tarball cocoa
|
||||||
wine make windows
|
wine make windows windows_tiny
|
||||||
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`; upx mongoose.exe; cp mongoose.exe mongoose-$$V.exe; cp mongoose.exe mongoose_php_bundle/; zip -r mongoose_php_bundle_$$V.zip mongoose_php_bundle/
|
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' ../mongoose.c`; upx mongoose.exe; upx mongoose-tiny.exe ; cp mongoose.exe mongoose-$$V.exe; cp mongoose-tiny.exe mongoose-tiny-$$V.exe; cp mongoose.exe mongoose_php_bundle/; zip -r mongoose-php-$$V.zip mongoose_php_bundle/
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
cd examples && $(MAKE) clean
|
cd ../examples && $(MAKE) clean
|
||||||
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz \
|
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz \
|
||||||
$(PROG).exe *.dll *.lib build/res.o build/res.RES *.dSYM
|
$(PROG).exe *.dll *.lib res.o res.RES *.dSYM *.zip *.pdb \
|
||||||
|
*.exe *dmg* $(ALL_OBJECTS) $(ALL_WINOBJS)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
LOCAL_PATH := $(call my-dir)/../..
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_CFLAGS := -std=c99 -O2 -W -Wall -pthread -pipe $(COPT)
|
||||||
|
LOCAL_MODULE := mongoose
|
||||||
|
LOCAL_SRC_FILES := main.c mongoose.c
|
||||||
|
include $(BUILD_EXECUTABLE)
|
||||||
+1
-3
@@ -31,9 +31,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define LUA_LIB
|
#define LUA_LIB
|
||||||
#include "lua.h"
|
#include "lua_5.2.1.h"
|
||||||
#include "lauxlib.h"
|
|
||||||
|
|
||||||
#include "sqlite3.h"
|
#include "sqlite3.h"
|
||||||
|
|
||||||
/* compile time features */
|
/* compile time features */
|
||||||
|
|||||||
+17698
File diff suppressed because it is too large.
Load diff
+1247
File diff suppressed because it is too large.
Load diff
+141
-57
@@ -41,21 +41,30 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winsvc.h>
|
#include <winsvc.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
#define PATH_MAX MAX_PATH
|
#define PATH_MAX MAX_PATH
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef S_ISDIR
|
||||||
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define DIRSEP '\\'
|
#define DIRSEP '\\'
|
||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
#define vsnprintf _vsnprintf
|
#define vsnprintf _vsnprintf
|
||||||
#define sleep(x) Sleep((x) * 1000)
|
#define sleep(x) Sleep((x) * 1000)
|
||||||
#define WINCDECL __cdecl
|
#define WINCDECL __cdecl
|
||||||
|
#define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size))
|
||||||
#else
|
#else
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#define DIRSEP '/'
|
#define DIRSEP '/'
|
||||||
#define WINCDECL
|
#define WINCDECL
|
||||||
|
#define abs_path(rel, abs, abs_size) realpath((rel), (abs))
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
#define MAX_OPTIONS 40
|
#define MAX_OPTIONS 100
|
||||||
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
|
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
|
||||||
|
|
||||||
static int exit_flag;
|
static int exit_flag;
|
||||||
@@ -68,7 +77,15 @@ static struct mg_context *ctx; // Set by start_mongoose()
|
|||||||
#endif /* !CONFIG_FILE */
|
#endif /* !CONFIG_FILE */
|
||||||
|
|
||||||
static void WINCDECL signal_handler(int sig_num) {
|
static void WINCDECL signal_handler(int sig_num) {
|
||||||
exit_flag = sig_num;
|
#if !defined(_WIN32)
|
||||||
|
// Do not do the trick with ignoring SIGCHLD, cause not all OSes (e.g. QNX)
|
||||||
|
// reap zombies if SIGCHLD is ignored. On QNX, for example, waitpid()
|
||||||
|
// fails if SIGCHLD is ignored, making system() non-functional.
|
||||||
|
if (sig_num == SIGCHLD) {
|
||||||
|
do {} while (waitpid(-1, &sig_num, WNOHANG) > 0);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{ exit_flag = sig_num; }
|
||||||
}
|
}
|
||||||
|
|
||||||
static void die(const char *fmt, ...) {
|
static void die(const char *fmt, ...) {
|
||||||
@@ -92,18 +109,18 @@ static void show_usage_and_exit(void) {
|
|||||||
const char **names;
|
const char **names;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built %s\n",
|
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
|
||||||
mg_version(), __DATE__);
|
mg_version(), __DATE__);
|
||||||
fprintf(stderr, "Usage:\n");
|
fprintf(stderr, "Usage:\n");
|
||||||
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
|
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
|
||||||
fprintf(stderr, " mongoose <config_file>\n");
|
fprintf(stderr, " mongoose [config_file]\n");
|
||||||
fprintf(stderr, " mongoose [-option value ...]\n");
|
fprintf(stderr, " mongoose [-option value ...]\n");
|
||||||
fprintf(stderr, "\nOPTIONS:\n");
|
fprintf(stderr, "\nOPTIONS:\n");
|
||||||
|
|
||||||
names = mg_get_valid_option_names();
|
names = mg_get_valid_option_names();
|
||||||
for (i = 0; names[i] != NULL; i += 3) {
|
for (i = 0; names[i] != NULL; i += 2) {
|
||||||
fprintf(stderr, " -%s %s (default: \"%s\")\n",
|
fprintf(stderr, " -%s %s\n",
|
||||||
names[i], names[i + 1], names[i + 2] == NULL ? "" : names[i + 2]);
|
names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]);
|
||||||
}
|
}
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@@ -146,32 +163,15 @@ static void create_config_file(const char *path) {
|
|||||||
} else if ((fp = fopen(path, "a+")) != NULL) {
|
} else if ((fp = fopen(path, "a+")) != NULL) {
|
||||||
fprintf(fp, "%s", config_file_top_comment);
|
fprintf(fp, "%s", config_file_top_comment);
|
||||||
names = mg_get_valid_option_names();
|
names = mg_get_valid_option_names();
|
||||||
for (i = 0; names[i] != NULL; i += 3) {
|
for (i = 0; names[i * 2] != NULL; i++) {
|
||||||
value = mg_get_option(ctx, names[i]);
|
value = mg_get_option(ctx, names[i * 2]);
|
||||||
fprintf(fp, "# %s %s\n", names[i + 1], *value ? value : "<value>");
|
fprintf(fp, "# %s %s\n", names[i * 2], value ? value : "<value>");
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void verify_document_root(const char *root) {
|
|
||||||
const char *p, *path;
|
|
||||||
char buf[PATH_MAX];
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
path = root;
|
|
||||||
if ((p = strchr(root, ',')) != NULL && (size_t) (p - root) < sizeof(buf)) {
|
|
||||||
memcpy(buf, root, p - root);
|
|
||||||
buf[p - root] = '\0';
|
|
||||||
path = buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode)) {
|
|
||||||
die("Invalid root directory: [%s]: %s", root, strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *sdup(const char *str) {
|
static char *sdup(const char *str) {
|
||||||
char *p;
|
char *p;
|
||||||
if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
|
if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
|
||||||
@@ -183,16 +183,16 @@ static char *sdup(const char *str) {
|
|||||||
static void set_option(char **options, const char *name, const char *value) {
|
static void set_option(char **options, const char *name, const char *value) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!strcmp(name, "document_root") || !(strcmp(name, "r"))) {
|
|
||||||
verify_document_root(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_OPTIONS - 3; i++) {
|
for (i = 0; i < MAX_OPTIONS - 3; i++) {
|
||||||
if (options[i] == NULL) {
|
if (options[i] == NULL) {
|
||||||
options[i] = sdup(name);
|
options[i] = sdup(name);
|
||||||
options[i + 1] = sdup(value);
|
options[i + 1] = sdup(value);
|
||||||
options[i + 2] = NULL;
|
options[i + 2] = NULL;
|
||||||
break;
|
break;
|
||||||
|
} else if (!strcmp(options[i], name)) {
|
||||||
|
free(options[i + 1]);
|
||||||
|
options[i + 1] = sdup(value);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,8 +206,6 @@ static void process_command_line_arguments(char *argv[], char **options) {
|
|||||||
FILE *fp = NULL;
|
FILE *fp = NULL;
|
||||||
size_t i, cmd_line_opts_start = 1, line_no = 0;
|
size_t i, cmd_line_opts_start = 1, line_no = 0;
|
||||||
|
|
||||||
options[0] = NULL;
|
|
||||||
|
|
||||||
// Should we use a config file ?
|
// Should we use a config file ?
|
||||||
if (argv[1] != NULL && argv[1][0] != '-') {
|
if (argv[1] != NULL && argv[1][0] != '-') {
|
||||||
snprintf(config_file, sizeof(config_file), "%s", argv[1]);
|
snprintf(config_file, sizeof(config_file), "%s", argv[1]);
|
||||||
@@ -278,6 +276,69 @@ static int log_message(const struct mg_connection *conn, const char *message) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int is_path_absolute(const char *path) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return path != NULL &&
|
||||||
|
((path[0] == '\\' && path[1] == '\\') || // UNC path, e.g. \\server\dir
|
||||||
|
(isalpha(path[0]) && path[1] == ':' && path[2] == '\\')); // E.g. X:\dir
|
||||||
|
#else
|
||||||
|
return path != NULL && path[0] == '/';
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *get_option(char **options, const char *option_name) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; options[i] != NULL; i++)
|
||||||
|
if (!strcmp(options[i], option_name))
|
||||||
|
return options[i + 1];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void verify_existence(char **options, const char *option_name,
|
||||||
|
int must_be_dir) {
|
||||||
|
struct stat st;
|
||||||
|
const char *path = get_option(options, option_name);
|
||||||
|
|
||||||
|
if (path != NULL && (stat(path, &st) != 0 ||
|
||||||
|
((S_ISDIR(st.st_mode) ? 1 : 0) != must_be_dir))) {
|
||||||
|
die("Invalid path for %s: [%s]: (%s). Make sure that path is either "
|
||||||
|
"absolute, or it is relative to mongoose executable.",
|
||||||
|
option_name, path, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_absolute_path(char *options[], const char *option_name,
|
||||||
|
const char *path_to_mongoose_exe) {
|
||||||
|
char path[PATH_MAX], abs[PATH_MAX], *option_value;
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
// Check whether option is already set
|
||||||
|
option_value = get_option(options, option_name);
|
||||||
|
|
||||||
|
// If option is already set and it is an absolute path,
|
||||||
|
// leave it as it is -- it's already absolute.
|
||||||
|
if (option_value != NULL && !is_path_absolute(option_value)) {
|
||||||
|
// Not absolute. Use the directory where mongoose executable lives
|
||||||
|
// be the relative directory for everything.
|
||||||
|
// Extract mongoose executable directory into path.
|
||||||
|
if ((p = strrchr(path_to_mongoose_exe, DIRSEP)) == NULL) {
|
||||||
|
getcwd(path, sizeof(path));
|
||||||
|
} else {
|
||||||
|
snprintf(path, sizeof(path), "%.*s", (int) (p - path_to_mongoose_exe),
|
||||||
|
path_to_mongoose_exe);
|
||||||
|
}
|
||||||
|
|
||||||
|
strncat(path, "/", sizeof(path) - 1);
|
||||||
|
strncat(path, option_value, sizeof(path) - 1);
|
||||||
|
|
||||||
|
// Absolutize the path, and set the option
|
||||||
|
abs_path(path, abs, sizeof(abs));
|
||||||
|
set_option(options, option_name, abs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void start_mongoose(int argc, char *argv[]) {
|
static void start_mongoose(int argc, char *argv[]) {
|
||||||
struct mg_callbacks callbacks;
|
struct mg_callbacks callbacks;
|
||||||
char *options[MAX_OPTIONS];
|
char *options[MAX_OPTIONS];
|
||||||
@@ -297,14 +358,32 @@ static void start_mongoose(int argc, char *argv[]) {
|
|||||||
show_usage_and_exit();
|
show_usage_and_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update config based on command line arguments */
|
options[0] = NULL;
|
||||||
|
set_option(options, "document_root", ".");
|
||||||
|
|
||||||
|
// Update config based on command line arguments
|
||||||
process_command_line_arguments(argv, options);
|
process_command_line_arguments(argv, options);
|
||||||
|
|
||||||
/* Setup signal handler: quit on Ctrl-C */
|
// Make sure we have absolute paths for files and directories
|
||||||
|
// https://github.com/valenok/mongoose/issues/181
|
||||||
|
set_absolute_path(options, "document_root", argv[0]);
|
||||||
|
set_absolute_path(options, "put_delete_auth_file", argv[0]);
|
||||||
|
set_absolute_path(options, "cgi_interpreter", argv[0]);
|
||||||
|
set_absolute_path(options, "access_log_file", argv[0]);
|
||||||
|
set_absolute_path(options, "error_log_file", argv[0]);
|
||||||
|
set_absolute_path(options, "global_auth_file", argv[0]);
|
||||||
|
set_absolute_path(options, "ssl_certificate", argv[0]);
|
||||||
|
|
||||||
|
// Make extra verification for certain options
|
||||||
|
verify_existence(options, "document_root", 1);
|
||||||
|
verify_existence(options, "cgi_interpreter", 0);
|
||||||
|
verify_existence(options, "ssl_certificate", 0);
|
||||||
|
|
||||||
|
// Setup signal handler: quit on Ctrl-C
|
||||||
signal(SIGTERM, signal_handler);
|
signal(SIGTERM, signal_handler);
|
||||||
signal(SIGINT, signal_handler);
|
signal(SIGINT, signal_handler);
|
||||||
|
|
||||||
/* Start Mongoose */
|
// Start Mongoose
|
||||||
memset(&callbacks, 0, sizeof(callbacks));
|
memset(&callbacks, 0, sizeof(callbacks));
|
||||||
callbacks.log_message = &log_message;
|
callbacks.log_message = &log_message;
|
||||||
ctx = mg_start(&callbacks, NULL, (const char **) options);
|
ctx = mg_start(&callbacks, NULL, (const char **) options);
|
||||||
@@ -410,16 +489,16 @@ static void save_config(HWND hDlg, FILE *fp) {
|
|||||||
|
|
||||||
fprintf(fp, "%s", config_file_top_comment);
|
fprintf(fp, "%s", config_file_top_comment);
|
||||||
options = mg_get_valid_option_names();
|
options = mg_get_valid_option_names();
|
||||||
for (i = 0; options[i] != NULL; i += 3) {
|
for (i = 0; options[i * 2] != NULL; i++) {
|
||||||
name = options[i + 1];
|
name = options[i * 2];
|
||||||
id = ID_CONTROLS + i / 3;
|
id = ID_CONTROLS + i;
|
||||||
if (is_boolean_option(name)) {
|
if (is_boolean_option(name)) {
|
||||||
snprintf(value, sizeof(value), "%s",
|
snprintf(value, sizeof(value), "%s",
|
||||||
IsDlgButtonChecked(hDlg, id) ? "yes" : "no");
|
IsDlgButtonChecked(hDlg, id) ? "yes" : "no");
|
||||||
} else {
|
} else {
|
||||||
GetDlgItemText(hDlg, id, value, sizeof(value));
|
GetDlgItemText(hDlg, id, value, sizeof(value));
|
||||||
}
|
}
|
||||||
default_value = options[i + 2] == NULL ? "" : options[i + 2];
|
default_value = options[i * 2 + 1] == NULL ? "" : options[i * 2 + 1];
|
||||||
// If value is the same as default, skip it
|
// If value is the same as default, skip it
|
||||||
if (strcmp(value, default_value) != 0) {
|
if (strcmp(value, default_value) != 0) {
|
||||||
fprintf(fp, "%s %s\n", name, value);
|
fprintf(fp, "%s %s\n", name, value);
|
||||||
@@ -450,23 +529,23 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) {
|
|||||||
EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
|
EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
|
||||||
break;
|
break;
|
||||||
case ID_RESET_DEFAULTS:
|
case ID_RESET_DEFAULTS:
|
||||||
for (i = 0; options[i] != NULL; i += 3) {
|
for (i = 0; options[i * 2] != NULL; i++) {
|
||||||
name = options[i + 1];
|
name = options[i * 2];
|
||||||
value = options[i + 2] == NULL ? "" : options[i + 2];
|
value = options[i * 2 + 1] == NULL ? "" : options[i * 2 + 1];
|
||||||
if (is_boolean_option(name)) {
|
if (is_boolean_option(name)) {
|
||||||
CheckDlgButton(hDlg, ID_CONTROLS + i / 3, !strcmp(value, "yes") ?
|
CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ?
|
||||||
BST_CHECKED : BST_UNCHECKED);
|
BST_CHECKED : BST_UNCHECKED);
|
||||||
} else {
|
} else {
|
||||||
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i / 3), value);
|
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; options[i] != NULL; i += 3) {
|
for (i = 0; options[i * 2] != NULL; i++) {
|
||||||
name = options[i + 1];
|
name = options[i * 2];
|
||||||
if ((is_filename_option(name) || is_directory_option(name)) &&
|
if ((is_filename_option(name) || is_directory_option(name)) &&
|
||||||
LOWORD(wParam) == ID_CONTROLS + i / 3 + ID_FILE_BUTTONS_DELTA) {
|
LOWORD(wParam) == ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA) {
|
||||||
OPENFILENAME of;
|
OPENFILENAME of;
|
||||||
BROWSEINFO bi;
|
BROWSEINFO bi;
|
||||||
char path[PATH_MAX] = "";
|
char path[PATH_MAX] = "";
|
||||||
@@ -491,7 +570,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (path[0] != '\0') {
|
if (path[0] != '\0') {
|
||||||
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i / 3), path);
|
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i), path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -503,14 +582,14 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) {
|
|||||||
SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_BIG, (LPARAM) hIcon);
|
SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_BIG, (LPARAM) hIcon);
|
||||||
SetWindowText(hDlg, "Mongoose settings");
|
SetWindowText(hDlg, "Mongoose settings");
|
||||||
SetFocus(GetDlgItem(hDlg, ID_SAVE));
|
SetFocus(GetDlgItem(hDlg, ID_SAVE));
|
||||||
for (i = 0; options[i] != NULL; i += 3) {
|
for (i = 0; options[i * 2] != NULL; i++) {
|
||||||
name = options[i + 1];
|
name = options[i * 2];
|
||||||
value = mg_get_option(ctx, name);
|
value = mg_get_option(ctx, name);
|
||||||
if (is_boolean_option(name)) {
|
if (is_boolean_option(name)) {
|
||||||
CheckDlgButton(hDlg, ID_CONTROLS + i / 3, !strcmp(value, "yes") ?
|
CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ?
|
||||||
BST_CHECKED : BST_UNCHECKED);
|
BST_CHECKED : BST_UNCHECKED);
|
||||||
} else {
|
} else {
|
||||||
SetDlgItemText(hDlg, ID_CONTROLS + i / 3, value == NULL ? "" : value);
|
SetDlgItemText(hDlg, ID_CONTROLS + i, value == NULL ? "" : value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -587,8 +666,8 @@ static void show_settings_dialog() {
|
|||||||
p = mem + sizeof(dialog_header);
|
p = mem + sizeof(dialog_header);
|
||||||
|
|
||||||
option_names = mg_get_valid_option_names();
|
option_names = mg_get_valid_option_names();
|
||||||
for (i = 0; option_names[i] != NULL; i += 3) {
|
for (i = 0; option_names[i * 2] != NULL; i++) {
|
||||||
long_option_name = option_names[i + 1];
|
long_option_name = option_names[i * 2];
|
||||||
style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
|
style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
|
||||||
x = 10 + (WIDTH / 2) * (nelems % 2);
|
x = 10 + (WIDTH / 2) * (nelems % 2);
|
||||||
y = (nelems/2 + 1) * HEIGHT + 5;
|
y = (nelems/2 + 1) * HEIGHT + 5;
|
||||||
@@ -606,7 +685,7 @@ static void show_settings_dialog() {
|
|||||||
width -= 20;
|
width -= 20;
|
||||||
cl = 0x81;
|
cl = 0x81;
|
||||||
add_control(&p, dia, 0x80,
|
add_control(&p, dia, 0x80,
|
||||||
ID_CONTROLS + (i / 3) + ID_FILE_BUTTONS_DELTA,
|
ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA,
|
||||||
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
|
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
|
||||||
(WORD) (x + width + LABEL_WIDTH + 5),
|
(WORD) (x + width + LABEL_WIDTH + 5),
|
||||||
y, 15, 12, "...");
|
y, 15, 12, "...");
|
||||||
@@ -616,7 +695,7 @@ static void show_settings_dialog() {
|
|||||||
}
|
}
|
||||||
add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
|
add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
|
||||||
x, y, LABEL_WIDTH, HEIGHT, long_option_name);
|
x, y, LABEL_WIDTH, HEIGHT, long_option_name);
|
||||||
add_control(&p, dia, cl, ID_CONTROLS + (i / 3), style,
|
add_control(&p, dia, cl, ID_CONTROLS + i, style,
|
||||||
(WORD) (x + LABEL_WIDTH), y, width, 12, "");
|
(WORD) (x + LABEL_WIDTH), y, width, 12, "");
|
||||||
nelems++;
|
nelems++;
|
||||||
}
|
}
|
||||||
@@ -690,6 +769,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
|||||||
char buf[200], *service_argv[] = {__argv[0], NULL};
|
char buf[200], *service_argv[] = {__argv[0], NULL};
|
||||||
POINT pt;
|
POINT pt;
|
||||||
HMENU hMenu;
|
HMENU hMenu;
|
||||||
|
static UINT s_uTaskbarRestart; // for taskbar creation
|
||||||
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case WM_CREATE:
|
case WM_CREATE:
|
||||||
@@ -700,6 +780,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
|||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
} else {
|
} else {
|
||||||
start_mongoose(__argc, __argv);
|
start_mongoose(__argc, __argv);
|
||||||
|
s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case WM_COMMAND:
|
case WM_COMMAND:
|
||||||
@@ -757,6 +838,9 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
|||||||
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
|
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
return 0; // We've just sent our own quit message, with proper hwnd.
|
return 0; // We've just sent our own quit message, with proper hwnd.
|
||||||
|
default:
|
||||||
|
if (msg==s_uTaskbarRestart)
|
||||||
|
Shell_NotifyIcon(NIM_ADD, &TrayIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefWindowProc(hWnd, msg, wParam, lParam);
|
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||||
+393
@@ -0,0 +1,393 @@
|
|||||||
|
#include "lua_5.2.1.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
|
||||||
|
int offset) {
|
||||||
|
HANDLE fh = (HANDLE) _get_osfhandle(fd);
|
||||||
|
HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
|
||||||
|
void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
|
||||||
|
CloseHandle(mh);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#define munmap(x, y) UnmapViewOfFile(x)
|
||||||
|
#define MAP_FAILED NULL
|
||||||
|
#define MAP_PRIVATE 0
|
||||||
|
#define PROT_READ 0
|
||||||
|
#else
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *LUASOCKET = "luasocket";
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
static void handle_request(struct mg_connection *);
|
||||||
|
static int handle_lsp_request(struct mg_connection *, const char *,
|
||||||
|
struct file *, struct lua_State *);
|
||||||
|
|
||||||
|
static void reg_string(struct lua_State *L, const char *name, const char *val) {
|
||||||
|
lua_pushstring(L, name);
|
||||||
|
lua_pushstring(L, val);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reg_int(struct lua_State *L, const char *name, int val) {
|
||||||
|
lua_pushstring(L, name);
|
||||||
|
lua_pushinteger(L, val);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reg_function(struct lua_State *L, const char *name,
|
||||||
|
lua_CFunction func, struct mg_connection *conn) {
|
||||||
|
lua_pushstring(L, name);
|
||||||
|
lua_pushlightuserdata(L, conn);
|
||||||
|
lua_pushcclosure(L, func, 1);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lsp_sock_close(lua_State *L) {
|
||||||
|
if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
|
||||||
|
lua_getfield(L, -1, "sock");
|
||||||
|
closesocket((SOCKET) lua_tonumber(L, -1));
|
||||||
|
} else {
|
||||||
|
return luaL_error(L, "invalid :close() call");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lsp_sock_recv(lua_State *L) {
|
||||||
|
char buf[2000];
|
||||||
|
int n;
|
||||||
|
|
||||||
|
if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
|
||||||
|
lua_getfield(L, -1, "sock");
|
||||||
|
n = recv((SOCKET) lua_tonumber(L, -1), buf, sizeof(buf), 0);
|
||||||
|
if (n <= 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
} else {
|
||||||
|
lua_pushlstring(L, buf, n);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return luaL_error(L, "invalid :close() call");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lsp_sock_send(lua_State *L) {
|
||||||
|
const char *buf;
|
||||||
|
size_t len, sent = 0;
|
||||||
|
int n, sock;
|
||||||
|
|
||||||
|
if (lua_gettop(L) > 1 && lua_istable(L, -2) && lua_isstring(L, -1)) {
|
||||||
|
buf = lua_tolstring(L, -1, &len);
|
||||||
|
lua_getfield(L, -2, "sock");
|
||||||
|
sock = (int) lua_tonumber(L, -1);
|
||||||
|
while (sent < len) {
|
||||||
|
if ((n = send(sock, buf + sent, len - sent, 0)) <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sent += n;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, n);
|
||||||
|
} else {
|
||||||
|
return luaL_error(L, "invalid :close() call");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct luaL_Reg luasocket_methods[] = {
|
||||||
|
{"close", lsp_sock_close},
|
||||||
|
{"send", lsp_sock_send},
|
||||||
|
{"recv", lsp_sock_recv},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int lsp_connect(lua_State *L) {
|
||||||
|
char ebuf[100];
|
||||||
|
SOCKET sock;
|
||||||
|
|
||||||
|
if (lua_isstring(L, -3) && lua_isnumber(L, -2) && lua_isnumber(L, -1)) {
|
||||||
|
sock = conn2(lua_tostring(L, -3), (int) lua_tonumber(L, -2),
|
||||||
|
(int) lua_tonumber(L, -1), ebuf, sizeof(ebuf));
|
||||||
|
if (sock == INVALID_SOCKET) {
|
||||||
|
return luaL_error(L, ebuf);
|
||||||
|
} else {
|
||||||
|
lua_newtable(L);
|
||||||
|
reg_int(L, "sock", sock);
|
||||||
|
reg_string(L, "host", lua_tostring(L, -4));
|
||||||
|
luaL_getmetatable(L, LUASOCKET);
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return luaL_error(L, "connect(host,port,is_ssl): invalid parameter given.");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lsp_error(lua_State *L) {
|
||||||
|
lua_getglobal(L, "mg");
|
||||||
|
lua_getfield(L, -1, "onerror");
|
||||||
|
lua_pushvalue(L, -3);
|
||||||
|
lua_pcall(L, 1, 0, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Silently stop processing chunks.
|
||||||
|
static void lsp_abort(lua_State *L) {
|
||||||
|
int top = lua_gettop(L);
|
||||||
|
lua_getglobal(L, "mg");
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_setfield(L, -2, "onerror");
|
||||||
|
lua_settop(L, top);
|
||||||
|
lua_pushstring(L, "aborting");
|
||||||
|
lua_error(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lsp(struct mg_connection *conn, const char *path,
|
||||||
|
const char *p, int64_t len, lua_State *L) {
|
||||||
|
int i, j, pos = 0, lines = 1, lualines = 0;
|
||||||
|
char chunkname[MG_BUF_LEN];
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (p[i] == '\n') lines++;
|
||||||
|
if (p[i] == '<' && p[i + 1] == '?') {
|
||||||
|
for (j = i + 1; j < len ; j++) {
|
||||||
|
if (p[j] == '\n') lualines++;
|
||||||
|
if (p[j] == '?' && p[j + 1] == '>') {
|
||||||
|
mg_write(conn, p + pos, i - pos);
|
||||||
|
|
||||||
|
snprintf(chunkname, sizeof(chunkname), "@%s+%i", path, lines);
|
||||||
|
lua_pushlightuserdata(L, conn);
|
||||||
|
lua_pushcclosure(L, lsp_error, 1);
|
||||||
|
if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), chunkname)) {
|
||||||
|
// Syntax error or OOM. Error message is pushed on stack.
|
||||||
|
lua_pcall(L, 1, 0, 0);
|
||||||
|
} else {
|
||||||
|
// Success loading chunk. Call it.
|
||||||
|
lua_pcall(L, 0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = j + 2;
|
||||||
|
i = pos - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lualines > 0) {
|
||||||
|
lines += lualines;
|
||||||
|
lualines = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > pos) {
|
||||||
|
mg_write(conn, p + pos, i - pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lsp_write(lua_State *L) {
|
||||||
|
int i, num_args;
|
||||||
|
const char *str;
|
||||||
|
size_t size;
|
||||||
|
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||||
|
|
||||||
|
num_args = lua_gettop(L);
|
||||||
|
for (i = 1; i <= num_args; i++) {
|
||||||
|
if (lua_isstring(L, i)) {
|
||||||
|
str = lua_tolstring(L, i, &size);
|
||||||
|
mg_write(conn, str, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lsp_read(lua_State *L) {
|
||||||
|
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||||
|
char buf[1024];
|
||||||
|
int len = mg_read(conn, buf, sizeof(buf));
|
||||||
|
|
||||||
|
if (len <= 0) return 0;
|
||||||
|
lua_pushlstring(L, buf, len);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mg.include: Include another .lp file
|
||||||
|
static int lsp_include(lua_State *L) {
|
||||||
|
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||||
|
struct file file = STRUCT_FILE_INITIALIZER;
|
||||||
|
if (handle_lsp_request(conn, lua_tostring(L, -1), &file, L)) {
|
||||||
|
// handle_lsp_request returned an error code, meaning an error occured in
|
||||||
|
// the included page and mg.onerror returned non-zero. Stop processing.
|
||||||
|
lsp_abort(L);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mg.cry: Log an error. Default value for mg.onerror.
|
||||||
|
static int lsp_cry(lua_State *L){
|
||||||
|
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||||
|
cry(conn, "%s", lua_tostring(L, -1));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mg.redirect: Redirect the request (internally).
|
||||||
|
static int lsp_redirect(lua_State *L) {
|
||||||
|
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||||
|
conn->request_info.uri = lua_tostring(L, -1);
|
||||||
|
handle_request(conn);
|
||||||
|
lsp_abort(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prepare_lua_environment(struct mg_connection *conn, lua_State *L) {
|
||||||
|
const struct mg_request_info *ri = mg_get_request_info(conn);
|
||||||
|
extern void luaL_openlibs(lua_State *);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
luaL_openlibs(L);
|
||||||
|
#ifdef USE_LUA_SQLITE3
|
||||||
|
{ extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
luaL_newmetatable(L, LUASOCKET);
|
||||||
|
lua_pushliteral(L, "__index");
|
||||||
|
luaL_newlib(L, luasocket_methods);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
lua_register(L, "connect", lsp_connect);
|
||||||
|
|
||||||
|
if (conn == NULL) return;
|
||||||
|
|
||||||
|
// Register mg module
|
||||||
|
lua_newtable(L);
|
||||||
|
|
||||||
|
reg_function(L, "read", lsp_read, conn);
|
||||||
|
reg_function(L, "write", lsp_write, conn);
|
||||||
|
reg_function(L, "cry", lsp_cry, conn);
|
||||||
|
reg_function(L, "include", lsp_include, conn);
|
||||||
|
reg_function(L, "redirect", lsp_redirect, conn);
|
||||||
|
reg_string(L, "version", MONGOOSE_VERSION);
|
||||||
|
|
||||||
|
// Export request_info
|
||||||
|
lua_pushstring(L, "request_info");
|
||||||
|
lua_newtable(L);
|
||||||
|
reg_string(L, "request_method", ri->request_method);
|
||||||
|
reg_string(L, "uri", ri->uri);
|
||||||
|
reg_string(L, "http_version", ri->http_version);
|
||||||
|
reg_string(L, "query_string", ri->query_string);
|
||||||
|
reg_int(L, "remote_ip", ri->remote_ip);
|
||||||
|
reg_int(L, "remote_port", ri->remote_port);
|
||||||
|
reg_int(L, "num_headers", ri->num_headers);
|
||||||
|
lua_pushstring(L, "http_headers");
|
||||||
|
lua_newtable(L);
|
||||||
|
for (i = 0; i < ri->num_headers; i++) {
|
||||||
|
reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
|
||||||
|
}
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
|
||||||
|
lua_setglobal(L, "mg");
|
||||||
|
|
||||||
|
// Register default mg.onerror function
|
||||||
|
luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua error:\\n', "
|
||||||
|
"debug.traceback(e, 1)) end");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lua_error_handler(lua_State *L) {
|
||||||
|
const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
|
||||||
|
|
||||||
|
lua_getglobal(L, "mg");
|
||||||
|
if (!lua_isnil(L, -1)) {
|
||||||
|
lua_getfield(L, -1, "write"); // call mg.write()
|
||||||
|
lua_pushstring(L, error_msg);
|
||||||
|
lua_pushliteral(L, "\n");
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
luaL_dostring(L, "mg.write(debug.traceback(), '\\n')");
|
||||||
|
} else {
|
||||||
|
printf("Lua error: [%s]\n", error_msg);
|
||||||
|
luaL_dostring(L, "print(debug.traceback(), '\\n')");
|
||||||
|
}
|
||||||
|
// TODO(lsm): leave the stack balanced
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mg_exec_lua_script(struct mg_connection *conn, const char *path,
|
||||||
|
const void **exports) {
|
||||||
|
int i;
|
||||||
|
lua_State *L;
|
||||||
|
|
||||||
|
if (path != NULL && (L = luaL_newstate()) != NULL) {
|
||||||
|
prepare_lua_environment(conn, L);
|
||||||
|
lua_pushcclosure(L, &lua_error_handler, 0);
|
||||||
|
|
||||||
|
lua_pushglobaltable(L);
|
||||||
|
if (exports != NULL) {
|
||||||
|
for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
|
||||||
|
lua_pushstring(L, exports[i]);
|
||||||
|
lua_pushcclosure(L, (lua_CFunction) exports[i + 1], 0);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (luaL_loadfile(L, path) != 0) {
|
||||||
|
lua_error_handler(L);
|
||||||
|
}
|
||||||
|
lua_pcall(L, 0, 0, -2);
|
||||||
|
lua_close(L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lsp_send_err(struct mg_connection *conn, struct lua_State *L,
|
||||||
|
const char *fmt, ...) {
|
||||||
|
char buf[MG_BUF_LEN];
|
||||||
|
va_list ap;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (L == NULL) {
|
||||||
|
send_http_error(conn, 500, http_500_error, "%s", buf);
|
||||||
|
} else {
|
||||||
|
lua_pushstring(L, buf);
|
||||||
|
lua_error(L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_lsp_request(struct mg_connection *conn, const char *path,
|
||||||
|
struct file *filep, struct lua_State *ls) {
|
||||||
|
void *p = NULL;
|
||||||
|
lua_State *L = NULL;
|
||||||
|
int error = 1;
|
||||||
|
|
||||||
|
// We need both mg_stat to get file size, and mg_fopen to get fd
|
||||||
|
if (!mg_stat(conn, path, filep) || !mg_fopen(conn, path, "r", filep)) {
|
||||||
|
lsp_send_err(conn, ls, "File [%s] not found", path);
|
||||||
|
} else if (filep->membuf == NULL &&
|
||||||
|
(p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
|
||||||
|
fileno(filep->fp), 0)) == MAP_FAILED) {
|
||||||
|
lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
|
||||||
|
fileno(filep->fp), strerror(errno));
|
||||||
|
} else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) {
|
||||||
|
send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed");
|
||||||
|
} else {
|
||||||
|
// We're not sending HTTP headers here, Lua page must do it.
|
||||||
|
if (ls == NULL) {
|
||||||
|
prepare_lua_environment(conn, L);
|
||||||
|
if (conn->ctx->callbacks.init_lua != NULL) {
|
||||||
|
conn->ctx->callbacks.init_lua(conn, L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error = lsp(conn, path, filep->membuf == NULL ? p : filep->membuf,
|
||||||
|
filep->size, L);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (L != NULL && ls == NULL) lua_close(L);
|
||||||
|
if (p != NULL) munmap(p, filep->size);
|
||||||
|
mg_fclose(filep);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 669 B |
Binary file not shown.
|
After Width: | Height: | Size: 872 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
+4046
-1004
File diff suppressed because it is too large.
Load diff
+111
-26
@@ -107,9 +107,9 @@ extern "C" {
|
|||||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||||
** [sqlite_version()] and [sqlite_source_id()].
|
** [sqlite_version()] and [sqlite_source_id()].
|
||||||
*/
|
*/
|
||||||
#define SQLITE_VERSION "3.7.15.2"
|
#define SQLITE_VERSION "3.7.17"
|
||||||
#define SQLITE_VERSION_NUMBER 3007015
|
#define SQLITE_VERSION_NUMBER 3007017
|
||||||
#define SQLITE_SOURCE_ID "2013-01-09 11:53:05 c0e09560d26f0a6456be9dd3447f5311eb4f238f"
|
#define SQLITE_SOURCE_ID "2013-05-20 00:56:22 118a3b35693b134d56ebd780123b7fd6f1497668"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Run-Time Library Version Numbers
|
** CAPI3REF: Run-Time Library Version Numbers
|
||||||
@@ -288,7 +288,7 @@ typedef sqlite_uint64 sqlite3_uint64;
|
|||||||
** [sqlite3_blob_close | close] all [BLOB handles], and
|
** [sqlite3_blob_close | close] all [BLOB handles], and
|
||||||
** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
|
** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
|
||||||
** with the [sqlite3] object prior to attempting to close the object. ^If
|
** with the [sqlite3] object prior to attempting to close the object. ^If
|
||||||
** sqlite3_close() is called on a [database connection] that still has
|
** sqlite3_close_v2() is called on a [database connection] that still has
|
||||||
** outstanding [prepared statements], [BLOB handles], and/or
|
** outstanding [prepared statements], [BLOB handles], and/or
|
||||||
** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation
|
** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation
|
||||||
** of resources is deferred until all [prepared statements], [BLOB handles],
|
** of resources is deferred until all [prepared statements], [BLOB handles],
|
||||||
@@ -425,6 +425,8 @@ SQLITE_API int sqlite3_exec(
|
|||||||
#define SQLITE_FORMAT 24 /* Auxiliary database format error */
|
#define SQLITE_FORMAT 24 /* Auxiliary database format error */
|
||||||
#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */
|
#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */
|
||||||
#define SQLITE_NOTADB 26 /* File opened that is not a database file */
|
#define SQLITE_NOTADB 26 /* File opened that is not a database file */
|
||||||
|
#define SQLITE_NOTICE 27 /* Notifications from sqlite3_log() */
|
||||||
|
#define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */
|
||||||
#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
|
#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
|
||||||
#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
|
#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
|
||||||
/* end-of-error-codes */
|
/* end-of-error-codes */
|
||||||
@@ -475,6 +477,7 @@ SQLITE_API int sqlite3_exec(
|
|||||||
#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8))
|
#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8))
|
||||||
#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8))
|
#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8))
|
||||||
#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8))
|
#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8))
|
||||||
|
#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8))
|
||||||
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
||||||
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
||||||
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
|
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
|
||||||
@@ -483,7 +486,19 @@ SQLITE_API int sqlite3_exec(
|
|||||||
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
|
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
|
||||||
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
|
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
|
||||||
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
|
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
|
||||||
|
#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
|
||||||
#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8))
|
#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8))
|
||||||
|
#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8))
|
||||||
|
#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8))
|
||||||
|
#define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3<<8))
|
||||||
|
#define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4<<8))
|
||||||
|
#define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8))
|
||||||
|
#define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8))
|
||||||
|
#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8))
|
||||||
|
#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8))
|
||||||
|
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
|
||||||
|
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
|
||||||
|
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Flags For File Open Operations
|
** CAPI3REF: Flags For File Open Operations
|
||||||
@@ -723,6 +738,9 @@ struct sqlite3_io_methods {
|
|||||||
void (*xShmBarrier)(sqlite3_file*);
|
void (*xShmBarrier)(sqlite3_file*);
|
||||||
int (*xShmUnmap)(sqlite3_file*, int deleteFlag);
|
int (*xShmUnmap)(sqlite3_file*, int deleteFlag);
|
||||||
/* Methods above are valid for version 2 */
|
/* Methods above are valid for version 2 */
|
||||||
|
int (*xFetch)(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
|
||||||
|
int (*xUnfetch)(sqlite3_file*, sqlite3_int64 iOfst, void *p);
|
||||||
|
/* Methods above are valid for version 3 */
|
||||||
/* Additional methods may be added in future releases */
|
/* Additional methods may be added in future releases */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -859,7 +877,8 @@ struct sqlite3_io_methods {
|
|||||||
** it is able to override built-in [PRAGMA] statements.
|
** it is able to override built-in [PRAGMA] statements.
|
||||||
**
|
**
|
||||||
** <li>[[SQLITE_FCNTL_BUSYHANDLER]]
|
** <li>[[SQLITE_FCNTL_BUSYHANDLER]]
|
||||||
** ^This file-control may be invoked by SQLite on the database file handle
|
** ^The [SQLITE_FCNTL_BUSYHANDLER]
|
||||||
|
** file-control may be invoked by SQLite on the database file handle
|
||||||
** shortly after it is opened in order to provide a custom VFS with access
|
** shortly after it is opened in order to provide a custom VFS with access
|
||||||
** to the connections busy-handler callback. The argument is of type (void **)
|
** to the connections busy-handler callback. The argument is of type (void **)
|
||||||
** - an array of two (void *) values. The first (void *) actually points
|
** - an array of two (void *) values. The first (void *) actually points
|
||||||
@@ -870,13 +889,24 @@ struct sqlite3_io_methods {
|
|||||||
** current operation.
|
** current operation.
|
||||||
**
|
**
|
||||||
** <li>[[SQLITE_FCNTL_TEMPFILENAME]]
|
** <li>[[SQLITE_FCNTL_TEMPFILENAME]]
|
||||||
** ^Application can invoke this file-control to have SQLite generate a
|
** ^Application can invoke the [SQLITE_FCNTL_TEMPFILENAME] file-control
|
||||||
|
** to have SQLite generate a
|
||||||
** temporary filename using the same algorithm that is followed to generate
|
** temporary filename using the same algorithm that is followed to generate
|
||||||
** temporary filenames for TEMP tables and other internal uses. The
|
** temporary filenames for TEMP tables and other internal uses. The
|
||||||
** argument should be a char** which will be filled with the filename
|
** argument should be a char** which will be filled with the filename
|
||||||
** written into memory obtained from [sqlite3_malloc()]. The caller should
|
** written into memory obtained from [sqlite3_malloc()]. The caller should
|
||||||
** invoke [sqlite3_free()] on the result to avoid a memory leak.
|
** invoke [sqlite3_free()] on the result to avoid a memory leak.
|
||||||
**
|
**
|
||||||
|
** <li>[[SQLITE_FCNTL_MMAP_SIZE]]
|
||||||
|
** The [SQLITE_FCNTL_MMAP_SIZE] file control is used to query or set the
|
||||||
|
** maximum number of bytes that will be used for memory-mapped I/O.
|
||||||
|
** The argument is a pointer to a value of type sqlite3_int64 that
|
||||||
|
** is an advisory maximum number of bytes in the file to memory map. The
|
||||||
|
** pointer is overwritten with the old value. The limit is not changed if
|
||||||
|
** the value originally pointed to is negative, and so the current limit
|
||||||
|
** can be queried by passing in a pointer to a negative number. This
|
||||||
|
** file-control is used internally to implement [PRAGMA mmap_size].
|
||||||
|
**
|
||||||
** </ul>
|
** </ul>
|
||||||
*/
|
*/
|
||||||
#define SQLITE_FCNTL_LOCKSTATE 1
|
#define SQLITE_FCNTL_LOCKSTATE 1
|
||||||
@@ -895,6 +925,7 @@ struct sqlite3_io_methods {
|
|||||||
#define SQLITE_FCNTL_PRAGMA 14
|
#define SQLITE_FCNTL_PRAGMA 14
|
||||||
#define SQLITE_FCNTL_BUSYHANDLER 15
|
#define SQLITE_FCNTL_BUSYHANDLER 15
|
||||||
#define SQLITE_FCNTL_TEMPFILENAME 16
|
#define SQLITE_FCNTL_TEMPFILENAME 16
|
||||||
|
#define SQLITE_FCNTL_MMAP_SIZE 18
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Mutex Handle
|
** CAPI3REF: Mutex Handle
|
||||||
@@ -1561,7 +1592,9 @@ struct sqlite3_mem_methods {
|
|||||||
** page cache implementation into that object.)^ </dd>
|
** page cache implementation into that object.)^ </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
|
** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
|
||||||
** <dd> ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
|
** <dd> The SQLITE_CONFIG_LOG option is used to configure the SQLite
|
||||||
|
** global [error log].
|
||||||
|
** (^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
|
||||||
** function with a call signature of void(*)(void*,int,const char*),
|
** function with a call signature of void(*)(void*,int,const char*),
|
||||||
** and a pointer to void. ^If the function pointer is not NULL, it is
|
** and a pointer to void. ^If the function pointer is not NULL, it is
|
||||||
** invoked by [sqlite3_log()] to process each logging event. ^If the
|
** invoked by [sqlite3_log()] to process each logging event. ^If the
|
||||||
@@ -1607,12 +1640,12 @@ struct sqlite3_mem_methods {
|
|||||||
** <dt>SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE
|
** <dt>SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE
|
||||||
** <dd> These options are obsolete and should not be used by new code.
|
** <dd> These options are obsolete and should not be used by new code.
|
||||||
** They are retained for backwards compatibility but are now no-ops.
|
** They are retained for backwards compatibility but are now no-ops.
|
||||||
** </dl>
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_SQLLOG]]
|
** [[SQLITE_CONFIG_SQLLOG]]
|
||||||
** <dt>SQLITE_CONFIG_SQLLOG
|
** <dt>SQLITE_CONFIG_SQLLOG
|
||||||
** <dd>This option is only available if sqlite is compiled with the
|
** <dd>This option is only available if sqlite is compiled with the
|
||||||
** SQLITE_ENABLE_SQLLOG pre-processor macro defined. The first argument should
|
** [SQLITE_ENABLE_SQLLOG] pre-processor macro defined. The first argument should
|
||||||
** be a pointer to a function of type void(*)(void*,sqlite3*,const char*, int).
|
** be a pointer to a function of type void(*)(void*,sqlite3*,const char*, int).
|
||||||
** The second should be of type (void*). The callback is invoked by the library
|
** The second should be of type (void*). The callback is invoked by the library
|
||||||
** in three separate circumstances, identified by the value passed as the
|
** in three separate circumstances, identified by the value passed as the
|
||||||
@@ -1622,7 +1655,23 @@ struct sqlite3_mem_methods {
|
|||||||
** fourth parameter is 1, then the SQL statement that the third parameter
|
** fourth parameter is 1, then the SQL statement that the third parameter
|
||||||
** points to has just been executed. Or, if the fourth parameter is 2, then
|
** points to has just been executed. Or, if the fourth parameter is 2, then
|
||||||
** the connection being passed as the second parameter is being closed. The
|
** the connection being passed as the second parameter is being closed. The
|
||||||
** third parameter is passed NULL In this case.
|
** third parameter is passed NULL In this case. An example of using this
|
||||||
|
** configuration option can be seen in the "test_sqllog.c" source file in
|
||||||
|
** the canonical SQLite source tree.</dd>
|
||||||
|
**
|
||||||
|
** [[SQLITE_CONFIG_MMAP_SIZE]]
|
||||||
|
** <dt>SQLITE_CONFIG_MMAP_SIZE
|
||||||
|
** <dd>SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values
|
||||||
|
** that are the default mmap size limit (the default setting for
|
||||||
|
** [PRAGMA mmap_size]) and the maximum allowed mmap size limit.
|
||||||
|
** The default setting can be overridden by each database connection using
|
||||||
|
** either the [PRAGMA mmap_size] command, or by using the
|
||||||
|
** [SQLITE_FCNTL_MMAP_SIZE] file control. The maximum allowed mmap size
|
||||||
|
** cannot be changed at run-time. Nor may the maximum allowed mmap size
|
||||||
|
** exceed the compile-time maximum mmap size set by the
|
||||||
|
** [SQLITE_MAX_MMAP_SIZE] compile-time option.
|
||||||
|
** If either argument to this option is negative, then that argument is
|
||||||
|
** changed to its compile-time default.
|
||||||
** </dl>
|
** </dl>
|
||||||
*/
|
*/
|
||||||
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
||||||
@@ -1646,6 +1695,7 @@ struct sqlite3_mem_methods {
|
|||||||
#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
|
#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
|
||||||
#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */
|
#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */
|
||||||
#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
|
#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
|
||||||
|
#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Database Connection Configuration Options
|
** CAPI3REF: Database Connection Configuration Options
|
||||||
@@ -2479,6 +2529,9 @@ SQLITE_API int sqlite3_set_authorizer(
|
|||||||
** as each triggered subprogram is entered. The callbacks for triggers
|
** as each triggered subprogram is entered. The callbacks for triggers
|
||||||
** contain a UTF-8 SQL comment that identifies the trigger.)^
|
** contain a UTF-8 SQL comment that identifies the trigger.)^
|
||||||
**
|
**
|
||||||
|
** The [SQLITE_TRACE_SIZE_LIMIT] compile-time option can be used to limit
|
||||||
|
** the length of [bound parameter] expansion in the output of sqlite3_trace().
|
||||||
|
**
|
||||||
** ^The callback function registered by sqlite3_profile() is invoked
|
** ^The callback function registered by sqlite3_profile() is invoked
|
||||||
** as each SQL statement finishes. ^The profile callback contains
|
** as each SQL statement finishes. ^The profile callback contains
|
||||||
** the original statement text and an estimate of wall-clock time
|
** the original statement text and an estimate of wall-clock time
|
||||||
@@ -2670,7 +2723,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
|||||||
** sqlite3_open_v2(). ^Setting the cache parameter to "private" is
|
** sqlite3_open_v2(). ^Setting the cache parameter to "private" is
|
||||||
** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.
|
** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.
|
||||||
** ^If sqlite3_open_v2() is used and the "cache" parameter is present in
|
** ^If sqlite3_open_v2() is used and the "cache" parameter is present in
|
||||||
** a URI filename, its value overrides any behaviour requested by setting
|
** a URI filename, its value overrides any behavior requested by setting
|
||||||
** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag.
|
** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag.
|
||||||
** </ul>
|
** </ul>
|
||||||
**
|
**
|
||||||
@@ -3017,7 +3070,8 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
|
|||||||
** <li>
|
** <li>
|
||||||
** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it
|
** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it
|
||||||
** always used to do, [sqlite3_step()] will automatically recompile the SQL
|
** always used to do, [sqlite3_step()] will automatically recompile the SQL
|
||||||
** statement and try to run it again.
|
** statement and try to run it again. As many as [SQLITE_MAX_SCHEMA_RETRY]
|
||||||
|
** retries will occur before sqlite3_step() gives up and returns an error.
|
||||||
** </li>
|
** </li>
|
||||||
**
|
**
|
||||||
** <li>
|
** <li>
|
||||||
@@ -3221,6 +3275,9 @@ typedef struct sqlite3_context sqlite3_context;
|
|||||||
** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999).
|
** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999).
|
||||||
**
|
**
|
||||||
** ^The third argument is the value to bind to the parameter.
|
** ^The third argument is the value to bind to the parameter.
|
||||||
|
** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16()
|
||||||
|
** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter
|
||||||
|
** is ignored and the end result is the same as sqlite3_bind_null().
|
||||||
**
|
**
|
||||||
** ^(In those routines that have a fourth argument, its value is the
|
** ^(In those routines that have a fourth argument, its value is the
|
||||||
** number of bytes in the parameter. To be clear: the value is the
|
** number of bytes in the parameter. To be clear: the value is the
|
||||||
@@ -3988,7 +4045,8 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*);
|
|||||||
SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
|
SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
|
||||||
SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void);
|
SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void);
|
||||||
SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void);
|
SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void);
|
||||||
SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64);
|
SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),
|
||||||
|
void*,sqlite3_int64);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4068,14 +4126,17 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
|
|||||||
** In those cases, sqlite3_aggregate_context() might be called for the
|
** In those cases, sqlite3_aggregate_context() might be called for the
|
||||||
** first time from within xFinal().)^
|
** first time from within xFinal().)^
|
||||||
**
|
**
|
||||||
** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer if N is
|
** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
|
||||||
** less than or equal to zero or if a memory allocate error occurs.
|
** when first called if N is less than or equal to zero or if a memory
|
||||||
|
** allocate error occurs.
|
||||||
**
|
**
|
||||||
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
|
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
|
||||||
** determined by the N parameter on first successful call. Changing the
|
** determined by the N parameter on first successful call. Changing the
|
||||||
** value of N in subsequent call to sqlite3_aggregate_context() within
|
** value of N in subsequent call to sqlite3_aggregate_context() within
|
||||||
** the same aggregate function instance will not resize the memory
|
** the same aggregate function instance will not resize the memory
|
||||||
** allocation.)^
|
** allocation.)^ Within the xFinal callback, it is customary to set
|
||||||
|
** N=0 in calls to sqlite3_aggregate_context(C,N) so that no
|
||||||
|
** pointless memory allocations occur.
|
||||||
**
|
**
|
||||||
** ^SQLite automatically frees the memory allocated by
|
** ^SQLite automatically frees the memory allocated by
|
||||||
** sqlite3_aggregate_context() when the aggregate query concludes.
|
** sqlite3_aggregate_context() when the aggregate query concludes.
|
||||||
@@ -4173,7 +4234,7 @@ SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(voi
|
|||||||
** the content before returning.
|
** the content before returning.
|
||||||
**
|
**
|
||||||
** The typedef is necessary to work around problems in certain
|
** The typedef is necessary to work around problems in certain
|
||||||
** C++ compilers. See ticket #2191.
|
** C++ compilers.
|
||||||
*/
|
*/
|
||||||
typedef void (*sqlite3_destructor_type)(void*);
|
typedef void (*sqlite3_destructor_type)(void*);
|
||||||
#define SQLITE_STATIC ((sqlite3_destructor_type)0)
|
#define SQLITE_STATIC ((sqlite3_destructor_type)0)
|
||||||
@@ -4972,11 +5033,20 @@ SQLITE_API int sqlite3_table_column_metadata(
|
|||||||
** ^This interface loads an SQLite extension library from the named file.
|
** ^This interface loads an SQLite extension library from the named file.
|
||||||
**
|
**
|
||||||
** ^The sqlite3_load_extension() interface attempts to load an
|
** ^The sqlite3_load_extension() interface attempts to load an
|
||||||
** SQLite extension library contained in the file zFile.
|
** [SQLite extension] library contained in the file zFile. If
|
||||||
|
** the file cannot be loaded directly, attempts are made to load
|
||||||
|
** with various operating-system specific extensions added.
|
||||||
|
** So for example, if "samplelib" cannot be loaded, then names like
|
||||||
|
** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might
|
||||||
|
** be tried also.
|
||||||
**
|
**
|
||||||
** ^The entry point is zProc.
|
** ^The entry point is zProc.
|
||||||
** ^zProc may be 0, in which case the name of the entry point
|
** ^(zProc may be 0, in which case SQLite will try to come up with an
|
||||||
** defaults to "sqlite3_extension_init".
|
** entry point name on its own. It first tries "sqlite3_extension_init".
|
||||||
|
** If that does not work, it constructs a name "sqlite3_X_init" where the
|
||||||
|
** X is consists of the lower-case equivalent of all ASCII alphabetic
|
||||||
|
** characters in the filename from the last "/" to the first following
|
||||||
|
** "." and omitting any initial "lib".)^
|
||||||
** ^The sqlite3_load_extension() interface returns
|
** ^The sqlite3_load_extension() interface returns
|
||||||
** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong.
|
** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong.
|
||||||
** ^If an error occurs and pzErrMsg is not 0, then the
|
** ^If an error occurs and pzErrMsg is not 0, then the
|
||||||
@@ -5002,11 +5072,11 @@ SQLITE_API int sqlite3_load_extension(
|
|||||||
** CAPI3REF: Enable Or Disable Extension Loading
|
** CAPI3REF: Enable Or Disable Extension Loading
|
||||||
**
|
**
|
||||||
** ^So as not to open security holes in older applications that are
|
** ^So as not to open security holes in older applications that are
|
||||||
** unprepared to deal with extension loading, and as a means of disabling
|
** unprepared to deal with [extension loading], and as a means of disabling
|
||||||
** extension loading while evaluating user-entered SQL, the following API
|
** [extension loading] while evaluating user-entered SQL, the following API
|
||||||
** is provided to turn the [sqlite3_load_extension()] mechanism on and off.
|
** is provided to turn the [sqlite3_load_extension()] mechanism on and off.
|
||||||
**
|
**
|
||||||
** ^Extension loading is off by default. See ticket #1863.
|
** ^Extension loading is off by default.
|
||||||
** ^Call the sqlite3_enable_load_extension() routine with onoff==1
|
** ^Call the sqlite3_enable_load_extension() routine with onoff==1
|
||||||
** to turn extension loading on and call it with onoff==0 to turn
|
** to turn extension loading on and call it with onoff==0 to turn
|
||||||
** it back off again.
|
** it back off again.
|
||||||
@@ -5018,7 +5088,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
|
|||||||
**
|
**
|
||||||
** ^This interface causes the xEntryPoint() function to be invoked for
|
** ^This interface causes the xEntryPoint() function to be invoked for
|
||||||
** each new [database connection] that is created. The idea here is that
|
** each new [database connection] that is created. The idea here is that
|
||||||
** xEntryPoint() is the entry point for a statically linked SQLite extension
|
** xEntryPoint() is the entry point for a statically linked [SQLite extension]
|
||||||
** that is to be automatically loaded into all new database connections.
|
** that is to be automatically loaded into all new database connections.
|
||||||
**
|
**
|
||||||
** ^(Even though the function prototype shows that xEntryPoint() takes
|
** ^(Even though the function prototype shows that xEntryPoint() takes
|
||||||
@@ -6369,7 +6439,7 @@ struct sqlite3_pcache_page {
|
|||||||
** parameter to help it determined what action to take:
|
** parameter to help it determined what action to take:
|
||||||
**
|
**
|
||||||
** <table border=1 width=85% align=center>
|
** <table border=1 width=85% align=center>
|
||||||
** <tr><th> createFlag <th> Behaviour when page is not already in cache
|
** <tr><th> createFlag <th> Behavior when page is not already in cache
|
||||||
** <tr><td> 0 <td> Do not allocate a new page. Return NULL.
|
** <tr><td> 0 <td> Do not allocate a new page. Return NULL.
|
||||||
** <tr><td> 1 <td> Allocate a new page if it easy and convenient to do so.
|
** <tr><td> 1 <td> Allocate a new page if it easy and convenient to do so.
|
||||||
** Otherwise return NULL.
|
** Otherwise return NULL.
|
||||||
@@ -6798,10 +6868,25 @@ SQLITE_API int sqlite3_unlock_notify(
|
|||||||
SQLITE_API int sqlite3_stricmp(const char *, const char *);
|
SQLITE_API int sqlite3_stricmp(const char *, const char *);
|
||||||
SQLITE_API int sqlite3_strnicmp(const char *, const char *, int);
|
SQLITE_API int sqlite3_strnicmp(const char *, const char *, int);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: String Globbing
|
||||||
|
*
|
||||||
|
** ^The [sqlite3_strglob(P,X)] interface returns zero if string X matches
|
||||||
|
** the glob pattern P, and it returns non-zero if string X does not match
|
||||||
|
** the glob pattern P. ^The definition of glob pattern matching used in
|
||||||
|
** [sqlite3_strglob(P,X)] is the same as for the "X GLOB P" operator in the
|
||||||
|
** SQL dialect used by SQLite. ^The sqlite3_strglob(P,X) function is case
|
||||||
|
** sensitive.
|
||||||
|
**
|
||||||
|
** Note that this routine returns zero on a match and non-zero if the strings
|
||||||
|
** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()].
|
||||||
|
*/
|
||||||
|
SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Error Logging Interface
|
** CAPI3REF: Error Logging Interface
|
||||||
**
|
**
|
||||||
** ^The [sqlite3_log()] interface writes a message into the error log
|
** ^The [sqlite3_log()] interface writes a message into the [error log]
|
||||||
** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()].
|
** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()].
|
||||||
** ^If logging is enabled, the zFormat string and subsequent arguments are
|
** ^If logging is enabled, the zFormat string and subsequent arguments are
|
||||||
** used with [sqlite3_snprintf()] to generate the final output string.
|
** used with [sqlite3_snprintf()] to generate the final output string.
|
||||||
|
|||||||
+47
-19
@@ -1,28 +1,56 @@
|
|||||||
CFLAGS= -W -Wall -I.. -pthread -g
|
CFLAGS = -W -Wall -I.. -pthread -g -pipe $(COPT)
|
||||||
|
DLL_FLAGS = -DLUA_COMPAT_ALL -I../build
|
||||||
|
|
||||||
all:
|
ifeq ($(OS),Windows_NT)
|
||||||
OS=`uname`; \
|
else
|
||||||
test "$$OS" = Linux && LIBS="-ldl" ; \
|
UNAME_S := $(shell uname -s)
|
||||||
$(CC) $(CFLAGS) hello.c ../mongoose.c $$LIBS $(ADD) -o hello;
|
DLL_FLAGS += -shared
|
||||||
$(CC) $(CFLAGS) upload.c ../mongoose.c $$LIBS $(ADD) -o upload;
|
|
||||||
$(CC) $(CFLAGS) post.c ../mongoose.c $$LIBS $(ADD) -o post;
|
ifeq ($(UNAME_S),Linux)
|
||||||
$(CC) $(CFLAGS) -DUSE_WEBSOCKET websocket.c ../mongoose.c $$LIBS $(ADD) -o websocket;
|
CFLAGS += -ldl
|
||||||
$(CC) $(CFLAGS) chat.c ../mongoose.c $$LIBS $(ADD) -o chat
|
endif
|
||||||
|
|
||||||
|
ifeq ($(UNAME_S),Darwin)
|
||||||
|
# DLL_FLAGS += -bundle -undefined dynamic_lookup -dynamiclib
|
||||||
|
DLL_FLAGS += -flat_namespace -undefined suppress -dynamiclib
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: hello upload post websocket chat
|
||||||
|
|
||||||
|
hello:
|
||||||
|
$(CC) hello.c ../mongoose.c -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
upload:
|
||||||
|
$(CC) upload.c ../mongoose.c -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
post:
|
||||||
|
$(CC) post.c ../mongoose.c -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
websocket:
|
||||||
|
$(CC) -DUSE_WEBSOCKET websocket.c ../mongoose.c -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
chat:
|
||||||
|
$(CC) chat.c ../mongoose.c -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
lua_dll:
|
||||||
|
$(CC) lua_dll.c ../build/lua_5.2.1.c -o $@.so $(CFLAGS) $(DLL_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
MSVC = e:/vc6
|
MSVC = ../../vc6
|
||||||
CL = $(MSVC)/bin/cl
|
CL = $(MSVC)/bin/cl
|
||||||
CLFLAGS = /MD /TC /nologo $(DBG) /W3 /DNO_SSL \
|
CLFLAGS = /MD /TC /nologo $(DBG) /W3 /DNO_SSL \
|
||||||
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf \
|
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf
|
||||||
/link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
|
LFLAGS = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
|
||||||
|
|
||||||
windows:
|
windows:
|
||||||
$(CL) upload.c ../mongoose.c $(CLFLAGS)
|
$(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||||
$(CL) hello.c ../mongoose.c $(CLFLAGS)
|
$(CL) hello.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||||
$(CL) upload.c ../mongoose.c $(CLFLAGS)
|
$(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||||
$(CL) post.c ../mongoose.c $(CLFLAGS)
|
$(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||||
$(CL) post.c ../mongoose.c $(CLFLAGS)
|
$(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||||
$(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS)
|
$(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||||
|
#$(CL) lua_dll.c $(CLFLAGS) $(DLL_FLAGS) /DLL $(LFLAGS) /SUBSYSTEM:WINDOWS /ENTRY:luaopen_lua_dll /EXPORT:luaopen_lua_dll /out:lua_dll.dll
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf hello upload post websocket chat *.exe *.dSYM *.obj
|
rm -rf hello upload post websocket chat *.exe *.dSYM *.obj .*o
|
||||||
+2
-1
@@ -56,9 +56,10 @@ static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
|
|||||||
// Get session object for the connection. Caller must hold the lock.
|
// Get session object for the connection. Caller must hold the lock.
|
||||||
static struct session *get_session(const struct mg_connection *conn) {
|
static struct session *get_session(const struct mg_connection *conn) {
|
||||||
int i;
|
int i;
|
||||||
|
const char *cookie = mg_get_header(conn, "Cookie");
|
||||||
char session_id[33];
|
char session_id[33];
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
mg_get_cookie(conn, "session", session_id, sizeof(session_id));
|
mg_get_cookie(cookie, "session", session_id, sizeof(session_id));
|
||||||
for (i = 0; i < MAX_SESSIONS; i++) {
|
for (i = 0; i < MAX_SESSIONS; i++) {
|
||||||
if (sessions[i].expire != 0 &&
|
if (sessions[i].expire != 0 &&
|
||||||
sessions[i].expire > now &&
|
sessions[i].expire > now &&
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
HTTP/1.0 200 OK
|
||||||
|
Content-Type: text/html
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<p>Prime numbers from 0 to 100, calculated by Lua:</p>
|
||||||
|
<?
|
||||||
|
function is_prime(n)
|
||||||
|
if n <= 0 then return false end
|
||||||
|
if n <= 2 then return true end
|
||||||
|
if (n % 2 == 0) then return false end
|
||||||
|
for i = 3, n / 2, 2 do
|
||||||
|
if (n % i == 0) then return false end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, 100 do
|
||||||
|
if is_prime(i) then mg.write('<span>' .. i .. '</span> ') end
|
||||||
|
end
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<p>Reading POST data from Lua (click submit):</p>
|
||||||
|
<form method="POST" ><input type="text" name="t1"/><input type="submit"></form>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
POST data: [<?
|
||||||
|
local post_data = ''
|
||||||
|
if mg.request_info.request_method == 'POST' then
|
||||||
|
post_data = mg.read()
|
||||||
|
end
|
||||||
|
mg.write(post_data)
|
||||||
|
?>]
|
||||||
|
request method: [<? mg.write(mg.request_info.request_method) ?>]
|
||||||
|
IP/port: [<? mg.write(mg.request_info.remote_ip, ':',
|
||||||
|
mg.request_info.remote_port) ?>]
|
||||||
|
URI: [<? mg.write(mg.request_info.uri) ?>]
|
||||||
|
HTTP version [<? mg.write(mg.request_info.http_version) ?>]
|
||||||
|
HEADERS:
|
||||||
|
<?
|
||||||
|
for name, value in pairs(mg.request_info.http_headers) do
|
||||||
|
mg.write(name, ':', value, '\n')
|
||||||
|
end
|
||||||
|
?>
|
||||||
|
</pre>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "lua-5.2.1.h"
|
||||||
|
|
||||||
|
static int smile(lua_State *L) {
|
||||||
|
(void) L; // Unused
|
||||||
|
printf("%s\n", ":-)");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LUA_API luaopen_lua_dll(lua_State *L) {
|
||||||
|
static const struct luaL_Reg api[] = {
|
||||||
|
{"smile", smile},
|
||||||
|
{NULL, NULL},
|
||||||
|
};
|
||||||
|
luaL_openlib(L, "lua_dll", api, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
+12
-44
@@ -6,54 +6,22 @@
|
|||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
static void websocket_ready_handler(struct mg_connection *conn) {
|
static void websocket_ready_handler(struct mg_connection *conn) {
|
||||||
unsigned char buf[40];
|
static const char *message = "server ready";
|
||||||
buf[0] = 0x81;
|
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, message, strlen(message));
|
||||||
buf[1] = snprintf((char *) buf + 2, sizeof(buf) - 2, "%s", "server ready");
|
|
||||||
mg_write(conn, buf, 2 + buf[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int websocket_data_handler(struct mg_connection *conn) {
|
// Arguments:
|
||||||
unsigned char buf[200], reply[200];
|
// flags: first byte of websocket frame, see websocket RFC,
|
||||||
int n, i, mask_len, xor, msg_len, len;
|
// http://tools.ietf.org/html/rfc6455, section 5.2
|
||||||
|
// data, data_len: payload data. Mask, if any, is already applied.
|
||||||
|
static int websocket_data_handler(struct mg_connection *conn, int flags,
|
||||||
|
char *data, size_t data_len) {
|
||||||
|
(void) flags; // Unused
|
||||||
|
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
|
||||||
|
|
||||||
// Read message from the client.
|
// Returning zero means stoping websocket conversation.
|
||||||
// Accept only small (<126 bytes) messages.
|
|
||||||
len = 0;
|
|
||||||
msg_len = mask_len = 0;
|
|
||||||
for (;;) {
|
|
||||||
if ((n = mg_read(conn, buf + len, sizeof(buf) - len)) <= 0) {
|
|
||||||
return 0; // Read error, close websocket
|
|
||||||
}
|
|
||||||
len += n;
|
|
||||||
if (len >= 2) {
|
|
||||||
msg_len = buf[1] & 127;
|
|
||||||
mask_len = (buf[1] & 128) ? 4 : 0;
|
|
||||||
if (msg_len > 125) {
|
|
||||||
return 0; // Message is too long, close websocket
|
|
||||||
}
|
|
||||||
// If we've buffered the whole message, exit the loop
|
|
||||||
if (len >= 2 + mask_len + msg_len) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare frame
|
|
||||||
reply[0] = 0x81; // text, FIN set
|
|
||||||
reply[1] = msg_len;
|
|
||||||
|
|
||||||
// Copy message from request to reply, applying the mask if required.
|
|
||||||
for (i = 0; i < msg_len; i++) {
|
|
||||||
xor = mask_len == 0 ? 0 : buf[2 + (i % 4)];
|
|
||||||
reply[i + 2] = buf[i + 2 + mask_len] ^ xor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Echo the message back to the client
|
|
||||||
mg_write(conn, reply, 2 + msg_len);
|
|
||||||
|
|
||||||
// Returnint zero means stoping websocket conversation.
|
|
||||||
// Close the conversation if client has sent us "exit" string.
|
// Close the conversation if client has sent us "exit" string.
|
||||||
return memcmp(reply + 2, "exit", 4);
|
return memcmp(data, "exit", 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|||||||
+798
-503
File diff suppressed because it is too large.
Load diff
+139
-34
@@ -1,22 +1,19 @@
|
|||||||
// Copyright (c) 2004-2012 Sergey Lyubka
|
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||||
|
// Copyright (c) 2013 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// This library is dual-licensed: you can redistribute it and/or modify
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// it under the terms of the GNU General Public License version 2 as
|
||||||
// in the Software without restriction, including without limitation the rights
|
// published by the Free Software Foundation. For the terms of this
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
// license, see <http://www.gnu.org/licenses/>.
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
//
|
||||||
// The above copyright notice and this permission notice shall be included in
|
// You are free to use this library under the terms of the GNU General
|
||||||
// all copies or substantial portions of the Software.
|
// Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
// See the GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
// Alternatively, you can license this library under a commercial
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
// license, as set out in <http://cesanta.com/products.html>.
|
||||||
// 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 MONGOOSE_HEADER_INCLUDED
|
#ifndef MONGOOSE_HEADER_INCLUDED
|
||||||
#define MONGOOSE_HEADER_INCLUDED
|
#define MONGOOSE_HEADER_INCLUDED
|
||||||
@@ -43,6 +40,7 @@ struct mg_request_info {
|
|||||||
int remote_port; // Client's port
|
int remote_port; // Client's port
|
||||||
int is_ssl; // 1 if SSL-ed, 0 if not
|
int is_ssl; // 1 if SSL-ed, 0 if not
|
||||||
void *user_data; // User data pointer passed to mg_start()
|
void *user_data; // User data pointer passed to mg_start()
|
||||||
|
void *conn_data; // Connection-specific, per-thread user data.
|
||||||
|
|
||||||
int num_headers; // Number of HTTP headers
|
int num_headers; // Number of HTTP headers
|
||||||
struct mg_header {
|
struct mg_header {
|
||||||
@@ -56,17 +54,85 @@ struct mg_request_info {
|
|||||||
// which callbacks to invoke. For detailed description, see
|
// which callbacks to invoke. For detailed description, see
|
||||||
// https://github.com/valenok/mongoose/blob/master/UserManual.md
|
// https://github.com/valenok/mongoose/blob/master/UserManual.md
|
||||||
struct mg_callbacks {
|
struct mg_callbacks {
|
||||||
|
// Called when mongoose has received new HTTP request.
|
||||||
|
// If callback returns non-zero,
|
||||||
|
// callback must process the request by sending valid HTTP headers and body,
|
||||||
|
// and mongoose will not do any further processing.
|
||||||
|
// If callback returns 0, mongoose processes the request itself. In this case,
|
||||||
|
// callback must not send any data to the client.
|
||||||
int (*begin_request)(struct mg_connection *);
|
int (*begin_request)(struct mg_connection *);
|
||||||
|
|
||||||
|
// Called when mongoose has finished processing request.
|
||||||
void (*end_request)(const struct mg_connection *, int reply_status_code);
|
void (*end_request)(const struct mg_connection *, int reply_status_code);
|
||||||
|
|
||||||
|
// Called when mongoose is about to log a message. If callback returns
|
||||||
|
// non-zero, mongoose does not log anything.
|
||||||
int (*log_message)(const struct mg_connection *, const char *message);
|
int (*log_message)(const struct mg_connection *, const char *message);
|
||||||
int (*init_ssl)(void *ssl_context);
|
|
||||||
|
// Called when mongoose initializes SSL library.
|
||||||
|
int (*init_ssl)(void *ssl_context, void *user_data);
|
||||||
|
|
||||||
|
// Called when websocket request is received, before websocket handshake.
|
||||||
|
// If callback returns 0, mongoose proceeds with handshake, otherwise
|
||||||
|
// cinnection is closed immediately.
|
||||||
int (*websocket_connect)(const struct mg_connection *);
|
int (*websocket_connect)(const struct mg_connection *);
|
||||||
|
|
||||||
|
// Called when websocket handshake is successfully completed, and
|
||||||
|
// connection is ready for data exchange.
|
||||||
void (*websocket_ready)(struct mg_connection *);
|
void (*websocket_ready)(struct mg_connection *);
|
||||||
int (*websocket_data)(struct mg_connection *);
|
|
||||||
|
// Called when data frame has been received from the client.
|
||||||
|
// Parameters:
|
||||||
|
// bits: first byte of the websocket frame, see websocket RFC at
|
||||||
|
// http://tools.ietf.org/html/rfc6455, section 5.2
|
||||||
|
// data, data_len: payload, with mask (if any) already applied.
|
||||||
|
// Return value:
|
||||||
|
// non-0: keep this websocket connection opened.
|
||||||
|
// 0: close this websocket connection.
|
||||||
|
int (*websocket_data)(struct mg_connection *, int bits,
|
||||||
|
char *data, size_t data_len);
|
||||||
|
|
||||||
|
// Called when mongoose tries to open a file. Used to intercept file open
|
||||||
|
// calls, and serve file data from memory instead.
|
||||||
|
// Parameters:
|
||||||
|
// path: Full path to the file to open.
|
||||||
|
// data_len: Placeholder for the file size, if file is served from memory.
|
||||||
|
// Return value:
|
||||||
|
// NULL: do not serve file from memory, proceed with normal file open.
|
||||||
|
// non-NULL: pointer to the file contents in memory. data_len must be
|
||||||
|
// initilized with the size of the memory block.
|
||||||
const char * (*open_file)(const struct mg_connection *,
|
const char * (*open_file)(const struct mg_connection *,
|
||||||
const char *path, size_t *data_len);
|
const char *path, size_t *data_len);
|
||||||
|
|
||||||
|
// Called when mongoose is about to serve Lua server page (.lp file), if
|
||||||
|
// Lua support is enabled.
|
||||||
|
// Parameters:
|
||||||
|
// lua_context: "lua_State *" pointer.
|
||||||
void (*init_lua)(struct mg_connection *, void *lua_context);
|
void (*init_lua)(struct mg_connection *, void *lua_context);
|
||||||
|
|
||||||
|
// Called when mongoose has uploaded a file to a temporary directory as a
|
||||||
|
// result of mg_upload() call.
|
||||||
|
// Parameters:
|
||||||
|
// file_file: full path name to the uploaded file.
|
||||||
void (*upload)(struct mg_connection *, const char *file_name);
|
void (*upload)(struct mg_connection *, const char *file_name);
|
||||||
|
|
||||||
|
// Called at the beginning of mongoose's thread execution in the context of
|
||||||
|
// that thread. To be used to perform any extra per-thread initialization.
|
||||||
|
// Parameters:
|
||||||
|
// user_data: pointer passed to mg_start
|
||||||
|
// conn_data: per-connection, i.e. per-thread pointer. Can be used to
|
||||||
|
// store per-thread data, for example, database connection
|
||||||
|
// handles. Persistent between connections handled by the
|
||||||
|
// same thread.
|
||||||
|
// NOTE: this parameter is NULL for master thread, and non-NULL
|
||||||
|
// for worker threads.
|
||||||
|
void (*thread_start)(void *user_data, void **conn_data);
|
||||||
|
|
||||||
|
// Called when mongoose's thread is about to terminate.
|
||||||
|
// Same as thread_start() callback, but called when thread is about to be
|
||||||
|
// destroyed. Used to cleanup the state initialized by thread_start().
|
||||||
|
// Parameters: see thread_start().
|
||||||
|
void (*thread_stop)(void *user_data, void **conn_data);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start web server.
|
// Start web server.
|
||||||
@@ -76,10 +142,12 @@ struct mg_callbacks {
|
|||||||
// options: NULL terminated list of option_name, option_value pairs that
|
// options: NULL terminated list of option_name, option_value pairs that
|
||||||
// specify Mongoose configuration parameters.
|
// specify Mongoose configuration parameters.
|
||||||
//
|
//
|
||||||
// Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
|
// Side-effects: on UNIX, ignores SIGPIPE signals. If custom
|
||||||
// processing is required for these, signal handlers must be set up
|
// processing is required SIGPIPE, signal handler must be set up
|
||||||
// after calling mg_start().
|
// after calling mg_start().
|
||||||
//
|
//
|
||||||
|
// Important: Mongoose does not install SIGCHLD handler. If CGI is used,
|
||||||
|
// SIGCHLD handler must be set up to reap CGI zombie processes.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
// const char *options[] = {
|
// const char *options[] = {
|
||||||
@@ -89,7 +157,7 @@ struct mg_callbacks {
|
|||||||
// };
|
// };
|
||||||
// struct mg_context *ctx = mg_start(&my_func, NULL, options);
|
// struct mg_context *ctx = mg_start(&my_func, NULL, options);
|
||||||
//
|
//
|
||||||
// Please refer to http://code.google.com/p/mongoose/wiki/MongooseManual
|
// Refer to https://github.com/valenok/mongoose/blob/master/UserManual.md
|
||||||
// for the list of valid option and their possible values.
|
// for the list of valid option and their possible values.
|
||||||
//
|
//
|
||||||
// Return:
|
// Return:
|
||||||
@@ -117,7 +185,8 @@ const char *mg_get_option(const struct mg_context *ctx, const char *name);
|
|||||||
|
|
||||||
|
|
||||||
// Return array of strings that represent valid configuration options.
|
// Return array of strings that represent valid configuration options.
|
||||||
// For each option, a short name, long name, and default value is returned.
|
// For each option, option name and default value is returned, i.e. the
|
||||||
|
// number of entries in the array equals to number_of_options x 2.
|
||||||
// Array is NULL terminated.
|
// Array is NULL terminated.
|
||||||
const char **mg_get_valid_option_names(void);
|
const char **mg_get_valid_option_names(void);
|
||||||
|
|
||||||
@@ -148,14 +217,37 @@ struct mg_request_info *mg_get_request_info(struct mg_connection *);
|
|||||||
// Return:
|
// Return:
|
||||||
// 0 when the connection has been closed
|
// 0 when the connection has been closed
|
||||||
// -1 on error
|
// -1 on error
|
||||||
// number of bytes written on success
|
// >0 number of bytes written on success
|
||||||
int mg_write(struct mg_connection *, const void *buf, size_t len);
|
int mg_write(struct mg_connection *, const void *buf, size_t len);
|
||||||
|
|
||||||
|
|
||||||
|
// Send data to a websocket client wrapped in a websocket frame.
|
||||||
|
// It is unsafe to read/write to this connection from another thread.
|
||||||
|
// This function is available when mongoose is compiled with -DUSE_WEBSOCKET
|
||||||
|
//
|
||||||
|
// Return:
|
||||||
|
// 0 when the connection has been closed
|
||||||
|
// -1 on error
|
||||||
|
// >0 number of bytes written on success
|
||||||
|
int mg_websocket_write(struct mg_connection* conn, int opcode,
|
||||||
|
const char *data, size_t data_len);
|
||||||
|
|
||||||
|
// Opcodes, from http://tools.ietf.org/html/rfc6455
|
||||||
|
enum {
|
||||||
|
WEBSOCKET_OPCODE_CONTINUATION = 0x0,
|
||||||
|
WEBSOCKET_OPCODE_TEXT = 0x1,
|
||||||
|
WEBSOCKET_OPCODE_BINARY = 0x2,
|
||||||
|
WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
|
||||||
|
WEBSOCKET_OPCODE_PING = 0x9,
|
||||||
|
WEBSOCKET_OPCODE_PONG = 0xa
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Macros for enabling compiler-specific checks for printf-like arguments.
|
||||||
#undef PRINTF_FORMAT_STRING
|
#undef PRINTF_FORMAT_STRING
|
||||||
#if _MSC_VER >= 1400
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||||
#include <sal.h>
|
#include <sal.h>
|
||||||
#if _MSC_VER > 1400
|
#if defined(_MSC_VER) && _MSC_VER > 1400
|
||||||
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
|
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
|
||||||
#else
|
#else
|
||||||
#define PRINTF_FORMAT_STRING(s) __format_string s
|
#define PRINTF_FORMAT_STRING(s) __format_string s
|
||||||
@@ -170,11 +262,9 @@ int mg_write(struct mg_connection *, const void *buf, size_t len);
|
|||||||
#define PRINTF_ARGS(x, y)
|
#define PRINTF_ARGS(x, y)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Send data to the browser using printf() semantics.
|
// Send data to the client using printf() semantics.
|
||||||
//
|
//
|
||||||
// Works exactly like mg_write(), but allows to do message formatting.
|
// Works exactly like mg_write(), but allows to do message formatting.
|
||||||
// Below are the macros for enabling compiler-specific checks for
|
|
||||||
// printf-like arguments.
|
|
||||||
int mg_printf(struct mg_connection *,
|
int mg_printf(struct mg_connection *,
|
||||||
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
|
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
|
||||||
|
|
||||||
@@ -184,6 +274,10 @@ void mg_send_file(struct mg_connection *conn, const char *path);
|
|||||||
|
|
||||||
|
|
||||||
// Read data from the remote end, return number of bytes read.
|
// Read data from the remote end, return number of bytes read.
|
||||||
|
// Return:
|
||||||
|
// 0 connection has been closed by peer. No more data could be read.
|
||||||
|
// < 0 read error. No more data could be read from the connection.
|
||||||
|
// > 0 number of bytes read into the buffer.
|
||||||
int mg_read(struct mg_connection *, void *buf, size_t len);
|
int mg_read(struct mg_connection *, void *buf, size_t len);
|
||||||
|
|
||||||
|
|
||||||
@@ -209,7 +303,8 @@ const char *mg_get_header(const struct mg_connection *, const char *name);
|
|||||||
// On success, length of the decoded variable.
|
// On success, length of the decoded variable.
|
||||||
// On error:
|
// On error:
|
||||||
// -1 (variable not found).
|
// -1 (variable not found).
|
||||||
// -2 (destination buffer is NULL, zero length or too small to hold the decoded variable).
|
// -2 (destination buffer is NULL, zero length or too small to hold the
|
||||||
|
// decoded variable).
|
||||||
//
|
//
|
||||||
// Destination buffer is guaranteed to be '\0' - terminated if it is not
|
// Destination buffer is guaranteed to be '\0' - terminated if it is not
|
||||||
// NULL or zero length.
|
// NULL or zero length.
|
||||||
@@ -225,10 +320,12 @@ int mg_get_var(const char *data, size_t data_len,
|
|||||||
// Return:
|
// Return:
|
||||||
// On success, value length.
|
// On success, value length.
|
||||||
// On error:
|
// On error:
|
||||||
// -1 (either "Cookie:" header is not present at all or the requested parameter is not found).
|
// -1 (either "Cookie:" header is not present at all or the requested
|
||||||
// -2 (destination buffer is NULL, zero length or too small to hold the value).
|
// parameter is not found).
|
||||||
int mg_get_cookie(const struct mg_connection *,
|
// -2 (destination buffer is NULL, zero length or too small to hold the
|
||||||
const char *cookie_name, char *buf, size_t buf_len);
|
// value).
|
||||||
|
int mg_get_cookie(const char *cookie, const char *var_name,
|
||||||
|
char *buf, size_t buf_len);
|
||||||
|
|
||||||
|
|
||||||
// Download data from the remote web server.
|
// Download data from the remote web server.
|
||||||
@@ -275,6 +372,14 @@ const char *mg_get_builtin_mime_type(const char *file_name);
|
|||||||
// Return Mongoose version.
|
// Return Mongoose version.
|
||||||
const char *mg_version(void);
|
const char *mg_version(void);
|
||||||
|
|
||||||
|
// URL-decode input buffer into destination buffer.
|
||||||
|
// 0-terminate the destination buffer.
|
||||||
|
// form-url-encoded data differs from URI encoding in a way that it
|
||||||
|
// uses '+' as character for space, see RFC 1866 section 8.2.1
|
||||||
|
// http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
|
||||||
|
// Return: length of the decoded data, or -1 if dst buffer is too small.
|
||||||
|
int mg_url_decode(const char *src, int src_len, char *dst,
|
||||||
|
int dst_len, int is_form_url_encoded);
|
||||||
|
|
||||||
// MD5 hash given strings.
|
// MD5 hash given strings.
|
||||||
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
|
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
|
||||||
@@ -282,7 +387,7 @@ const char *mg_version(void);
|
|||||||
// MD5 hash. Example:
|
// MD5 hash. Example:
|
||||||
// char buf[33];
|
// char buf[33];
|
||||||
// mg_md5(buf, "aa", "bb", NULL);
|
// mg_md5(buf, "aa", "bb", NULL);
|
||||||
void mg_md5(char buf[33], ...);
|
char *mg_md5(char buf[33], ...);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
print "Content-Type: text/plain\n\nhello\n";
|
||||||
+16
-11
@@ -1,22 +1,25 @@
|
|||||||
<?
|
HTTP/1.0 200 OK
|
||||||
-- Lua server pages have full control over the output, including HTTP
|
Content-Type: text/html
|
||||||
-- headers they send to the client. Send HTTP headers:
|
|
||||||
print('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n')
|
<html><body>
|
||||||
?><html><body>
|
|
||||||
|
|
||||||
<p>This is an example Lua server page served by
|
<p>This is an example Lua server page served by
|
||||||
<a href="http://code.google.com/p/mongoose">Mongoose web server</a>.
|
<a href="http://code.google.com/p/mongoose">Mongoose web server</a>.
|
||||||
Mongoose has Lua, Sqlite, and other functionality built in the binary.
|
Mongoose has Lua, Sqlite, and other functionality built in the binary.
|
||||||
This example page stores the request in the Sqlite database, and shows
|
This example page stores the request in the Sqlite database, and shows
|
||||||
all requests done previously.</p>
|
all requests done previously.</p>
|
||||||
|
<p> Today is <? mg.write(os.date("%A")) ?>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<?
|
<?
|
||||||
|
-- for k,v in pairs(_G) do mg.write(k, '\n') end
|
||||||
|
|
||||||
-- Open database
|
-- Open database
|
||||||
local db = sqlite3.open('requests.db')
|
local db = sqlite3.open('requests.db')
|
||||||
|
|
||||||
-- Setup a trace callback, to show SQL statements we'll be executing.
|
-- Setup a trace callback, to show SQL statements we'll be executing.
|
||||||
-- db:trace(function(data, sql) print('Executing: ' .. sql .. '\n') end, nil)
|
-- db:trace(function(data, sql) mg.write('Executing: ', sql: '\n') end, nil)
|
||||||
|
|
||||||
-- Create a table if it is not created already
|
-- Create a table if it is not created already
|
||||||
db:exec([[
|
db:exec([[
|
||||||
@@ -25,24 +28,26 @@ all requests done previously.</p>
|
|||||||
timestamp NOT NULL,
|
timestamp NOT NULL,
|
||||||
method NOT NULL,
|
method NOT NULL,
|
||||||
uri NOT NULL,
|
uri NOT NULL,
|
||||||
user_agent
|
addr
|
||||||
);
|
);
|
||||||
]])
|
]])
|
||||||
|
|
||||||
|
|
||||||
-- Add entry about this request
|
-- Add entry about this request
|
||||||
local stmt = db:prepare(
|
local stmt = db:prepare(
|
||||||
'INSERT INTO requests VALUES(NULL, datetime("now"), ?, ?, ?);');
|
'INSERT INTO requests VALUES(NULL, datetime("now"), ?, ?, ?);');
|
||||||
stmt:bind_values(request_info.request_method, request_info.uri,
|
stmt:bind_values(mg.request_info.request_method,
|
||||||
request_info.http_headers['User-Agent'])
|
mg.request_info.uri,
|
||||||
|
mg.request_info.remote_port)
|
||||||
stmt:step()
|
stmt:step()
|
||||||
stmt:finalize()
|
stmt:finalize()
|
||||||
|
|
||||||
-- Show all previous records
|
-- Show all previous records
|
||||||
print('Previous requests:\n')
|
mg.write('Previous requests:\n')
|
||||||
stmt = db:prepare('SELECT * FROM requests ORDER BY id DESC;')
|
stmt = db:prepare('SELECT * FROM requests ORDER BY id DESC;')
|
||||||
while stmt:step() == sqlite3.ROW do
|
while stmt:step() == sqlite3.ROW do
|
||||||
local v = stmt:get_values()
|
local v = stmt:get_values()
|
||||||
print(v[1] .. ' ' .. v[2] .. ' ' .. v[3] .. ' '
|
mg.write(v[1] .. ' ' .. v[2] .. ' ' .. v[3] .. ' '
|
||||||
.. v[4] .. ' ' .. v[5] .. '\n')
|
.. v[4] .. ' ' .. v[5] .. '\n')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<html><pre>
|
<html><pre>
|
||||||
ssi_begin
|
ssi_begin
|
||||||
<!--#include file="Makefile" -->
|
<!--#include file="../build/Makefile" -->
|
||||||
ssi_end
|
ssi_end
|
||||||
</pre></html>
|
</pre></html>
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<html><pre>
|
<html><pre>
|
||||||
ssi_begin
|
ssi_begin
|
||||||
<!--#include file="/etc/passwd" -->
|
<!--#include abspath="/etc/passwd" -->
|
||||||
ssi_end
|
ssi_end
|
||||||
</pre></html>
|
</pre></html>
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<html><pre>
|
<html><pre>
|
||||||
ssi_begin
|
ssi_begin
|
||||||
<!--#include file="c:\boot.ini" -->
|
<!--#include abspath="c:\boot.ini" -->
|
||||||
ssi_end
|
ssi_end
|
||||||
</pre></html>
|
</pre></html>
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
<html><pre>
|
<html><pre>
|
||||||
ssi_begin
|
ssi_begin
|
||||||
<!--#include "embed.c" -->
|
<!--#include "unit_test.c" -->
|
||||||
ssi_end
|
ssi_end
|
||||||
</pre></html>
|
</pre></html>
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
ssi_begin
|
ssi_begin
|
||||||
<!--#include file="Makefile" -->
|
<!--#include file="../build/Makefile" -->
|
||||||
ssi_end
|
ssi_end
|
||||||
+28
-18
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
use IO::Socket;
|
use IO::Socket;
|
||||||
use File::Path;
|
use File::Path;
|
||||||
|
use File::Basename;
|
||||||
|
use Cwd;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
#use diagnostics;
|
#use diagnostics;
|
||||||
@@ -16,12 +18,15 @@ my $num_requests;
|
|||||||
my $dir_separator = on_windows() ? '\\' : '/';
|
my $dir_separator = on_windows() ? '\\' : '/';
|
||||||
my $copy_cmd = on_windows() ? 'copy' : 'cp';
|
my $copy_cmd = on_windows() ? 'copy' : 'cp';
|
||||||
my $test_dir_uri = "test_dir";
|
my $test_dir_uri = "test_dir";
|
||||||
my $root = 'test';
|
my $root = '../test';
|
||||||
my $test_dir = $root . $dir_separator. $test_dir_uri;
|
my $abs_root = Cwd::abs_path(dirname($0) . $dir_separator . $root);
|
||||||
|
my $test_dir = $abs_root . $dir_separator. $test_dir_uri;
|
||||||
|
#print "$test_dir\n"; exit 0;
|
||||||
my $config = 'mongoose.conf';
|
my $config = 'mongoose.conf';
|
||||||
my $exe = '.' . $dir_separator . 'mongoose';
|
my $exe_ext = on_windows() ? '.exe' : '';
|
||||||
my $embed_exe = '.' . $dir_separator . 'embed';
|
my $mongoose_exe = '.' . $dir_separator . 'mongoose' . $exe_ext;
|
||||||
my $unit_test_exe = '.' . $dir_separator . 'unit_test';
|
my $embed_exe = '.' . $dir_separator . 'embed' . $exe_ext;
|
||||||
|
my $unit_test_exe = '.' . $dir_separator . 'unit_test' . $exe_ext;
|
||||||
my $exit_code = 0;
|
my $exit_code = 0;
|
||||||
|
|
||||||
my @files_to_delete = ('debug.log', 'access.log', $config, "$root/a/put.txt",
|
my @files_to_delete = ('debug.log', 'access.log', $config, "$root/a/put.txt",
|
||||||
@@ -51,9 +56,9 @@ sub get_num_of_log_entries {
|
|||||||
# Send the request to the 127.0.0.1:$port and return the reply
|
# Send the request to the 127.0.0.1:$port and return the reply
|
||||||
sub req {
|
sub req {
|
||||||
my ($request, $inc, $timeout) = @_;
|
my ($request, $inc, $timeout) = @_;
|
||||||
my $sock = IO::Socket::INET->new(Proto=>"tcp",
|
my $sock = IO::Socket::INET->new(Proto => 6,
|
||||||
PeerAddr => '127.0.0.1', PeerPort => $port);
|
PeerAddr => '127.0.0.1', PeerPort => $port);
|
||||||
fail("Cannot connect: $!") unless $sock;
|
fail("Cannot connect to http://127.0.0.1:$port : $!") unless $sock;
|
||||||
$sock->autoflush(1);
|
$sock->autoflush(1);
|
||||||
foreach my $byte (split //, $request) {
|
foreach my $byte (split //, $request) {
|
||||||
last unless print $sock $byte;
|
last unless print $sock $byte;
|
||||||
@@ -97,7 +102,6 @@ sub spawn {
|
|||||||
if (on_windows()) {
|
if (on_windows()) {
|
||||||
my @args = split /\s+/, $cmdline;
|
my @args = split /\s+/, $cmdline;
|
||||||
my $executable = $args[0];
|
my $executable = $args[0];
|
||||||
$executable .= '.exe';
|
|
||||||
Win32::Spawn($executable, $cmdline, $pid);
|
Win32::Spawn($executable, $cmdline, $pid);
|
||||||
die "Cannot spawn @_: $!" unless $pid;
|
die "Cannot spawn @_: $!" unless $pid;
|
||||||
} else {
|
} else {
|
||||||
@@ -154,23 +158,24 @@ if (scalar(@ARGV) > 0 and $ARGV[0] eq 'unit') {
|
|||||||
# Make sure we load config file if no options are given.
|
# Make sure we load config file if no options are given.
|
||||||
# Command line options override config files settings
|
# Command line options override config files settings
|
||||||
write_file($config, "access_log_file access.log\n" .
|
write_file($config, "access_log_file access.log\n" .
|
||||||
|
"document_root $root\n" .
|
||||||
"listening_ports 127.0.0.1:12345\n");
|
"listening_ports 127.0.0.1:12345\n");
|
||||||
spawn("$exe -p 127.0.0.1:$port");
|
spawn("$mongoose_exe -listening_ports 127.0.0.1:$port");
|
||||||
o("GET /test/hello.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'Loading config file');
|
o("GET /hello.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'Loading config file');
|
||||||
unlink $config;
|
unlink $config;
|
||||||
kill_spawned_child();
|
kill_spawned_child();
|
||||||
|
|
||||||
# Spawn the server on port $port
|
# Spawn the server on port $port
|
||||||
my $cmd = "$exe ".
|
my $cmd = "$mongoose_exe ".
|
||||||
"-listening_ports 127.0.0.1:$port ".
|
"-listening_ports 127.0.0.1:$port ".
|
||||||
"-access_log_file access.log ".
|
"-access_log_file access.log ".
|
||||||
"-error_log_file debug.log ".
|
"-error_log_file debug.log ".
|
||||||
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
|
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
|
||||||
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
|
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
|
||||||
'-put_delete_auth_file test/passfile ' .
|
"-put_delete_auth_file $abs_root/passfile " .
|
||||||
'-access_control_list -0.0.0.0/0,+127.0.0.1 ' .
|
'-access_control_list -0.0.0.0/0,+127.0.0.1 ' .
|
||||||
"-document_root $root ".
|
"-document_root $root ".
|
||||||
"-hide_files_patterns **exploit.pl ".
|
"-hide_files_patterns **exploit.PL ".
|
||||||
"-enable_keep_alive yes ".
|
"-enable_keep_alive yes ".
|
||||||
"-url_rewrite_patterns /aiased=/etc/,/ta=$test_dir";
|
"-url_rewrite_patterns /aiased=/etc/,/ta=$test_dir";
|
||||||
$cmd .= ' -cgi_interpreter perl' if on_windows();
|
$cmd .= ' -cgi_interpreter perl' if on_windows();
|
||||||
@@ -243,8 +248,9 @@ write_file($path, read_file($root . $dir_separator . 'env.cgi'));
|
|||||||
chmod(0755, $path);
|
chmod(0755, $path);
|
||||||
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n", "Content-Type: text/html\r\n\r\n",
|
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n", "Content-Type: text/html\r\n\r\n",
|
||||||
'index.cgi execution');
|
'index.cgi execution');
|
||||||
|
|
||||||
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n",
|
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n",
|
||||||
"SCRIPT_FILENAME=test/test_dir/x/index.cgi", 'SCRIPT_FILENAME');
|
"SCRIPT_FILENAME=$test_dir/x/index.cgi", 'SCRIPT_FILENAME');
|
||||||
o("GET /ta/x/ HTTP/1.0\n\n", "SCRIPT_NAME=/ta/x/index.cgi",
|
o("GET /ta/x/ HTTP/1.0\n\n", "SCRIPT_NAME=/ta/x/index.cgi",
|
||||||
'Aliases SCRIPT_NAME');
|
'Aliases SCRIPT_NAME');
|
||||||
o("GET /hello.txt HTTP/1.1\nConnection: close\n\n", 'Connection: close',
|
o("GET /hello.txt HTTP/1.1\nConnection: close\n\n", 'Connection: close',
|
||||||
@@ -352,6 +358,8 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
|
|||||||
unlink "$root/.htpasswd";
|
unlink "$root/.htpasswd";
|
||||||
|
|
||||||
|
|
||||||
|
o("GET /dir%20with%20spaces/hello.cgi HTTP/1.0\n\r\n",
|
||||||
|
'HTTP/1.1 200 OK.+hello', 'CGI script with spaces in path');
|
||||||
o("GET /env.cgi HTTP/1.0\n\r\n", 'HTTP/1.1 200 OK', 'GET CGI file');
|
o("GET /env.cgi HTTP/1.0\n\r\n", 'HTTP/1.1 200 OK', 'GET CGI file');
|
||||||
o("GET /bad2.cgi HTTP/1.0\n\n", "HTTP/1.1 123 Please pass me to the client\r",
|
o("GET /bad2.cgi HTTP/1.0\n\n", "HTTP/1.1 123 Please pass me to the client\r",
|
||||||
'CGI Status code text');
|
'CGI Status code text');
|
||||||
@@ -382,6 +390,8 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
|
|||||||
o("GET /env.cgi HTTP/1.0\n\r\n", '\nCGI_BAZ=baz\n', '-cgi_env 3');
|
o("GET /env.cgi HTTP/1.0\n\r\n", '\nCGI_BAZ=baz\n', '-cgi_env 3');
|
||||||
o("GET /env.cgi/a/b/98 HTTP/1.0\n\r\n", 'PATH_INFO=/a/b/98\n', 'PATH_INFO');
|
o("GET /env.cgi/a/b/98 HTTP/1.0\n\r\n", 'PATH_INFO=/a/b/98\n', 'PATH_INFO');
|
||||||
o("GET /env.cgi/a/b/9 HTTP/1.0\n\r\n", 'PATH_INFO=/a/b/9\n', 'PATH_INFO');
|
o("GET /env.cgi/a/b/9 HTTP/1.0\n\r\n", 'PATH_INFO=/a/b/9\n', 'PATH_INFO');
|
||||||
|
o("GET /env.cgi/foo/bar?a=b HTTP/1.0\n\n",
|
||||||
|
'SCRIPT_NAME=/env.cgi\s', 'SCRIPT_NAME for CGI with PATH_INFO');
|
||||||
|
|
||||||
# Check that CGI's current directory is set to script's directory
|
# Check that CGI's current directory is set to script's directory
|
||||||
my $copy_cmd = on_windows() ? 'copy' : 'cp';
|
my $copy_cmd = on_windows() ? 'copy' : 'cp';
|
||||||
@@ -401,7 +411,7 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
|
|||||||
my $abs_path = on_windows() ? 'ssi6.shtml' : 'ssi5.shtml';
|
my $abs_path = on_windows() ? 'ssi6.shtml' : 'ssi5.shtml';
|
||||||
my $word = on_windows() ? 'boot loader' : 'root';
|
my $word = on_windows() ? 'boot loader' : 'root';
|
||||||
o("GET /$abs_path HTTP/1.0\n\n",
|
o("GET /$abs_path HTTP/1.0\n\n",
|
||||||
"ssi_begin.+$word.+ssi_end", 'SSI #include file= (absolute)');
|
"ssi_begin.+$word.+ssi_end", 'SSI #include abspath');
|
||||||
o("GET /ssi7.shtml HTTP/1.0\n\n",
|
o("GET /ssi7.shtml HTTP/1.0\n\n",
|
||||||
'ssi_begin.+Unit test.+ssi_end', 'SSI #include "..."');
|
'ssi_begin.+Unit test.+ssi_end', 'SSI #include "..."');
|
||||||
o("GET /ssi8.shtml HTTP/1.0\n\n",
|
o("GET /ssi8.shtml HTTP/1.0\n\n",
|
||||||
@@ -410,9 +420,9 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
|
|||||||
# Manipulate the passwords file
|
# Manipulate the passwords file
|
||||||
my $path = 'test_htpasswd';
|
my $path = 'test_htpasswd';
|
||||||
unlink $path;
|
unlink $path;
|
||||||
system("$exe -A $path a b c") == 0
|
system("$mongoose_exe -A $path a b c") == 0
|
||||||
or fail("Cannot add user in a passwd file");
|
or fail("Cannot add user in a passwd file");
|
||||||
system("$exe -A $path a b c2") == 0
|
system("$mongoose_exe -A $path a b c2") == 0
|
||||||
or fail("Cannot edit user in a passwd file");
|
or fail("Cannot edit user in a passwd file");
|
||||||
my $content = read_file($path);
|
my $content = read_file($path);
|
||||||
$content =~ /^b:a:\w+$/gs or fail("Bad content of the passwd file");
|
$content =~ /^b:a:\w+$/gs or fail("Bad content of the passwd file");
|
||||||
@@ -450,7 +460,7 @@ sub do_PUT_test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub do_unit_test {
|
sub do_unit_test {
|
||||||
my $target = on_windows() ? 'w' : 'u';
|
my $target = on_windows() ? 'wi' : 'un';
|
||||||
system("make $target") == 0 or fail("Unit test failed!");
|
system("make $target") == 0 or fail("Unit test failed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+217
-31
@@ -22,13 +22,28 @@
|
|||||||
|
|
||||||
#define USE_WEBSOCKET
|
#define USE_WEBSOCKET
|
||||||
#define USE_LUA
|
#define USE_LUA
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#define __cdecl
|
||||||
|
#define USE_IPV6
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// USE_* definitions must be made before #include "mongoose.c" !
|
||||||
|
|
||||||
#include "mongoose.c"
|
#include "mongoose.c"
|
||||||
|
|
||||||
#define FATAL(str, line) do { \
|
static int s_total_tests = 0;
|
||||||
|
static int s_failed_tests = 0;
|
||||||
|
|
||||||
|
#define FAIL(str, line) do { \
|
||||||
printf("Fail on line %d: [%s]\n", line, str); \
|
printf("Fail on line %d: [%s]\n", line, str); \
|
||||||
abort(); \
|
s_failed_tests++; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ASSERT(expr) do { \
|
||||||
|
s_total_tests++; \
|
||||||
|
if (!(expr)) FAIL(#expr, __LINE__); \
|
||||||
} while (0)
|
} while (0)
|
||||||
#define ASSERT(expr) do { if (!(expr)) FATAL(#expr, __LINE__); } while (0)
|
|
||||||
|
|
||||||
#define HTTP_PORT "56789"
|
#define HTTP_PORT "56789"
|
||||||
#define HTTPS_PORT "56790"
|
#define HTTPS_PORT "56790"
|
||||||
@@ -38,10 +53,6 @@
|
|||||||
",127.0.0.1:" HTTPS_PORT "s" \
|
",127.0.0.1:" HTTPS_PORT "s" \
|
||||||
",127.0.0.1:" HTTP_PORT2
|
",127.0.0.1:" HTTP_PORT2
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
#define __cdecl
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void test_parse_http_message() {
|
static void test_parse_http_message() {
|
||||||
struct mg_request_info ri;
|
struct mg_request_info ri;
|
||||||
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
||||||
@@ -52,6 +63,10 @@ static void test_parse_http_message() {
|
|||||||
char req6[] = "G";
|
char req6[] = "G";
|
||||||
char req7[] = " blah ";
|
char req7[] = " blah ";
|
||||||
char req8[] = " HTTP/1.1 200 OK \n\n";
|
char req8[] = " HTTP/1.1 200 OK \n\n";
|
||||||
|
char req9[] = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
|
||||||
|
|
||||||
|
ASSERT(parse_http_message(req9, sizeof(req9), &ri) == sizeof(req9) - 1);
|
||||||
|
ASSERT(ri.num_headers == 1);
|
||||||
|
|
||||||
ASSERT(parse_http_message(req1, sizeof(req1), &ri) == sizeof(req1) - 1);
|
ASSERT(parse_http_message(req1, sizeof(req1), &ri) == sizeof(req1) - 1);
|
||||||
ASSERT(strcmp(ri.http_version, "1.1") == 0);
|
ASSERT(strcmp(ri.http_version, "1.1") == 0);
|
||||||
@@ -147,7 +162,8 @@ static void test_match_prefix(void) {
|
|||||||
ASSERT(match_prefix("*", 1, "/hello/") == 0);
|
ASSERT(match_prefix("*", 1, "/hello/") == 0);
|
||||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
|
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
|
||||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
|
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
|
||||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.a") == 6);
|
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
|
||||||
|
ASSERT(match_prefix("**o$", 4, "HELLO") == 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_remove_double_dots() {
|
static void test_remove_double_dots() {
|
||||||
@@ -187,6 +203,7 @@ static char *read_file(const char *path, int *size) {
|
|||||||
static const char *fetch_data = "hello world!\n";
|
static const char *fetch_data = "hello world!\n";
|
||||||
static const char *inmemory_file_data = "hi there";
|
static const char *inmemory_file_data = "hi there";
|
||||||
static const char *upload_filename = "upload_test.txt";
|
static const char *upload_filename = "upload_test.txt";
|
||||||
|
static const char *upload_filename2 = "upload_test2.txt";
|
||||||
static const char *upload_ok_message = "upload successful";
|
static const char *upload_ok_message = "upload successful";
|
||||||
|
|
||||||
static const char *open_file_cb(const struct mg_connection *conn,
|
static const char *open_file_cb(const struct mg_connection *conn,
|
||||||
@@ -200,16 +217,39 @@ static const char *open_file_cb(const struct mg_connection *conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void upload_cb(struct mg_connection *conn, const char *path) {
|
static void upload_cb(struct mg_connection *conn, const char *path) {
|
||||||
|
const struct mg_request_info *ri = mg_get_request_info(conn);
|
||||||
char *p1, *p2;
|
char *p1, *p2;
|
||||||
int len1, len2;
|
int len1, len2;
|
||||||
|
|
||||||
|
if (atoi(ri->query_string) == 1) {
|
||||||
ASSERT(!strcmp(path, "./upload_test.txt"));
|
ASSERT(!strcmp(path, "./upload_test.txt"));
|
||||||
ASSERT((p1 = read_file("mongoose.c", &len1)) != NULL);
|
ASSERT((p1 = read_file("main.c", &len1)) != NULL);
|
||||||
ASSERT((p2 = read_file(path, &len2)) != NULL);
|
ASSERT((p2 = read_file(path, &len2)) != NULL);
|
||||||
ASSERT(len1 == len2);
|
ASSERT(len1 == len2);
|
||||||
ASSERT(memcmp(p1, p2, len1) == 0);
|
ASSERT(memcmp(p1, p2, len1) == 0);
|
||||||
free(p1), free(p2);
|
free(p1), free(p2);
|
||||||
remove(upload_filename);
|
remove(upload_filename);
|
||||||
|
} else if (atoi(ri->query_string) == 2) {
|
||||||
|
if (!strcmp(path, "./upload_test.txt")) {
|
||||||
|
ASSERT((p1 = read_file("lua_5.2.1.h", &len1)) != NULL);
|
||||||
|
ASSERT((p2 = read_file(path, &len2)) != NULL);
|
||||||
|
ASSERT(len1 == len2);
|
||||||
|
ASSERT(memcmp(p1, p2, len1) == 0);
|
||||||
|
free(p1), free(p2);
|
||||||
|
remove(upload_filename);
|
||||||
|
} else if (!strcmp(path, "./upload_test2.txt")) {
|
||||||
|
ASSERT((p1 = read_file("mod_lua.c", &len1)) != NULL);
|
||||||
|
ASSERT((p2 = read_file(path, &len2)) != NULL);
|
||||||
|
ASSERT(len1 == len2);
|
||||||
|
ASSERT(memcmp(p1, p2, len1) == 0);
|
||||||
|
free(p1), free(p2);
|
||||||
|
remove(upload_filename);
|
||||||
|
} else {
|
||||||
|
ASSERT(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n\r\n%s",
|
mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n\r\n%s",
|
||||||
(int) strlen(upload_ok_message), upload_ok_message);
|
(int) strlen(upload_ok_message), upload_ok_message);
|
||||||
@@ -220,14 +260,15 @@ static int begin_request_handler_cb(struct mg_connection *conn) {
|
|||||||
|
|
||||||
if (!strcmp(ri->uri, "/data")) {
|
if (!strcmp(ri->uri, "/data")) {
|
||||||
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
||||||
"Content-Length: %d\r\n"
|
|
||||||
"Content-Type: text/plain\r\n\r\n"
|
"Content-Type: text/plain\r\n\r\n"
|
||||||
"%s", (int) strlen(fetch_data), fetch_data);
|
"%s", fetch_data);
|
||||||
|
close_connection(conn);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(ri->uri, "/upload")) {
|
if (!strcmp(ri->uri, "/upload")) {
|
||||||
ASSERT(mg_upload(conn, ".") == 1);
|
ASSERT(ri->query_string != NULL);
|
||||||
|
ASSERT(mg_upload(conn, ".") == atoi(ri->query_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -241,13 +282,14 @@ static int log_message_cb(const struct mg_connection *conn, const char *msg) {
|
|||||||
|
|
||||||
static const struct mg_callbacks CALLBACKS = {
|
static const struct mg_callbacks CALLBACKS = {
|
||||||
&begin_request_handler_cb, NULL, &log_message_cb, NULL, NULL, NULL, NULL,
|
&begin_request_handler_cb, NULL, &log_message_cb, NULL, NULL, NULL, NULL,
|
||||||
&open_file_cb, NULL, &upload_cb
|
&open_file_cb, NULL, &upload_cb, NULL, NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *OPTIONS[] = {
|
static const char *OPTIONS[] = {
|
||||||
"document_root", ".",
|
"document_root", ".",
|
||||||
"listening_ports", LISTENING_ADDR,
|
"listening_ports", LISTENING_ADDR,
|
||||||
"ssl_certificate", "build/ssl_cert.pem",
|
"enable_keep_alive", "yes",
|
||||||
|
"ssl_certificate", "ssl_cert.pem",
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -286,17 +328,18 @@ static void test_mg_download(void) {
|
|||||||
"GET / HTTP/1.0\r\n\r\n")) != NULL);
|
"GET / HTTP/1.0\r\n\r\n")) != NULL);
|
||||||
mg_close_connection(conn);
|
mg_close_connection(conn);
|
||||||
|
|
||||||
// Fetch mongoose.c, should succeed
|
// Fetch main.c, should succeed
|
||||||
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
|
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
|
||||||
"GET /mongoose.c HTTP/1.0\r\n\r\n")) != NULL);
|
"GET /main.c HTTP/1.0\r\n\r\n")) != NULL);
|
||||||
ASSERT(!strcmp(conn->request_info.uri, "200"));
|
ASSERT(!strcmp(conn->request_info.uri, "200"));
|
||||||
ASSERT((p1 = read_conn(conn, &len1)) != NULL);
|
ASSERT((p1 = read_conn(conn, &len1)) != NULL);
|
||||||
ASSERT((p2 = read_file("mongoose.c", &len2)) != NULL);
|
ASSERT((p2 = read_file("main.c", &len2)) != NULL);
|
||||||
ASSERT(len1 == len2);
|
ASSERT(len1 == len2);
|
||||||
ASSERT(memcmp(p1, p2, len1) == 0);
|
ASSERT(memcmp(p1, p2, len1) == 0);
|
||||||
free(p1), free(p2);
|
free(p1), free(p2);
|
||||||
mg_close_connection(conn);
|
mg_close_connection(conn);
|
||||||
|
|
||||||
|
|
||||||
// Fetch in-memory file, should succeed.
|
// Fetch in-memory file, should succeed.
|
||||||
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
|
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
|
||||||
"GET /blah HTTP/1.1\r\n\r\n")) != NULL);
|
"GET /blah HTTP/1.1\r\n\r\n")) != NULL);
|
||||||
@@ -306,6 +349,15 @@ static void test_mg_download(void) {
|
|||||||
free(p1);
|
free(p1);
|
||||||
mg_close_connection(conn);
|
mg_close_connection(conn);
|
||||||
|
|
||||||
|
// Fetch in-memory data with no Content-Length, should succeed.
|
||||||
|
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
|
||||||
|
"GET /data HTTP/1.1\r\n\r\n")) != NULL);
|
||||||
|
ASSERT((p1 = read_conn(conn, &len1)) != NULL);
|
||||||
|
ASSERT(len1 == (int) strlen(fetch_data));
|
||||||
|
ASSERT(memcmp(p1, fetch_data, len1) == 0);
|
||||||
|
free(p1);
|
||||||
|
mg_close_connection(conn);
|
||||||
|
|
||||||
// Test SSL redirect, IP address
|
// Test SSL redirect, IP address
|
||||||
ASSERT((conn = mg_download("localhost", atoi(HTTP_PORT), 0,
|
ASSERT((conn = mg_download("localhost", atoi(HTTP_PORT), 0,
|
||||||
ebuf, sizeof(ebuf), "%s",
|
ebuf, sizeof(ebuf), "%s",
|
||||||
@@ -337,24 +389,27 @@ static void test_mg_upload(void) {
|
|||||||
static const char *boundary = "OOO___MY_BOUNDARY___OOO";
|
static const char *boundary = "OOO___MY_BOUNDARY___OOO";
|
||||||
struct mg_context *ctx;
|
struct mg_context *ctx;
|
||||||
struct mg_connection *conn;
|
struct mg_connection *conn;
|
||||||
char ebuf[100], buf[20], *file_data, *post_data = NULL;
|
char ebuf[100], buf[20], *file_data, *file2_data, *post_data;
|
||||||
int file_len, post_data_len;
|
int file_len, file2_len, post_data_len;
|
||||||
|
|
||||||
ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
|
ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
|
||||||
ASSERT((file_data = read_file("mongoose.c", &file_len)) != NULL);
|
|
||||||
|
// Upload one file
|
||||||
|
ASSERT((file_data = read_file("main.c", &file_len)) != NULL);
|
||||||
|
post_data = NULL;
|
||||||
post_data_len = alloc_printf(&post_data, 0,
|
post_data_len = alloc_printf(&post_data, 0,
|
||||||
"--%s\r\n"
|
"--%s\r\n"
|
||||||
"Content-Disposition: form-data; "
|
"Content-Disposition: form-data; "
|
||||||
"name=\"file\"; "
|
"name=\"file\"; "
|
||||||
"filename=\"%s\"\r\n\r\n"
|
"filename=\"%s\"\r\n\r\n"
|
||||||
"%.*s\r\n"
|
"%.*s\r\n"
|
||||||
"--%s\r\n",
|
"--%s--\r\n",
|
||||||
boundary, upload_filename,
|
boundary, upload_filename,
|
||||||
file_len, file_data, boundary);
|
file_len, file_data, boundary);
|
||||||
ASSERT(post_data_len > 0);
|
ASSERT(post_data_len > 0);
|
||||||
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
|
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
|
||||||
ebuf, sizeof(ebuf),
|
ebuf, sizeof(ebuf),
|
||||||
"POST /upload HTTP/1.1\r\n"
|
"POST /upload?1 HTTP/1.1\r\n"
|
||||||
"Content-Length: %d\r\n"
|
"Content-Length: %d\r\n"
|
||||||
"Content-Type: multipart/form-data; "
|
"Content-Type: multipart/form-data; "
|
||||||
"boundary=%s\r\n\r\n"
|
"boundary=%s\r\n\r\n"
|
||||||
@@ -364,6 +419,47 @@ static void test_mg_upload(void) {
|
|||||||
ASSERT(mg_read(conn, buf, sizeof(buf)) == (int) strlen(upload_ok_message));
|
ASSERT(mg_read(conn, buf, sizeof(buf)) == (int) strlen(upload_ok_message));
|
||||||
ASSERT(memcmp(buf, upload_ok_message, strlen(upload_ok_message)) == 0);
|
ASSERT(memcmp(buf, upload_ok_message, strlen(upload_ok_message)) == 0);
|
||||||
mg_close_connection(conn);
|
mg_close_connection(conn);
|
||||||
|
|
||||||
|
// Upload two files
|
||||||
|
ASSERT((file_data = read_file("lua_5.2.1.h", &file_len)) != NULL);
|
||||||
|
ASSERT((file2_data = read_file("mod_lua.c", &file2_len)) != NULL);
|
||||||
|
post_data = NULL;
|
||||||
|
post_data_len = alloc_printf(&post_data, 0,
|
||||||
|
// First file
|
||||||
|
"--%s\r\n"
|
||||||
|
"Content-Disposition: form-data; "
|
||||||
|
"name=\"file\"; "
|
||||||
|
"filename=\"%s\"\r\n\r\n"
|
||||||
|
"%.*s\r\n"
|
||||||
|
|
||||||
|
// Second file
|
||||||
|
"--%s\r\n"
|
||||||
|
"Content-Disposition: form-data; "
|
||||||
|
"name=\"file\"; "
|
||||||
|
"filename=\"%s\"\r\n\r\n"
|
||||||
|
"%.*s\r\n"
|
||||||
|
|
||||||
|
// Final boundary
|
||||||
|
"--%s--\r\n",
|
||||||
|
boundary, upload_filename,
|
||||||
|
file_len, file_data,
|
||||||
|
boundary, upload_filename2,
|
||||||
|
file2_len, file2_data,
|
||||||
|
boundary);
|
||||||
|
ASSERT(post_data_len > 0);
|
||||||
|
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
|
||||||
|
ebuf, sizeof(ebuf),
|
||||||
|
"POST /upload?2 HTTP/1.1\r\n"
|
||||||
|
"Content-Length: %d\r\n"
|
||||||
|
"Content-Type: multipart/form-data; "
|
||||||
|
"boundary=%s\r\n\r\n"
|
||||||
|
"%.*s", post_data_len, boundary,
|
||||||
|
post_data_len, post_data)) != NULL);
|
||||||
|
free(file_data), free(file2_data), free(post_data);
|
||||||
|
ASSERT(mg_read(conn, buf, sizeof(buf)) == (int) strlen(upload_ok_message));
|
||||||
|
ASSERT(memcmp(buf, upload_ok_message, strlen(upload_ok_message)) == 0);
|
||||||
|
mg_close_connection(conn);
|
||||||
|
|
||||||
mg_stop(ctx);
|
mg_stop(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,13 +562,13 @@ static void test_lua(void) {
|
|||||||
ASSERT(lua_gettop(L) == 0);
|
ASSERT(lua_gettop(L) == 0);
|
||||||
|
|
||||||
check_lua_expr(L, "'hi'", "hi");
|
check_lua_expr(L, "'hi'", "hi");
|
||||||
check_lua_expr(L, "request_info.request_method", "POST");
|
check_lua_expr(L, "mg.request_info.request_method", "POST");
|
||||||
check_lua_expr(L, "request_info.uri", "/foo/bar");
|
check_lua_expr(L, "mg.request_info.uri", "/foo/bar");
|
||||||
check_lua_expr(L, "request_info.num_headers", "2");
|
check_lua_expr(L, "mg.request_info.num_headers", "2");
|
||||||
check_lua_expr(L, "request_info.remote_ip", "0");
|
check_lua_expr(L, "mg.request_info.remote_ip", "0");
|
||||||
check_lua_expr(L, "request_info.http_headers['Content-Length']", "12");
|
check_lua_expr(L, "mg.request_info.http_headers['Content-Length']", "12");
|
||||||
check_lua_expr(L, "request_info.http_headers['Connection']", "close");
|
check_lua_expr(L, "mg.request_info.http_headers['Connection']", "close");
|
||||||
(void) luaL_dostring(L, "post = read()");
|
(void) luaL_dostring(L, "post = mg.read()");
|
||||||
check_lua_expr(L, "# post", "12");
|
check_lua_expr(L, "# post", "12");
|
||||||
check_lua_expr(L, "post", "hello world!");
|
check_lua_expr(L, "post", "hello world!");
|
||||||
lua_close(L);
|
lua_close(L);
|
||||||
@@ -576,7 +672,93 @@ static void test_api_calls(void) {
|
|||||||
mg_stop(ctx);
|
mg_stop(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_url_decode(void) {
|
||||||
|
char buf[100];
|
||||||
|
|
||||||
|
ASSERT(mg_url_decode("foo", 3, buf, 3, 0) == -1); // No space for \0
|
||||||
|
ASSERT(mg_url_decode("foo", 3, buf, 4, 0) == 3);
|
||||||
|
ASSERT(strcmp(buf, "foo") == 0);
|
||||||
|
|
||||||
|
ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 0) == 2);
|
||||||
|
ASSERT(strcmp(buf, "a+") == 0);
|
||||||
|
|
||||||
|
ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 1) == 2);
|
||||||
|
ASSERT(strcmp(buf, "a ") == 0);
|
||||||
|
|
||||||
|
ASSERT(mg_url_decode("%61", 1, buf, sizeof(buf), 1) == 1);
|
||||||
|
ASSERT(strcmp(buf, "%") == 0);
|
||||||
|
|
||||||
|
ASSERT(mg_url_decode("%61", 2, buf, sizeof(buf), 1) == 2);
|
||||||
|
ASSERT(strcmp(buf, "%6") == 0);
|
||||||
|
|
||||||
|
ASSERT(mg_url_decode("%61", 3, buf, sizeof(buf), 1) == 1);
|
||||||
|
ASSERT(strcmp(buf, "a") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_mg_strcasestr(void) {
|
||||||
|
static const char *big1 = "abcdef";
|
||||||
|
ASSERT(mg_strcasestr("Y", "X") == NULL);
|
||||||
|
ASSERT(mg_strcasestr("Y", "y") != NULL);
|
||||||
|
ASSERT(mg_strcasestr(big1, "X") == NULL);
|
||||||
|
ASSERT(mg_strcasestr(big1, "CD") == big1 + 2);
|
||||||
|
ASSERT(mg_strcasestr("aa", "AAB") == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_mg_get_cookie(void) {
|
||||||
|
char buf[20];
|
||||||
|
|
||||||
|
ASSERT(mg_get_cookie("", "foo", NULL, sizeof(buf)) == -2);
|
||||||
|
ASSERT(mg_get_cookie("", "foo", buf, 0) == -2);
|
||||||
|
ASSERT(mg_get_cookie("", "foo", buf, sizeof(buf)) == -1);
|
||||||
|
ASSERT(mg_get_cookie("", NULL, buf, sizeof(buf)) == -1);
|
||||||
|
ASSERT(mg_get_cookie("a=1; b=2; c; d", "a", buf, sizeof(buf)) == 1);
|
||||||
|
ASSERT(strcmp(buf, "1") == 0);
|
||||||
|
ASSERT(mg_get_cookie("a=1; b=2; c; d", "b", buf, sizeof(buf)) == 1);
|
||||||
|
ASSERT(strcmp(buf, "2") == 0);
|
||||||
|
ASSERT(mg_get_cookie("a=1; b=123", "b", buf, sizeof(buf)) == 3);
|
||||||
|
ASSERT(strcmp(buf, "123") == 0);
|
||||||
|
ASSERT(mg_get_cookie("a=1; b=2; c; d", "c", buf, sizeof(buf)) == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_strtoll(void) {
|
||||||
|
ASSERT(strtoll("0", NULL, 10) == 0);
|
||||||
|
ASSERT(strtoll("123", NULL, 10) == 123);
|
||||||
|
ASSERT(strtoll("-34", NULL, 10) == -34);
|
||||||
|
ASSERT(strtoll("3566626116", NULL, 10) == 3566626116);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_parse_port_string(void) {
|
||||||
|
static const char *valid[] = {
|
||||||
|
"1", "1s", "1r", "1.2.3.4:1", "1.2.3.4:1s", "1.2.3.4:1r",
|
||||||
|
#if defined(USE_IPV6)
|
||||||
|
"[::1]:123", "[3ffe:2a00:100:7031::1]:900",
|
||||||
|
#endif
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
static const char *invalid[] = {
|
||||||
|
"0", "99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
struct socket so;
|
||||||
|
struct vec vec;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; valid[i] != NULL; i++) {
|
||||||
|
vec.ptr = valid[i];
|
||||||
|
vec.len = strlen(vec.ptr);
|
||||||
|
ASSERT(parse_port_string(&vec, &so) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; invalid[i] != NULL; i++) {
|
||||||
|
vec.ptr = invalid[i];
|
||||||
|
vec.len = strlen(vec.ptr);
|
||||||
|
ASSERT(parse_port_string(&vec, &so) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int __cdecl main(void) {
|
int __cdecl main(void) {
|
||||||
|
test_parse_port_string();
|
||||||
|
test_mg_strcasestr();
|
||||||
test_alloc_vprintf();
|
test_alloc_vprintf();
|
||||||
test_base64_encode();
|
test_base64_encode();
|
||||||
test_match_prefix();
|
test_match_prefix();
|
||||||
@@ -592,10 +774,14 @@ int __cdecl main(void) {
|
|||||||
test_mg_upload();
|
test_mg_upload();
|
||||||
test_request_replies();
|
test_request_replies();
|
||||||
test_api_calls();
|
test_api_calls();
|
||||||
|
test_url_decode();
|
||||||
|
test_mg_get_cookie();
|
||||||
|
test_strtoll();
|
||||||
#ifdef USE_LUA
|
#ifdef USE_LUA
|
||||||
test_lua();
|
test_lua();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
printf("%s\n", "PASSED");
|
printf("TOTAL TESTS: %d, FAILED: %d\n", s_total_tests, s_failed_tests);
|
||||||
return 0;
|
|
||||||
|
return s_failed_tests == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user