mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
268d2e9cbe | ||
|
|
1e7da874a4 | ||
|
|
7875a8c2bd | ||
|
|
b0355d45ae | ||
|
|
91136d7731 | ||
|
|
01a058d29b | ||
|
|
e6eca1738f | ||
|
|
2108ec5a0c | ||
|
|
bc4e47630a | ||
|
|
f41c9a2a1f | ||
|
|
6c77208236 | ||
|
|
386d2e5cd4 | ||
|
|
b738fdbffa | ||
|
|
b5704850db | ||
|
|
9c67e8960e | ||
|
|
d3dff4e1d5 | ||
|
|
82872e86fd | ||
|
|
ac229b6606 | ||
|
|
a6bf2bcbf4 | ||
|
|
b0cf8304cb | ||
|
|
0d9fe39dbb | ||
|
|
708af6d9c5 | ||
|
|
e4c71425c9 | ||
|
|
6e07215c1c | ||
|
|
ca9603301e | ||
|
|
d6c388042c | ||
|
|
6f66d794e8 | ||
|
|
1389eec784 | ||
|
|
6ef795e082 | ||
|
|
1e5d258e9e | ||
|
|
70eb4c4bbc | ||
|
|
a639e10a4d | ||
|
|
48750e67f6 | ||
|
|
ce60379af6 | ||
|
|
e0b9e4abfd | ||
|
|
665f205527 | ||
|
|
030daaf5d5 | ||
|
|
760511ec49 | ||
|
|
203688a34b | ||
|
|
29f4703e4f | ||
|
|
3cec6f6745 | ||
|
|
abcea07641 | ||
|
|
6a54704b29 | ||
|
|
8a2fbac4c0 | ||
|
|
96ce6bac69 | ||
|
|
50c0b958ff | ||
|
|
44416902d6 | ||
|
|
25e646c1a1 | ||
|
|
bd723bdc38 | ||
|
|
5e57031724 | ||
|
|
c583913290 | ||
|
|
870ca615de | ||
|
|
64b51699f8 | ||
|
|
50a889dac0 | ||
|
|
0ff0788458 | ||
|
|
fec6009edb | ||
|
|
c4f172a120 | ||
|
|
464c77314d | ||
|
|
f51ca8d5d1 | ||
|
|
7b37e0cb8b | ||
|
|
c7925353e7 | ||
|
|
bd23ec4323 | ||
|
|
3671b2632d | ||
|
|
25115a694c | ||
|
|
2436f9d6c3 | ||
|
|
2238951c18 | ||
|
|
f266dd2c94 | ||
|
|
b03358cb03 | ||
|
|
1bede7e28b | ||
|
|
3536670b82 | ||
|
|
3235c05fd6 | ||
|
|
63db54e7b5 | ||
|
|
1db51eb788 | ||
|
|
0e640ae885 | ||
|
|
1bff6df2a7 | ||
|
|
d0e66de225 | ||
|
|
f537670ce2 | ||
|
|
4cb3341a15 | ||
|
|
d52d9a4a6f | ||
|
|
4a076c7ea4 | ||
|
|
db6e883c22 | ||
|
|
804da054b5 | ||
|
|
fb4e701545 | ||
|
|
ccff4abf13 | ||
|
|
24a7936907 | ||
|
|
0993994139 | ||
|
|
8b740536de | ||
|
|
e851aac853 | ||
|
|
a39861b7bf | ||
|
|
63490249ba | ||
|
|
704f057384 | ||
|
|
40968bc496 | ||
|
|
6873f3c5f6 | ||
|
|
ba5cd7267a | ||
|
|
1bb98e5e8e | ||
|
|
a7774d0f1a | ||
|
|
f52c756fbb | ||
|
|
cc586c6ba2 | ||
|
|
606eab7f4f | ||
|
|
872b85b5f2 | ||
|
|
35bd2b6032 | ||
|
|
c5287b9044 | ||
|
|
223f6d960e | ||
|
|
464b895116 | ||
|
|
05d7b7c8bc | ||
|
|
d8b4db37a0 | ||
|
|
6bb49c780c | ||
|
|
3a60c5764d | ||
|
|
a8b38e4ab9 | ||
|
|
8750f8af1f | ||
|
|
e94a02dc12 | ||
|
|
5680b0fcc5 | ||
|
|
33c56b0edc | ||
|
|
e62e48397d | ||
|
|
8be30350be | ||
|
|
2609a2ab72 | ||
|
|
5a04ab2c56 | ||
|
|
4739de6c28 | ||
|
|
639d7a2fd8 | ||
|
|
8115ce1d82 | ||
|
|
3558b587b7 | ||
|
|
e21ec58ea8 | ||
|
|
74135bca7d | ||
|
|
d369f31608 | ||
|
|
d4b14ca3c1 | ||
|
|
f0994896d5 | ||
|
|
cfdd430b98 | ||
|
|
a6c05a9e83 | ||
|
|
94f63787bb | ||
|
|
367f5df328 | ||
|
|
f096138d6e | ||
|
|
0d00cd51e6 | ||
|
|
b216210a13 | ||
|
|
166833f330 | ||
|
|
4d6bedfd4e | ||
|
|
ace8c1736c | ||
|
|
c658a54d18 | ||
|
|
781a1dc25e | ||
|
|
3ea3b8dfa4 | ||
|
|
a514e143ff | ||
|
|
e1486a79e3 | ||
|
|
ee615248e3 | ||
|
|
687e0e2ee8 | ||
|
|
19aefcb7dd | ||
|
|
6a46244a21 | ||
|
|
60674c11c9 | ||
|
|
91c9ab807b | ||
|
|
9231c7346c | ||
|
|
20cb3a823a | ||
|
|
cbd8d69e6d | ||
|
|
f7fcfa6a42 | ||
|
|
293c5e5852 | ||
|
|
ceb866b4ee | ||
|
|
2f9166e96e | ||
|
|
fe0e0e69e5 | ||
|
|
db96641c10 | ||
|
|
fe83928ca4 | ||
|
|
014414f17f | ||
|
|
6ffe2d7f7a | ||
|
|
30c82d7efe | ||
|
|
999c4ad26c | ||
|
|
30a7965135 | ||
|
|
a8a017a048 | ||
|
|
eac8ffabc4 | ||
|
|
a7a17711ec | ||
|
|
18e28ef879 | ||
|
|
ce7515fca9 | ||
|
|
d5a78fb664 | ||
|
|
1b3a1b74ac | ||
|
|
8dd76e6ef4 | ||
|
|
976b5f37c1 | ||
|
|
31489ab7b1 | ||
|
|
1eb59940a4 | ||
|
|
576d769b56 | ||
|
|
afaba7ee17 | ||
|
|
40f257ef2b | ||
|
|
5840dc8153 | ||
|
|
5ed3dafb56 | ||
|
|
b99bf306ec | ||
|
|
89fae72aa7 | ||
|
|
ab147e8f19 | ||
|
|
81a78f9c8b | ||
|
|
370aeefdde | ||
|
|
c5407a75f4 | ||
|
|
7217aa22f1 | ||
|
|
69bd5f7aa2 | ||
|
|
41b3c1b23e | ||
|
|
f639409533 | ||
|
|
67c44799c8 | ||
|
|
4646e2ad55 | ||
|
|
b77532ea3f | ||
|
|
ed8168e8a1 | ||
|
|
39e2d2ad55 | ||
|
|
27c6656398 | ||
|
|
ffe83051fe | ||
|
|
93cd77ffad | ||
|
|
279c06c64c | ||
|
|
570c8213b6 | ||
|
|
f521999ea6 | ||
|
|
e41df3cf9c | ||
|
|
65f484291f | ||
|
|
77db89961b | ||
|
|
ad50310bb6 | ||
|
|
66ec3d1f1e | ||
|
|
a9da86d05c | ||
|
|
5f25a88821 | ||
|
|
fd7dc33645 | ||
|
|
9c22771a43 | ||
|
|
11640cc3c6 | ||
|
|
2264520a2f | ||
|
|
2f0b411ddd | ||
|
|
e0aceb4f3a | ||
|
|
ea9a60a59a | ||
|
|
458320cffb | ||
|
|
e0214af422 | ||
|
|
df5e997ea7 | ||
|
|
0a3cb63c73 | ||
|
|
37f7f3fb12 | ||
|
|
2ac970fa16 | ||
|
|
de35a353d4 | ||
|
|
dbbda02f70 | ||
|
|
725c30446c | ||
|
|
60473ae41a | ||
|
|
951d20f904 | ||
|
|
85a6abd58b | ||
|
|
2fbe09ad7c | ||
|
|
23325efab2 | ||
|
|
107902bae4 | ||
|
|
9bbf4c8384 | ||
|
|
c39a712540 | ||
|
|
e9f6111ba1 | ||
|
|
ba267a32fd | ||
|
|
f64ebbe0b1 | ||
|
|
8865dd5e84 | ||
|
|
ce57f47de0 | ||
|
|
48be2dbc2a | ||
|
|
8905522a94 | ||
|
|
eea1680cf9 | ||
|
|
d5004cb7eb | ||
|
|
1d1a05bf39 | ||
|
|
2009069037 | ||
|
|
8f7703c2a9 | ||
|
|
8e87c4f013 | ||
|
|
639a6dc2a8 | ||
|
|
cead9a04ca | ||
|
|
e528093027 | ||
|
|
c432b783fb | ||
|
|
e06976836f | ||
|
|
c9f7407573 | ||
|
|
a40e59dbe6 | ||
|
|
61ee3d36d0 | ||
|
|
94dbf21ffb | ||
|
|
998425b14b | ||
|
|
6900dd733a | ||
|
|
9e4cd054c8 | ||
|
|
5dc317fcb0 | ||
|
|
fbd5d77ce8 | ||
|
|
6f8751b479 | ||
|
|
877c4dc698 | ||
|
|
2dfc79129a | ||
|
|
416f4bf10f | ||
|
|
f9a99530ff | ||
|
|
940b65e4db | ||
|
|
b22f7d3a49 | ||
|
|
eaa0e26a85 | ||
|
|
a3a63f7e56 | ||
|
|
c9036f3ac0 | ||
|
|
60858d7fe5 | ||
|
|
1c914bde1d | ||
|
|
a487332aa0 | ||
|
|
3987d4d560 | ||
|
|
8d3181e0bf | ||
|
|
0f1a59aeb4 | ||
|
|
2cdf259940 | ||
|
|
0187fe943a | ||
|
|
a34d1ec00a | ||
|
|
e70d98b7b0 | ||
|
|
bea012a3c1 | ||
|
|
eb2d470f65 | ||
|
|
652c352394 | ||
|
|
f1b37b3101 | ||
|
|
81b9876af8 | ||
|
|
9e118c8a31 |
No files matched your search
@@ -5,7 +5,7 @@ All rights reserved
|
|||||||
This code is dual-licensed: you can redistribute it and/or modify
|
This code is dual-licensed: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License version 2 as
|
it under the terms of the GNU General Public License version 2 as
|
||||||
published by the Free Software Foundation. For the terms of this
|
published by the Free Software Foundation. For the terms of this
|
||||||
license, see http://www.gnu.org/licenses.
|
license, see <http://www.gnu.org/licenses>.
|
||||||
|
|
||||||
You are free to use this code under the terms of the GNU General
|
You are free to use this code under the terms of the GNU General
|
||||||
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
@@ -13,4 +13,4 @@ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|||||||
See the GNU General Public License for more details.
|
See the GNU General Public License for more details.
|
||||||
|
|
||||||
Alternatively, you can license this code under a commercial
|
Alternatively, you can license this code under a commercial
|
||||||
license, as set out in http://cesanta.com/products.html.
|
license, as set out in <http://cesanta.com/>.
|
||||||
@@ -1,30 +1,47 @@
|
|||||||
# Mongoose Web Server
|
# <img src="http://cesanta.com/images/mongoose_logo.png" width="64" height="64"> Mongoose Web Server
|
||||||
|
|
||||||
|
Mongoose is the most easy to use web server on the planet. A web server of choice for Web developers (PHP, Ruby, Python, etc) and Web designers.
|
||||||
|
|
||||||
|
Mongoose is built on top of Libmongoose embedded library, which can turn
|
||||||
|
anything into a web server in 5 minutes worth of effort and few lines of code.
|
||||||
|
Libmongoose is used to serve Web GUI on embedded devices, implement RESTful
|
||||||
|
services, RPC frameworks (e.g. JSON-RPC), handle telemetry data exchange, and
|
||||||
|
perform many other tasks in various different industries including aerospace,
|
||||||
|
manufacturing, finance, research, automotive, gaming, IT.
|
||||||
|
|
||||||
|
|
||||||
Mongoose is easy to use, powerful, embeddable web server.
|
|
||||||
* [Downloads](http://cesanta.com/downloads.html)
|
|
||||||
* [Documentation](http://cesanta.com/docs.html)
|
|
||||||
* [Mailing list](http://groups.google.com/group/mongoose-users)
|
* [Mailing list](http://groups.google.com/group/mongoose-users)
|
||||||
|
* [Downloads](http://cesanta.com/mongoose.shtml)
|
||||||
|
* [Documentation](http://cesanta.com/docs.shtml)
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
- Works on Windows, Mac, UNIX/Linux, iPhone, Android, and many other platforms
|
- Works on Windows, Mac, UNIX/Linux, iPhone, Android eCos, QNX
|
||||||
- CGI, SSL, Digest auth, Websocket, WEbDAV, Resumed download, URL rewrite, file blacklist, IP-based ACL, Windows service
|
and many other platforms
|
||||||
- Scripting and database support ([Lua](http://lua.org) +
|
- CGI, SSI, SSL, Digest auth, Websocket, WEbDAV, Resumed download,
|
||||||
[Sqlite](http://sqlite.org), see
|
URL rewrite, file blacklist
|
||||||
[dirscan.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/dirscan.lp) ),
|
- Custom error pages, Virtual hosts, IP-based ACL, Windows service,
|
||||||
which provides ready to go, powerful web development platform in
|
HTTP/HTTPS client
|
||||||
one single-click executable with no dependencies
|
|
||||||
- Simple and clean
|
- Simple and clean
|
||||||
[embedding API](https://github.com/cesanta/mongoose/blob/master/mongoose.h).
|
[embedding API](https://github.com/cesanta/mongoose/blob/master/mongoose.h).
|
||||||
The source is in single
|
The source is in single
|
||||||
[mongoose.c](https://github.com/cesanta/mongoose/blob/master/mongoose.c) file
|
[mongoose.c](https://github.com/cesanta/mongoose/blob/master/mongoose.c) file
|
||||||
to make embedding easy
|
to make embedding easy
|
||||||
- Extremely lightweight, has a core of under 40kB and tiny runtime footprint
|
- Extremely lightweight, has a core of under 40kB and tiny runtime footprint
|
||||||
- Asyncronouns, non-blocking core supporting single- or multi-threaded usage
|
- Asynchronous, non-blocking core supporting single- or multi-threaded usage
|
||||||
- On the market since 2004, stable, mature and tested,
|
- On the market since 2004 with over 1 million cumulative downloads
|
||||||
has several man-years invested in continuous improvement and refinement
|
- Stable, mature and tested, has several man-years invested
|
||||||
- Professional support and consultancy available from
|
in continuous improvement and refinement
|
||||||
[Cesanta Software](http://cesanta.com), developer of Mongoose
|
|
||||||
|
# Screenshots
|
||||||
|
|
||||||
|
Download, double-click to start, run browser -- that's all!
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
# Acknowledgements
|
# Acknowledgements
|
||||||
|
|
||||||
@@ -47,14 +64,23 @@ F.Morenius, and 10 others.
|
|||||||
|
|
||||||
# Licensing
|
# Licensing
|
||||||
|
|
||||||
Mongoose is released under
|
Mongoose is released under commercial and
|
||||||
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html).
|
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) open
|
||||||
Businesses have an option to get non-restrictive, royalty-free commercial
|
source licenses. The GPLv2 open source License does not generally permit
|
||||||
license and professional support from
|
incorporating this software into non-open source programs.
|
||||||
[Cesanta Software](http://cesanta.com).
|
For those customers who do not wish to comply with the GPLv2 open
|
||||||
|
source license requirements,
|
||||||
|
[Cesanta Software](http://cesanta.com) offers a full,
|
||||||
|
royalty-free commercial license and professional support
|
||||||
|
without any of the GPL restrictions.
|
||||||
|
|
||||||
[Frozen JSON parser](https://github.com/cesanta/frozen),
|
# Other products by Cesanta Software: simple and effective
|
||||||
[Super Light Regular Expression library](https://github.com/cesanta/slre),
|
|
||||||
[Super Light DNS Resolver](https://github.com/cesanta/sldr)
|
- [SSL Wrapper](https://github.com/cesanta/ssl_wrapper) - application to
|
||||||
are other projects by Cesanta Software, developed with the same philosophy
|
secure network communications
|
||||||
of functionality and simplicity.
|
- [Frozen](https://github.com/cesanta/frozen) - JSON parser and generator
|
||||||
|
- [SLRE](https://github.com/cesanta/slre) - Super Light Regular Expression
|
||||||
|
library
|
||||||
|
- [Net Skeleton](https://github.com/cesanta/net_skeleton) - framework for
|
||||||
|
building network applications
|
||||||
|
- [SLDR](https://github.com/cesanta/sldr) - Super Light DNS Resolver
|
||||||
+92
-55
@@ -7,8 +7,7 @@ there is not enough memory. `server_param`: Could be any pointer, or NULL.
|
|||||||
This pointer will be passed
|
This pointer will be passed
|
||||||
to the callback functions as `struct mg_connection::server_param` field.
|
to the callback functions as `struct mg_connection::server_param` field.
|
||||||
A common use case is to pass `this` pointer of the C++ wrapper class
|
A common use case is to pass `this` pointer of the C++ wrapper class
|
||||||
as `user_param`, to let the callback get the pointer to the C++
|
as `user_param`, to let the callback get the pointer to the C++ object.
|
||||||
object.
|
|
||||||
|
|
||||||
Note that this function doesn't make the
|
Note that this function doesn't make the
|
||||||
server instance to serve. Serving is done by `mg_poll_server()` function.
|
server instance to serve. Serving is done by `mg_poll_server()` function.
|
||||||
@@ -17,10 +16,7 @@ When server instance is created, it contains an information about
|
|||||||
the configuration and the state of each connection.
|
the configuration and the state of each connection.
|
||||||
Server instance is capable on listening on only one port. After creation,
|
Server instance is capable on listening on only one port. After creation,
|
||||||
`struct mg_server` has a list
|
`struct mg_server` has a list
|
||||||
of active connections, initially empty. It has a list of URI handlers,
|
of active connections and configuration parameters.
|
||||||
initially empty, and configuration parameters. Configuration can be
|
|
||||||
altered by `mg_set_option()`, and new URI handlers could be added by
|
|
||||||
`mg_add_uri_handler()`.
|
|
||||||
|
|
||||||
Side-effect: on UNIX, `mg_create_server()` ignores SIGPIPE signals. If custom
|
Side-effect: on UNIX, `mg_create_server()` ignores SIGPIPE signals. If custom
|
||||||
processing is required SIGPIPE, signal handler must be set up after
|
processing is required SIGPIPE, signal handler must be set up after
|
||||||
@@ -38,57 +34,84 @@ server pointer a NULL pointer.
|
|||||||
const char mg_set_option(struct mg_server *server, const char *name,
|
const char mg_set_option(struct mg_server *server, const char *name,
|
||||||
const char *value);
|
const char *value);
|
||||||
|
|
||||||
Sets a particular server option. Please refer to a separate documentation page
|
Sets a particular server option. Note that at least one option,
|
||||||
that lists all available option names. Note that at least one option,
|
|
||||||
`listening_port`, must be specified. To serve static files, `document_root`
|
`listening_port`, must be specified. To serve static files, `document_root`
|
||||||
must be specified too. If `document_root` option is left unset, Mongoose
|
must be specified too. If `document_root` option is left unset, Mongoose
|
||||||
will not access filesystem at all. This function returns NULL if option was
|
will not access filesystem at all. `mg_set_option()` returns NULL if option was
|
||||||
set successfully, otherwise it returns human-readable error string. It is
|
set successfully, otherwise it returns human-readable error string. It is
|
||||||
allowed to call `mg_set_option()` by the same thread that does
|
allowed to call `mg_set_option()` by the same thread that does
|
||||||
`mg_poll_server()` (an IO thread) and change server configuration while it
|
`mg_poll_server()` (Mongoose thread) and change server configuration while it
|
||||||
is serving, in between `mg_poll_server()` calls.
|
is serving, in between `mg_poll_server()` calls.
|
||||||
|
|
||||||
void mg_poll_server(struct mg_server *server, int milliseconds);
|
void mg_poll_server(struct mg_server *server, int milliseconds);
|
||||||
|
|
||||||
This function performs one iteration of IO loop by iterating over all
|
Performs one iteration of IO loop by iterating over all
|
||||||
active connections, performing `select()` syscall on all sockets with a timeout
|
active connections, performing `select()` syscall on all sockets with a timeout
|
||||||
of `milliseconds` number of milliseconds. When `select()` returns, Mongoose
|
of `milliseconds`. When `select()` returns, Mongoose
|
||||||
does an IO for each socket that has data to be sent or received. Application
|
does an IO for each socket that has data to be sent or received. Application
|
||||||
code must call `mg_poll_server()` in a loop. It is an error to have more then
|
code must call `mg_poll_server()` in a loop. It is an error to have more then
|
||||||
one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
|
one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
|
||||||
that take `struct mg_server *` parameter. Mongoose does not
|
that take `struct mg_server *` parameter. Mongoose does not
|
||||||
mutex-protect `struct mg_server *`, therefore the best practice is
|
mutex-protect `struct mg_server *`, therefore only single thread
|
||||||
to call server management functions from the same thread (an IO thread).
|
(Mongoose thread) should make Mongoose calls.
|
||||||
|
|
||||||
void mg_set_auth_handler(struct mg_server *, mg_handler_t handler);
|
`mg_poll_server()` calls user-specified event handler when certain events
|
||||||
|
occur. Sequence of events for the accepted connection is this:
|
||||||
|
|
||||||
Sets authorization handler. Called by Mongoose on each request, before
|
* `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
|
||||||
performing any other action. Handler can return either `MG_AUTH_OK` or
|
handler returns `MG_FALSE`, then Mongoose does not serve the request but
|
||||||
`MG_AUTH_FAIL`. If `handler` returns `MG_AUTH_FAIL`, then Mongoose sends
|
sends authorization request to the client. If `MG_TRUE` is returned,
|
||||||
digest authorization request to the client. If `handler returns `MG_AUTH_OK`,
|
then Mongoose continues on with the request.
|
||||||
then mongoose proceeds with handling the request. Handler function can use
|
* `MG_REQUEST` - Mongoose asks event handler to serve the request. If
|
||||||
`mg_authorize_digest()` function to verify authorization, or implement any other
|
event handler serves the request by sending a reply,
|
||||||
custom authorization mechanism.
|
it should return `MG_TRUE`. Otherwise,
|
||||||
|
it should return `MG_FALSE` which tells Mongoose that request is not
|
||||||
|
served and Mongoose should serve it. For example, event handler might
|
||||||
|
choose to serve only RESTful API requests with URIs that start with
|
||||||
|
certain prefix, and let Mongoose serve all static files.
|
||||||
|
If event handler decides to serve the request, but doesn't have
|
||||||
|
all the data at the moment, it should return `MG_MORE`. That tells
|
||||||
|
Mongoose to keep the connection open after callback returns.
|
||||||
|
|
||||||
void mg_set_request_handler(struct mg_server *, mg_handler_t handler);
|
`mg_connection::connection_param` pointer is a placeholder to keep
|
||||||
|
user-specific data. For example, handler could decide to open a DB
|
||||||
|
connection and store DB connection handle in `connection_param`.
|
||||||
|
* `MG_POLL` is sent to every connection on every iteration of
|
||||||
|
`mg_poll_server()`. Event handler should return `MG_FALSE` to ignore
|
||||||
|
this event. If event handler returns `MG_TRUE`, then Mongoose assumes
|
||||||
|
that event handler has finished sending data, and Mongoose will
|
||||||
|
close the connection.
|
||||||
|
* `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
|
||||||
|
to the client. Event handler can choose to send a reply itself, in which
|
||||||
|
case event handler must return `MG_TRUE`. Otherwise, event handler must
|
||||||
|
return `MG_FALSE`.
|
||||||
|
* `MG_CLOSE` is sent when the connection is closed. This event is used
|
||||||
|
to cleanup per-connection state stored in `connection_param`
|
||||||
|
if it was allocated. Event handler return value is ignored.
|
||||||
|
|
||||||
Sets a request handler. When set, `handler` will be called for each request.
|
Sequence of events for the client connection is this:
|
||||||
Possible return values from a handler function are `MG_REQUEST_NOT_PROCESSED`,
|
|
||||||
`MG_REQUEST_PROCESSED` and `MG_REQUEST_CALL_AGAIN`. If handler returns
|
|
||||||
`MG_REQUEST_NOT_PROCESSED`, mongoose will proceed with handling the request.
|
|
||||||
If handler returns `MG_REQUEST_PROCESSED`, that signals mongoose that handler
|
|
||||||
has already processed the connection, and mongoose will skip to the next
|
|
||||||
request. `MG_REQUEST_CALL_AGAIN` tells mongoose to call request handler
|
|
||||||
again and again on each `mg_poll_server()` iteration.
|
|
||||||
|
|
||||||
When mongoose buffers in HTTP request and successfully parses it, it calls
|
* `MG_CONNECT` sent when Mongoose has connected to the remote host.
|
||||||
appropriate URI handler immediately for GET requests. For POST requests,
|
This event is sent to the connection initiated by `mg_connect()` call.
|
||||||
|
Connection status is held in `mg_connection::status_code`: if zero,
|
||||||
|
then connection was successful, otherwise connection was not established.
|
||||||
|
User should send a request upon successful connection.
|
||||||
|
Event handler should return `MG_TRUE` if connection was successful and
|
||||||
|
HTTP request has been sent. Otherwise, it should send `MG_FALSE`.
|
||||||
|
* `MG_REPLY` is sent when response has been received from the remote host.
|
||||||
|
If event handler sends another request, then it should return `MG_TRUE`.
|
||||||
|
Otherwise it should return `MG_FALSE` and Mongoose will close the connection.
|
||||||
|
* `MG_CLOSE` same as for the accepted connection.
|
||||||
|
|
||||||
|
|
||||||
|
When mongoose buffers in HTTP request and successfully parses it, it sends
|
||||||
|
`MG_REQUEST` event for GET requests immediately. For POST requests,
|
||||||
Mongoose delays the call until the whole POST request is buffered in memory.
|
Mongoose delays the call until the whole POST request is buffered in memory.
|
||||||
POST data is available to the callback as `struct mg_connection::content`,
|
POST data is available to the callback as `struct mg_connection::content`,
|
||||||
and POST data length is in `struct mg_connection::content_len`.
|
and POST data length is in `struct mg_connection::content_len`.
|
||||||
|
|
||||||
Note that websocket connections are treated the same way. Mongoose buffers
|
Note that websocket connections are treated the same way. Mongoose buffers
|
||||||
websocket frame in memory, and calls URI handler when frame is fully
|
websocket frame in memory, and calls event handler when frame is fully
|
||||||
buffered. Frame data is available `struct mg_connection::content`, and
|
buffered. Frame data is available `struct mg_connection::content`, and
|
||||||
data length is in `struct mg_connection::content_len`, i.e. very similar to
|
data length is in `struct mg_connection::content_len`, i.e. very similar to
|
||||||
the POST request. `struct mg_connection::is_websocket` flag indicates
|
the POST request. `struct mg_connection::is_websocket` flag indicates
|
||||||
@@ -96,18 +119,8 @@ whether the request is websocket or not. Also, for websocket requests,
|
|||||||
there is `struct mg_connection::wsbits` field which contains first byte
|
there is `struct mg_connection::wsbits` field which contains first byte
|
||||||
of the websocket frame which URI handler can examine. Note that to
|
of the websocket frame which URI handler can examine. Note that to
|
||||||
reply to the websocket client, `mg_websocket_write()` should be used.
|
reply to the websocket client, `mg_websocket_write()` should be used.
|
||||||
To reply to the plain HTTP client, `mg_write_data()` should be used. Note that
|
To reply to the plain HTTP client, `mg_write_data()` should be used.
|
||||||
websocket handler must return either `MG_CLIENT_CLOSE` or `MG_CLIENT_CONTINUE`
|
|
||||||
value.
|
|
||||||
|
|
||||||
void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
|
|
||||||
|
|
||||||
Adds HTTP error handler. An actual HTTP error is passed as
|
|
||||||
`struct mg_connection::status_code` parameter.
|
|
||||||
Hanlder should return either `MG_ERROR_PROCESSED` or `MG_ERROR_NOT_PROCESSED`.
|
|
||||||
If handler returns `MG_ERROR_NOT_PROCESSED`, it
|
|
||||||
means a handler has not processed the connection, and mongoose proceeds
|
|
||||||
with sending HTTP error to the client. Otherwise, mongoose does nothing.
|
|
||||||
|
|
||||||
const char **mg_get_valid_option_names(void);
|
const char **mg_get_valid_option_names(void);
|
||||||
|
|
||||||
@@ -123,17 +136,14 @@ given parameter name is not valid, NULL is returned. For valid names, return
|
|||||||
value is guaranteed to be non-NULL. If parameter is not set, zero-length string
|
value is guaranteed to be non-NULL. If parameter is not set, zero-length string
|
||||||
is returned.
|
is returned.
|
||||||
|
|
||||||
|
void mg_wakeup_server_ex(struct mg_server *, mg_handler_t func,
|
||||||
|
const char *fmt, ...);
|
||||||
|
|
||||||
int mg_iterate_over_connections(struct mg_server *,
|
Sends string message to a server. Function `func` is called for every active
|
||||||
void (*func)(struct mg_connection *, void *),
|
connection. String message is passed in `struct mg_connection::callback_param`.
|
||||||
void *param);
|
This function is designed to push data to the connected clients, and
|
||||||
|
can be called from any thread. There is a limitation on the length of
|
||||||
This is an interface primarily designed to push arbitrary data to websocket
|
the message, currently at 8 kilobytes.
|
||||||
connections at any time. This function could be called from any thread. When
|
|
||||||
it returns, an IO thread called `func()` on each active connection,
|
|
||||||
passing `param` as an extra parameter. It is allowed to call `mg_send_data()` or
|
|
||||||
`mg_websocket_write()` within a callback, cause `func` is executed in the
|
|
||||||
context of the IO thread.
|
|
||||||
|
|
||||||
void mg_send_status(struct mg_connection *, int status_code);
|
void mg_send_status(struct mg_connection *, int status_code);
|
||||||
void mg_send_header(struct mg_connection *, const char *name,
|
void mg_send_header(struct mg_connection *, const char *name,
|
||||||
@@ -162,6 +172,15 @@ a response body. Mongoose provides functions for all three parts:
|
|||||||
|
|
||||||
<!-- -->
|
<!-- -->
|
||||||
|
|
||||||
|
void mg_send_file(struct mg_connection *, const char *path);
|
||||||
|
|
||||||
|
Tells Mongoose to serve given file. Mongoose handles file according to
|
||||||
|
it's extensions, i.e. Mongoose will invoke CGI script if `path` has CGI
|
||||||
|
extension, it'll render SSI file if `path` has SSI extension, etc. If `path`
|
||||||
|
points to a directory, Mongoose will show directory listing. If this function
|
||||||
|
is used, no calls to `mg_send*` or `mg_printf*` functions must be made, and
|
||||||
|
event handler must return `MG_MORE`.
|
||||||
|
|
||||||
int mg_websocket_write(struct mg_connection* conn, int opcode,
|
int mg_websocket_write(struct mg_connection* conn, int opcode,
|
||||||
const char *data, size_t data_len);
|
const char *data, size_t data_len);
|
||||||
|
|
||||||
@@ -206,3 +225,21 @@ cookie-based way please refer to the examples/chat.c in the source tree.
|
|||||||
If password is not NULL, entry is added (or modified if already exists).
|
If password is not NULL, entry is added (or modified if already exists).
|
||||||
If password is NULL, entry is deleted.
|
If password is NULL, entry is deleted.
|
||||||
Return: 1 on success, 0 on error.
|
Return: 1 on success, 0 on error.
|
||||||
|
|
||||||
|
|
||||||
|
int mg_parse_multipart(const char *buf, int buf_len,
|
||||||
|
char *var_name, int var_name_len,
|
||||||
|
char *file_name, int file_name_len,
|
||||||
|
const char **data, int *data_len);
|
||||||
|
|
||||||
|
Parses a buffer that contains multipart form data. Stores chunk name
|
||||||
|
in a `var_name` buffer. If chunk is an uploaded file, then `file_name`
|
||||||
|
will have a file name. `data` and `data_len` will point to the chunk data.
|
||||||
|
Returns number of bytes to skip to the next chunk.
|
||||||
|
|
||||||
|
struct mg_connection *mg_connect(struct mg_server *server,
|
||||||
|
const char *host, int port, int use_ssl);
|
||||||
|
|
||||||
|
Create connection to the remote host. Returns `NULL` on error, non-null
|
||||||
|
if the connection has been scheduled for connection. Upon a connection,
|
||||||
|
Mongoose will send `MG_CONNECT` event to the event handler.
|
||||||
@@ -7,7 +7,7 @@ Note : You dont need root access to run mongoose on Android.
|
|||||||
|
|
||||||
- Clone Mongoose Git repo
|
- Clone Mongoose Git repo
|
||||||
- Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html)
|
- 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`
|
- Run `/path-to-ndk/ndk-build -C /path-to-mongoose/examples`
|
||||||
That should generate mongoose/lib/armeabi/mongoose
|
That should generate mongoose/lib/armeabi/mongoose
|
||||||
- Using the adb tool (you need to have Android SDK installed for that),
|
- Using the adb tool (you need to have Android SDK installed for that),
|
||||||
push the generated mongoose binary to `/data/local` folder on device.
|
push the generated mongoose binary to `/data/local` folder on device.
|
||||||
@@ -15,7 +15,7 @@ Note : You dont need root access to run mongoose on Android.
|
|||||||
- To test if the server is running fine, visit your web-browser and
|
- 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.
|
navigate to `http://127.0.0.1:8080` You should see the `Index of /` page.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
How To Create Basic Website With Mongoose
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
|
||||||
|
|
||||||
|

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

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

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

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

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

|
||||||
|
|
||||||
|
## 7. Click on the mongoose icon and choose "Go to my address" menu:
|
||||||
|

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

|
||||||
+108
-37
@@ -13,34 +13,81 @@ Embedding Mongoose is done in two steps:
|
|||||||
Here's a minimal application `app.c` that embeds mongoose:
|
Here's a minimal application `app.c` that embeds mongoose:
|
||||||
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_server *server = mg_create_server(NULL);
|
struct mg_server *server = mg_create_server(NULL, NULL);
|
||||||
mg_set_option(server, "document_root", ".");
|
mg_set_option(server, "document_root", "."); // Serve current directory
|
||||||
mg_set_option(server, "listening_port", "8080");
|
mg_set_option(server, "listening_port", "8080"); // Open port 8080
|
||||||
for (;;) mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
|
|
||||||
|
for (;;) {
|
||||||
|
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
|
||||||
|
}
|
||||||
mg_destroy_server(&server);
|
mg_destroy_server(&server);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
To compile it, put `mongoose.c`, `mongoose.h` and `minimal.c` into one
|
To compile it, put `mongoose.c`, `mongoose.h` and `app.c` into one
|
||||||
folder, start terminal on UNIX or Visual Studio command line prompt on Windows,
|
folder, start terminal on UNIX or Visual Studio command line prompt on Windows,
|
||||||
and run the following command:
|
and run the following command:
|
||||||
|
|
||||||
cc app.c mongoose.c -o app # on Unix
|
cc app.c mongoose.c -pthread -o app # on Unix
|
||||||
cl app.c mongoose.c /TC /MD # on Windows
|
cl.exe app.c mongoose.c /TC /MD # on Windows
|
||||||
|
|
||||||
When run, this simple application opens port 8080 and serves static files,
|
When run, this simple application opens port 8080 and serves static files,
|
||||||
CGI files and lists directory content in the current working directory.
|
CGI files and lists directory content in the current working directory.
|
||||||
|
|
||||||
Mongoose can call user-defined functions when certain URIs are requested.
|
It is possible to generate HTML page content. Mongoose can call user-defined
|
||||||
These functions are _called uri handlers_. `mg_add_uri_handler()` registers
|
function when certain events occur.
|
||||||
an URI handler, and there is no restriction exist on the number of URI handlers.
|
That function is called _an event handler_, and it is the second parameter
|
||||||
Also, mongoose can call a user-defined function when it is about to send
|
to `mg_create_server()` function. Here is the example event handler function:
|
||||||
HTTP error back to client. That function is called _http error handler_ and
|
|
||||||
can be registered by `mg_set_http_error_handler()`. Handlers are called
|
int event_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
by Mongoose with `struct mg_connection *` pointer as a parameter, which
|
switch (ev) {
|
||||||
|
case MG_AUTH: return MG_TRUE;
|
||||||
|
default: return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Event handler is called by Mongoose with `struct mg_connection *`
|
||||||
|
pointer and an event number. `struct mg_connection *conn`
|
||||||
has all information about the request: HTTP headers, POST or websocket
|
has all information about the request: HTTP headers, POST or websocket
|
||||||
data buffer, etcetera.
|
data buffer, etcetera. `enum mg_event ev` tells which exactly event is sent.
|
||||||
|
For each event, an event handler returns a value which tells Mongoose how
|
||||||
|
to behave.
|
||||||
|
|
||||||
|
The sequence of events for every connection is this:
|
||||||
|
|
||||||
|
* `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
|
||||||
|
handler returns `MG_FALSE`, then Mongoose does not serve the request but
|
||||||
|
sends authorization request to the client. If `MG_TRUE` is returned,
|
||||||
|
then Mongoose continues on with the request.
|
||||||
|
* `MG_REQUEST` - Mongoose asks event handler to serve the request. If
|
||||||
|
event handler serves the request by sending a reply,
|
||||||
|
it should return `MG_TRUE`. Otherwise,
|
||||||
|
it should return `MG_FALSE` which tells Mongoose that request is not
|
||||||
|
served and Mongoose should serve it. For example, event handler might
|
||||||
|
choose to serve only RESTful API requests with URIs that start with
|
||||||
|
certain prefix, and let Mongoose serve all static files.
|
||||||
|
If event handler decides to serve the request, but doesn't have
|
||||||
|
all the data at the moment, it should return `MG_MORE`. That tells
|
||||||
|
Mongoose to keep the connection open after callback returns.
|
||||||
|
|
||||||
|
`mg_connection::connection_param` pointer is a placeholder to keep
|
||||||
|
user-specific data. For example, handler could decide to open a DB
|
||||||
|
connection and store DB connection handle in `connection_param`.
|
||||||
|
* `MG_POLL` is sent to every connection on every iteration of
|
||||||
|
`mg_poll_server()`. Event handler should return `MG_FALSE` to ignore
|
||||||
|
this event. If event handler returns `MG_TRUE`, then Mongoose assumes
|
||||||
|
that event handler has finished sending data, and Mongoose will
|
||||||
|
close the connection.
|
||||||
|
* `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
|
||||||
|
to the client. Event handler can choose to send a reply itself, in which
|
||||||
|
case event handler must return `MG_TRUE`. Otherwise, event handler must
|
||||||
|
return `MG_FALSE`
|
||||||
|
* `MG_CLOSE` is sent when the connection is closed. This event is used
|
||||||
|
to cleanup per-connection state stored in `connection_param`
|
||||||
|
if it was allocated.
|
||||||
|
|
||||||
Let's extend our minimal application example and
|
Let's extend our minimal application example and
|
||||||
create an URI that will be served by user's C code. The app will handle
|
create an URI that will be served by user's C code. The app will handle
|
||||||
@@ -50,22 +97,46 @@ http://127.0.0.1:8080/hello will say hello, and here's the code:
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
static int handle_hello(struct mg_connection *conn) {
|
static int event_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
static const char *reply = "Hello world!\n";
|
if (ev == MG_AUTH) {
|
||||||
mg_write_data(conn, reply, strlen(reply));
|
return MG_TRUE; // Authorize all requests
|
||||||
return 1;
|
} else if (ev == MG_REQUEST && !strcmp(conn->uri, "/hello")) {
|
||||||
|
mg_printf_data(conn, "%s", "Hello world");
|
||||||
|
return MG_TRUE; // Mark as processed
|
||||||
|
} else {
|
||||||
|
return MG_FALSE; // Rest of the events are not processed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_server *server = mg_create_server(NULL);
|
struct mg_server *server = mg_create_server(NULL, event_handler);
|
||||||
mg_set_option(server, "document_root", ".");
|
mg_set_option(server, "document_root", ".");
|
||||||
mg_set_option(server, "listening_port", "8080");
|
mg_set_option(server, "listening_port", "8080");
|
||||||
mg_add_uri_handler(server, "/hello", &handle_hello);
|
|
||||||
for (;;) mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
|
for (;;) {
|
||||||
|
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
|
||||||
|
}
|
||||||
mg_destroy_server(&server);
|
mg_destroy_server(&server);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## Example code
|
||||||
|
|
||||||
|
Mongoose source code contains a well-commented example code, listed below:
|
||||||
|
|
||||||
|
* [hello.c](https://github.com/cesanta/mongoose/blob/master/examples/hello.c)
|
||||||
|
a minimalistic hello world example
|
||||||
|
* [post.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
|
||||||
|
shows how to handle form input
|
||||||
|
* [upload.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
|
||||||
|
shows how to upload files
|
||||||
|
* [websocket.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates websocket usage
|
||||||
|
* [auth.c](https://github.com/cesanta/mongoose/blob/master/examples/auth.c) demonstrates API-controlled Digest authorization
|
||||||
|
* [mjpg.c](https://github.com/cesanta/mongoose/blob/master/examples/mjpg.c) demonstrates MJPEG streaming implementation
|
||||||
|
|
||||||
|
## Compilation flags
|
||||||
|
|
||||||
Below is the list of compilation flags that enable or disable certain
|
Below is the list of compilation flags that enable or disable certain
|
||||||
features. By default, some features are enabled, and could be disabled
|
features. By default, some features are enabled, and could be disabled
|
||||||
by setting appropriate `NO_*` flag. Features that are disabled by default
|
by setting appropriate `NO_*` flag. Features that are disabled by default
|
||||||
@@ -73,6 +144,12 @@ could be enabled by setting appropriate `USE_*` flag. Bare bones Mongoose
|
|||||||
is quite small, about 30 kilobytes of compiled x86 code. Each feature adds
|
is quite small, about 30 kilobytes of compiled x86 code. Each feature adds
|
||||||
a couple of kilobytes to the executable size, and also has some runtime penalty.
|
a couple of kilobytes to the executable size, and also has some runtime penalty.
|
||||||
|
|
||||||
|
Note that some flags start with `NS_` prefix. This is because Mongoose uses
|
||||||
|
[Net Skeleton](http://github.com/cesanta/net_skeleton) as a low-level
|
||||||
|
networking engine. If user code has `#include <net_skeleton.h>`, then
|
||||||
|
all Net Skeleton functions will be available too.
|
||||||
|
|
||||||
|
|
||||||
-DMONGOOSE_NO_AUTH Disable MD5 authorization support
|
-DMONGOOSE_NO_AUTH Disable MD5 authorization support
|
||||||
-DMONGOOSE_NO_CGI Disable CGI support
|
-DMONGOOSE_NO_CGI Disable CGI support
|
||||||
-DMONGOOSE_NO_DAV Disable WebDAV support
|
-DMONGOOSE_NO_DAV Disable WebDAV support
|
||||||
@@ -84,25 +161,19 @@ a couple of kilobytes to the executable size, and also has some runtime penalty.
|
|||||||
-DMONGOOSE_NO_WEBSOCKET Disable WebSocket support
|
-DMONGOOSE_NO_WEBSOCKET Disable WebSocket support
|
||||||
|
|
||||||
-DMONGOOSE_USE_IDLE_TIMEOUT_SECONDS=X Idle connection timeout, default is 30
|
-DMONGOOSE_USE_IDLE_TIMEOUT_SECONDS=X Idle connection timeout, default is 30
|
||||||
-DMONGOOSE_USE_IPV6 Enable IPv6 support
|
|
||||||
-DMONGOOSE_USE_LUA Enable Lua scripting
|
-DMONGOOSE_USE_LUA Enable Lua scripting
|
||||||
-DMONGOOSE_USE_LUA_SQLITE3 Enable sqlite3 binding for Lua
|
-DMONGOOSE_USE_LUA_SQLITE3 Enable sqlite3 binding for Lua
|
||||||
-DMONGOOSE_USE_SSL Enable SSL
|
|
||||||
-DMONGOOSE_USE_POST_SIZE_LIMIT=X POST requests larger than X will be
|
-DMONGOOSE_USE_POST_SIZE_LIMIT=X POST requests larger than X will be
|
||||||
rejected, not set by default
|
rejected, not set by default
|
||||||
-DMONGOOSE_USE_EXTRA_HTTP_HEADERS=X Append X to the HTTP headers
|
-DMONGOOSE_USE_EXTRA_HTTP_HEADERS=X Append X to the HTTP headers
|
||||||
for static files, empty by default
|
for static files, empty by default
|
||||||
-DMONGOOSE_USE_STACK_SIZE=X Let mg_start_thread() use stack
|
|
||||||
size X, default is OS default
|
|
||||||
-DMONGOOSE_ENABLE_DEBUG Enables debug messages on stdout, very noisy
|
|
||||||
|
|
||||||
Mongoose source code contains a well-commented example code, listed below:
|
-DNS_ENABLE_DEBUG Enables debug messages on stdout, very noisy
|
||||||
|
-DNS_ENABLE_SSL Enable SSL
|
||||||
* [hello.c](https://github.com/cesanta/mongoose/blob/master/examples/hello.c)
|
-DNS_ENABLE_IPV6 Enable IPv6 support
|
||||||
a minimalistic hello world example
|
-DNS_ENABLE_HEXDUMP Enables hexdump of sent and received traffic
|
||||||
* [post.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
|
-DNS_STACK_SIZE=X Sets stack size to X for ns_start_thread()
|
||||||
shows how to handle form input
|
-DNS_DISABLE_THREADS Disable threads support
|
||||||
* [upload.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
|
-DNS_DISABLE_SOCKETPAIR For systems without loopback interface
|
||||||
shows how to upload files
|
-DMONGOOSE_SEND_NS_EVENTS Send Net Skeleton events to the event handler
|
||||||
* [websocket.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates websocket usage
|
in addition to the Mongoose events
|
||||||
* [auth.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates API-controlled Digest authorization
|
|
||||||
+24
-2
@@ -1,6 +1,28 @@
|
|||||||
# Mongoose FAQ
|
# Mongoose FAQ
|
||||||
|
|
||||||
### PHP doesn't work: getting empty page, or 'File not found' error
|
## My Antivirus Software reports Mongoose as a security threat
|
||||||
|
|
||||||
|
Mongoose doesn't contain any malicious logic. Antivirus reports a
|
||||||
|
[false positive](http://en.wikipedia.org/wiki/Type_I_and_type_II_errors#False_positive_error).
|
||||||
|
This is when certain byte sequence in Mongoose accidentally matches
|
||||||
|
virus signature in the Antivirus database.
|
||||||
|
|
||||||
|
## Download page doesn't work
|
||||||
|
|
||||||
|
Please make sure Javascript is enabled in your browser, and that the
|
||||||
|
antivirus software is not blocking the download.
|
||||||
|
|
||||||
|
|
||||||
|
## MacOS message: "Mongoose.app is damaged and can’t be opened. You should move it to the Trash"
|
||||||
|
|
||||||
|
This happens on newer MacOS systems. The reason for the message
|
||||||
|
is the fact Mongoose.app is not digitally signed.
|
||||||
|
Mongoose download procedure changes the app on the fly by injecting
|
||||||
|
user information in the binary, making any prior digital signature void.
|
||||||
|
Open "System Preferences" -> "Security" and set "Allow apps downloaded from"
|
||||||
|
to "Anywhere". Revert the settings once Mongoose is installed.
|
||||||
|
|
||||||
|
## PHP doesn't work: getting empty page, or 'File not found' error
|
||||||
|
|
||||||
The reason for that is wrong paths to the interpreter. Remember that with PHP,
|
The reason for that is wrong paths to the interpreter. Remember that with PHP,
|
||||||
correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify
|
correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify
|
||||||
@@ -8,7 +30,7 @@ full path to the PHP interpreter, e.g.:
|
|||||||
|
|
||||||
mongoose -cgi_interpreter /full/path/to/php-cgi
|
mongoose -cgi_interpreter /full/path/to/php-cgi
|
||||||
|
|
||||||
### Mongoose fails to start
|
## Mongoose fails to start
|
||||||
|
|
||||||
If Mongoose exits immediately when run, this
|
If Mongoose exits immediately when run, this
|
||||||
usually indicates a syntax error in the configuration file
|
usually indicates a syntax error in the configuration file
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
How To Share Files With Mongoose
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
## 1. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside the directory you want to share:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 2. Double-click mongoose executable. A browser will start automatically, an icon will appear on a system tray in the bottom right corner of the desktop:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 3. Click on the mongoose icon
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## 4. Click on "Go to my address" to launch a browser locally. Or, to access a folder from another machine, launch a browser and type in the URL:
|
||||||
|
|
||||||
|

|
||||||
+9
-17
@@ -4,16 +4,16 @@ Pre-built Windows and Mac mongoose binaries support Lua Server Pages
|
|||||||
functionality.
|
functionality.
|
||||||
That means it is possible to write PHP-like scripts with mongoose
|
That means it is possible to write PHP-like scripts with mongoose
|
||||||
using Lua programming language instead of PHP. Lua is known
|
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
|
for it's speed and small size. Mongoose uses Lua version 5.2.3, the
|
||||||
documentation for it can be found at
|
documentation for it can be found at
|
||||||
[Lua 5.2 reference manual](http://www.lua.org/manual/5.2/).
|
[Lua 5.2 reference manual](http://www.lua.org/manual/5.2/).
|
||||||
|
|
||||||
To create a Lua Page, make a file that is called `<something>.lp`. For example,
|
To create a Lua Page, make a file that is called `ANY_NAME.lp`. For example,
|
||||||
let's say it is going to be `my_page.lp`. It is important to have a file
|
`my_page.lp`. It is important to have a file
|
||||||
name that ends up with `.lp`, cause this is the way mongoose recognises
|
name that ends up with `.lp`, cause this is the way mongoose recognises
|
||||||
Lua Page file. The contents of the file, just like
|
Lua Page file. The contents of the file, just like
|
||||||
with PHP, is HTML with embedded Lua code. Lua code must be enclosed within
|
with PHP, is HTML with embedded Lua code. Lua code must be enclosed within
|
||||||
`<? ?>` blocks, and can appear anywhere on the page.
|
`<? ?>` blocks, and can appear anywhere on the page.
|
||||||
|
|
||||||
Mongoose does not send HTTP headers for Lua pages. Therefore,
|
Mongoose does not send HTTP headers for Lua pages. Therefore,
|
||||||
every Lua Page must begin with HTTP status line and headers, like this:
|
every Lua Page must begin with HTTP status line and headers, like this:
|
||||||
@@ -27,26 +27,18 @@ 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
|
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
|
inside Lua code. In addition to `mg.write()`, all standard library functions
|
||||||
are accessible from the Lua code (please check reference manual for details).
|
are accessible from the Lua code (please check reference manual for details).
|
||||||
Information about the request is available in `mg.request_info` object.
|
Information about the request is available via the `mg.request_info` object.
|
||||||
I contains request method, all headers, etcetera. Please refer to
|
I contains request method, all headers, etcetera. Please refer to
|
||||||
`struct mg_request_info` definition in
|
`struct mg_request_info` definition in
|
||||||
[mongoose.h](https://github.com/cesanta/mongoose/blob/master/mongoose.h)
|
[mongoose.h](https://github.com/cesanta/mongoose/blob/master/mongoose.h)
|
||||||
to see what is available in `mg.request_info`. Also,
|
to see what is available via the `mg.request_info` object.
|
||||||
[page.lp](https://github.com/cesanta/mongoose/blob/master/test/page.lp) and
|
Check out [prime_numbers.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/prime_numbers.lp) for some example.
|
||||||
[prime_numbers.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/prime_numbers.lp)
|
|
||||||
contain some example code that uses `request_info` and other functions,
|
|
||||||
like form submission.
|
|
||||||
|
|
||||||
Mongoose exports the following to the Lua server page:
|
Mongoose exports the following to the Lua Server Page:
|
||||||
|
|
||||||
mg.write(str) -- writes string to the client
|
mg.write(str) -- writes string to the client
|
||||||
mg.onerror(msg) -- error handler, can be overridden
|
mg.onerror(msg) -- error handler, can be overridden
|
||||||
mg.request_info -- a table with request information
|
mg.request_info -- a table with request information
|
||||||
sqlite3 -- Sqlite3 interface
|
|
||||||
|
|
||||||
slite3 functions are documented at [lua.sqlite.org](http://lua.sqlite.org/),
|
|
||||||
and usage example is at
|
|
||||||
[page.lp](https://github.com/cesanta/mongoose/blob/master/test/page.lp).
|
|
||||||
|
|
||||||
Using Lua scripting it is easy to emulate SSI functionality. For example,
|
Using Lua scripting it is easy to emulate SSI functionality. For example,
|
||||||
to include the content of another file, one can write:
|
to include the content of another file, one can write:
|
||||||
@@ -56,4 +48,4 @@ to include the content of another file, one can write:
|
|||||||
To serve a Lua Page, mongoose creates Lua context. That context is used for
|
To serve a Lua Page, mongoose creates Lua context. That context is used for
|
||||||
all Lua blocks within the page. That means, all Lua blocks on the same page
|
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
|
share the same context. If one block defines a variable, for example, that
|
||||||
variable is visible in the block that follows.
|
variable is visible in all following blocks.
|
||||||
+32
-24
@@ -10,8 +10,12 @@ this means to deny only that single IP address.
|
|||||||
|
|
||||||
Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow
|
Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow
|
||||||
all accesses. On each request the full list is traversed, and
|
all accesses. On each request the full list is traversed, and
|
||||||
the last match wins. Example: `$ mongoose -access_control_list -0.0.0.0/0,+192.168/16` to deny all acccesses except from 192.168/16 subnet. To learn
|
the last match wins. Example: `$ mongoose -access_control_list -0.0.0.0/0,+192.168/16` to deny all acccesses except those from `192.168/16` subnet. Note that if the option is set, then all accesses are forbidden
|
||||||
more about subnet masks, see the
|
by default. Thus in a previous example, `-0.0.0.0` part is not necessary.
|
||||||
|
For example, `$mongoose access_control_list +10.0.0.0/8`
|
||||||
|
means disallow all, allow subnet 10/8 only.
|
||||||
|
|
||||||
|
To learn more about subnet masks, see the
|
||||||
[Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork)
|
[Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork)
|
||||||
|
|
||||||
Default: not set, all accesses are allowed.
|
Default: not set, all accesses are allowed.
|
||||||
@@ -24,27 +28,32 @@ mongoose executable. Default: not set, no query logging is done.
|
|||||||
Authorization realm used in `.htpasswd` authorization. Default: `mydomain.com`
|
Authorization realm used in `.htpasswd` authorization. Default: `mydomain.com`
|
||||||
|
|
||||||
### cgi_interpreter
|
### cgi_interpreter
|
||||||
Path to an executable to use as CGI interpreter for __all__ CGI scripts
|
Path to an executable to be used use as an interpreter for __all__ CGI scripts
|
||||||
regardless script extension.
|
regardless script extension. Default: not set, Mongoose looks at
|
||||||
for an interpreter. Default: not set, Mongoose looks at
|
|
||||||
[shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\).
|
[shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\).
|
||||||
|
|
||||||
For example, if both PHP and perl CGIs are used, then
|
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
|
`#!/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,
|
respective CGI scripts. Note that paths should be either full file paths,
|
||||||
or file paths relative to the directory where mongoose server is located.
|
or file paths relative to the directory where mongoose executable is located.
|
||||||
|
|
||||||
If all CGIs use the same interpreter, for example they are all PHP, then
|
If all CGIs use the same interpreter, for example they are all PHP, then
|
||||||
`cgi_interpreter` option can be set to the path to `php-cgi.exe` executable and
|
`cgi_interpreter` option can be set to the path to `php-cgi.exe` executable and
|
||||||
shebang line in the CGI scripts can be omitted.
|
shebang line in the CGI scripts can be omitted.
|
||||||
Note that PHP scripts must use `php-cgi.exe` executable, not `php.exe`.
|
**Note**: PHP scripts must use `php-cgi.exe`, not `php.exe`.
|
||||||
|
|
||||||
### cgi_pattern
|
### cgi_pattern
|
||||||
All files that match `cgi_pattern` are treated as CGI files. Default pattern
|
All files that match `cgi_pattern` are treated as CGI files. Default pattern
|
||||||
allows CGI files be anywhere. To restrict CGIs to a certain directory,
|
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
|
use `/path/to/cgi-bin/**.cgi` as a pattern. Note that **full file path** is
|
||||||
matched against the pattern, not the URI. Note: if `MONGOOSE_CGI` environment
|
matched against the pattern, not the URI.
|
||||||
variable is set, then Mongoose passes it to the CGI script.
|
|
||||||
|
When Mongoose starts CGI program, it creates new environment for it (in
|
||||||
|
contrast, usually child program inherits the environment from parent). Several
|
||||||
|
environment variables however are inherited from Mongoose's environment,
|
||||||
|
they are: `PATH`, `TMP`, `TEMP`, `TMPDIR`, `PERLLIB`, `MONGOOSE_CGI`. On UNIX
|
||||||
|
it is also `LD_LIBRARY_PATH`. On Windows it is also `COMSPEC`, `SYSTEMROOT`,
|
||||||
|
`SystemDrive`, `ProgramFiles`, `ProgramFiles(x86)`, `CommonProgramFiles(x86)`.
|
||||||
|
|
||||||
Default: `**.cgi$|**.pl$|**.php$`
|
Default: `**.cgi$|**.pl$|**.php$`
|
||||||
|
|
||||||
@@ -60,10 +69,6 @@ A directory to serve. Default: current working directory.
|
|||||||
### enable\_directory\_listing
|
### enable\_directory\_listing
|
||||||
Enable directory listing, either `yes` or `no`. Default: `yes`.
|
Enable directory listing, either `yes` or `no`. Default: `yes`.
|
||||||
|
|
||||||
### error\_log\_file
|
|
||||||
Path to a file for error logs. Either full path, or relative to the
|
|
||||||
mongoose executable. Default: not set, no errors are logged.
|
|
||||||
|
|
||||||
### extra\_mime\_types
|
### extra\_mime\_types
|
||||||
Extra mime types to recognize, in form `extension1=type1,extension2=type2,...`.
|
Extra mime types to recognize, in form `extension1=type1,extension2=type2,...`.
|
||||||
Extension must include dot. Example:
|
Extension must include dot. Example:
|
||||||
@@ -85,9 +90,6 @@ must be for a file name only, not including directory name, e.g.
|
|||||||
`mongoose -hide_files_patterns secret.txt|even_more_secret.txt`. Default:
|
`mongoose -hide_files_patterns secret.txt|even_more_secret.txt`. Default:
|
||||||
not set.
|
not set.
|
||||||
|
|
||||||
### idle\_timeout\_ms
|
|
||||||
Timeout for idle connections. Default: 30000 (30 seconds)
|
|
||||||
|
|
||||||
### index_files
|
### index_files
|
||||||
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,index.shtml,index.php`
|
files. Default: `index.html,index.htm,index.cgi,index.shtml,index.php`
|
||||||
@@ -107,7 +109,7 @@ will bind to all addresses. Default: 8080.
|
|||||||
Switch to given user credentials after startup. UNIX-only. This option is
|
Switch to given user credentials after startup. UNIX-only. This option is
|
||||||
required when mongoose needs to bind on privileged port on UNIX, e.g.
|
required when mongoose needs to bind on privileged port on UNIX, e.g.
|
||||||
|
|
||||||
$ sudo mongoose -listening_ports 80 -run_as_user nobody
|
$ sudo mongoose -listening_port 80 -run_as_user nobody
|
||||||
|
|
||||||
Default: not set.
|
Default: not set.
|
||||||
|
|
||||||
@@ -116,17 +118,20 @@ Comma-separated list of URL rewrites in the form of
|
|||||||
`uri_pattern=file_or_directory_path`. When Mongoose receives the request,
|
`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.
|
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
|
However, if the rewrite option is used and `uri_pattern` matches the
|
||||||
requested URI, then `document_root` is ignored. Insted,
|
requested URI, then `document_root` is ignored. Instead,
|
||||||
`file_or_directory_path` is used, which should be a full path name or
|
`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
|
a path relative to the web server's current working directory. Note that
|
||||||
`uri_pattern`, as all mongoose patterns, is a prefix pattern. If `uri_patters`
|
`uri_pattern`, as all mongoose patterns, is a prefix pattern. If `uri_pattern`
|
||||||
is a number, then it is treated as HTTP error code, and `file_or_directory_path`
|
is a number, then it is treated as HTTP error code, and `file_or_directory_path`
|
||||||
should be an URI to redirect to. Mongoose will issue `302` temporary redirect
|
should be an URI to redirect to. Mongoose will issue `302` temporary redirect
|
||||||
to the specified URI, appending two parameters:
|
to the specified URI with following parameters:
|
||||||
`?code=<http_error_code&orig_uri=<original_uri>`.
|
`?code=HTTP_ERROR_CODE&orig_uri=ORIGINAL_URI&query_string=QUERY_STRING`.
|
||||||
|
|
||||||
This makes it possible to serve many directories outside from `document_root`,
|
If `uri_pattern` starts with `@` symbol, then Mongoose compares
|
||||||
redirect all requests to scripts, and do other tricky things. Examples:
|
it with the `HOST` header of the request. If they are equal, Mongoose sets
|
||||||
|
document root to `file_or_directory_path`, implementing virtual hosts support.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
# Redirect all accesses to `.doc` files to a special script
|
# Redirect all accesses to `.doc` files to a special script
|
||||||
mongoose -url_rewrites **.doc$=/path/to/cgi-bin/handle_doc.cgi
|
mongoose -url_rewrites **.doc$=/path/to/cgi-bin/handle_doc.cgi
|
||||||
@@ -137,4 +142,7 @@ redirect all requests to scripts, and do other tricky things. Examples:
|
|||||||
# Redirect 404 errors to a specific error page
|
# Redirect 404 errors to a specific error page
|
||||||
mongoose -url_rewrites 404=/cgi-bin/error.cgi
|
mongoose -url_rewrites 404=/cgi-bin/error.cgi
|
||||||
|
|
||||||
|
# Virtual hosts example: serve foo.com domain from different directory
|
||||||
|
mongoose -url_rewrites @foo.com=/var/www/foo.com
|
||||||
|
|
||||||
Default: not set.
|
Default: not set.
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
How To Create A PHP Website With Mongoose
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
|
||||||
|
|
||||||
|

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

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

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

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

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

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

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

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

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

|
||||||
|
|
||||||
|
## 11. Click on the mongoose icon and choose "Go to my address" menu:
|
||||||
|

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

|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
# Mongoose Release Notes
|
||||||
|
|
||||||
|
## Release 5.4, July 28 2014
|
||||||
|
|
||||||
|
Changes in Libmongoose library:
|
||||||
|
|
||||||
|
- Added `hexdump_file` option for low-level request/reply debugging
|
||||||
|
- Added `mg_template()` API function for generating HTML pages from
|
||||||
|
templates with expansions
|
||||||
|
- Fixed `struct mg_connection::local_ip` handling, `mg_set_option()`
|
||||||
|
behavior with NULL values
|
||||||
|
- Added `mg_send_file()` call to send arbitrary file to the client
|
||||||
|
- Added `mg_terminate_ssl()` for SSL termination functionality
|
||||||
|
- Added HTTP proxy support, `enable_proxy` config option
|
||||||
|
- Added `mg_next()` for iterating over existing active connections
|
||||||
|
- Added client-side SSL auth, `ssl_ca_certificate` option
|
||||||
|
- Added `mg_wakeup_server_ex()` for pushing messages to existing connections
|
||||||
|
- Added `MG_WS_HANDSHAKE` and `MG_WS_CONNECT` events that are sent on
|
||||||
|
Websocket handshake is connection establishment, respectively
|
||||||
|
- Removed server-side Lua support
|
||||||
|
- Filesystem access, reading from socket/SSL performance improvements
|
||||||
|
- DAV PROPFIND memory leak fixed
|
||||||
|
- Added `big_upload.c` and enhanced `upload.c` example
|
||||||
|
- Added `proxy.c` example that demonstrates proxy functionality and SSE pushes
|
||||||
|
- Added `websocket2.c` example that shows simple web chat implementation
|
||||||
|
over websockets
|
||||||
|
- Various minor fixes
|
||||||
|
|
||||||
|
|
||||||
|
Changes in pre-compiled binaries:
|
||||||
|
|
||||||
|
- Created HTML administration console
|
||||||
|
- When server is started, browser is started automatically
|
||||||
|
- Fixed directory listing bug when directory contains `#` character
|
||||||
|
- Removed built-in Lua Server Pages in the binary, and instead
|
||||||
|
added Mongoose + Lua developer bundle which has Lua Server Pages support.
|
||||||
|
That also solves external Lua modules loading problem.
|
||||||
|
|
||||||
|
|
||||||
|
## Release 5.3, March 10 2014
|
||||||
|
|
||||||
|
Changes in Libmongoose library:
|
||||||
|
|
||||||
|
* Moved to the evented API. Updated API documentation is at
|
||||||
|
http://cesanta.com/docs/Embed.shtml
|
||||||
|
http://cesanta.com/docs/API.shtml
|
||||||
|
* Added `MG_LUA` event for exporting custom variables to the Lua environment
|
||||||
|
* Added virtual hosts capability, see `url_rewrites` option description at
|
||||||
|
http://cesanta.com/docs/Options.shtml
|
||||||
|
* Added mjpg serving example
|
||||||
|
* Cleaned up and documented HTTP client API, with unit tests
|
||||||
|
* Added `mg_wakeup_server()` to awaken `mg_poll_server()`
|
||||||
|
from another thread
|
||||||
|
* Moved Mongoose IO core to [https://github.com/cesanta/net_skeleton](Net Skeleton)
|
||||||
|
* Added connection hexdump functionality for developers
|
||||||
|
* Bug fixes
|
||||||
|
|
||||||
|
Changes in pre-compiled binaries:
|
||||||
|
|
||||||
|
* New awesome Mongoose logos buy our designer Katrin - thanks Katrin!
|
||||||
|
Check them out at http://cesanta.com/products.shtml
|
||||||
|
* Added Lua Server Pages support to the free version, quick intro is at
|
||||||
|
http://cesanta.com/docs/Lua.shtml
|
||||||
|
* Added quick "Set shared directory" menu item to set `document_root`
|
||||||
|
* Added SSI support to the Pro version
|
||||||
|
* Removed SSL support from the Pro version
|
||||||
|
|
||||||
|
## Release 5.2, Feb 1 2014
|
||||||
|
|
||||||
|
* Windows binary made fully UNICODE aware. In previous versions,
|
||||||
|
the presence of non-ASCII chars in document root, CGI script name,
|
||||||
|
or directory name might have broken Mongoose as stand-alone
|
||||||
|
or as Windows service. Now Mongoose works with non-ASCII paths properly.
|
||||||
|
Internally, Mongoose uses UTF8 encoding. When making WinAPI calls,
|
||||||
|
mongoose converts UTF8 strings to wide chars and calls UNICODE API.
|
||||||
|
* Enhanced authorization API by providing `mg_set_auth_handler()` and
|
||||||
|
`mg_authorize_digest()`
|
||||||
|
* Removed `mg_add_uri_handler()`, added `mg_set_request_handler()`.
|
||||||
|
There is only oneURI handler that handles all requests, just like in 4.x.
|
||||||
|
The reason for this change is to provide an ability to catch all URIs,
|
||||||
|
and at the same time signal Mongoose to continue handling specific URIs.
|
||||||
|
* Added `mg_parse_multipart()` API for file uploads.
|
||||||
|
Note that the restriction on uploading huge files still exists,
|
||||||
|
and will be eliminated in the next release.
|
||||||
|
* Allowing mongoose to bind to port 0, in which case it'll bind to any
|
||||||
|
random unused port.
|
||||||
|
* Moved `idle_timeout_ms` run-time option to compile-time flag
|
||||||
|
* Added asynchronous HTTP client, not documented yet. Documentation and
|
||||||
|
examples are coming in the next couple of weeks. Async Websocket client
|
||||||
|
is scheduled for the next release. See usage examples at `unit_test.c`
|
||||||
|
* Windows and MacOS pre-built binaries are now split to free and paid ones,
|
||||||
|
paid binaries include CGI, SSL, Lua, Sqlite, support and updates.
|
||||||
|
Linux pre-built binary includes all functionality and is free, and will
|
||||||
|
continue to be free. Source code for Windows and MacOS GUI is closed.
|
||||||
|
Disclaimer: source code for the command line stand-alone server,
|
||||||
|
as well as Mongoose library itself, will never be closed.
|
||||||
|
* Multiple bug fixes and minor enhancements
|
||||||
|
|
||||||
|
## Release 5.1, Jan 10 2014
|
||||||
|
|
||||||
|
* CGI-related bugs where fixed, primarily for Windows platform
|
||||||
|
* Bugs on Windows related to UNICODE support were fixed
|
||||||
|
* Added a feature to support "error pages" through redirect.
|
||||||
|
Done using `-url_redirects` option, details are on
|
||||||
|
http://cesanta.com/docs/Options.shtml
|
||||||
|
|
||||||
|
## Release 5.0, Jan 6 2014
|
||||||
|
|
||||||
|
* Internal core has been changed from blocking, thread-per-connection to
|
||||||
|
non-blocking, asynchronous, one thread for all.
|
||||||
|
* API modification for server creation and response creation. That allowed
|
||||||
|
keep-alive support for dynamic requests, boosting the embedded performance
|
||||||
|
to 100+ thousands requests per second on a single core
|
||||||
|
(as measured on my development MacBook laptop)
|
||||||
|
* Unified handling of POST requests and Websocket requests by putting a
|
||||||
|
payload into `conn->content`, `conn->content_len` attributes.
|
||||||
|
That simplified user code and eliminated the need of `mg_read()`,
|
||||||
|
since mongoose buffers all data prior to calling the callback
|
||||||
|
* keep-alive support is the default
|
||||||
|
* Dropped SSI support and throttling support
|
||||||
|
* Several configuraition parameters are gone:
|
||||||
|
* `cgi_environment` (replaced with MONGOOSE_CGI),
|
||||||
|
* `protect_uri` (not useful)
|
||||||
|
* `ssi_pattern` (SSI support is gone)
|
||||||
|
* `throttle` (throttling support is gone)
|
||||||
|
* `error_log_file` (not used)
|
||||||
|
* `enable_keep_alive` (enabled by default)
|
||||||
|
* `listening_ports` (renamed to listening_port)
|
||||||
|
* `num_threads` (core has changed to single thread)
|
||||||
|
* `put_delete_auth_file` (renamed to dav_auth_file)
|
||||||
|
* `authentication_domain` (renamed to auth_domain)
|
||||||
|
* Due to the async, non-blocking nature of the core, few restrictions
|
||||||
|
are now in place:
|
||||||
|
* user callbacks must not block
|
||||||
|
* POST and Websocket data are now buffered, and cannot be huge
|
||||||
|
* mongoose is now capable on listening on only one port
|
||||||
|
|
||||||
|
## Release 4.1, Oct 2013
|
||||||
|
## Release 4.0, Oct 2013
|
||||||
|
## Release 3.8, Sep 2013
|
||||||
|
|
||||||
|
## Release 3.7, Feb 2 2013
|
||||||
|
|
||||||
|
* Added "redirect to SSL port" functionality, e.g. if you specify
|
||||||
|
`-listening_ports 8080r,8043s`
|
||||||
|
then all requests to HTTP port 8080 will be redirected to HTTPS port 8043
|
||||||
|
* Added `mg_download()` API, an HTTP client interface!
|
||||||
|
* Lua server pages now must output HTTP headers -- full control for Lua
|
||||||
|
* Added pre-built binary for MacOS, with initial GUI support
|
||||||
|
* API change: got rid of events, moved to struct `mg_callbacks`
|
||||||
|
* Bugfixes, thanks to contributors
|
||||||
|
|
||||||
|
|
||||||
|
## Release 3.7, Jan 18 2013
|
||||||
|
* Fixed source code archive (main.c was missing)
|
||||||
|
* Extended Windows GUI functionality:
|
||||||
|
* Added "Start browser" systray popup menu item
|
||||||
|
* Enhanced configuration editor
|
||||||
|
* Renamed config options:
|
||||||
|
* `put_delete_passwords_file` -> `put_delete_auth_file`
|
||||||
|
* `global_passwords_file` -> `global_auth_file`
|
||||||
|
* `select()` changed to `poll()`, to avoid big file descriptor
|
||||||
|
`FD_SET` problem on UNIX
|
||||||
|
* Couple of bugfixes, thanks to contributors
|
||||||
|
|
||||||
|
|
||||||
|
Eearlier release notes could be found by searching
|
||||||
|
[Mongoose mailing list](https://groups.google.com/forum/#!forum/mongoose-users)
|
||||||
+11
-10
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
Mongoose is small and easy to use web server built on top of
|
Mongoose is small and easy to use web server built on top of
|
||||||
mongoose library. It is designed with maximum simplicity in mind. For example,
|
mongoose library. It is designed with maximum simplicity in mind. For example,
|
||||||
to share any directory, just drop mongoose executable file in that directory,
|
to share any directory, just drop mongoose executable in that directory,
|
||||||
double-click it (on UNIX, run it from shell) and launch a browser at
|
double-click it (on UNIX, run it from shell) and launch a browser at
|
||||||
[http://localhost:8080](http://localhost:8080) Note that 'localhost' should
|
[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.
|
be changed to a machine's name if a folder is accessed from other computer.
|
||||||
|
|
||||||
On Windows and Mac, Mongoose iconifies itself to the system tray when started.
|
On Windows and Mac, Mongoose iconifies itself to the system tray when started.
|
||||||
Right-click on the icon pops up a menu, where it is possible to stop
|
Right-click on the icon to pop up a menu, where it is possible to stop
|
||||||
mongoose, or configure it.
|
mongoose, or configure it.
|
||||||
|
|
||||||
On UNIX, `mongoose` is a command line utility. Running `mongoose` in
|
On UNIX, `mongoose` is a command line utility. Running `mongoose` in
|
||||||
@@ -68,17 +68,18 @@ recognized by the file name pattern. Mongoose uses shell-like glob
|
|||||||
patterns. Pattern match starts at the beginning of the string, so essentially
|
patterns. Pattern match starts at the beginning of the string, so essentially
|
||||||
patterns are prefix patterns. Syntax is as follows:
|
patterns are prefix patterns. Syntax is as follows:
|
||||||
|
|
||||||
** Matches everything
|
** Matches everything
|
||||||
* Matches everything but slash character, '/'
|
* Matches everything but slash character, '/'
|
||||||
? Matches any character
|
? Matches any character
|
||||||
$ Matches the end of the string
|
$ Matches the end of the string
|
||||||
| Matches if pattern on the left side or the right side matches.
|
| Matches if pattern on the left side or the right side matches.
|
||||||
|
|
||||||
All other characters in the pattern match themselves. Examples:
|
All other characters in the pattern match themselves. Examples:
|
||||||
|
|
||||||
**.cgi$ Any string that ends with .cgi
|
# Pattern Meaning
|
||||||
/foo Any string that begins with /foo
|
**.cgi$ Any string that ends with .cgi
|
||||||
**a$|**b$ Any string that ends with a or b
|
/foo Any string that begins with /foo
|
||||||
|
**a$|**b$ Any string that ends with a or b
|
||||||
|
|
||||||
To restrict CGI files only to `/cgi-bin/` directory, use this setting:
|
To restrict CGI files only to `/cgi-bin/` directory, use this setting:
|
||||||
|
|
||||||
|
|||||||
+56
-32
@@ -1,54 +1,78 @@
|
|||||||
CFLAGS = -W -Wall -I.. -pthread -g -pipe $(CFLAGS_EXTRA)
|
CFLAGS = -W -Wall -I.. -pthread -g -pipe $(CFLAGS_EXTRA)
|
||||||
DLL_FLAGS = -DLUA_COMPAT_ALL -I../build
|
|
||||||
RM = rm -rf
|
RM = rm -rf
|
||||||
|
OUT = -o $@
|
||||||
|
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
|
MSVC = ../../vc6
|
||||||
RM = del /q /f
|
RM = del /q /f
|
||||||
|
OUT =
|
||||||
|
CC = $(MSVC)/bin/cl
|
||||||
|
CFLAGS = /MD /TC /nologo /W3 /I$(MSVC)/include /I..
|
||||||
|
CFLAGS += /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
|
||||||
|
CFLAGS += $(CFLAGS_EXTRA)
|
||||||
else
|
else
|
||||||
UNAME_S := $(shell uname -s)
|
UNAME_S := $(shell uname -s)
|
||||||
DLL_FLAGS += -shared
|
CC = g++
|
||||||
|
CFLAGS += -lssl -g -O0
|
||||||
|
|
||||||
ifeq ($(UNAME_S),Linux)
|
ifeq ($(UNAME_S),Linux)
|
||||||
CFLAGS += -ldl
|
CFLAGS += -ldl
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(UNAME_S),Darwin)
|
ifeq ($(UNAME_S),Darwin)
|
||||||
# DLL_FLAGS += -bundle -undefined dynamic_lookup -dynamiclib
|
|
||||||
DLL_FLAGS += -flat_namespace -undefined suppress -dynamiclib
|
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: websocket_html.c
|
all: hello websocket server post multi_threaded upload auth
|
||||||
$(CC) hello.c ../mongoose.c -o hello $(CFLAGS)
|
|
||||||
$(CC) websocket.c websocket_html.c ../mongoose.c -o websocket $(CFLAGS)
|
|
||||||
$(CC) post.c ../mongoose.c -o post $(CFLAGS)
|
|
||||||
$(CC) multi_threaded.c ../mongoose.c -o multi_threaded $(CFLAGS)
|
|
||||||
$(CC) upload.c ../mongoose.c -o upload $(CFLAGS)
|
|
||||||
$(CC) auth.c ../mongoose.c -o auth $(CFLAGS)
|
|
||||||
$(CC) server.c ../mongoose.c -o server $(CFLAGS)
|
|
||||||
|
|
||||||
# $(CC) chat.c ../mongoose.c -o chat $(CFLAGS)
|
# To enable Lua in a server, uncomment following lines
|
||||||
# $(CC) lua_dll.c ../build/lua_5.2.1.c -o $@.so $(CFLAGS) $(DLL_FLAGS)
|
LUA = ../lua-5.2.3/src
|
||||||
|
#CFLAGS += -I$(LUA) -L$(LUA) -llua -DMONGOOSE_USE_LUA
|
||||||
|
|
||||||
|
server: server.c ../mongoose.c
|
||||||
|
$(CC) server.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
hello: hello.c ../mongoose.c
|
||||||
|
$(CC) hello.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
websocket: websocket_html.c websocket.c ../mongoose.c
|
||||||
|
$(CC) websocket.c websocket_html.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
post: post.c ../mongoose.c
|
||||||
|
$(CC) post.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
multi_threaded: multi_threaded.c ../mongoose.c
|
||||||
|
$(CC) multi_threaded.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
upload: upload.c ../mongoose.c
|
||||||
|
$(CC) upload.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
auth: auth.c ../mongoose.c
|
||||||
|
$(CC) auth.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
file: file.c ../mongoose.c
|
||||||
|
$(CC) file.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
form: form.c ../mongoose.c
|
||||||
|
$(CC) form.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
mjpg: mjpg.c ../mongoose.c
|
||||||
|
$(CC) mjpg.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
pubsub: websocket2.c ../mongoose.c
|
||||||
|
$(CC) websocket2.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
proxy: proxy.c ../mongoose.c
|
||||||
|
$(CC) proxy.c ../mongoose.c $(OUT) \
|
||||||
|
-I../../net_skeleton -DNS_ENABLE_SSL -lssl $(CFLAGS)
|
||||||
|
|
||||||
websocket_html.c: websocket.html
|
websocket_html.c: websocket.html
|
||||||
perl mkdata.pl $< > $@
|
perl mkdata.pl $< > $@
|
||||||
|
|
||||||
MSVC = ../../vc6
|
u:
|
||||||
CL = $(MSVC)/bin/cl
|
$(CC) unit_test.c $(OUT) $(CFLAGS)
|
||||||
CLFLAGS = /MD /TC /nologo $(DBG) /W3 \
|
./$@
|
||||||
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf
|
|
||||||
LFLAGS = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
|
|
||||||
|
|
||||||
windows: websocket_html.c
|
|
||||||
$(CL) hello.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
|
||||||
$(CL) websocket.c websocket_html.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
|
||||||
$(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
|
||||||
$(CL) multi_threaded.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
|
||||||
$(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
|
||||||
$(CL) auth.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
|
||||||
$(CL) server.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) hello upload post websocket chat *.exe *.dSYM *.obj .*o
|
-@$(RM) hello mjpg upload post websocket auth file server multi_threaded proxy websocket_html.c *.exe *.dSYM *.obj .*o u a.out
|
||||||
+15
-11
@@ -2,25 +2,29 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
static int auth_handler(struct mg_connection *conn) {
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
int result = MG_AUTH_FAIL; // Not authorized
|
|
||||||
FILE *fp;
|
|
||||||
|
|
||||||
// To populate passwords file, do
|
if (ev == MG_AUTH) {
|
||||||
// mongoose -A my_passwords.txt mydomain.com admin admin
|
int result = MG_FALSE; // Not authorized
|
||||||
if ((fp = fopen("my_passwords.txt", "r")) != NULL) {
|
FILE *fp;
|
||||||
result = mg_authorize_digest(conn, fp);
|
|
||||||
fclose(fp);
|
// To populate passwords file, do
|
||||||
|
// mongoose -A my_passwords.txt mydomain.com admin admin
|
||||||
|
if ((fp = fopen("my_passwords.txt", "r")) != NULL) {
|
||||||
|
result = mg_authorize_digest(conn, fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return MG_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_server *server = mg_create_server(NULL);
|
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||||
mg_set_option(server, "listening_port", "8080");
|
mg_set_option(server, "listening_port", "8080");
|
||||||
mg_set_option(server, "document_root", ".");
|
mg_set_option(server, "document_root", ".");
|
||||||
mg_set_auth_handler(server, auth_handler);
|
|
||||||
|
|
||||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
struct file_data {
|
||||||
|
FILE *fp;
|
||||||
|
const char *data;
|
||||||
|
int data_len;
|
||||||
|
int written;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int handle_request(struct mg_connection *conn) {
|
||||||
|
const char *data;
|
||||||
|
int data_len;
|
||||||
|
char var_name[100], file_name[100], path[100];
|
||||||
|
|
||||||
|
if (strcmp(conn->uri, "/upload") == 0) {
|
||||||
|
if (mg_parse_multipart(conn->content, conn->content_len,
|
||||||
|
var_name, sizeof(var_name),
|
||||||
|
file_name, sizeof(file_name),
|
||||||
|
&data, &data_len) > 0) {
|
||||||
|
struct file_data *p = (struct file_data *) malloc(sizeof(*p));
|
||||||
|
snprintf(path, sizeof(path), "UPLOAD_%s", file_name);
|
||||||
|
p->fp = fopen(path, "wb");
|
||||||
|
p->data = data;
|
||||||
|
p->data_len = data_len;
|
||||||
|
p->written = 0;
|
||||||
|
conn->connection_param = p;
|
||||||
|
mg_send_header(conn, "Content-Type", "text/html");
|
||||||
|
}
|
||||||
|
return MG_MORE; // Tell mongoose to keep this connection open
|
||||||
|
} else {
|
||||||
|
mg_printf_data(conn, "%s",
|
||||||
|
"<html><body>Upload example."
|
||||||
|
"<form method=\"POST\" action=\"/upload\" "
|
||||||
|
" enctype=\"multipart/form-data\">"
|
||||||
|
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||||
|
"<input type=\"submit\" value=\"Upload\" />"
|
||||||
|
"</form></body></html>");
|
||||||
|
return MG_TRUE; // Tell mongoose to close this connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_poll(struct mg_connection *conn) {
|
||||||
|
struct file_data *p = (struct file_data *) conn->connection_param;
|
||||||
|
if (p != NULL) {
|
||||||
|
// Write no more then 100 bytes in one go
|
||||||
|
int len = p->data_len - p->written;
|
||||||
|
int n = fwrite(p->data + p->written, 1, len > 100 ? 100 : len, p->fp);
|
||||||
|
if (n > 0) {
|
||||||
|
p->written += n;
|
||||||
|
mg_send_data(conn, " ", 1); // Send something back to wake up select()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If everything is written, close the connection
|
||||||
|
if (p->written >= p->data_len) {
|
||||||
|
mg_printf_data(conn, "Written %d bytes.", p->written);
|
||||||
|
fclose(p->fp);
|
||||||
|
free(p);
|
||||||
|
conn->connection_param = NULL;
|
||||||
|
return MG_TRUE; // Tell mongoose to close this connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MG_FALSE; // Tell mongoose to keep this connection open
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
switch (ev) {
|
||||||
|
case MG_AUTH: return MG_TRUE;
|
||||||
|
case MG_REQUEST: return handle_request(conn);
|
||||||
|
case MG_POLL: return handle_poll(conn);
|
||||||
|
default: return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||||
|
mg_set_option(server, "listening_port", "8080");
|
||||||
|
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
mg_poll_server(server, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
mg_destroy_server(&server);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+28
-22
@@ -2,36 +2,42 @@
|
|||||||
// https://github.com/cesanta/mongoose
|
// https://github.com/cesanta/mongoose
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
public class Program {
|
public class Program {
|
||||||
static private int EventHandler(MongooseEvent ev) {
|
static private int EventHandler(IntPtr conn_ptr, int ev) {
|
||||||
if (ev.type != 1) {
|
MongooseConnection conn = (MongooseConnection)
|
||||||
return 0; // Mark as unhandled
|
System.Runtime.InteropServices.Marshal.PtrToStructure(
|
||||||
}
|
conn_ptr , typeof(MongooseConnection));
|
||||||
|
|
||||||
MongooseRequestInfo request_info = (MongooseRequestInfo)
|
if (ev == 102) {
|
||||||
Marshal.PtrToStructure(ev.request_info, typeof(MongooseRequestInfo));
|
// MG_AUTH
|
||||||
|
return 1;
|
||||||
|
} else if (ev == 103) {
|
||||||
|
// MG_REQUEST
|
||||||
|
Mongoose.send_data(conn_ptr, "Hello from C#!\n");
|
||||||
|
Mongoose.send_data(conn_ptr, "URI: " + conn.uri + "\n");
|
||||||
|
Mongoose.send_data(conn_ptr, "HTTP Headers:\n");
|
||||||
|
|
||||||
if (request_info.uri != "/test") {
|
for (int i = 0; i < conn.num_headers; i++) {
|
||||||
return 0; // Mark as unhandled
|
IntPtr name = conn.http_headers[i].name;
|
||||||
}
|
IntPtr val = conn.http_headers[i].value;
|
||||||
|
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name);
|
||||||
Mongoose.write(ev.conn, "HTTP/1.1 200 OK\r\n\r\n");
|
Mongoose.send_data(conn_ptr, " " +
|
||||||
Mongoose.write(ev.conn, "Hello from C#!\n");
|
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name) + ": " +
|
||||||
|
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(val) + "\n");
|
||||||
return 1; // Mark as handled
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Main() {
|
static void Main() {
|
||||||
Mongoose web_server = new Mongoose(".", "9000",
|
Mongoose web_server = new Mongoose(".", "9001",
|
||||||
new MongooseEventHandler(EventHandler));
|
new MongooseEventHandler(EventHandler));
|
||||||
|
|
||||||
Console.WriteLine("Mongoose v." + web_server.version_ + " started.");
|
Console.WriteLine("Mongoose started, press Ctrl-C to exit.");
|
||||||
Console.WriteLine("Press enter to exit program.");
|
for (;;) {
|
||||||
|
web_server.poll(1000);
|
||||||
// Serve requests until user presses "enter" on a keyboard
|
}
|
||||||
Console.ReadLine();
|
|
||||||
web_server.stop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+37
-62
@@ -9,85 +9,60 @@ using System.Runtime.InteropServices;
|
|||||||
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr value;
|
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr value;
|
||||||
};
|
};
|
||||||
|
|
||||||
// mongoose.h :: struct mg_request_info
|
// mongoose.h :: struct mg_connection
|
||||||
[StructLayout(LayoutKind.Sequential)] public struct MongooseRequestInfo {
|
[StructLayout(LayoutKind.Sequential)] public struct MongooseConnection {
|
||||||
[MarshalAs(UnmanagedType.LPTStr)] public string request_method;
|
[MarshalAs(UnmanagedType.LPTStr)] public string request_method;
|
||||||
[MarshalAs(UnmanagedType.LPTStr)] public string uri;
|
[MarshalAs(UnmanagedType.LPTStr)] public string uri;
|
||||||
[MarshalAs(UnmanagedType.LPTStr)] public string http_version;
|
[MarshalAs(UnmanagedType.LPTStr)] public string http_version;
|
||||||
[MarshalAs(UnmanagedType.LPTStr)] public string query_string;
|
[MarshalAs(UnmanagedType.LPTStr)] public string query_string;
|
||||||
[MarshalAs(UnmanagedType.LPTStr)] public string remote_user;
|
|
||||||
public int remote_ip;
|
[MarshalAs(UnmanagedType.ByValArray,SizeConst=48)] public char[] remote_ip;
|
||||||
public int remote_port;
|
[MarshalAs(UnmanagedType.LPTStr)] public string local_ip;
|
||||||
public int is_ssl;
|
[MarshalAs(UnmanagedType.U2)] public short remote_port;
|
||||||
[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]
|
[MarshalAs(UnmanagedType.U2)] public short local_port;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.SysInt)] public int num_headers;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray,SizeConst=30)]
|
||||||
public MongooseHeader[] http_headers;
|
public MongooseHeader[] http_headers;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr content;
|
||||||
|
[MarshalAs(UnmanagedType.SysInt)] public int content_len;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.SysInt)] public int is_websocket;
|
||||||
|
[MarshalAs(UnmanagedType.SysInt)] public int status_code;
|
||||||
|
[MarshalAs(UnmanagedType.SysInt)] public int wsbits;
|
||||||
};
|
};
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)] public struct MongooseEvent {
|
public delegate int MongooseEventHandler(IntPtr c, int ev);
|
||||||
public int type;
|
|
||||||
public IntPtr user_data;
|
|
||||||
public IntPtr conn_data;
|
|
||||||
public IntPtr event_param;
|
|
||||||
public IntPtr conn;
|
|
||||||
public IntPtr request_info;
|
|
||||||
};
|
|
||||||
|
|
||||||
public delegate int MongooseEventHandlerN(ref MongooseEvent ev);
|
|
||||||
public delegate int MongooseEventHandler(MongooseEvent ev);
|
|
||||||
|
|
||||||
public class Mongoose {
|
public class Mongoose {
|
||||||
public const string dll_name_ = "mongoose";
|
public const string dll_ = "mongoose";
|
||||||
public string version_ = "??";
|
private IntPtr server_;
|
||||||
|
|
||||||
// These are here to store a ref to the callbacks
|
[DllImport(dll_)] private static extern IntPtr
|
||||||
// while they are over in unmanaged code, to prevent garbage collection.
|
mg_create_server(IntPtr user_data, MongooseEventHandler eh);
|
||||||
private event MongooseEventHandlerN delegates;
|
[DllImport(dll_)] private static extern int
|
||||||
|
mg_poll_server(IntPtr server, int milli);
|
||||||
private IntPtr ctx_;
|
[DllImport(dll_)] private static extern IntPtr
|
||||||
|
mg_set_option(IntPtr server, string name, string value);
|
||||||
[DllImport(dll_name_)] private static extern
|
[DllImport(dll_)] public static extern int
|
||||||
IntPtr mg_start([MarshalAs(UnmanagedType.LPArray,
|
mg_send_data(IntPtr conn, string data, int length);
|
||||||
ArraySubType=UnmanagedType.LPTStr)] string[] options,
|
|
||||||
MongooseEventHandlerN callback,
|
|
||||||
IntPtr user_data);
|
|
||||||
[DllImport(dll_name_)] private static extern void mg_stop(IntPtr ctx);
|
|
||||||
[DllImport(dll_name_)] private static extern IntPtr mg_version();
|
|
||||||
[DllImport(dll_name_)] public static extern int mg_write(IntPtr conn,
|
|
||||||
string data, int length);
|
|
||||||
|
|
||||||
public Mongoose(string document_root,
|
public Mongoose(string document_root,
|
||||||
string listening_ports,
|
string listening_port,
|
||||||
MongooseEventHandler event_handler) {
|
MongooseEventHandler event_handler) {
|
||||||
version_ = Marshal.PtrToStringAnsi(mg_version());
|
server_ = mg_create_server(IntPtr.Zero, event_handler);
|
||||||
|
mg_set_option(server_, "document_root", document_root);
|
||||||
string[] options = {
|
mg_set_option(server_, "listening_port", listening_port);
|
||||||
"document_root", document_root,
|
|
||||||
"listening_ports", listening_ports,
|
|
||||||
null
|
|
||||||
};
|
|
||||||
|
|
||||||
MongooseEventHandlerN cb = delegate(ref MongooseEvent ev) {
|
|
||||||
return event_handler(ev);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prevent garbage collection
|
|
||||||
delegates += cb;
|
|
||||||
|
|
||||||
ctx_ = mg_start(options, cb, IntPtr.Zero);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int write(IntPtr conn, string data) {
|
public static int send_data(IntPtr conn, string data) {
|
||||||
return mg_write(conn, data, data.Length);
|
return mg_send_data(conn, data, data.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void poll(int milli) {
|
||||||
if (this.ctx_ != IntPtr.Zero) {
|
mg_poll_server(server_, milli);
|
||||||
mg_stop(this.ctx_);
|
|
||||||
}
|
|
||||||
this.ctx_ = IntPtr.Zero;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~Mongoose() {
|
// TODO: add destructor and call mg_destroy_server()
|
||||||
stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEA8FV466yTGIUkJqjBv2CTU/HeYKXYd7D6m8fY9daRLm9cKPhB
|
||||||
|
kPvLyhdenOH88uAAYqmKf2dZvP5SxFnBKT52Hr0XpUfC8Y3iVjHxwMgNRaiSkfDP
|
||||||
|
mIEb2qcpJoMrdos/ff46itqwr9wW3+7kjRlxM1Mu5pq4EibSHeVyaPWVCgENcA1m
|
||||||
|
4beQujI1SSBkLOfmgpw/SsyzyTgXm+kOdj7vKy3EbXseGPtHJ04oZ7qwol1NFrrq
|
||||||
|
HwO87HWHxyQr7uJ4TBH37gL7QCdjHsOdN94alxEUWTRfmzZFXVlsTnq5DBnnGhtq
|
||||||
|
ibpUC8WeI3QiFNtTBKkHviAm+6iWW6gG2TO4twIDAQABAoIBAHxLWBxaxkB6KzYr
|
||||||
|
NPirE8boYiAN5aQRTarfcp/B9pfTVBCERDSdZMoByBbO8akSnGBqWoyFgO2zgk62
|
||||||
|
NKYF/cTr+pyqPnH69UGq1uLjE6ohApyjtFmk/CjbJvcaA5JtdcrNfq1taM+qvdHW
|
||||||
|
lTE8JUVsEeeFwQjyNtzrW35Y9FmVLSmMNsESChLnrsa7IxwnfRpHc+hmj+7lxZO6
|
||||||
|
ZzAaCCo9qHR1Ddi5f+0DJoXGBBGOk6C4BWLaV+fHPcsYHESrepitGfS96AQ0Wspn
|
||||||
|
qqtAp6jv+iIfgpohYylIjOPcHf3dXpINTxwz/r3XicqBaSAx2lXb+vnxERmagGKC
|
||||||
|
RUpELVECgYEA/EHUDwJRBUMvGXjRQO9R0JLp59U2UZOvAjmIAHkLM/48k8//kpgJ
|
||||||
|
CDUVvuyQUfwCphmaiAm5BZfDbmhBEirjCai8fYkrGugQl9H1YrVsi+UhQudnSRM0
|
||||||
|
7plKIb9nz9by31J1iu5YHTpovO5MQjH5UP1boqAioBNoBuzFI0QK1uMCgYEA8+Za
|
||||||
|
0NDocnE+V2NpRag+oMbM/LBgSWyk49NXYUOhuv4CZ8f20nFYx6p9RLD0ICUOxC3B
|
||||||
|
TkHybnGhRjuR+LkbuynW2wM2+jwcLe+ZPnXZsNKJwST9fzus06BWHyBTJHfhUDO3
|
||||||
|
cq6PUDJua6NQa++rj+ur1LSFPuwnC+X2FxMd6x0CgYEAweyhZ4ByZMdHWB+nTnIs
|
||||||
|
xVmL9CvW/FO6gM1Vnndvg2L5kvCeG0ZB+0tEEmiucjh4mNSqFdbTpFcNaL1A+QR7
|
||||||
|
UZukfCWNO7wXtX81U9MBRXK6ZUKcBnN7h8nUUalh10f5onEz58BfgOi8SDIx8dmV
|
||||||
|
Pwvm/Fh7BfClUl2HGBtg8d0CgYBEDWwdg4y4EdzhKT3AT9dZqN6dSuEy23uOjfN9
|
||||||
|
kg9YS3OEXYkC4tlwrKNTPUSAi5Z5KoMbQM9R/aaQICL0kUGpkVz/sv/uBkOelZHe
|
||||||
|
madp7PS+zYFltNLeIYimGrDYJhLZ+/QdrZMTP18L7X+M/kySVaGWMeBT1DR432sw
|
||||||
|
uQbKCQKBgQC9D9fxDXCFxJmJc2Cp8ZYQha4Oc10JsanM1DM1YWIaOILfoExz46vZ
|
||||||
|
UJzRfe2LkkhhRN9YG4ZW6zJ1E/HZSjKC2iwHog/4bDjykVPrFIfPdWS2C1CQF/Jq
|
||||||
|
tYnHTpuxE3JFXZrLXbDobFKbxHOZRdlSUu8NK/V507Y34NaMPHnffw==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIID4DCCAsigAwIBAgIJAM1yCH8CR4Z+MA0GCSqGSIb3DQEBBQUAMFMxFDASBgNV
|
||||||
|
BAMTC2Nlc2FudGEuY29tMR0wGwYDVQQKExRDZXNhbnRhIFNvZnR3YXJlIEx0ZDEL
|
||||||
|
MAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA2MjcxMDUwNTBaFw0y
|
||||||
|
NDA2MjQxMDUwNTBaMFMxFDASBgNVBAMTC2Nlc2FudGEuY29tMR0wGwYDVQQKExRD
|
||||||
|
ZXNhbnRhIFNvZnR3YXJlIEx0ZDELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1Ymxp
|
||||||
|
bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPBVeOuskxiFJCaowb9g
|
||||||
|
k1Px3mCl2Hew+pvH2PXWkS5vXCj4QZD7y8oXXpzh/PLgAGKpin9nWbz+UsRZwSk+
|
||||||
|
dh69F6VHwvGN4lYx8cDIDUWokpHwz5iBG9qnKSaDK3aLP33+OorasK/cFt/u5I0Z
|
||||||
|
cTNTLuaauBIm0h3lcmj1lQoBDXANZuG3kLoyNUkgZCzn5oKcP0rMs8k4F5vpDnY+
|
||||||
|
7ystxG17Hhj7RydOKGe6sKJdTRa66h8DvOx1h8ckK+7ieEwR9+4C+0AnYx7DnTfe
|
||||||
|
GpcRFFk0X5s2RV1ZbE56uQwZ5xobaom6VAvFniN0IhTbUwSpB74gJvuolluoBtkz
|
||||||
|
uLcCAwEAAaOBtjCBszAdBgNVHQ4EFgQUwvetHY2/J2F788UvFPADTlaUS10wgYMG
|
||||||
|
A1UdIwR8MHqAFML3rR2Nvydhe/PFLxTwA05WlEtdoVekVTBTMRQwEgYDVQQDEwtj
|
||||||
|
ZXNhbnRhLmNvbTEdMBsGA1UEChMUQ2VzYW50YSBTb2Z0d2FyZSBMdGQxCzAJBgNV
|
||||||
|
BAYTAklFMQ8wDQYDVQQHEwZEdWJsaW6CCQDNcgh/AkeGfjAMBgNVHRMEBTADAQH/
|
||||||
|
MA0GCSqGSIb3DQEBBQUAA4IBAQDA3QTlZ2aUe4IIDU+yZmu3XRxJ4FGpOlC0nD1j
|
||||||
|
uwHWDaHfr0WMDjwSoEHbhsc0tapXOpG+hFImHemDS82SuC2c8lNKEJeXgdF3Rqeo
|
||||||
|
aIB8sklgHsJG1DoCwC5TIMaccmbALWctouELGFb8aDF+dczKaaDTW+/dgR47GvD5
|
||||||
|
NBTka2A56U3emmXRpOOtzOz/68lxgACXc+M3yrxbYt9a95rl6wwzNlLHDwPzqhzd
|
||||||
|
ayQ9Q8wmZpWZK1oc8oKKuEXA3zG6jJkrmC8VD5j0WRLgC7SV/zlaAUR/zEisstrh
|
||||||
|
ZVMiJ0NoZmAPVRm0MyBHtBHJFcn/z/FlhqCBLsmDxJ/aOEMT
|
||||||
|
-----END CERTIFICATE-----
|
||||||
+8
-379
@@ -1,386 +1,15 @@
|
|||||||
// This file is part of the Mongoose project, http://code.google.com/p/mongoose
|
|
||||||
// It implements an online chat server. For more details,
|
|
||||||
// see the documentation on the project web site.
|
|
||||||
// To test the application,
|
|
||||||
// 1. type "make" in the directory where this file lives
|
|
||||||
// 2. point your browser to http://127.0.0.1:8081
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
#define MAX_USER_LEN 20
|
|
||||||
#define MAX_MESSAGE_LEN 100
|
|
||||||
#define MAX_MESSAGES 5
|
|
||||||
#define MAX_SESSIONS 2
|
|
||||||
#define SESSION_TTL 120
|
|
||||||
|
|
||||||
static const char *authorize_url = "/authorize";
|
|
||||||
static const char *login_url = "/login.html";
|
|
||||||
static const char *ajax_reply_start =
|
|
||||||
"HTTP/1.1 200 OK\r\n"
|
|
||||||
"Cache: no-cache\r\n"
|
|
||||||
"Content-Type: application/x-javascript\r\n"
|
|
||||||
"\r\n";
|
|
||||||
|
|
||||||
// Describes single message sent to a chat. If user is empty (0 length),
|
|
||||||
// the message is then originated from the server itself.
|
|
||||||
struct message {
|
|
||||||
long id; // Message ID
|
|
||||||
char user[MAX_USER_LEN]; // User that have sent the message
|
|
||||||
char text[MAX_MESSAGE_LEN]; // Message text
|
|
||||||
time_t timestamp; // Message timestamp, UTC
|
|
||||||
};
|
|
||||||
|
|
||||||
// Describes web session.
|
|
||||||
struct session {
|
|
||||||
char session_id[33]; // Session ID, must be unique
|
|
||||||
char random[20]; // Random data used for extra user validation
|
|
||||||
char user[MAX_USER_LEN]; // Authenticated user
|
|
||||||
time_t expire; // Expiration timestamp, UTC
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct message messages[MAX_MESSAGES]; // Ringbuffer for messages
|
|
||||||
static struct session sessions[MAX_SESSIONS]; // Current sessions
|
|
||||||
static long last_message_id;
|
|
||||||
|
|
||||||
// Protects messages, sessions, last_message_id
|
|
||||||
static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
|
|
||||||
|
|
||||||
// Get session object for the connection. Caller must hold the lock.
|
|
||||||
static struct session *get_session(const struct mg_connection *conn) {
|
|
||||||
int i;
|
|
||||||
const char *cookie = mg_get_header(conn, "Cookie");
|
|
||||||
char session_id[33];
|
|
||||||
time_t now = time(NULL);
|
|
||||||
mg_get_cookie(cookie, "session", session_id, sizeof(session_id));
|
|
||||||
for (i = 0; i < MAX_SESSIONS; i++) {
|
|
||||||
if (sessions[i].expire != 0 &&
|
|
||||||
sessions[i].expire > now &&
|
|
||||||
strcmp(sessions[i].session_id, session_id) == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i == MAX_SESSIONS ? NULL : &sessions[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void get_qsvar(const struct mg_request_info *request_info,
|
|
||||||
const char *name, char *dst, size_t dst_len) {
|
|
||||||
const char *qs = request_info->query_string;
|
|
||||||
mg_get_var(qs, strlen(qs == NULL ? "" : qs), name, dst, dst_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a get of messages with IDs greater than last_id and transform them
|
|
||||||
// into a JSON string. Return that string to the caller. The string is
|
|
||||||
// dynamically allocated, caller must free it. If there are no messages,
|
|
||||||
// NULL is returned.
|
|
||||||
static char *messages_to_json(long last_id) {
|
|
||||||
const struct message *message;
|
|
||||||
int max_msgs, len;
|
|
||||||
char buf[sizeof(messages)]; // Large enough to hold all messages
|
|
||||||
|
|
||||||
// Read-lock the ringbuffer. Loop over all messages, making a JSON string.
|
|
||||||
pthread_rwlock_rdlock(&rwlock);
|
|
||||||
len = 0;
|
|
||||||
max_msgs = sizeof(messages) / sizeof(messages[0]);
|
|
||||||
// If client is too far behind, return all messages.
|
|
||||||
if (last_message_id - last_id > max_msgs) {
|
|
||||||
last_id = last_message_id - max_msgs;
|
|
||||||
}
|
|
||||||
for (; last_id < last_message_id; last_id++) {
|
|
||||||
message = &messages[last_id % max_msgs];
|
|
||||||
if (message->timestamp == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// buf is allocated on stack and hopefully is large enough to hold all
|
|
||||||
// messages (it may be too small if the ringbuffer is full and all
|
|
||||||
// messages are large. in this case asserts will trigger).
|
|
||||||
len += snprintf(buf + len, sizeof(buf) - len,
|
|
||||||
"{user: '%s', text: '%s', timestamp: %lu, id: %lu},",
|
|
||||||
message->user, message->text, message->timestamp, message->id);
|
|
||||||
assert(len > 0);
|
|
||||||
assert((size_t) len < sizeof(buf));
|
|
||||||
}
|
|
||||||
pthread_rwlock_unlock(&rwlock);
|
|
||||||
|
|
||||||
return len == 0 ? NULL : strdup(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If "callback" param is present in query string, this is JSONP call.
|
|
||||||
// Return 1 in this case, or 0 if "callback" is not specified.
|
|
||||||
// Wrap an output in Javascript function call.
|
|
||||||
static int handle_jsonp(struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
char cb[64];
|
|
||||||
|
|
||||||
get_qsvar(request_info, "callback", cb, sizeof(cb));
|
|
||||||
if (cb[0] != '\0') {
|
|
||||||
mg_printf(conn, "%s(", cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cb[0] == '\0' ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A handler for the /ajax/get_messages endpoint.
|
|
||||||
// Return a list of messages with ID greater than requested.
|
|
||||||
static void ajax_get_messages(struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
char last_id[32], *json;
|
|
||||||
int is_jsonp;
|
|
||||||
|
|
||||||
mg_printf(conn, "%s", ajax_reply_start);
|
|
||||||
is_jsonp = handle_jsonp(conn, request_info);
|
|
||||||
|
|
||||||
get_qsvar(request_info, "last_id", last_id, sizeof(last_id));
|
|
||||||
if ((json = messages_to_json(strtoul(last_id, NULL, 10))) != NULL) {
|
|
||||||
mg_printf(conn, "[%s]", json);
|
|
||||||
free(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_jsonp) {
|
|
||||||
mg_printf(conn, "%s", ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate new message. Caller must hold the lock.
|
|
||||||
static struct message *new_message(void) {
|
|
||||||
static int size = sizeof(messages) / sizeof(messages[0]);
|
|
||||||
struct message *message = &messages[last_message_id % size];
|
|
||||||
message->id = last_message_id++;
|
|
||||||
message->timestamp = time(0);
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void my_strlcpy(char *dst, const char *src, size_t len) {
|
|
||||||
strncpy(dst, src, len);
|
|
||||||
dst[len - 1] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
// A handler for the /ajax/send_message endpoint.
|
|
||||||
static void ajax_send_message(struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
struct message *message;
|
|
||||||
struct session *session;
|
|
||||||
char text[sizeof(message->text) - 1];
|
|
||||||
int is_jsonp;
|
|
||||||
|
|
||||||
mg_printf(conn, "%s", ajax_reply_start);
|
|
||||||
is_jsonp = handle_jsonp(conn, request_info);
|
|
||||||
|
|
||||||
get_qsvar(request_info, "text", text, sizeof(text));
|
|
||||||
if (text[0] != '\0') {
|
|
||||||
// We have a message to store. Write-lock the ringbuffer,
|
|
||||||
// grab the next message and copy data into it.
|
|
||||||
pthread_rwlock_wrlock(&rwlock);
|
|
||||||
message = new_message();
|
|
||||||
// TODO(lsm): JSON-encode all text strings
|
|
||||||
session = get_session(conn);
|
|
||||||
assert(session != NULL);
|
|
||||||
my_strlcpy(message->text, text, sizeof(text));
|
|
||||||
my_strlcpy(message->user, session->user, sizeof(message->user));
|
|
||||||
pthread_rwlock_unlock(&rwlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
mg_printf(conn, "%s", text[0] == '\0' ? "false" : "true");
|
|
||||||
|
|
||||||
if (is_jsonp) {
|
|
||||||
mg_printf(conn, "%s", ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect user to the login form. In the cookie, store the original URL
|
|
||||||
// we came from, so that after the authorization we could redirect back.
|
|
||||||
static void redirect_to_login(struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
|
|
||||||
"Set-Cookie: original_url=%s\r\n"
|
|
||||||
"Location: %s\r\n\r\n",
|
|
||||||
request_info->uri, login_url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return 1 if username/password is allowed, 0 otherwise.
|
|
||||||
static int check_password(const char *user, const char *password) {
|
|
||||||
// In production environment we should ask an authentication system
|
|
||||||
// to authenticate the user.
|
|
||||||
// Here however we do trivial check that user and password are not empty
|
|
||||||
return (user[0] && password[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate new session object
|
|
||||||
static struct session *new_session(void) {
|
|
||||||
int i;
|
|
||||||
time_t now = time(NULL);
|
|
||||||
pthread_rwlock_wrlock(&rwlock);
|
|
||||||
for (i = 0; i < MAX_SESSIONS; i++) {
|
|
||||||
if (sessions[i].expire == 0 || sessions[i].expire < now) {
|
|
||||||
sessions[i].expire = time(0) + SESSION_TTL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pthread_rwlock_unlock(&rwlock);
|
|
||||||
return i == MAX_SESSIONS ? NULL : &sessions[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate session ID. buf must be 33 bytes in size.
|
|
||||||
// Note that it is easy to steal session cookies by sniffing traffic.
|
|
||||||
// This is why all communication must be SSL-ed.
|
|
||||||
static void generate_session_id(char *buf, const char *random,
|
|
||||||
const char *user) {
|
|
||||||
mg_md5(buf, random, user, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void send_server_message(const char *fmt, ...) {
|
|
||||||
va_list ap;
|
|
||||||
struct message *message;
|
|
||||||
|
|
||||||
pthread_rwlock_wrlock(&rwlock);
|
|
||||||
message = new_message();
|
|
||||||
message->user[0] = '\0'; // Empty user indicates server message
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vsnprintf(message->text, sizeof(message->text), fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
pthread_rwlock_unlock(&rwlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A handler for the /authorize endpoint.
|
|
||||||
// Login page form sends user name and password to this endpoint.
|
|
||||||
static void authorize(struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
char user[MAX_USER_LEN], password[MAX_USER_LEN];
|
|
||||||
struct session *session;
|
|
||||||
|
|
||||||
// Fetch user name and password.
|
|
||||||
get_qsvar(request_info, "user", user, sizeof(user));
|
|
||||||
get_qsvar(request_info, "password", password, sizeof(password));
|
|
||||||
|
|
||||||
if (check_password(user, password) && (session = new_session()) != NULL) {
|
|
||||||
// Authentication success:
|
|
||||||
// 1. create new session
|
|
||||||
// 2. set session ID token in the cookie
|
|
||||||
// 3. remove original_url from the cookie - not needed anymore
|
|
||||||
// 4. redirect client back to the original URL
|
|
||||||
//
|
|
||||||
// The most secure way is to stay HTTPS all the time. However, just to
|
|
||||||
// show the technique, we redirect to HTTP after the successful
|
|
||||||
// authentication. The danger of doing this is that session cookie can
|
|
||||||
// be stolen and an attacker may impersonate the user.
|
|
||||||
// Secure application must use HTTPS all the time.
|
|
||||||
my_strlcpy(session->user, user, sizeof(session->user));
|
|
||||||
snprintf(session->random, sizeof(session->random), "%d", rand());
|
|
||||||
generate_session_id(session->session_id, session->random, session->user);
|
|
||||||
send_server_message("<%s> joined", session->user);
|
|
||||||
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
|
|
||||||
"Set-Cookie: session=%s; max-age=3600; http-only\r\n" // Session ID
|
|
||||||
"Set-Cookie: user=%s\r\n" // Set user, needed by Javascript code
|
|
||||||
"Set-Cookie: original_url=/; max-age=0\r\n" // Delete original_url
|
|
||||||
"Location: /\r\n\r\n",
|
|
||||||
session->session_id, session->user);
|
|
||||||
} else {
|
|
||||||
// Authentication failure, redirect to login.
|
|
||||||
redirect_to_login(conn, request_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return 1 if request is authorized, 0 otherwise.
|
|
||||||
static int is_authorized(const struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
struct session *session;
|
|
||||||
char valid_id[33];
|
|
||||||
int authorized = 0;
|
|
||||||
|
|
||||||
// Always authorize accesses to login page and to authorize URI
|
|
||||||
if (!strcmp(request_info->uri, login_url) ||
|
|
||||||
!strcmp(request_info->uri, authorize_url)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_rwlock_rdlock(&rwlock);
|
|
||||||
if ((session = get_session(conn)) != NULL) {
|
|
||||||
generate_session_id(valid_id, session->random, session->user);
|
|
||||||
if (strcmp(valid_id, session->session_id) == 0) {
|
|
||||||
session->expire = time(0) + SESSION_TTL;
|
|
||||||
authorized = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pthread_rwlock_unlock(&rwlock);
|
|
||||||
|
|
||||||
return authorized;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redirect_to_ssl(struct mg_connection *conn,
|
|
||||||
const struct mg_request_info *request_info) {
|
|
||||||
const char *p, *host = mg_get_header(conn, "Host");
|
|
||||||
if (host != NULL && (p = strchr(host, ':')) != NULL) {
|
|
||||||
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
|
|
||||||
"Location: https://%.*s:8082/%s:8082\r\n\r\n",
|
|
||||||
(int) (p - host), host, request_info->uri);
|
|
||||||
} else {
|
|
||||||
mg_printf(conn, "%s", "HTTP/1.1 500 Error\r\n\r\nHost: header is not set");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int event_handler(struct mg_event *event) {
|
|
||||||
struct mg_request_info *request_info = event->request_info;
|
|
||||||
struct mg_connection *conn = event->conn;
|
|
||||||
int result = 1;
|
|
||||||
|
|
||||||
if (event->type != MG_REQUEST_BEGIN) return 0;
|
|
||||||
|
|
||||||
if (!request_info->is_ssl) {
|
|
||||||
redirect_to_ssl(conn, request_info);
|
|
||||||
} else if (!is_authorized(conn, request_info)) {
|
|
||||||
redirect_to_login(conn, request_info);
|
|
||||||
} else if (strcmp(request_info->uri, authorize_url) == 0) {
|
|
||||||
authorize(conn, request_info);
|
|
||||||
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
|
|
||||||
ajax_get_messages(conn, request_info);
|
|
||||||
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
|
|
||||||
ajax_send_message(conn, request_info);
|
|
||||||
} else {
|
|
||||||
// No suitable handler found, mark as not processed. Mongoose will
|
|
||||||
// try to serve the request.
|
|
||||||
result = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *options[] = {
|
|
||||||
"document_root", "html",
|
|
||||||
"listening_ports", "8081,8082s",
|
|
||||||
"ssl_certificate", "ssl_cert.pem",
|
|
||||||
"num_threads", "5",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_context *ctx;
|
struct mg_server *server = mg_create_server(NULL);
|
||||||
|
mg_set_option(server, "listening_port", "8080");
|
||||||
|
mg_set_option(server, "document_root", "chat_html_root");
|
||||||
|
|
||||||
// Initialize random number generator. It will be used later on for
|
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||||
// the session identifier creation.
|
for (;;) {
|
||||||
srand((unsigned) time(0));
|
mg_poll_server(server, 1000);
|
||||||
|
|
||||||
// Setup and start Mongoose
|
|
||||||
if ((ctx = mg_start(options, event_handler, NULL)) == NULL) {
|
|
||||||
printf("%s\n", "Cannot start chat server, fatal exit");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
mg_destroy_server(&server);
|
||||||
|
|
||||||
// Wait until enter is pressed, then exit
|
return 0;
|
||||||
printf("Chat server started on ports %s, press enter to quit.\n",
|
|
||||||
mg_get_option(ctx, "listening_ports"));
|
|
||||||
getchar();
|
|
||||||
mg_stop(ctx);
|
|
||||||
printf("%s\n", "Chat server stopped.");
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim:ts=2:sw=2:et
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -3,7 +3,7 @@
|
|||||||
var chat = {
|
var chat = {
|
||||||
// Backend URL, string.
|
// Backend URL, string.
|
||||||
// 'http://backend.address.com' or '' if backend is the same as frontend
|
// 'http://backend.address.com' or '' if backend is the same as frontend
|
||||||
backendUrl: '',
|
backendUrl: 'api.lp',
|
||||||
maxVisibleMessages: 10,
|
maxVisibleMessages: 10,
|
||||||
errorMessageFadeOutTimeoutMs: 2000,
|
errorMessageFadeOutTimeoutMs: 2000,
|
||||||
errorMessageFadeOutTimer: null,
|
errorMessageFadeOutTimer: null,
|
||||||
@@ -49,9 +49,7 @@ chat.refresh = function(data) {
|
|||||||
|
|
||||||
chat.getMessages = function(enter_loop) {
|
chat.getMessages = function(enter_loop) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
dataType: 'jsonp',
|
data: {last_id: chat.lastMessageId, cmd: 'get_messages'},
|
||||||
url: chat.backendUrl + '/ajax/get_messages',
|
|
||||||
data: {last_id: chat.lastMessageId},
|
|
||||||
success: chat.refresh,
|
success: chat.refresh,
|
||||||
error: function() {
|
error: function() {
|
||||||
},
|
},
|
||||||
@@ -82,9 +80,7 @@ chat.handleMessageInput = function(ev) {
|
|||||||
return;
|
return;
|
||||||
//input.disabled = true;
|
//input.disabled = true;
|
||||||
$.ajax({
|
$.ajax({
|
||||||
dataType: 'jsonp',
|
data: { text: input.value, cmd: 'send_message' },
|
||||||
url: chat.backendUrl + '/ajax/send_message',
|
|
||||||
data: {text: input.value},
|
|
||||||
success: function(ev) {
|
success: function(ev) {
|
||||||
input.value = '';
|
input.value = '';
|
||||||
input.disabled = false;
|
input.disabled = false;
|
||||||
@@ -98,6 +94,10 @@ chat.handleMessageInput = function(ev) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
$.ajaxSetup({
|
||||||
|
dataType: 'json',
|
||||||
|
url: chat.backendUrl
|
||||||
|
});
|
||||||
$('.menu-item').click(chat.handleMenuItemClick);
|
$('.menu-item').click(chat.handleMenuItemClick);
|
||||||
$('.message-input').keypress(chat.handleMessageInput);
|
$('.message-input').keypress(chat.handleMessageInput);
|
||||||
chat.getMessages(true);
|
chat.getMessages(true);
|
||||||
File renamed without changes.
@@ -0,0 +1,46 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEA4+LVrSV4kvgr2AM5NSIewSfGwK5fXUOoYQ4vpjq2+H2hhOPP
|
||||||
|
0WVyHkLWZXYek8ZmCqPFBvavzO9bveBprQIDTHexj83paaoV8IMugzymeWVONEwU
|
||||||
|
8E9S6sGihCF01yOO5g4X7lmX7mypUDNJHZd/0SuSGezwSd+c41pY/CWmCW+61hls
|
||||||
|
HjKdBp3Nked/u2aJ619D8NYPeu03V89P5x5hWrOWLSfmpd8pQ5tceVO56XjgmZLr
|
||||||
|
FwUTK5Dq/3hUv1lyxHoSWGp5sQirZ5artLgkqCI+K+dwc2YZeeJBACllSqVuzJYh
|
||||||
|
HSpnmKUwMXykJfKtTckfLoJX2VGgbqJWSZFkowIDAQABAoIBAHAKDR30p0QyyXOJ
|
||||||
|
mi26Wb6DbN88m9xBsF4PWHP0uBKRycCJNJpxstqX58q4eS4URAUFIZBb5nDuWX+P
|
||||||
|
/sS1kaOd0WmvRgvSOLkn9c1GG17h5MInib/gcBbFHr6xqD1B2TbYRQHFgMITIGcy
|
||||||
|
38axvoC7WDhw2m6nhv3sZIWLiQdnvsUWF+mXQoRwyK9Me/3/N9atRELPd9rvbwkz
|
||||||
|
ITyZQPE7IScSwVClspSBQMdJj4Bak8sQ8uKpxmIqpRGe9bqz2IO2hxkW9RiAT2CE
|
||||||
|
4KZ1eqSM8q+VUlFfEo7IJ1lh8Anuo/h/qVnN2JTyCWoAW/YLIWUreLAOVCm3feV/
|
||||||
|
fFO5sQkCgYEA8yzhvPegx1cPgtvEaAVJKbI9WBVCT7qAroDrfxBS3m37jHip8SDr
|
||||||
|
4qfh+5OWG6J0hXEKCf1FbhDCDQOf6sQCMlBgR5BU/ALqiFWfEp+ysC10VtdCpEhA
|
||||||
|
PgldtzLhXyApw536y05Ak3Yucii70m80bsXZR2alGWTfJ64e511hSM0CgYEA7+eI
|
||||||
|
QXh9kuKwM143Bk7HWvLNJ71TAb+mp0OkOjmzMe2duoGi0DzTBlSh6AYdSYkMYSbc
|
||||||
|
PW8bF88bTiq4rak9PuJGiFr7Iq5SMccpaZ+JIPnF2S8gdNB1hgfRsUDNHhb691/u
|
||||||
|
3Q/7gOUpiWGrP5uP55/yuFyKkrp+gPGKLhrFIy8CgYA096q34WxI30LGqx3t1nDo
|
||||||
|
PyWzgPXvrd0HpZcmFyUlw+kN4N6LYjNfLFhB1xTUMh+VTVj1chBkeAv8j7SOi2E7
|
||||||
|
GlzOWbbZKb1yMswyX0ypqUz0+XBPnlk36gxash96++N883VEQWg5dn2Jaiv3Gzfd
|
||||||
|
hQTd05LUpC8jUn7ywFlHZQKBgE3agWldxMKqNiw1Yv31CtUE/nbrBckHfPnDBcSv
|
||||||
|
fOuzBQDFmSSi2IixZml5sn9urknI5qg5Kj2rxFy1mIEi7vE9VU5i2CUFFT96NoJE
|
||||||
|
d6fv5rj4hjvieoEogHBZazdclc05oLa0vRWzSKuAuqZFwYmVaptRzNaZFvyZZhJY
|
||||||
|
NamZAoGBANL8Jqht1s1qcLuBis1jZdjzUuF3xNMuxwqszD0zFZFAIUN097460zw+
|
||||||
|
8LIeZgVMJOCmbVHja/RII9PI4vQ7h5rhTmeFWS80QMe22C1yukqEqljYO0POhxsu
|
||||||
|
sm3plGS3rfGkMp1URDgnJL6m/y8gC4+/O6NIEoVaHpraW8//qCD6
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDEjCCAfoCBRQDhmJSMA0GCSqGSIb3DQEBBQUAMFMxFDASBgNVBAMTC2Nlc2Fu
|
||||||
|
dGEuY29tMR0wGwYDVQQKExRDZXNhbnRhIFNvZnR3YXJlIEx0ZDELMAkGA1UEBhMC
|
||||||
|
SUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA2MjcxMDUwNTFaFw0yNDA2MjQxMDUw
|
||||||
|
NTFaMEcxFTATBgNVBAMTDDE5Mi4xNjguMS4yODEQMA4GA1UEChMHRm9vK0x0ZDEL
|
||||||
|
MAkGA1UEBhMCSUUxDzANBgNVBAcTBkdhbHdheTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||||
|
ggEPADCCAQoCggEBAOPi1a0leJL4K9gDOTUiHsEnxsCuX11DqGEOL6Y6tvh9oYTj
|
||||||
|
z9Flch5C1mV2HpPGZgqjxQb2r8zvW73gaa0CA0x3sY/N6WmqFfCDLoM8pnllTjRM
|
||||||
|
FPBPUurBooQhdNcjjuYOF+5Zl+5sqVAzSR2Xf9Erkhns8EnfnONaWPwlpglvutYZ
|
||||||
|
bB4ynQadzZHnf7tmietfQ/DWD3rtN1fPT+ceYVqzli0n5qXfKUObXHlTuel44JmS
|
||||||
|
6xcFEyuQ6v94VL9ZcsR6ElhqebEIq2eWq7S4JKgiPivncHNmGXniQQApZUqlbsyW
|
||||||
|
IR0qZ5ilMDF8pCXyrU3JHy6CV9lRoG6iVkmRZKMCAwEAATANBgkqhkiG9w0BAQUF
|
||||||
|
AAOCAQEAAxJSilcxos3v5aKllKTsx04Q/P39wQ07ywS1Yf9NsA7MlPU++qs3BqJJ
|
||||||
|
kAaCPpfeAl8GjQLh75XV7rLvycvrluthpHzMRZhnbd+DLCt+FrS5zWWhm6M7535V
|
||||||
|
FHGjuW59sAurARgcB530+bh6a0YNhKJbp3Si5ONhQfEuKPf+R6bPA1Slr2k/ts+K
|
||||||
|
GIzwJbNTVRSor/wlb4F0seHFEv2rdtHt1g+VYlFVwBFawIOx3dU7hHxgzeoRYnik
|
||||||
|
RGrPvhqK3u1wQ+2s/m2HPWj8CFjZ8V7f4sYRjLhpsbeJCy4A28nWvl+cCjAhCQKq
|
||||||
|
Rawa0TcZiQtsj0jaTe71jhAMSMC6qw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
+9
-34
@@ -1,46 +1,21 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
static int index_html(struct mg_connection *conn) {
|
switch (ev) {
|
||||||
FILE *fp = (FILE *) conn->connection_param;
|
case MG_REQUEST:
|
||||||
char buf[200];
|
mg_send_file(conn, "file.c");
|
||||||
size_t n = 0;
|
return MG_MORE; // It is important to return MG_MORE after mg_send_file!
|
||||||
|
case MG_AUTH: return MG_TRUE;
|
||||||
if (fp == NULL) {
|
default: return MG_FALSE;
|
||||||
fp = fopen("../mongoose.c", "r");
|
|
||||||
conn->connection_param = (void *) fp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fp != NULL) {
|
|
||||||
n = fread(buf, 1, sizeof(buf), fp);
|
|
||||||
mg_send_data(conn, buf, n);
|
|
||||||
|
|
||||||
if (n < sizeof(buf) || conn->wsbits != 0) {
|
|
||||||
fclose(fp);
|
|
||||||
conn->connection_param = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n < sizeof(buf) ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_server *server;
|
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||||
|
|
||||||
// Create and configure the server
|
|
||||||
server = mg_create_server(NULL);
|
|
||||||
mg_set_option(server, "listening_port", "8080");
|
mg_set_option(server, "listening_port", "8080");
|
||||||
mg_add_uri_handler(server, "/", index_html);
|
|
||||||
|
|
||||||
// Serve request. Hit Ctrl-C to terminate the program
|
|
||||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||||
for (;;) {
|
for (;;) mg_poll_server(server, 1000);
|
||||||
mg_poll_server(server, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup, and free server instance
|
|
||||||
mg_destroy_server(&server);
|
mg_destroy_server(&server);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int static_value = 123; // Exposed and changeable via the form
|
||||||
|
|
||||||
|
static void print_html_form(struct mg_connection *conn) {
|
||||||
|
mg_send_header(conn, "Content-Type", "text/html");
|
||||||
|
mg_send_header(conn, "Cache-Control", "max-age=0, post-check=0, "
|
||||||
|
"pre-check=0, no-store, no-cache, must-revalidate");
|
||||||
|
|
||||||
|
// Note that all the following normally should reside in static HTML page
|
||||||
|
mg_printf_data(conn, "%s", "<html><head>");
|
||||||
|
// It is better to use local copy though
|
||||||
|
mg_printf_data(conn, "<script src=\"%s\"></script>",
|
||||||
|
"http://code.jquery.com/jquery-1.11.0.min.js");
|
||||||
|
mg_printf_data(conn, "%s", "<script> jQuery(function() {\n");
|
||||||
|
// Here is the ajax call that fetches data from the device and
|
||||||
|
// updates the form
|
||||||
|
mg_printf_data(conn, "%s", "$.ajax({ url: '/get_value', dataType: 'json', "
|
||||||
|
"success: function(d) { $('#val').val(d.value); }});\n");
|
||||||
|
// This ajax call is triggered when submit button is pressed. It sends new
|
||||||
|
// value to the device.
|
||||||
|
mg_printf_data(conn, "%s", "$(document).on('click', '#button', function() {"
|
||||||
|
" $.ajax({ url: '/set_value', dataType: 'json', "
|
||||||
|
" data: { new_value: $('#val').val() } });\n"
|
||||||
|
" return false; });\n");
|
||||||
|
mg_printf_data(conn, "%s", "});</script>");
|
||||||
|
mg_printf_data(conn, "%s", "</head><body>");
|
||||||
|
mg_printf_data(conn, "%s", "<h1>Ajax form submission example</h1>");
|
||||||
|
mg_printf_data(conn, "%s", "<form>");
|
||||||
|
mg_printf_data(conn, "%s", "Device value: <input type=text id=val />");
|
||||||
|
mg_printf_data(conn, "%s", "<input type=submit id=button />");
|
||||||
|
mg_printf_data(conn, "%s", "</form>");
|
||||||
|
mg_printf_data(conn, "%s", "</body></html>");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
if (ev == MG_REQUEST) {
|
||||||
|
if (strcmp(conn->uri, "/get_value") == 0) {
|
||||||
|
mg_printf_data(conn, "{\"value\": %d}", static_value);
|
||||||
|
} else if (strcmp(conn->uri, "/set_value") == 0) {
|
||||||
|
// This Ajax endpoint sets the new value for the device variable
|
||||||
|
char buf[100] = "";
|
||||||
|
mg_get_var(conn, "new_value", buf, sizeof(buf)); // Get form variable
|
||||||
|
static_value = atoi(buf); // Set new value
|
||||||
|
mg_printf_data(conn, "%s", "{\"success\": true}");
|
||||||
|
printf("Setting value to [%d]\n", static_value); // Debug trace
|
||||||
|
} else {
|
||||||
|
// Better way is to set "document_root" option, put "index.html" file
|
||||||
|
// into document_root and return MG_FALSE here. We're printing HTML
|
||||||
|
// page by hands just to keep everything in one C file.
|
||||||
|
print_html_form(conn);
|
||||||
|
}
|
||||||
|
return MG_TRUE;
|
||||||
|
} else if (ev == MG_AUTH) {
|
||||||
|
return MG_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MG_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_server *server;
|
||||||
|
|
||||||
|
// Create and configure the server
|
||||||
|
server = mg_create_server(NULL, ev_handler);
|
||||||
|
mg_set_option(server, "listening_port", "8000");
|
||||||
|
|
||||||
|
// Serve request. Hit Ctrl-C to terminate the program
|
||||||
|
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||||
|
for (;;) {
|
||||||
|
mg_poll_server(server, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup, and free server instance
|
||||||
|
mg_destroy_server(&server);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+12
-6
@@ -2,19 +2,25 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
// This function will be called by mongoose on every new request
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
static int index_html(struct mg_connection *conn) {
|
int result = MG_FALSE;
|
||||||
mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
|
|
||||||
return MG_REQUEST_PROCESSED;
|
if (ev == MG_REQUEST) {
|
||||||
|
mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
|
||||||
|
result = MG_TRUE;
|
||||||
|
} else if (ev == MG_AUTH) {
|
||||||
|
result = MG_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_server *server;
|
struct mg_server *server;
|
||||||
|
|
||||||
// Create and configure the server
|
// Create and configure the server
|
||||||
server = mg_create_server(NULL);
|
server = mg_create_server(NULL, ev_handler);
|
||||||
mg_set_option(server, "listening_port", "8080");
|
mg_set_option(server, "listening_port", "8080");
|
||||||
mg_set_request_handler(server, index_html);
|
|
||||||
|
|
||||||
// Serve request. Hit Ctrl-C to terminate the program
|
// Serve request. Hit Ctrl-C to terminate the program
|
||||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<? mg.write('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') ?>
|
||||||
|
<html><body>
|
||||||
|
<span>Today is:</span> <? mg.write(os.date("%A")) ?>
|
||||||
|
</body></html>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
LOCAL_PATH := $(call my-dir)/../..
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
# To build with lua support, uncomment two lines below:
|
||||||
|
#LUA_SOURCES := build/lua_5.2.1.c build/sqlite3.c build/lsqlite3.c
|
||||||
|
#LUA_FLAGS := -I$(LOCAL_PATH)/build -DTHREADSAFE=1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DUSE_LUA -DUSE_LUA_SQLITE3 -DLUA_COMPAT_ALL -D"getlocaledecpoint() ='.'"
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := -std=c99 -O2 -W -Wall -pthread -pipe $(LUA_FLAGS) $(COPT)
|
||||||
|
LOCAL_MODULE := mongoose
|
||||||
|
LOCAL_SRC_FILES := examples/server.c mongoose.c $(LUA_SOURCES)
|
||||||
|
|
||||||
|
include $(BUILD_EXECUTABLE)
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static void send_file(struct mg_connection *conn, const char *path) {
|
||||||
|
char buf[1024];
|
||||||
|
struct stat st;
|
||||||
|
int n;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (stat(path, &st) == 0 && (fp = fopen(path, "rb")) != NULL) {
|
||||||
|
mg_printf(conn, "--myboundary\r\nContent-Type: image/jpeg\r\n"
|
||||||
|
"Content-Length: %llu\r\n\r\n", (unsigned long long) st.st_size);
|
||||||
|
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
|
||||||
|
mg_write(conn, buf, n);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
mg_write(conn, "\r\n", 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct conn_state {
|
||||||
|
int file_index;
|
||||||
|
time_t last_poll;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
const char **file_names = (const char **) conn->server_param;
|
||||||
|
struct conn_state *state;
|
||||||
|
int result = MG_FALSE;
|
||||||
|
|
||||||
|
if (ev == MG_REQUEST) {
|
||||||
|
mg_printf(conn, "%s",
|
||||||
|
"HTTP/1.0 200 OK\r\n" "Cache-Control: no-cache\r\n"
|
||||||
|
"Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
|
||||||
|
"Connection: close\r\nContent-Type: multipart/x-mixed-replace; "
|
||||||
|
"boundary=--myboundary\r\n\r\n");
|
||||||
|
|
||||||
|
send_file(conn, file_names[0]);
|
||||||
|
|
||||||
|
state = (struct conn_state *) malloc(sizeof(*state));
|
||||||
|
conn->connection_param = state;
|
||||||
|
state->file_index = 1; // First file is already sent
|
||||||
|
state->last_poll = time(NULL);
|
||||||
|
result = MG_MORE;
|
||||||
|
|
||||||
|
} else if (ev == MG_POLL) {
|
||||||
|
|
||||||
|
time_t now = time(NULL);
|
||||||
|
state = (struct conn_state *) conn->connection_param;
|
||||||
|
|
||||||
|
if (state != NULL && now > state->last_poll) {
|
||||||
|
if (file_names[state->file_index] != NULL) {
|
||||||
|
send_file(conn, file_names[state->file_index]);
|
||||||
|
state->file_index++;
|
||||||
|
if (file_names[state->file_index] == NULL) {
|
||||||
|
result = MG_TRUE; // No more images, close connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state->last_poll = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (ev == MG_CLOSE) {
|
||||||
|
free(conn->connection_param);
|
||||||
|
conn->connection_param = NULL;
|
||||||
|
} else if (ev == MG_AUTH) {
|
||||||
|
result = MG_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct mg_server *server;
|
||||||
|
|
||||||
|
if (argc < 3) {
|
||||||
|
printf("Usage: %s image1.jpg image2.jpg ...\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = mg_create_server(&argv[1], ev_handler);
|
||||||
|
mg_set_option(server, "listening_port", "8080");
|
||||||
|
|
||||||
|
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||||
|
for (;;) {
|
||||||
|
mg_poll_server(server, 1000);
|
||||||
|
}
|
||||||
|
mg_destroy_server(&server);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+13
-10
@@ -2,11 +2,17 @@
|
|||||||
|
|
||||||
// Start a browser and hit refresh couple of times. The replies will
|
// Start a browser and hit refresh couple of times. The replies will
|
||||||
// come from both server instances.
|
// come from both server instances.
|
||||||
static int request_handler(struct mg_connection *conn) {
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
mg_send_header(conn, "Content-Type", "text/plain");
|
if (ev == MG_REQUEST) {
|
||||||
mg_printf_data(conn, "This is a reply from server instance # %s",
|
mg_send_header(conn, "Content-Type", "text/plain");
|
||||||
(char *) conn->server_param);
|
mg_printf_data(conn, "This is a reply from server instance # %s",
|
||||||
return MG_REQUEST_PROCESSED;
|
(char *) conn->server_param);
|
||||||
|
return MG_TRUE;
|
||||||
|
} else if (ev == MG_AUTH) {
|
||||||
|
return MG_TRUE;
|
||||||
|
} else {
|
||||||
|
return MG_FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *serve(void *server) {
|
static void *serve(void *server) {
|
||||||
@@ -17,11 +23,8 @@ static void *serve(void *server) {
|
|||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_server *server1, *server2;
|
struct mg_server *server1, *server2;
|
||||||
|
|
||||||
server1 = mg_create_server((void *) "1");
|
server1 = mg_create_server((void *) "1", ev_handler);
|
||||||
server2 = mg_create_server((void *) "2");
|
server2 = mg_create_server((void *) "2", ev_handler);
|
||||||
|
|
||||||
mg_set_request_handler(server1, request_handler);
|
|
||||||
mg_set_request_handler(server2, request_handler);
|
|
||||||
|
|
||||||
// Make both server1 and server2 listen on the same socket
|
// Make both server1 and server2 listen on the same socket
|
||||||
mg_set_option(server1, "listening_port", "8080");
|
mg_set_option(server1, "listening_port", "8080");
|
||||||
|
|||||||
+16
-4
@@ -10,7 +10,7 @@ static const char *html_form =
|
|||||||
"<input type=\"submit\" />"
|
"<input type=\"submit\" />"
|
||||||
"</form></body></html>";
|
"</form></body></html>";
|
||||||
|
|
||||||
static int handler(struct mg_connection *conn) {
|
static void send_reply(struct mg_connection *conn) {
|
||||||
char var1[500], var2[500];
|
char var1[500], var2[500];
|
||||||
|
|
||||||
if (strcmp(conn->uri, "/handle_post_request") == 0) {
|
if (strcmp(conn->uri, "/handle_post_request") == 0) {
|
||||||
@@ -33,18 +33,30 @@ static int handler(struct mg_connection *conn) {
|
|||||||
// Show HTML form.
|
// Show HTML form.
|
||||||
mg_send_data(conn, html_form, strlen(html_form));
|
mg_send_data(conn, html_form, strlen(html_form));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return MG_REQUEST_PROCESSED;
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
if (ev == MG_REQUEST) {
|
||||||
|
send_reply(conn);
|
||||||
|
return MG_TRUE;
|
||||||
|
} else if (ev == MG_AUTH) {
|
||||||
|
return MG_TRUE;
|
||||||
|
} else {
|
||||||
|
return MG_FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_server *server = mg_create_server(NULL);
|
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||||
|
|
||||||
mg_set_option(server, "listening_port", "8080");
|
mg_set_option(server, "listening_port", "8080");
|
||||||
mg_set_request_handler(server, handler);
|
|
||||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||||
for (;;) {
|
for (;;) {
|
||||||
mg_poll_server(server, 1000);
|
mg_poll_server(server, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
mg_destroy_server(&server);
|
mg_destroy_server(&server);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
// Copyright (c) 2014 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// To build and run this example:
|
||||||
|
// git clone https://github.com/cesanta/net_skeleton.git
|
||||||
|
// git clone https://github.com/cesanta/mongoose.git
|
||||||
|
// cd mongoose/examples
|
||||||
|
// make proxy
|
||||||
|
// ./proxy
|
||||||
|
//
|
||||||
|
// Configure your browser to use localhost:2014 as a proxy for all protocols
|
||||||
|
// Then, navigate to https://cesanta.com
|
||||||
|
|
||||||
|
#include "net_skeleton.h"
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int s_received_signal = 0;
|
||||||
|
static struct mg_server *s_server = NULL;
|
||||||
|
|
||||||
|
#define SSE_CONNECTION ((void *) 1)
|
||||||
|
|
||||||
|
static void elog(int do_exit, const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
fputc('\n', stderr);
|
||||||
|
if (do_exit) exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void signal_handler(int sig_num) {
|
||||||
|
signal(sig_num, signal_handler);
|
||||||
|
s_received_signal = sig_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sse_push(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
if (ev == MG_POLL && conn->connection_param == SSE_CONNECTION) {
|
||||||
|
mg_printf(conn, "data: %s\r\n\r\n", (const char *) conn->callback_param);
|
||||||
|
}
|
||||||
|
return MG_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *sse_pusher_thread_func(void *param) {
|
||||||
|
while (s_received_signal == 0) {
|
||||||
|
mg_wakeup_server_ex(s_server, sse_push, "%lu %s",
|
||||||
|
(unsigned long) time(NULL), (const char *) param);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return: 1 if regular file, 2 if directory, 0 if not found
|
||||||
|
static int exists(const char *path) {
|
||||||
|
struct stat st;
|
||||||
|
return stat(path, &st) != 0 ? 0 : S_ISDIR(st.st_mode) == 0 ? 1 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return: 1 if regular file, 2 if directory, 0 if not found
|
||||||
|
static int is_local_file(const char *uri, char *path, size_t path_len) {
|
||||||
|
snprintf(path, path_len, "%s/%s",
|
||||||
|
mg_get_option(s_server, "document_root"), uri);
|
||||||
|
return exists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int try_to_serve_locally(struct mg_connection *conn) {
|
||||||
|
char path[500], buf[2000];
|
||||||
|
int n, res;
|
||||||
|
FILE *fp = NULL;
|
||||||
|
|
||||||
|
if ((res = is_local_file(conn->uri, path, sizeof(path))) == 2) {
|
||||||
|
strncat(path, "/index.html", sizeof(path) - strlen(path) - 1);
|
||||||
|
res = exists(path);
|
||||||
|
printf("PATH: [%s]\n", path);
|
||||||
|
}
|
||||||
|
if (res == 0) return MG_FALSE;
|
||||||
|
|
||||||
|
if ((fp = fopen(path, "rb")) != NULL) {
|
||||||
|
printf("Serving [%s] locally \n", path);
|
||||||
|
mg_send_header(conn, "Connection", "close");
|
||||||
|
mg_send_header(conn, "Content-Type", mg_get_mime_type(path, "text/plain"));
|
||||||
|
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
|
||||||
|
mg_send_data(conn, buf, n);
|
||||||
|
}
|
||||||
|
mg_send_data(conn, "", 0);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
return fp == NULL ? MG_FALSE : MG_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_resource_present_locally(const char *uri) {
|
||||||
|
char path[500];
|
||||||
|
return is_local_file(uri, path, sizeof(path)) || strcmp(uri, "/api/sse") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int proxy_event_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
static const char target_url[] = "http://cesanta.com";
|
||||||
|
static int target_url_size = sizeof(target_url) - 1;
|
||||||
|
const char *host;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_REQUEST:
|
||||||
|
host = mg_get_header(conn, "Host");
|
||||||
|
printf("[%s] [%s] [%s]\n", conn->request_method, conn->uri,
|
||||||
|
host == NULL ? "" : host);
|
||||||
|
if (strstr(conn->uri, "/qqq") != NULL) s_received_signal = SIGTERM;
|
||||||
|
|
||||||
|
// Proxied HTTPS requests use "CONNECT foo.com:443"
|
||||||
|
// Proxied HTTP requests use "GET http://..... "
|
||||||
|
// Serve requests for target_url from the local FS.
|
||||||
|
if (memcmp(conn->uri, target_url, target_url_size) == 0 &&
|
||||||
|
is_resource_present_locally(conn->uri + target_url_size)) {
|
||||||
|
conn->uri += target_url_size; // Leave only path in the URI
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(conn->uri, "/api/sse") == 0) {
|
||||||
|
conn->connection_param = SSE_CONNECTION;
|
||||||
|
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n"
|
||||||
|
"Content-Type: text/event-stream\r\n"
|
||||||
|
"Cache-Control: no-cache\r\n\r\n");
|
||||||
|
return MG_MORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host != NULL && strstr(host, "cesanta") != NULL) {
|
||||||
|
return try_to_serve_locally(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable man-in-the-middle SSL mode for oracle.com
|
||||||
|
if (!strcmp(conn->request_method, "CONNECT") &&
|
||||||
|
!strcmp(host, "oracle.com")) {
|
||||||
|
mg_terminate_ssl(conn, "ssl_cert.pem"); // MUST return MG_MORE after
|
||||||
|
return MG_MORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MG_FALSE;
|
||||||
|
case MG_AUTH:
|
||||||
|
return MG_TRUE;
|
||||||
|
default:
|
||||||
|
return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setopt(struct mg_server *s, const char *opt, const char *val) {
|
||||||
|
const char *err_msg = mg_set_option(s, opt, val);
|
||||||
|
if (err_msg != NULL) {
|
||||||
|
elog(1, "Error setting [%s]: [%s]", opt, err_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
const char *cert = NULL, *ca_cert = NULL, *port = "2014";
|
||||||
|
const char *mitm = NULL, *dump = NULL, *root = "proxy_web_root";
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Parse command line options
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-ca_cert") == 0 && i + 1 < argc) {
|
||||||
|
ca_cert = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-cert") == 0 && i + 1 < argc) {
|
||||||
|
cert = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) {
|
||||||
|
port = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-mitm") == 0 && i + 1 < argc) {
|
||||||
|
mitm = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-root") == 0 && i + 1 < argc) {
|
||||||
|
root = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-dump") == 0 && i + 1 < argc) {
|
||||||
|
dump = argv[++i];
|
||||||
|
} else {
|
||||||
|
elog(1, "Usage: %s [-cert FILE] [-ca_cert FILE] [-port PORT]", argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
|
||||||
|
// Create and configure proxy server
|
||||||
|
s_server = mg_create_server(NULL, &proxy_event_handler);
|
||||||
|
setopt(s_server, "enable_proxy", "yes");
|
||||||
|
setopt(s_server, "document_root", root);
|
||||||
|
setopt(s_server, "listening_port", port);
|
||||||
|
setopt(s_server, "ssl_certificate", cert);
|
||||||
|
setopt(s_server, "ssl_ca_certificate", ca_cert);
|
||||||
|
setopt(s_server, "hexdump_file", dump);
|
||||||
|
setopt(s_server, "ssl_mitm_certs", mitm);
|
||||||
|
|
||||||
|
// Start two SSE pushing threads
|
||||||
|
mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_1");
|
||||||
|
mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_2");
|
||||||
|
|
||||||
|
// Start serving in the main thread
|
||||||
|
printf("Starting on port %s\n", mg_get_option(s_server, "listening_port"));
|
||||||
|
while (s_received_signal == 0) {
|
||||||
|
mg_poll_server(s_server, 1000);
|
||||||
|
}
|
||||||
|
printf("Existing on signal %d\n", s_received_signal);
|
||||||
|
mg_destroy_server(&s_server);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>App1 Index</title>
|
||||||
|
<style>
|
||||||
|
img { height: 40px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>App1 index page. Served locally from the the proxy server filesystem</h1>
|
||||||
|
|
||||||
|
<p>image that references non-existent local resource. Forwarded to
|
||||||
|
the 'real' proxy target:</p>
|
||||||
|
<img src="http://cesanta.com/images/logo.png" />
|
||||||
|
|
||||||
|
<p>Google logo via HTTPS (external resource, served by remote host):</p>
|
||||||
|
<img src="https://www.google.ie/images/srpr/logo11w.png" />
|
||||||
|
|
||||||
|
<p>Same image via HTTP:</p>
|
||||||
|
<img src="http://www.google.ie/images/srpr/logo11w.png" />
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>App2 Index</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script>
|
||||||
|
window.onload = function() {
|
||||||
|
// Using secure websocket connection, wss://
|
||||||
|
var ws = new WebSocket('wss://echo.websocket.org');
|
||||||
|
var div = document.getElementById('events');
|
||||||
|
ws.onmessage = function(ev) {
|
||||||
|
var el = document.createElement('div');
|
||||||
|
el.innerHTML = 'websocket message: ' + ev.data;
|
||||||
|
div.appendChild(el);
|
||||||
|
// Keep only last 5 messages in the list
|
||||||
|
while (div.childNodes.length > 5) div.removeChild(div.firstChild);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send random stuff to the websocket connection periodically.
|
||||||
|
// websocket server much echo that stuff back.
|
||||||
|
window.setInterval(function() {
|
||||||
|
var d = new Date();
|
||||||
|
ws.send(d.toString());
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>App2 index page. Served locally from the
|
||||||
|
the proxy's filesystem.</h1>
|
||||||
|
<p>
|
||||||
|
Following div shows proxy forwarding of websocket connection, served by
|
||||||
|
ws://echo.websocket.org:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div id="events"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title> proxy index </title>
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.onload = function() {
|
||||||
|
var es = new EventSource("/api/sse");
|
||||||
|
var div = document.getElementById('events');
|
||||||
|
es.onmessage = function(ev) {
|
||||||
|
var el = document.createElement('div');
|
||||||
|
el.innerHTML = 'sse message: ' + ev.data;
|
||||||
|
div.appendChild(el);
|
||||||
|
// Keep only last 5 messages in the list
|
||||||
|
while (div.childNodes.length > 5) div.removeChild(div.firstChild);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1> proxy index page.</h1>
|
||||||
|
<ul>
|
||||||
|
<li><a href="app1">App1</a> - App1 root</li>
|
||||||
|
<li><a href="app2">App2</a> - App2 root</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>SSE pushes, done by separate threads at random times:</h2>
|
||||||
|
<div id="events"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>WebSocket Test</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
background-color: #cde; margin: 0;
|
||||||
|
padding: 0; font: 14px Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
div.content {
|
||||||
|
width: 800px; margin: 2em auto; padding: 20px 50px;
|
||||||
|
background-color: #fff; border-radius: 1em;
|
||||||
|
}
|
||||||
|
#messages {
|
||||||
|
border: 2px solid #fec; border-radius: 1em;
|
||||||
|
height: 10em; overflow: scroll; padding: 0.5em 1em;
|
||||||
|
}
|
||||||
|
a:link, a:visited { color: #69c; text-decoration: none; }
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
body { background-color: #fff; }
|
||||||
|
div.content {
|
||||||
|
width: auto; margin: 0 auto; border-radius: 0;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script language="javascript" type="text/javascript">
|
||||||
|
|
||||||
|
var rooms = [];
|
||||||
|
var ws = new WebSocket('ws://' + location.host + '/ws');
|
||||||
|
|
||||||
|
if (!window.console) { window.console = { log: function() {} } };
|
||||||
|
|
||||||
|
ws.onopen = function(ev) { console.log(ev); };
|
||||||
|
ws.onerror = function(ev) { console.log(ev); };
|
||||||
|
ws.onclose = function(ev) { console.log(ev); };
|
||||||
|
ws.onmessage = function(ev) {
|
||||||
|
console.log(ev);
|
||||||
|
var m = (ev.data || '').match(/^(\S+) (.+)/);
|
||||||
|
if (m[1] == 'id') {
|
||||||
|
document.getElementById('my_id').innerText = m[2];
|
||||||
|
} else if (m[1] == 'msg') {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.innerHTML = m[2];
|
||||||
|
document.getElementById('messages').appendChild(div);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
document.getElementById('send_button').onclick = function(ev) {
|
||||||
|
var msg = document.getElementById('send_input').value;
|
||||||
|
ws.send('msg ' + msg);
|
||||||
|
};
|
||||||
|
document.getElementById('room_sel').onchange = function(ev) {
|
||||||
|
var roomName = this.value || '?';
|
||||||
|
ws.send('join ' + roomName);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<h1>Websocket PubSub Demonstration</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This page demonstrates how Mongoose web server could be used to implement
|
||||||
|
<a href="http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">
|
||||||
|
publish–subscribe pattern</a>. Open this page in several browser
|
||||||
|
windows. Each window initiates persistent
|
||||||
|
<a href="http://en.wikipedia.org/wiki/WebSocket">WebSocket</a>
|
||||||
|
connection with Mongoose, making each browser window a websocket client.
|
||||||
|
Join a room, send messages, and see messages sent by other clients.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
My ID: <b><span id="my_id"></b></span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Join room: <select id="room_sel">
|
||||||
|
<option value="">-- select room -- </option>
|
||||||
|
<option>A</option>
|
||||||
|
<option>B</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div id="messages">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<input type="text" id="send_input" />
|
||||||
|
<button id="send_button">Send Message</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+57
-30
@@ -25,6 +25,7 @@
|
|||||||
#define _WIN32_WINNT 0x500 // Enable MIIM_BITMAP
|
#define _WIN32_WINNT 0x500 // Enable MIIM_BITMAP
|
||||||
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
|
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
|
||||||
#define _XOPEN_SOURCE 600 // For PATH_MAX on linux
|
#define _XOPEN_SOURCE 600 // For PATH_MAX on linux
|
||||||
|
#undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
@@ -65,6 +67,11 @@ typedef struct _stat file_stat_t;
|
|||||||
typedef struct stat file_stat_t;
|
typedef struct stat file_stat_t;
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef IOS
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define DIRSEP '/'
|
#define DIRSEP '/'
|
||||||
#define __cdecl
|
#define __cdecl
|
||||||
#define abs_path(rel, abs, abs_size) realpath((rel), (abs))
|
#define abs_path(rel, abs, abs_size) realpath((rel), (abs))
|
||||||
@@ -73,12 +80,19 @@ typedef struct stat file_stat_t;
|
|||||||
#define MAX_OPTIONS 100
|
#define MAX_OPTIONS 100
|
||||||
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
|
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
|
||||||
|
|
||||||
|
#ifndef MVER
|
||||||
|
#define MVER MONGOOSE_VERSION
|
||||||
|
#endif
|
||||||
|
|
||||||
static int exit_flag;
|
static int exit_flag;
|
||||||
static char server_name[50]; // Set by init_server_name()
|
static char server_name[50]; // Set by init_server_name()
|
||||||
static char config_file[PATH_MAX]; // Set by process_command_line_arguments()
|
static char s_config_file[PATH_MAX]; // Set by process_command_line_arguments
|
||||||
static struct mg_server *server; // Set by start_mongoose()
|
static struct mg_server *server; // Set by start_mongoose()
|
||||||
static const char *s_default_document_root = ".";
|
static const char *s_default_document_root = ".";
|
||||||
static const char *s_default_listening_port = "8080";
|
static const char *s_default_listening_port = "8080";
|
||||||
|
static char **s_argv = { NULL };
|
||||||
|
|
||||||
|
static void set_options(char *argv[]);
|
||||||
|
|
||||||
#if !defined(CONFIG_FILE)
|
#if !defined(CONFIG_FILE)
|
||||||
#define CONFIG_FILE "mongoose.conf"
|
#define CONFIG_FILE "mongoose.conf"
|
||||||
@@ -129,7 +143,7 @@ static void show_usage_and_exit(void) {
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
|
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
|
||||||
MONGOOSE_VERSION, __DATE__);
|
MVER, __DATE__);
|
||||||
fprintf(stderr, "Usage:\n");
|
fprintf(stderr, "Usage:\n");
|
||||||
#ifndef MONGOOSE_NO_AUTH
|
#ifndef MONGOOSE_NO_AUTH
|
||||||
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
|
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
|
||||||
@@ -146,6 +160,8 @@ static void show_usage_and_exit(void) {
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define EV_HANDLER NULL
|
||||||
|
|
||||||
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) {
|
||||||
@@ -176,32 +192,34 @@ static void set_option(char **options, const char *name, const char *value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void process_command_line_arguments(char *argv[], char **options) {
|
static void process_command_line_arguments(char *argv[], char **options) {
|
||||||
char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)], *p;
|
char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)],
|
||||||
|
*p, cpath[PATH_MAX];
|
||||||
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;
|
||||||
|
|
||||||
// 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(cpath, sizeof(cpath), "%s", argv[1]);
|
||||||
cmd_line_opts_start = 2;
|
cmd_line_opts_start = 2;
|
||||||
} else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
|
} else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
|
||||||
// No command line flags specified. Look where binary lives
|
// No command line flags specified. Look where binary lives
|
||||||
snprintf(config_file, sizeof(config_file), "%s", CONFIG_FILE);
|
snprintf(cpath, sizeof(cpath), "%s", CONFIG_FILE);
|
||||||
} else {
|
} else {
|
||||||
snprintf(config_file, sizeof(config_file), "%.*s%c%s",
|
snprintf(cpath, sizeof(cpath), "%.*s%c%s",
|
||||||
(int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
|
(int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
|
||||||
}
|
}
|
||||||
|
abs_path(cpath, s_config_file, sizeof(s_config_file));
|
||||||
|
|
||||||
fp = fopen(config_file, "r");
|
fp = fopen(s_config_file, "r");
|
||||||
|
|
||||||
// If config file was set in command line and open failed, die
|
// If config file was set in command line and open failed, die
|
||||||
if (cmd_line_opts_start == 2 && fp == NULL) {
|
if (cmd_line_opts_start == 2 && fp == NULL) {
|
||||||
die("Cannot open config file %s: %s", config_file, strerror(errno));
|
die("Cannot open config file %s: %s", s_config_file, strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load config file settings first
|
// Load config file settings first
|
||||||
if (fp != NULL) {
|
if (fp != NULL) {
|
||||||
fprintf(stderr, "Loading config file %s\n", config_file);
|
fprintf(stderr, "Loading config file %s\n", s_config_file);
|
||||||
|
|
||||||
// Loop over the lines in config file
|
// Loop over the lines in config file
|
||||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||||
@@ -215,7 +233,7 @@ static void process_command_line_arguments(char *argv[], char **options) {
|
|||||||
|
|
||||||
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
|
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
|
||||||
printf("%s: line %d is invalid, ignoring it:\n %s",
|
printf("%s: line %d is invalid, ignoring it:\n %s",
|
||||||
config_file, (int) line_no, line);
|
s_config_file, (int) line_no, line);
|
||||||
} else {
|
} else {
|
||||||
set_option(options, opt, val);
|
set_option(options, opt, val);
|
||||||
}
|
}
|
||||||
@@ -242,7 +260,7 @@ static void process_command_line_arguments(char *argv[], char **options) {
|
|||||||
static void init_server_name(void) {
|
static void init_server_name(void) {
|
||||||
const char *descr = "";
|
const char *descr = "";
|
||||||
snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s%s",
|
snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s%s",
|
||||||
MONGOOSE_VERSION, descr);
|
MVER, descr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_path_absolute(const char *path) {
|
static int is_path_absolute(const char *path) {
|
||||||
@@ -265,6 +283,14 @@ static char *get_option(char **options, const char *option_name) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *serving_thread_func(void *param) {
|
||||||
|
struct mg_server *srv = (struct mg_server *) param;
|
||||||
|
while (exit_flag == 0) {
|
||||||
|
mg_poll_server(srv, 1000);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int path_exists(const char *path, int is_dir) {
|
static int path_exists(const char *path, int is_dir) {
|
||||||
file_stat_t st;
|
file_stat_t st;
|
||||||
return path == NULL || (stat(path, &st) == 0 &&
|
return path == NULL || (stat(path, &st) == 0 &&
|
||||||
@@ -280,8 +306,7 @@ static void verify_existence(char **options, const char *name, int is_dir) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_absolute_path(char *options[], const char *option_name,
|
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;
|
char path[PATH_MAX], abs[PATH_MAX], *option_value;
|
||||||
const char *p;
|
const char *p;
|
||||||
|
|
||||||
@@ -294,11 +319,11 @@ static void set_absolute_path(char *options[], const char *option_name,
|
|||||||
// Not absolute. Use the directory where mongoose executable lives
|
// Not absolute. Use the directory where mongoose executable lives
|
||||||
// be the relative directory for everything.
|
// be the relative directory for everything.
|
||||||
// Extract mongoose executable directory into path.
|
// Extract mongoose executable directory into path.
|
||||||
if ((p = strrchr(path_to_mongoose_exe, DIRSEP)) == NULL) {
|
if ((p = strrchr(s_config_file, DIRSEP)) == NULL) {
|
||||||
getcwd(path, sizeof(path));
|
getcwd(path, sizeof(path));
|
||||||
} else {
|
} else {
|
||||||
snprintf(path, sizeof(path), "%.*s", (int) (p - path_to_mongoose_exe),
|
snprintf(path, sizeof(path), "%.*s", (int) (p - s_config_file),
|
||||||
path_to_mongoose_exe);
|
s_config_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
strncat(path, "/", sizeof(path) - 1);
|
strncat(path, "/", sizeof(path) - 1);
|
||||||
@@ -376,10 +401,8 @@ int modify_passwords_file(const char *fname, const char *domain,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void start_mongoose(int argc, char *argv[]) {
|
static void start_mongoose(int argc, char *argv[]) {
|
||||||
char *options[MAX_OPTIONS];
|
s_argv = argv;
|
||||||
int i;
|
if ((server = mg_create_server(NULL, EV_HANDLER)) == NULL) {
|
||||||
|
|
||||||
if ((server = mg_create_server(NULL)) == NULL) {
|
|
||||||
die("%s", "Failed to start Mongoose.");
|
die("%s", "Failed to start Mongoose.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,6 +421,12 @@ static void start_mongoose(int argc, char *argv[]) {
|
|||||||
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
|
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
|
||||||
show_usage_and_exit();
|
show_usage_and_exit();
|
||||||
}
|
}
|
||||||
|
set_options(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_options(char *argv[]) {
|
||||||
|
char *options[MAX_OPTIONS];
|
||||||
|
int i;
|
||||||
|
|
||||||
options[0] = NULL;
|
options[0] = NULL;
|
||||||
set_option(options, "document_root", s_default_document_root);
|
set_option(options, "document_root", s_default_document_root);
|
||||||
@@ -408,16 +437,16 @@ static void start_mongoose(int argc, char *argv[]) {
|
|||||||
|
|
||||||
// Make sure we have absolute paths for files and directories
|
// Make sure we have absolute paths for files and directories
|
||||||
// https://github.com/valenok/mongoose/issues/181
|
// https://github.com/valenok/mongoose/issues/181
|
||||||
set_absolute_path(options, "document_root", argv[0]);
|
set_absolute_path(options, "document_root");
|
||||||
set_absolute_path(options, "dav_auth_file", argv[0]);
|
set_absolute_path(options, "dav_auth_file");
|
||||||
set_absolute_path(options, "cgi_interpreter", argv[0]);
|
set_absolute_path(options, "cgi_interpreter");
|
||||||
set_absolute_path(options, "access_log_file", argv[0]);
|
set_absolute_path(options, "access_log_file");
|
||||||
set_absolute_path(options, "global_auth_file", argv[0]);
|
set_absolute_path(options, "global_auth_file");
|
||||||
set_absolute_path(options, "ssl_certificate", argv[0]);
|
set_absolute_path(options, "ssl_certificate");
|
||||||
|
|
||||||
if (!path_exists(get_option(options, "document_root"), 1)) {
|
if (!path_exists(get_option(options, "document_root"), 1)) {
|
||||||
set_option(options, "document_root", s_default_document_root);
|
set_option(options, "document_root", s_default_document_root);
|
||||||
set_absolute_path(options, "document_root", argv[0]);
|
set_absolute_path(options, "document_root");
|
||||||
notify("Setting document_root to [%s]",
|
notify("Setting document_root to [%s]",
|
||||||
mg_get_option(server, "document_root"));
|
mg_get_option(server, "document_root"));
|
||||||
}
|
}
|
||||||
@@ -468,9 +497,7 @@ int main(int argc, char *argv[]) {
|
|||||||
server_name, mg_get_option(server, "document_root"),
|
server_name, mg_get_option(server, "document_root"),
|
||||||
mg_get_option(server, "listening_port"));
|
mg_get_option(server, "listening_port"));
|
||||||
fflush(stdout); // Needed, Windows terminals might not be line-buffered
|
fflush(stdout); // Needed, Windows terminals might not be line-buffered
|
||||||
while (exit_flag == 0) {
|
serving_thread_func(server);
|
||||||
mg_poll_server(server, 1000);
|
|
||||||
}
|
|
||||||
printf("Exiting on signal %d ...", exit_flag);
|
printf("Exiting on signal %d ...", exit_flag);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
mg_destroy_server(&server);
|
mg_destroy_server(&server);
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpQIBAAKCAQEAtafk8OvgJX6SLEtnwRmE1oEmk3zsDD0UGW+qD+DtIPOb37xR
|
||||||
|
NIpOGFU8GXAn2GmIIkk7L53bnyh08osTnJKnhprBMDIpwZoMp4HECH4Dk0M7H2S3
|
||||||
|
CMJRT+wYG4GJZt3Tp2U5y9T+T+4YMGCVXOOC7MiEq1nA/iQnljhhGJO61yVgDb/d
|
||||||
|
asnI6igrDQUY30NHIBdtws2s+ysWLYh/LugLZurEtvnzwnt/qQoe7UF/MW51gvu7
|
||||||
|
ywfSvxRksCKZKOFjLIMcEmkReA28Y+mhfKT1bjU6Gte2XZj+5f2TqYvCO+n/d5dY
|
||||||
|
41/Nuk9ujjo/G19dUUOZu3OLL59Q9Rg+6j1FVwIDAQABAoIBAQCE7iW0v4c5KNQi
|
||||||
|
odN9FQWGSuBF0YaStid9LZ+PGxOrp1kLcq0h+4uGdukR3GbsEN3T6S7se2H5qAtc
|
||||||
|
abWiQBdjgt+YOz0fNXHJKkafyEcpfmtHX9YpDLmf+K7zgelgYze9KXAP3v9y0lLV
|
||||||
|
I/hpDvwB9gJPudBmqWJmpT84UvLI+I1HtJ1Q3EgMxFSlx+TrGxqIQmYCaXIIZqdk
|
||||||
|
c1B2neQLGZQ1Dbulr7/KYOkkcPkAQKRY73WHvhhN/0LsD25U6P00etz1VQmLBcWe
|
||||||
|
Y9RaCBD7D3UqSat6ZABv7mtFp8USAEy4a8clayQRdolGTb+gxBUnEMDtRzwcE4zF
|
||||||
|
uh/SGFEBAoGBAPBU/19v82Jqy41D8nqjTN6WXRX37kN1ElS5yvstIdO3oHEywll8
|
||||||
|
tV8Bm+L0ODn/dOWmCZxKWO3hwr8KoEgk8ouv/vDsMRBuwtqZmmdtue5kPEnw3kMB
|
||||||
|
F6RR4Pd9dqmwbF/s4NwpIVFaQEZE6JkhEsXHAJSivKm4m0MadSfm6F5BAoGBAMF/
|
||||||
|
oArSDqsz5BvTZ3pbhUxIMY98IR7jf1ewjsgpthqZFhTggD0KxVcfUYSzg3BXUkl9
|
||||||
|
NFKPQgfOfgW2fiEe999/CRPB0lTtlMybs3L+gb74SmUWxkVmm4mjhfnPoh9AKga2
|
||||||
|
AFtZEcx+1bzkVvyZoSV4s0R61jJcVptutWFcDG2XAoGAKcltmad6dvtApzBQQdiQ
|
||||||
|
7gbQ6pfwOpRpeofPbjrbrdc4s1XzCuIvGvTJqP1zpUnwhlmHREB9E+d0GAl98u1R
|
||||||
|
qs4QEs3Ofh/0C1Q3z5H5iSSuguC5lIblGHIQHpgXAMwnG8qxEjaoj1+U22Lx2mYw
|
||||||
|
wGiohi+BhDUP8evwwN5aOsECgYEAq3qN+2jihQhsi9hSqNW4kLyd5zmrJHOaI70d
|
||||||
|
G/gTIQkvz3qRSwdjkWz40jBAOGu6SdRCbTP14XM2rZtntbR7GL687Zxj6M3Pg3As
|
||||||
|
nKk9lF73lxSSy2g+SMD490TxuN4lIGNkd4+8vUxd4JM/H4aKj+mifPqbHnBEdGSt
|
||||||
|
K0Xm4asCgYEAxM1MNlaS6BD8jLcEQZ/PX9G+ReN7u5zMiw62KQj0wsMy+p92ib1l
|
||||||
|
Jvsv2LI4Ps6SEf4XdDctoAjgk7wsv45rS2RxiBg3wSVz5SSR513+VjlIgmNGWTlI
|
||||||
|
U7tngF/p+qh2Kd5XtqSj5TK3Ts9UPfv3TjBe85LaxxT9WJ8SH3NiEAg=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDEjCCAfoCBRQDhmJRMA0GCSqGSIb3DQEBBQUAMFMxFDASBgNVBAMTC2Nlc2Fu
|
||||||
|
dGEuY29tMR0wGwYDVQQKExRDZXNhbnRhIFNvZnR3YXJlIEx0ZDELMAkGA1UEBhMC
|
||||||
|
SUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA2MjcxMDUwNTBaFw0yNDA2MjQxMDUw
|
||||||
|
NTBaMEcxFTATBgNVBAMTDDE5Mi4xNjguMS4yODEQMA4GA1UEChMHRm9vK0x0ZDEL
|
||||||
|
MAkGA1UEBhMCSUUxDzANBgNVBAcTBkdhbHdheTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||||
|
ggEPADCCAQoCggEBALWn5PDr4CV+kixLZ8EZhNaBJpN87Aw9FBlvqg/g7SDzm9+8
|
||||||
|
UTSKThhVPBlwJ9hpiCJJOy+d258odPKLE5ySp4aawTAyKcGaDKeBxAh+A5NDOx9k
|
||||||
|
twjCUU/sGBuBiWbd06dlOcvU/k/uGDBglVzjguzIhKtZwP4kJ5Y4YRiTutclYA2/
|
||||||
|
3WrJyOooKw0FGN9DRyAXbcLNrPsrFi2Ify7oC2bqxLb588J7f6kKHu1BfzFudYL7
|
||||||
|
u8sH0r8UZLAimSjhYyyDHBJpEXgNvGPpoXyk9W41OhrXtl2Y/uX9k6mLwjvp/3eX
|
||||||
|
WONfzbpPbo46PxtfXVFDmbtziy+fUPUYPuo9RVcCAwEAATANBgkqhkiG9w0BAQUF
|
||||||
|
AAOCAQEAtWSuAbC62J0rdSu5GZg7k7hu6e9s11dc5PS/VRk4h+99RoLxWYO7c6Bp
|
||||||
|
PdDGt6IAWDu2u/aowlKr0sWwThR+nAlQHlufSieS6udfFR1O6cjXa0hRUCKU5Ant
|
||||||
|
+mgPNsfmxV6A6pBSfwgYiv6KLaCRr7tvJqyaNl0MbxMCPmUHy+/Fplab6+h91DsO
|
||||||
|
FxECw/P73iNmSrx9sYrbl83snXMDoMgHq08LwebBcdfiKXGB7mobi0lRfNbh7rqv
|
||||||
|
xVZZ26shxUKLZrtDH0+ACPO6/e6dogOIO9/Frlp+E955Y6F+SeF4QexfbZn5mW8X
|
||||||
|
c7pDVtADI3bClzuYM6PPvOp+sXKM9Q==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
+120
-235
@@ -3,13 +3,13 @@
|
|||||||
// cl unit_test.c /MD
|
// cl unit_test.c /MD
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#define MONGOOSE_USE_IPV6
|
#define NS_ENABLE_IPV6
|
||||||
#define MONGOOSE_USE_SSL
|
#define NS_ENABLE_SSL
|
||||||
#endif
|
#endif
|
||||||
#define MONGOOSE_USE_POST_SIZE_LIMIT 999
|
#define MONGOOSE_POST_SIZE_LIMIT 999
|
||||||
|
|
||||||
// USE_* definitions must be made before #include "mongoose.c" !
|
// USE_* definitions must be made before #include "mongoose.c" !
|
||||||
#include "mongoose.c"
|
#include "../mongoose.c"
|
||||||
|
|
||||||
#define FAIL(str, line) do { \
|
#define FAIL(str, line) do { \
|
||||||
printf("Fail on line %d: [%s]\n", line, str); \
|
printf("Fail on line %d: [%s]\n", line, str); \
|
||||||
@@ -166,34 +166,34 @@ static const char *test_should_keep_alive(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const char *test_match_prefix(void) {
|
static const char *test_match_prefix(void) {
|
||||||
ASSERT(match_prefix("/api", 4, "/api") == 4);
|
ASSERT(mg_match_prefix("/api", 4, "/api") == 4);
|
||||||
ASSERT(match_prefix("/a/", 3, "/a/b/c") == 3);
|
ASSERT(mg_match_prefix("/a/", 3, "/a/b/c") == 3);
|
||||||
ASSERT(match_prefix("/a/", 3, "/ab/c") == -1);
|
ASSERT(mg_match_prefix("/a/", 3, "/ab/c") == -1);
|
||||||
ASSERT(match_prefix("/*/", 3, "/ab/c") == 4);
|
ASSERT(mg_match_prefix("/*/", 3, "/ab/c") == 4);
|
||||||
ASSERT(match_prefix("**", 2, "/a/b/c") == 6);
|
ASSERT(mg_match_prefix("**", 2, "/a/b/c") == 6);
|
||||||
ASSERT(match_prefix("/*", 2, "/a/b/c") == 2);
|
ASSERT(mg_match_prefix("/*", 2, "/a/b/c") == 2);
|
||||||
ASSERT(match_prefix("*/*", 3, "/a/b/c") == 2);
|
ASSERT(mg_match_prefix("*/*", 3, "/a/b/c") == 2);
|
||||||
ASSERT(match_prefix("**/", 3, "/a/b/c") == 5);
|
ASSERT(mg_match_prefix("**/", 3, "/a/b/c") == 5);
|
||||||
ASSERT(match_prefix("**.foo|**.bar", 13, "a.bar") == 5);
|
ASSERT(mg_match_prefix("**.foo|**.bar", 13, "a.bar") == 5);
|
||||||
ASSERT(match_prefix("a|b|cd", 6, "cdef") == 2);
|
ASSERT(mg_match_prefix("a|b|cd", 6, "cdef") == 2);
|
||||||
ASSERT(match_prefix("a|b|c?", 6, "cdef") == 2);
|
ASSERT(mg_match_prefix("a|b|c?", 6, "cdef") == 2);
|
||||||
ASSERT(match_prefix("a|?|cd", 6, "cdef") == 1);
|
ASSERT(mg_match_prefix("a|?|cd", 6, "cdef") == 1);
|
||||||
ASSERT(match_prefix("/a/**.cgi", 9, "/foo/bar/x.cgi") == -1);
|
ASSERT(mg_match_prefix("/a/**.cgi", 9, "/foo/bar/x.cgi") == -1);
|
||||||
ASSERT(match_prefix("/a/**.cgi", 9, "/a/bar/x.cgi") == 12);
|
ASSERT(mg_match_prefix("/a/**.cgi", 9, "/a/bar/x.cgi") == 12);
|
||||||
ASSERT(match_prefix("**/", 3, "/a/b/c") == 5);
|
ASSERT(mg_match_prefix("**/", 3, "/a/b/c") == 5);
|
||||||
ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1);
|
ASSERT(mg_match_prefix("**/$", 4, "/a/b/c") == -1);
|
||||||
ASSERT(match_prefix("**/$", 4, "/a/b/") == 5);
|
ASSERT(mg_match_prefix("**/$", 4, "/a/b/") == 5);
|
||||||
ASSERT(match_prefix("$", 1, "") == 0);
|
ASSERT(mg_match_prefix("$", 1, "") == 0);
|
||||||
ASSERT(match_prefix("$", 1, "x") == -1);
|
ASSERT(mg_match_prefix("$", 1, "x") == -1);
|
||||||
ASSERT(match_prefix("*$", 2, "x") == 1);
|
ASSERT(mg_match_prefix("*$", 2, "x") == 1);
|
||||||
ASSERT(match_prefix("/$", 2, "/") == 1);
|
ASSERT(mg_match_prefix("/$", 2, "/") == 1);
|
||||||
ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1);
|
ASSERT(mg_match_prefix("**/$", 4, "/a/b/c") == -1);
|
||||||
ASSERT(match_prefix("**/$", 4, "/a/b/") == 5);
|
ASSERT(mg_match_prefix("**/$", 4, "/a/b/") == 5);
|
||||||
ASSERT(match_prefix("*", 1, "/hello/") == 0);
|
ASSERT(mg_match_prefix("*", 1, "/hello/") == 0);
|
||||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
|
ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
|
||||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
|
ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
|
||||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
|
ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
|
||||||
ASSERT(match_prefix("**o$", 4, "HELLO") == 5);
|
ASSERT(mg_match_prefix("**o$", 4, "HELLO") == 5);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,6 +271,17 @@ static const char *test_url_decode(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *test_url_encode(void) {
|
||||||
|
char buf[100];
|
||||||
|
ASSERT(mg_url_encode("", 0, buf, sizeof(buf)) == 0);
|
||||||
|
ASSERT(buf[0] == '\0');
|
||||||
|
ASSERT(mg_url_encode("foo", 3, buf, sizeof(buf)) == 3);
|
||||||
|
ASSERT(strcmp(buf, "foo") == 0);
|
||||||
|
ASSERT(mg_url_encode("f o", 3, buf, sizeof(buf)) == 5);
|
||||||
|
ASSERT(strcmp(buf, "f%20o") == 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static const char *test_to64(void) {
|
static const char *test_to64(void) {
|
||||||
ASSERT(to64("0") == 0);
|
ASSERT(to64("0") == 0);
|
||||||
ASSERT(to64("") == 0);
|
ASSERT(to64("") == 0);
|
||||||
@@ -280,32 +291,6 @@ static const char *test_to64(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *test_parse_port_string(void) {
|
|
||||||
static const char *valid[] = {
|
|
||||||
"1", "1.2.3.4:1",
|
|
||||||
#if defined(USE_IPV6)
|
|
||||||
"[::1]:123", "[3ffe:2a00:100:7031::1]:900",
|
|
||||||
#endif
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
static const char *invalid[] = {
|
|
||||||
"99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p", NULL
|
|
||||||
};
|
|
||||||
union socket_address sa;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; valid[i] != NULL; i++) {
|
|
||||||
ASSERT(parse_port_string(valid[i], &sa) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; invalid[i] != NULL; i++) {
|
|
||||||
ASSERT(parse_port_string(invalid[i], &sa) == 0);
|
|
||||||
}
|
|
||||||
ASSERT(parse_port_string("0", &sa) != 0);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *test_base64_encode(void) {
|
static const char *test_base64_encode(void) {
|
||||||
const char *in[] = {"a", "ab", "abc", "abcd", NULL};
|
const char *in[] = {"a", "ab", "abc", "abcd", NULL};
|
||||||
const char *out[] = {"YQ==", "YWI=", "YWJj", "YWJjZA=="};
|
const char *out[] = {"YQ==", "YWI=", "YWJj", "YWJjZA=="};
|
||||||
@@ -365,60 +350,60 @@ static const char *test_next_option(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cb1(struct mg_connection *conn) {
|
static int evh1(struct mg_connection *conn, enum mg_event ev) {
|
||||||
int result = MG_REQUEST_NOT_PROCESSED;
|
char *buf = (char *) conn->connection_param;
|
||||||
if (!strcmp(conn->uri, "/cb1")) {
|
int result = MG_FALSE;
|
||||||
mg_printf(conn, "%s %s %s",
|
|
||||||
conn->server_param == NULL ? "?" : (char *) conn->server_param,
|
switch (ev) {
|
||||||
conn->connection_param == NULL ? "?" : "!", conn->remote_ip);
|
case MG_CONNECT:
|
||||||
result = MG_REQUEST_PROCESSED;
|
mg_printf(conn, "GET %s HTTP/1.0\r\n\r\n",
|
||||||
|
buf[0] == '1' ? "/cb1" : "/non_exist");
|
||||||
|
result = MG_TRUE;
|
||||||
|
break;
|
||||||
|
case MG_HTTP_ERROR:
|
||||||
|
mg_printf(conn, "HTTP/1.0 404 NF\r\n\r\nERR: %d", conn->status_code);
|
||||||
|
result = MG_TRUE;
|
||||||
|
break;
|
||||||
|
case MG_REQUEST:
|
||||||
|
if (!strcmp(conn->uri, "/cb1")) {
|
||||||
|
mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n%s %s %s",
|
||||||
|
(char *) conn->server_param,
|
||||||
|
buf == NULL ? "?" : "!", conn->remote_ip);
|
||||||
|
result = MG_TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_REPLY:
|
||||||
|
if (buf != NULL) {
|
||||||
|
sprintf(buf + 1, "%.*s", (int) conn->content_len, conn->content);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MG_AUTH:
|
||||||
|
result = MG_TRUE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int error_handler(struct mg_connection *conn) {
|
|
||||||
mg_printf(conn, "HTTP/1.0 404 NF\r\n\r\nERR: %d", conn->status_code);
|
|
||||||
return MG_ERROR_PROCESSED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ts1(struct mg_connection *conn) {
|
|
||||||
if (conn->status_code == MG_CONNECT_SUCCESS) {
|
|
||||||
mg_printf(conn, "%s", "GET /cb1 HTTP/1.0\r\n\r\n");
|
|
||||||
return MG_CLIENT_CONTINUE;
|
|
||||||
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
|
|
||||||
sprintf((char *) conn->connection_param, "%.*s",
|
|
||||||
(int) conn->content_len, conn->content);
|
|
||||||
}
|
|
||||||
return MG_CLIENT_CLOSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ts2(struct mg_connection *conn) {
|
|
||||||
if (conn->status_code == MG_CONNECT_SUCCESS) {
|
|
||||||
mg_printf(conn, "%s", "GET /non_exist HTTP/1.0\r\n\r\n");
|
|
||||||
return MG_CLIENT_CONTINUE;
|
|
||||||
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
|
|
||||||
sprintf((char *) conn->connection_param, "%s %.*s",
|
|
||||||
conn->uri, (int) conn->content_len, conn->content);
|
|
||||||
}
|
|
||||||
return MG_CLIENT_CLOSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *test_server(void) {
|
static const char *test_server(void) {
|
||||||
char buf1[100] = "", buf2[100] = "";
|
char buf1[100] = "1", buf2[100] = "2";
|
||||||
struct mg_server *server = mg_create_server((void *) "foo");
|
struct mg_server *server = mg_create_server((void *) "foo", evh1);
|
||||||
|
struct mg_connection *conn;
|
||||||
|
|
||||||
ASSERT(server != NULL);
|
ASSERT(server != NULL);
|
||||||
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
|
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
|
||||||
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
||||||
mg_set_request_handler(server, cb1);
|
|
||||||
mg_set_http_error_handler(server, error_handler);
|
|
||||||
|
|
||||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, ts1, buf1) == 1);
|
ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0)) != NULL);
|
||||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, ts2, buf2) == 1);
|
conn->connection_param = buf1;
|
||||||
|
ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0)) != NULL);
|
||||||
|
conn->connection_param = buf2;
|
||||||
|
|
||||||
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
|
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
|
||||||
ASSERT(strcmp(buf1, "foo ? 127.0.0.1") == 0);
|
ASSERT(strcmp(buf1, "1foo ? 127.0.0.1") == 0);
|
||||||
ASSERT(strcmp(buf2, "404 ERR: 404") == 0);
|
ASSERT(strcmp(buf2, "2ERR: 404") == 0);
|
||||||
|
|
||||||
ASSERT(strcmp(static_config_options[URL_REWRITES * 2], "url_rewrites") == 0);
|
ASSERT(strcmp(static_config_options[URL_REWRITES * 2], "url_rewrites") == 0);
|
||||||
mg_destroy_server(&server);
|
mg_destroy_server(&server);
|
||||||
@@ -426,90 +411,6 @@ static const char *test_server(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This http client sends two requests using one connection
|
|
||||||
static int cb2(struct mg_connection *conn) {
|
|
||||||
const char *file1 = "mongoose.h", *file2 = "mongoose.c", *file_name = file2;
|
|
||||||
char *buf = (char *) conn->connection_param, *file_data;
|
|
||||||
int file_len, ret_val = 1;
|
|
||||||
|
|
||||||
switch (conn->status_code) {
|
|
||||||
case MG_CONNECT_SUCCESS:
|
|
||||||
mg_printf(conn, "GET /%s HTTP/1.1\r\n\r\n", file1);
|
|
||||||
strcat(buf, "a");
|
|
||||||
ret_val = 0;
|
|
||||||
break;
|
|
||||||
case MG_CONNECT_FAILURE: strcat(buf, "b"); break;
|
|
||||||
case MG_DOWNLOAD_FAILURE: strcat(buf, "c"); break;
|
|
||||||
case MG_DOWNLOAD_SUCCESS:
|
|
||||||
if (!strcmp(buf, "a")) {
|
|
||||||
// Send second request
|
|
||||||
mg_printf(conn, "GET /%s HTTP/1.1\r\n\r\n", file2);
|
|
||||||
file_name = file1;
|
|
||||||
}
|
|
||||||
if ((file_data = read_file(file_name, &file_len)) != NULL) {
|
|
||||||
if ((size_t) file_len == conn->content_len &&
|
|
||||||
memcmp(file_data, conn->content, file_len) == 0) {
|
|
||||||
strcat(buf, "d");
|
|
||||||
} else {
|
|
||||||
strcat(buf, "f");
|
|
||||||
}
|
|
||||||
free(file_data);
|
|
||||||
}
|
|
||||||
ret_val = !strcmp(buf, "ad") ? 0 : 1;
|
|
||||||
break;
|
|
||||||
default: strcat(buf, "e"); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cb4h(struct mg_connection *conn) {
|
|
||||||
int result = MG_REQUEST_NOT_PROCESSED;
|
|
||||||
if (!strcmp(conn->uri, "/x")) {
|
|
||||||
mg_send_data(conn, ":-)", 3);
|
|
||||||
result = MG_REQUEST_PROCESSED;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cb4(struct mg_connection *conn) {
|
|
||||||
if (conn->status_code == MG_CONNECT_SUCCESS) {
|
|
||||||
mg_printf(conn, "%s", "POST /x HTTP/1.0\r\nContent-Length: 999999\r\n\r\n");
|
|
||||||
return MG_CLIENT_CONTINUE;
|
|
||||||
} else {
|
|
||||||
sprintf((char *) conn->connection_param, "%.*s",
|
|
||||||
(int) conn->content_len, conn->content);
|
|
||||||
}
|
|
||||||
return MG_CLIENT_CLOSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cb3(struct mg_connection *conn) {
|
|
||||||
sprintf((char *) conn->connection_param, "%d", conn->status_code);
|
|
||||||
return MG_CLIENT_CLOSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *test_mg_connect(void) {
|
|
||||||
char buf2[40] = "", buf3[40] = "", buf4[40] = "";
|
|
||||||
struct mg_server *server = mg_create_server(NULL);
|
|
||||||
|
|
||||||
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
|
|
||||||
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
|
||||||
mg_set_request_handler(server, cb4h);
|
|
||||||
ASSERT(mg_connect(server, "", 0, 0, NULL, NULL) == 0);
|
|
||||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, cb2, buf2) == 1);
|
|
||||||
ASSERT(mg_connect(server, "127.0.0.1", 29, 0, cb3, buf3) == 1);
|
|
||||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, cb4, buf4) == 1);
|
|
||||||
|
|
||||||
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
|
|
||||||
|
|
||||||
ASSERT(strcmp(buf2, "add") == 0);
|
|
||||||
ASSERT(strcmp(buf3, "1") == 0);
|
|
||||||
ASSERT(strcmp(buf4, "500 Server Error\nPOST size > 999") == 0);
|
|
||||||
mg_destroy_server(&server);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DISP "Content-Disposition: form/data; "
|
#define DISP "Content-Disposition: form/data; "
|
||||||
#define CRLF "\r\n"
|
#define CRLF "\r\n"
|
||||||
#define BOUNDARY "--xyz"
|
#define BOUNDARY "--xyz"
|
||||||
@@ -545,49 +446,36 @@ static const char *test_parse_multipart(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int us1(struct mg_connection *conn) {
|
static int evh2(struct mg_connection *conn, enum mg_event ev) {
|
||||||
static const char *file_name = "mongoose.h";
|
char *file_data, *cp = (char *) conn->connection_param;
|
||||||
char *cp = (char *) conn->connection_param;
|
int file_size, result = MG_FALSE;
|
||||||
int file_size;
|
|
||||||
|
|
||||||
if (conn->status_code == MG_CONNECT_SUCCESS) {
|
switch (ev) {
|
||||||
mg_printf(conn, "GET /%s HTTP/1.0\r\n\r\n", cp);
|
case MG_AUTH:
|
||||||
return 0;
|
result = MG_TRUE;
|
||||||
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
|
break;
|
||||||
char *file_data = read_file(file_name, &file_size);
|
case MG_CONNECT:
|
||||||
sprintf(cp, "%d %s", (size_t) file_size == conn->content_len &&
|
mg_printf(conn, "GET /%s HTTP/1.0\r\n\r\n", cp);
|
||||||
memcmp(file_data, conn->content, file_size) == 0 ? 1 : 0,
|
result = MG_TRUE;
|
||||||
conn->query_string == NULL ? "?" : conn->query_string);
|
break;
|
||||||
free(file_data);
|
case MG_REQUEST:
|
||||||
} else if (conn->status_code == MG_CONNECT_FAILURE) {
|
break;
|
||||||
sprintf(cp, "%s", "cf");
|
case MG_REPLY:
|
||||||
} else {
|
file_data = read_file("unit_test.c", &file_size);
|
||||||
sprintf(cp, "%s", "df");
|
sprintf(cp, "%d %s", (size_t) file_size == conn->content_len &&
|
||||||
|
memcmp(file_data, conn->content, file_size) == 0 ? 1 : 0,
|
||||||
|
conn->query_string == NULL ? "?" : conn->query_string);
|
||||||
|
free(file_data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return 1;
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MONGOOSE_USE_SSL
|
|
||||||
static const char *test_ssl(void) {
|
|
||||||
static const char *ssl_cert = "examples/ssl_cert.pem";
|
|
||||||
char buf1[100] = "mongoose.h";
|
|
||||||
struct mg_server *server = mg_create_server(NULL);
|
|
||||||
|
|
||||||
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
|
|
||||||
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
|
||||||
ASSERT(mg_set_option(server, "ssl_certificate", ssl_cert) == NULL);
|
|
||||||
|
|
||||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 1, us1, buf1) == 1);
|
|
||||||
|
|
||||||
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
|
|
||||||
ASSERT(strcmp(buf1, "1 ?") == 0);
|
|
||||||
mg_destroy_server(&server);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const char *test_mg_set_option(void) {
|
static const char *test_mg_set_option(void) {
|
||||||
struct mg_server *server = mg_create_server(NULL);
|
struct mg_server *server = mg_create_server(NULL, NULL);
|
||||||
ASSERT(mg_set_option(server, "listening_port", "0") == NULL);
|
ASSERT(mg_set_option(server, "listening_port", "0") == NULL);
|
||||||
ASSERT(mg_get_option(server, "listening_port")[0] != '\0');
|
ASSERT(mg_get_option(server, "listening_port")[0] != '\0');
|
||||||
mg_destroy_server(&server);
|
mg_destroy_server(&server);
|
||||||
@@ -596,18 +484,19 @@ static const char *test_mg_set_option(void) {
|
|||||||
|
|
||||||
static const char *test_rewrites(void) {
|
static const char *test_rewrites(void) {
|
||||||
char buf1[100] = "xx";
|
char buf1[100] = "xx";
|
||||||
struct mg_server *server = mg_create_server(NULL);
|
struct mg_server *server = mg_create_server(NULL, evh2);
|
||||||
|
struct mg_connection *conn;
|
||||||
|
const char *port;
|
||||||
|
|
||||||
ASSERT(mg_set_option(server, "listening_port", "0") == NULL);
|
ASSERT(mg_set_option(server, "listening_port", "0") == NULL);
|
||||||
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
||||||
ASSERT(mg_set_option(server, "url_rewrites", "/xx=./mongoose.h") == NULL);
|
ASSERT(mg_set_option(server, "url_rewrites", "/xx=unit_test.c") == NULL);
|
||||||
|
ASSERT((port = mg_get_option(server, "listening_port")) != NULL);
|
||||||
ASSERT(mg_connect(server, "127.0.0.1",
|
ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(port), 0)) != NULL);
|
||||||
atoi(mg_get_option(server, "listening_port")),
|
conn->connection_param = buf1;
|
||||||
0, us1, buf1) == 1);
|
|
||||||
|
|
||||||
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
|
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
|
||||||
//printf("[%s]\n", buf1);
|
|
||||||
ASSERT(strcmp(buf1, "1 ?") == 0);
|
ASSERT(strcmp(buf1, "1 ?") == 0);
|
||||||
mg_destroy_server(&server);
|
mg_destroy_server(&server);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -618,21 +507,17 @@ static const char *run_all_tests(void) {
|
|||||||
RUN_TEST(test_match_prefix);
|
RUN_TEST(test_match_prefix);
|
||||||
RUN_TEST(test_remove_double_dots);
|
RUN_TEST(test_remove_double_dots);
|
||||||
RUN_TEST(test_parse_http_message);
|
RUN_TEST(test_parse_http_message);
|
||||||
RUN_TEST(test_parse_port_string);
|
|
||||||
RUN_TEST(test_to64);
|
RUN_TEST(test_to64);
|
||||||
RUN_TEST(test_url_decode);
|
RUN_TEST(test_url_decode);
|
||||||
|
RUN_TEST(test_url_encode);
|
||||||
RUN_TEST(test_base64_encode);
|
RUN_TEST(test_base64_encode);
|
||||||
RUN_TEST(test_mg_parse_header);
|
RUN_TEST(test_mg_parse_header);
|
||||||
RUN_TEST(test_get_var);
|
RUN_TEST(test_get_var);
|
||||||
RUN_TEST(test_next_option);
|
RUN_TEST(test_next_option);
|
||||||
RUN_TEST(test_parse_multipart);
|
RUN_TEST(test_parse_multipart);
|
||||||
RUN_TEST(test_server);
|
|
||||||
RUN_TEST(test_mg_connect);
|
|
||||||
RUN_TEST(test_mg_set_option);
|
RUN_TEST(test_mg_set_option);
|
||||||
|
RUN_TEST(test_server);
|
||||||
RUN_TEST(test_rewrites);
|
RUN_TEST(test_rewrites);
|
||||||
#ifdef MONGOOSE_USE_SSL
|
|
||||||
RUN_TEST(test_ssl);
|
|
||||||
#endif
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
+19
-13
@@ -5,9 +5,9 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
static int index_html(struct mg_connection *conn) {
|
static int send_index_page(struct mg_connection *conn) {
|
||||||
const char *data;
|
const char *data;
|
||||||
int data_len;
|
int data_len, ofs = 0;
|
||||||
char var_name[100], file_name[100];
|
char var_name[100], file_name[100];
|
||||||
|
|
||||||
mg_printf_data(conn, "%s",
|
mg_printf_data(conn, "%s",
|
||||||
@@ -15,31 +15,37 @@ static int index_html(struct mg_connection *conn) {
|
|||||||
"<form method=\"POST\" action=\"/handle_post_request\" "
|
"<form method=\"POST\" action=\"/handle_post_request\" "
|
||||||
" enctype=\"multipart/form-data\">"
|
" enctype=\"multipart/form-data\">"
|
||||||
"<input type=\"file\" name=\"file\" /> <br/>"
|
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||||
|
"<input type=\"text\" name=\"input1\" value=hello! /> <br/>"
|
||||||
"<input type=\"submit\" value=\"Upload\" />"
|
"<input type=\"submit\" value=\"Upload\" />"
|
||||||
"</form>");
|
"</form>");
|
||||||
|
|
||||||
if (mg_parse_multipart(conn->content, conn->content_len,
|
while ((ofs = mg_parse_multipart(conn->content + ofs, conn->content_len - ofs,
|
||||||
var_name, sizeof(var_name),
|
var_name, sizeof(var_name),
|
||||||
file_name, sizeof(file_name),
|
file_name, sizeof(file_name),
|
||||||
&data, &data_len) > 0) {
|
&data, &data_len)) > 0) {
|
||||||
|
mg_printf_data(conn, "var: %s, file_name: %s, size: %d bytes<br>",
|
||||||
mg_printf_data(conn, "%s", "Uploaded file:<pre>");
|
var_name, file_name, data_len);
|
||||||
mg_send_data(conn, data, data_len);
|
|
||||||
mg_printf_data(conn, "%s", "/pre>");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mg_printf_data(conn, "%s", "</body></html>");
|
mg_printf_data(conn, "%s", "</body></html>");
|
||||||
|
|
||||||
return MG_REQUEST_PROCESSED;
|
return MG_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
switch (ev) {
|
||||||
|
case MG_AUTH: return MG_TRUE;
|
||||||
|
case MG_REQUEST: return send_index_page(conn);
|
||||||
|
default: return MG_FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_server *server;
|
struct mg_server *server;
|
||||||
|
|
||||||
// Create and configure the server
|
// Create and configure the server
|
||||||
server = mg_create_server(NULL);
|
server = mg_create_server(NULL, ev_handler);
|
||||||
mg_set_option(server, "listening_port", "8080");
|
mg_set_option(server, "listening_port", "8080");
|
||||||
mg_set_request_handler(server, index_html);
|
|
||||||
|
|
||||||
// Serve request. Hit Ctrl-C to terminate the program
|
// Serve request. Hit Ctrl-C to terminate the program
|
||||||
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
|
||||||
|
|||||||
+27
-11
@@ -1,18 +1,24 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
extern const char *find_embedded_file(const char *, size_t *);
|
extern const char *find_embedded_file(const char *, size_t *);
|
||||||
|
|
||||||
static int iterate_callback(struct mg_connection *c) {
|
static int iterate_callback(struct mg_connection *c, enum mg_event ev) {
|
||||||
if (c->is_websocket) {
|
if (ev == MG_POLL && c->is_websocket) {
|
||||||
char buf[20];
|
char buf[20];
|
||||||
int len = snprintf(buf, sizeof(buf), "%d", * (int *) c->connection_param);
|
int len = snprintf(buf, sizeof(buf), "%lu",
|
||||||
|
(unsigned long) * (time_t *) c->callback_param);
|
||||||
mg_websocket_write(c, 1, buf, len);
|
mg_websocket_write(c, 1, buf, len);
|
||||||
}
|
}
|
||||||
return MG_REQUEST_PROCESSED;
|
return MG_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int index_html(struct mg_connection *conn) {
|
static int send_reply(struct mg_connection *conn) {
|
||||||
size_t index_size;
|
size_t index_size;
|
||||||
const char *index_html = find_embedded_file("websocket.html", &index_size);
|
const char *index_html = find_embedded_file("websocket.html", &index_size);
|
||||||
|
|
||||||
@@ -22,24 +28,34 @@ static int index_html(struct mg_connection *conn) {
|
|||||||
// Echo websocket data back to the client.
|
// Echo websocket data back to the client.
|
||||||
mg_websocket_write(conn, 1, conn->content, conn->content_len);
|
mg_websocket_write(conn, 1, conn->content, conn->content_len);
|
||||||
return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ?
|
return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ?
|
||||||
MG_CLIENT_CLOSE : MG_CLIENT_CONTINUE;
|
MG_FALSE : MG_TRUE;
|
||||||
} else {
|
} else {
|
||||||
mg_send_header(conn, "Content-Type", "text/html");
|
mg_send_header(conn, "Content-Type", "text/html");
|
||||||
mg_send_data(conn, index_html, index_size);
|
mg_send_data(conn, index_html, index_size);
|
||||||
return MG_REQUEST_PROCESSED;
|
return MG_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
if (ev == MG_REQUEST) {
|
||||||
|
return send_reply(conn);
|
||||||
|
} else if (ev == MG_AUTH) {
|
||||||
|
return MG_TRUE;
|
||||||
|
} else {
|
||||||
|
return MG_FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_server *server = mg_create_server(NULL);
|
struct mg_server *server = mg_create_server(NULL, ev_handler);
|
||||||
unsigned int current_timer = 0, last_timer = 0;
|
time_t current_timer = 0, last_timer = time(NULL);
|
||||||
|
|
||||||
mg_set_option(server, "listening_port", "8080");
|
mg_set_option(server, "listening_port", "8080");
|
||||||
mg_set_request_handler(server, index_html);
|
|
||||||
|
|
||||||
printf("Started on port %s\n", mg_get_option(server, "listening_port"));
|
printf("Started on port %s\n", mg_get_option(server, "listening_port"));
|
||||||
for (;;) {
|
for (;;) {
|
||||||
current_timer = mg_poll_server(server, 100);
|
mg_poll_server(server, 100);
|
||||||
|
current_timer = time(NULL);
|
||||||
if (current_timer - last_timer > 0) {
|
if (current_timer - last_timer > 0) {
|
||||||
last_timer = current_timer;
|
last_timer = current_timer;
|
||||||
mg_iterate_over_connections(server, iterate_callback, ¤t_timer);
|
mg_iterate_over_connections(server, iterate_callback, ¤t_timer);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
out('<span style="color: blue;">RESPONSE: ' + ev.data + ' </span>');
|
out('<span style="color: blue;">RESPONSE: ' + ev.data + ' </span>');
|
||||||
num_messages++;
|
num_messages++;
|
||||||
}
|
}
|
||||||
if (num_messages > 15) {
|
if (num_messages > 3) {
|
||||||
websocket.send('exit');
|
websocket.send('exit');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static int s_signal_received = 0;
|
||||||
|
static struct mg_server *s_server = NULL;
|
||||||
|
|
||||||
|
// Data associated with each websocket connection
|
||||||
|
struct conn_data {
|
||||||
|
int room;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void signal_handler(int sig_num) {
|
||||||
|
signal(sig_num, signal_handler); // Reinstantiate signal handler
|
||||||
|
s_signal_received = sig_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_websocket_message(struct mg_connection *conn) {
|
||||||
|
struct conn_data *d = (struct conn_data *) conn->connection_param;
|
||||||
|
struct mg_connection *c;
|
||||||
|
|
||||||
|
printf("[%.*s]\n", (int) conn->content_len, conn->content);
|
||||||
|
if (conn->content_len > 5 && !memcmp(conn->content, "join ", 5)) {
|
||||||
|
// Client joined new room
|
||||||
|
d->room = conn->content[5];
|
||||||
|
} else if (conn->content_len > 4 && !memcmp(conn->content, "msg ", 4) &&
|
||||||
|
d->room != 0 && d->room != '?') {
|
||||||
|
// Client has sent a message. Push this message to all clients
|
||||||
|
// that are subscribed to the same room as client
|
||||||
|
for (c = mg_next(s_server, NULL); c != NULL; c = mg_next(s_server, c)) {
|
||||||
|
struct conn_data *d2 = (struct conn_data *) c->connection_param;
|
||||||
|
if (!c->is_websocket || d2->room != d->room) continue;
|
||||||
|
mg_websocket_printf(c, WEBSOCKET_OPCODE_TEXT, "msg %c %p %.*s",
|
||||||
|
(char) d->room, conn,
|
||||||
|
conn->content_len - 4, conn->content + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
switch (ev) {
|
||||||
|
case MG_REQUEST:
|
||||||
|
if (conn->is_websocket) {
|
||||||
|
handle_websocket_message(conn);
|
||||||
|
return MG_TRUE;
|
||||||
|
} else {
|
||||||
|
mg_send_file(conn, "pubsub.html"); // Return MG_MORE after mg_send_file()
|
||||||
|
return MG_MORE;
|
||||||
|
}
|
||||||
|
case MG_WS_CONNECT:
|
||||||
|
// New websocket connection. Send connection ID back to the client.
|
||||||
|
conn->connection_param = calloc(1, sizeof(struct conn_data));
|
||||||
|
mg_websocket_printf(conn, WEBSOCKET_OPCODE_TEXT, "id %p", conn);
|
||||||
|
return MG_FALSE;
|
||||||
|
case MG_CLOSE:
|
||||||
|
free(conn->connection_param);
|
||||||
|
return MG_TRUE;
|
||||||
|
case MG_AUTH:
|
||||||
|
return MG_TRUE;
|
||||||
|
default:
|
||||||
|
return MG_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
s_server = mg_create_server(NULL, ev_handler);
|
||||||
|
mg_set_option(s_server, "listening_port", "8080");
|
||||||
|
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
|
||||||
|
printf("Started on port %s\n", mg_get_option(s_server, "listening_port"));
|
||||||
|
while (s_signal_received == 0) {
|
||||||
|
mg_poll_server(s_server, 100);
|
||||||
|
}
|
||||||
|
mg_destroy_server(&s_server);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+2232
-1386
File diff suppressed because it is too large.
Load diff
+52
-28
@@ -20,7 +20,7 @@
|
|||||||
#ifndef MONGOOSE_HEADER_INCLUDED
|
#ifndef MONGOOSE_HEADER_INCLUDED
|
||||||
#define MONGOOSE_HEADER_INCLUDED
|
#define MONGOOSE_HEADER_INCLUDED
|
||||||
|
|
||||||
#define MONGOOSE_VERSION "5.2"
|
#define MONGOOSE_VERSION "5.4"
|
||||||
|
|
||||||
#include <stdio.h> // required for FILE
|
#include <stdio.h> // required for FILE
|
||||||
#include <stddef.h> // required for size_t
|
#include <stddef.h> // required for size_t
|
||||||
@@ -37,7 +37,7 @@ struct mg_connection {
|
|||||||
const char *query_string; // URL part after '?', not including '?', or NULL
|
const char *query_string; // URL part after '?', not including '?', or NULL
|
||||||
|
|
||||||
char remote_ip[48]; // Max IPv6 string length is 45 characters
|
char remote_ip[48]; // Max IPv6 string length is 45 characters
|
||||||
const char *local_ip; // Local IP address
|
char local_ip[48]; // Local IP address
|
||||||
unsigned short remote_port; // Client's port
|
unsigned short remote_port; // Client's port
|
||||||
unsigned short local_port; // Local port number
|
unsigned short local_port; // Local port number
|
||||||
|
|
||||||
@@ -48,44 +48,70 @@ struct mg_connection {
|
|||||||
} http_headers[30];
|
} http_headers[30];
|
||||||
|
|
||||||
char *content; // POST (or websocket message) data, or NULL
|
char *content; // POST (or websocket message) data, or NULL
|
||||||
size_t content_len; // content length
|
size_t content_len; // Data length
|
||||||
|
|
||||||
int is_websocket; // Connection is a websocket connection
|
int is_websocket; // Connection is a websocket connection
|
||||||
int status_code; // HTTP status code for HTTP error handler
|
int status_code; // HTTP status code for HTTP error handler
|
||||||
int wsbits; // First byte of the websocket frame
|
int wsbits; // First byte of the websocket frame
|
||||||
void *server_param; // Parameter passed to mg_add_uri_handler()
|
void *server_param; // Parameter passed to mg_add_uri_handler()
|
||||||
void *connection_param; // Placeholder for connection-specific data
|
void *connection_param; // Placeholder for connection-specific data
|
||||||
|
void *callback_param; // Needed by mg_iterate_over_connections()
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mg_server; // Opaque structure describing server instance
|
struct mg_server; // Opaque structure describing server instance
|
||||||
typedef int (*mg_handler_t)(struct mg_connection *);
|
enum mg_result { MG_FALSE, MG_TRUE, MG_MORE };
|
||||||
|
enum mg_event {
|
||||||
|
MG_POLL = 100, // Callback return value is ignored
|
||||||
|
MG_CONNECT, // If callback returns MG_FALSE, connect fails
|
||||||
|
MG_AUTH, // If callback returns MG_FALSE, authentication fails
|
||||||
|
MG_REQUEST, // If callback returns MG_FALSE, Mongoose continues with req
|
||||||
|
MG_REPLY, // If callback returns MG_FALSE, Mongoose closes connection
|
||||||
|
MG_CLOSE, // Connection is closed, callback return value is ignored
|
||||||
|
MG_WS_HANDSHAKE, // New websocket connection, handshake request
|
||||||
|
MG_WS_CONNECT, // New websocket connection established
|
||||||
|
MG_HTTP_ERROR // If callback returns MG_FALSE, Mongoose continues with err
|
||||||
|
};
|
||||||
|
typedef int (*mg_handler_t)(struct mg_connection *, enum mg_event);
|
||||||
|
|
||||||
|
// Websocket opcodes, from http://tools.ietf.org/html/rfc6455
|
||||||
|
enum {
|
||||||
|
WEBSOCKET_OPCODE_CONTINUATION = 0x0,
|
||||||
|
WEBSOCKET_OPCODE_TEXT = 0x1,
|
||||||
|
WEBSOCKET_OPCODE_BINARY = 0x2,
|
||||||
|
WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
|
||||||
|
WEBSOCKET_OPCODE_PING = 0x9,
|
||||||
|
WEBSOCKET_OPCODE_PONG = 0xa
|
||||||
|
};
|
||||||
|
|
||||||
// Server management functions
|
// Server management functions
|
||||||
struct mg_server *mg_create_server(void *server_param);
|
struct mg_server *mg_create_server(void *server_param, mg_handler_t handler);
|
||||||
void mg_destroy_server(struct mg_server **);
|
void mg_destroy_server(struct mg_server **);
|
||||||
const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
|
const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
|
||||||
unsigned int mg_poll_server(struct mg_server *, int milliseconds);
|
int mg_poll_server(struct mg_server *, int milliseconds);
|
||||||
void mg_set_request_handler(struct mg_server *, mg_handler_t);
|
|
||||||
void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
|
|
||||||
void mg_set_auth_handler(struct mg_server *, mg_handler_t);
|
|
||||||
const char **mg_get_valid_option_names(void);
|
const char **mg_get_valid_option_names(void);
|
||||||
const char *mg_get_option(const struct mg_server *server, const char *name);
|
const char *mg_get_option(const struct mg_server *server, const char *name);
|
||||||
void mg_set_listening_socket(struct mg_server *, int sock);
|
void mg_set_listening_socket(struct mg_server *, int sock);
|
||||||
int mg_get_listening_socket(struct mg_server *);
|
int mg_get_listening_socket(struct mg_server *);
|
||||||
void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *);
|
void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *);
|
||||||
|
struct mg_connection *mg_next(struct mg_server *, struct mg_connection *);
|
||||||
|
void mg_wakeup_server(struct mg_server *);
|
||||||
|
void mg_wakeup_server_ex(struct mg_server *, mg_handler_t, const char *, ...);
|
||||||
|
struct mg_connection *mg_connect(struct mg_server *, const char *, int, int);
|
||||||
|
|
||||||
// Connection management functions
|
// Connection management functions
|
||||||
void mg_send_status(struct mg_connection *, int status_code);
|
void mg_send_status(struct mg_connection *, int status_code);
|
||||||
void mg_send_header(struct mg_connection *, const char *name, const char *val);
|
void mg_send_header(struct mg_connection *, const char *name, const char *val);
|
||||||
void mg_send_data(struct mg_connection *, const void *data, int data_len);
|
size_t mg_send_data(struct mg_connection *, const void *data, int data_len);
|
||||||
void mg_printf_data(struct mg_connection *, const char *format, ...);
|
size_t mg_printf_data(struct mg_connection *, const char *format, ...);
|
||||||
|
size_t mg_write(struct mg_connection *, const void *buf, int len);
|
||||||
|
size_t mg_printf(struct mg_connection *conn, const char *fmt, ...);
|
||||||
|
|
||||||
int mg_websocket_write(struct mg_connection *, int opcode,
|
size_t mg_websocket_write(struct mg_connection *, int opcode,
|
||||||
const char *data, size_t data_len);
|
const char *data, size_t data_len);
|
||||||
|
size_t mg_websocket_printf(struct mg_connection* conn, int opcode,
|
||||||
|
const char *fmt, ...);
|
||||||
|
|
||||||
// Deprecated in favor of mg_send_* interface
|
void mg_send_file(struct mg_connection *, const char *path);
|
||||||
int mg_write(struct mg_connection *, const void *buf, int len);
|
|
||||||
int mg_printf(struct mg_connection *conn, const char *fmt, ...);
|
|
||||||
|
|
||||||
const char *mg_get_header(const struct mg_connection *, const char *name);
|
const char *mg_get_header(const struct mg_connection *, const char *name);
|
||||||
const char *mg_get_mime_type(const char *name, const char *default_mime_type);
|
const char *mg_get_mime_type(const char *name, const char *default_mime_type);
|
||||||
@@ -101,20 +127,18 @@ int mg_parse_multipart(const char *buf, int buf_len,
|
|||||||
void *mg_start_thread(void *(*func)(void *), void *param);
|
void *mg_start_thread(void *(*func)(void *), void *param);
|
||||||
char *mg_md5(char buf[33], ...);
|
char *mg_md5(char buf[33], ...);
|
||||||
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
|
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
|
||||||
|
int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len);
|
||||||
|
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int);
|
||||||
|
int mg_terminate_ssl(struct mg_connection *c, const char *cert);
|
||||||
|
|
||||||
// Callback function return codes
|
// Templates support
|
||||||
enum { MG_REQUEST_NOT_PROCESSED, MG_REQUEST_PROCESSED, MG_REQUEST_CALL_AGAIN };
|
struct mg_expansion {
|
||||||
enum { MG_AUTH_FAIL, MG_AUTH_OK };
|
const char *keyword;
|
||||||
enum { MG_ERROR_NOT_PROCESSED, MG_ERROR_PROCESSED };
|
void (*handler)(struct mg_connection *);
|
||||||
enum { MG_CLIENT_CONTINUE, MG_CLIENT_CLOSE };
|
|
||||||
|
|
||||||
// HTTP client events
|
|
||||||
enum {
|
|
||||||
MG_CONNECT_SUCCESS, MG_CONNECT_FAILURE,
|
|
||||||
MG_DOWNLOAD_SUCCESS, MG_DOWNLOAD_FAILURE
|
|
||||||
};
|
};
|
||||||
int mg_connect(struct mg_server *, const char *host, int port, int use_ssl,
|
void mg_template(struct mg_connection *, const char *text,
|
||||||
mg_handler_t handler, void *param);
|
struct mg_expansion *expansions);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user