mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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,14 +1,17 @@
|
|||||||
# Mongoose Web Server
|
# Mongoose Web Server
|
||||||
|
|
||||||
Mongoose is easy to use, powerful, embeddable web server.
|
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 & Documentation](http://cesanta.com/mongoose.shtml)
|
||||||
|
* [How to use Lua Server Pages](http://cesanta.com/docs/Lua.shtml)
|
||||||
|
* [How to create basic website with Mongoose](http://cesanta.com/docs/BasicWebsite.shtml)
|
||||||
|
* [How to create PHP website with Mongoose](http://cesanta.com/docs/PhpWebsite.shtml)
|
||||||
|
* [Embedding Guide](http://cesanta.com/docs/Embed.shtml)
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
- Works on Windows, Mac, UNIX/Linux, iPhone, Android, and many other platforms
|
- Works on Windows, Mac, UNIX/Linux, iPhone, Android, and many other platforms
|
||||||
- CGI, SSL, Digest auth, Websocket, WEbDAV, Resumed download, URL rewrite, file blacklist, IP-based ACL, Windows service
|
- CGI, SSI, SSL, Digest auth, Websocket, WEbDAV, Resumed download, URL rewrite, file blacklist, Custom error pages, Virtual hosts, IP-based ACL, Windows service
|
||||||
- Scripting and database support ([Lua](http://lua.org) +
|
- Scripting and database support ([Lua](http://lua.org) +
|
||||||
[Sqlite](http://sqlite.org), see
|
[Sqlite](http://sqlite.org), see
|
||||||
[dirscan.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/dirscan.lp) ),
|
[dirscan.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/dirscan.lp) ),
|
||||||
|
|||||||
+86
-53
@@ -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,80 @@ 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 send `MG_POLL` events on each iteration of `mg_poll_server()`
|
||||||
|
|
||||||
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 only to those connections which returned `MG_MORE`.
|
||||||
|
Event handler should try to complete the reply. If reply is completed,
|
||||||
|
then event handler should return `MG_TRUE`. Otherwise, it should
|
||||||
|
return `MG_FALSE`, and polling will continue until
|
||||||
|
handler returns `MG_TRUE`.
|
||||||
|
* `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
|
||||||
|
to the client. Event handler can choose to send a reply itself, in which
|
||||||
|
case event handler must return `MG_TRUE`. Otherwise, event handler must
|
||||||
|
return `MG_FALSE`
|
||||||
|
* `MG_CLOSE` is sent when the connection is closed. This event is used
|
||||||
|
to cleanup per-connection state stored in `connection_param`
|
||||||
|
if it was allocated.
|
||||||
|
|
||||||
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.
|
||||||
|
* `MG_REPLY` is sent when response has been received from the remote host.
|
||||||
|
* `MG_CLOSE` same as for the accepted connection.
|
||||||
|
|
||||||
|
|
||||||
|
When mongoose buffers in HTTP request and successfully parses it, it sends
|
||||||
|
`MG_REQUEST` event for GET requests immediately. For POST requests,
|
||||||
Mongoose delays the call until the whole POST request is buffered in memory.
|
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 +115,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);
|
||||||
|
|
||||||
@@ -124,16 +133,22 @@ value is guaranteed to be non-NULL. If parameter is not set, zero-length string
|
|||||||
is returned.
|
is returned.
|
||||||
|
|
||||||
|
|
||||||
int mg_iterate_over_connections(struct mg_server *,
|
void mg_iterate_over_connections(struct mg_server *, mg_handler_t func);
|
||||||
void (*func)(struct mg_connection *, void *),
|
|
||||||
void *param);
|
|
||||||
|
|
||||||
This is an interface primarily designed to push arbitrary data to websocket
|
This is an interface primarily designed to push arbitrary data to websocket
|
||||||
connections at any time. This function could be called from any thread. When
|
connections at any time. This function could be called from the Mongoose thread
|
||||||
it returns, an IO thread called `func()` on each active connection,
|
only. When it returns, Mongoose thread calls `func()` for each active
|
||||||
passing `param` as an extra parameter. It is allowed to call `mg_send_data()` or
|
connection.
|
||||||
`mg_websocket_write()` within a callback, cause `func` is executed in the
|
It is allowed to call `mg_send_data()` or `mg_websocket_write()` within a
|
||||||
context of the IO thread.
|
callback, cause `func` is executed in the context of the Mongoose thread.
|
||||||
|
|
||||||
|
void mg_wakeup_server(struct mg_server *);
|
||||||
|
|
||||||
|
Makes `mg_poll_server()` that could be sleeping in the `select()` syscall
|
||||||
|
to break the call and return. This function can be called from any thread.
|
||||||
|
It is designed to let other threads wake up Mongoose thread from the sleep
|
||||||
|
and let it do a fresh new IO iteration over all connection. Usually it is done
|
||||||
|
when other threads decides there is new data ready to be sent by Mongoose.
|
||||||
|
|
||||||
void mg_send_status(struct mg_connection *, int status_code);
|
void mg_send_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,
|
||||||
@@ -206,3 +221,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 A PHP Website With Mongoose
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
|
||||||
|
|
||||||
|

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

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

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

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

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

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

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

|
||||||
+102
-37
@@ -13,34 +13,77 @@ 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 send `MG_POLL` events on each iteration of `mg_poll_server()`
|
||||||
|
|
||||||
|
`mg_connection::connection_param` pointer is a placeholder to keep
|
||||||
|
user-specific data. For example, handler could decide to open a DB
|
||||||
|
connection and store DB connection handle in `connection_param`.
|
||||||
|
* `MG_POLL` is sent only to those connections which returned `MG_MORE`.
|
||||||
|
Event handler should try to complete the reply. If reply is completed,
|
||||||
|
then event handler should return `MG_TRUE`. Otherwise, it should
|
||||||
|
return `MG_FALSE`, and polling will continue until
|
||||||
|
handler returns `MG_TRUE`.
|
||||||
|
* `MG_CLOSE` is sent when the connection is closed. This event is used
|
||||||
|
to cleanup per-connection state stored in `connection_param`
|
||||||
|
if it was allocated.
|
||||||
|
|
||||||
Let's extend our minimal application example and
|
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 +93,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/websocket.c) demonstrates API-controlled Digest authorization
|
||||||
|
* [mjpg.c](https://github.com/cesanta/mongoose/blob/master/examples/mjpg.c) demonstrates MJPEG streaming implementation
|
||||||
|
|
||||||
|
## Compilation flags
|
||||||
|
|
||||||
Below is the list of compilation flags that enable or disable certain
|
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 +140,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 +157,17 @@ 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
|
|
||||||
* [websocket.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates websocket usage
|
|
||||||
* [auth.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates API-controlled Digest authorization
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
How To Share Files With Mongoose
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
## 1. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside the directory you want to share:
|
||||||
|
|
||||||
|

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

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

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

|
||||||
|
|
||||||
|
## Tip: to find out what is your machine IP address, take a look at http://windows.microsoft.com/en-us/windows/find-computers-ip-address
|
||||||
+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.
|
||||||
+28
-21
@@ -10,7 +10,7 @@ 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. To learn
|
||||||
more about subnet masks, see the
|
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)
|
||||||
|
|
||||||
@@ -24,27 +24,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 +65,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:
|
||||||
@@ -86,7 +87,7 @@ must be for a file name only, not including directory name, e.g.
|
|||||||
not set.
|
not set.
|
||||||
|
|
||||||
### idle\_timeout\_ms
|
### idle\_timeout\_ms
|
||||||
Timeout for idle connections. Default: 30000 (30 seconds)
|
Timeout for idle connections in milliseconds. 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
|
||||||
@@ -107,7 +108,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 +117,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 +141,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 "Start browser" menu:
|
||||||
|

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

|
||||||
+3
-2
@@ -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
|
||||||
@@ -76,6 +76,7 @@ patterns are prefix patterns. Syntax is as follows:
|
|||||||
|
|
||||||
All other characters in the pattern match themselves. Examples:
|
All other characters in the pattern match themselves. Examples:
|
||||||
|
|
||||||
|
# Pattern Meaning
|
||||||
**.cgi$ Any string that ends with .cgi
|
**.cgi$ Any string that ends with .cgi
|
||||||
/foo Any string that begins with /foo
|
/foo Any string that begins with /foo
|
||||||
**a$|**b$ Any string that ends with a or b
|
**a$|**b$ Any string that ends with a or b
|
||||||
|
|||||||
+39
-31
@@ -1,54 +1,62 @@
|
|||||||
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
|
|
||||||
|
|
||||||
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)
|
# To enable Lua in a server, uncomment following lines
|
||||||
$(CC) post.c ../mongoose.c -o post $(CFLAGS)
|
LUA = ../lua-5.2.3/src
|
||||||
$(CC) multi_threaded.c ../mongoose.c -o multi_threaded $(CFLAGS)
|
#CFLAGS += -I$(LUA) -L$(LUA) -llua -DMONGOOSE_USE_LUA
|
||||||
$(CC) upload.c ../mongoose.c -o upload $(CFLAGS)
|
|
||||||
$(CC) auth.c ../mongoose.c -o auth $(CFLAGS)
|
server: server.c ../mongoose.c
|
||||||
$(CC) server.c ../mongoose.c -o server $(CFLAGS)
|
$(CC) server.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
hello: hello.c ../mongoose.c
|
||||||
|
$(CC) hello.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
websocket: websocket_html.c websocket.c ../mongoose.c
|
||||||
|
$(CC) websocket.c websocket_html.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
post: post.c ../mongoose.c
|
||||||
|
$(CC) post.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
multi_threaded: multi_threaded.c ../mongoose.c
|
||||||
|
$(CC) multi_threaded.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
upload: upload.c ../mongoose.c
|
||||||
|
$(CC) upload.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
auth: auth.c ../mongoose.c
|
||||||
|
$(CC) auth.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
|
mjpg: mjpg.c ../mongoose.c
|
||||||
|
$(CC) mjpg.c ../mongoose.c $(OUT) $(CFLAGS)
|
||||||
|
|
||||||
# $(CC) chat.c ../mongoose.c -o chat $(CFLAGS)
|
|
||||||
# $(CC) lua_dll.c ../build/lua_5.2.1.c -o $@.so $(CFLAGS) $(DLL_FLAGS)
|
|
||||||
|
|
||||||
websocket_html.c: websocket.html
|
websocket_html.c: websocket.html
|
||||||
perl mkdata.pl $< > $@
|
perl mkdata.pl $< > $@
|
||||||
|
|
||||||
MSVC = ../../vc6
|
u:
|
||||||
CL = $(MSVC)/bin/cl
|
g++ unit_test.c -Wall -W -pedantic -lssl -g -O0 $(CFLAGS_EXTRA) && ./a.out
|
||||||
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 server multi_threaded websocket_html.c *.exe *.dSYM *.obj .*o
|
||||||
+8
-4
@@ -2,8 +2,10 @@
|
|||||||
#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
|
|
||||||
|
if (ev == MG_AUTH) {
|
||||||
|
int result = MG_FALSE; // Not authorized
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
// To populate passwords file, do
|
// To populate passwords file, do
|
||||||
@@ -16,11 +18,13 @@ static int auth_handler(struct mg_connection *conn) {
|
|||||||
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 (;;) {
|
||||||
|
|||||||
+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.
+2
-2
@@ -23,7 +23,7 @@ static int index_html(struct mg_connection *conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return n < sizeof(buf) ? 1 : 0;
|
return n < sizeof(buf) ? MG_REQUEST_PROCESSED : MG_REQUEST_CALL_AGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
@@ -32,7 +32,7 @@ int main(void) {
|
|||||||
// Create and configure the server
|
// Create and configure the server
|
||||||
server = mg_create_server(NULL);
|
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);
|
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"));
|
||||||
|
|||||||
+11
-5
@@ -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;
|
||||||
|
|
||||||
|
if (ev == MG_REQUEST) {
|
||||||
mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
|
mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
|
||||||
return MG_REQUEST_PROCESSED;
|
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,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: %zu\r\n\r\n", 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;
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
if (ev == MG_REQUEST) {
|
||||||
mg_send_header(conn, "Content-Type", "text/plain");
|
mg_send_header(conn, "Content-Type", "text/plain");
|
||||||
mg_printf_data(conn, "This is a reply from server instance # %s",
|
mg_printf_data(conn, "This is a reply from server instance # %s",
|
||||||
(char *) conn->server_param);
|
(char *) conn->server_param);
|
||||||
return MG_REQUEST_PROCESSED;
|
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;
|
||||||
}
|
}
|
||||||
+5
-1
@@ -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"
|
||||||
|
|
||||||
@@ -146,6 +148,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) {
|
||||||
@@ -379,7 +383,7 @@ static void start_mongoose(int argc, char *argv[]) {
|
|||||||
char *options[MAX_OPTIONS];
|
char *options[MAX_OPTIONS];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if ((server = mg_create_server(NULL)) == NULL) {
|
if ((server = mg_create_server(NULL, EV_HANDLER)) == NULL) {
|
||||||
die("%s", "Failed to start Mongoose.");
|
die("%s", "Failed to start Mongoose.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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); \
|
||||||
@@ -280,32 +280,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 +339,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;
|
||||||
|
int result = MG_FALSE;
|
||||||
|
|
||||||
|
switch (ev) {
|
||||||
|
case MG_CONNECT:
|
||||||
|
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")) {
|
if (!strcmp(conn->uri, "/cb1")) {
|
||||||
mg_printf(conn, "%s %s %s",
|
mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n%s %s %s",
|
||||||
conn->server_param == NULL ? "?" : (char *) conn->server_param,
|
(char *) conn->server_param,
|
||||||
conn->connection_param == NULL ? "?" : "!", conn->remote_ip);
|
buf == NULL ? "?" : "!", conn->remote_ip);
|
||||||
result = MG_REQUEST_PROCESSED;
|
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 +400,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 +435,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) {
|
||||||
|
case MG_AUTH:
|
||||||
|
result = MG_TRUE;
|
||||||
|
break;
|
||||||
|
case MG_CONNECT:
|
||||||
mg_printf(conn, "GET /%s HTTP/1.0\r\n\r\n", cp);
|
mg_printf(conn, "GET /%s HTTP/1.0\r\n\r\n", cp);
|
||||||
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_REQUEST:
|
||||||
|
break;
|
||||||
|
case MG_REPLY:
|
||||||
|
file_data = read_file("unit_test.c", &file_size);
|
||||||
sprintf(cp, "%d %s", (size_t) file_size == conn->content_len &&
|
sprintf(cp, "%d %s", (size_t) file_size == conn->content_len &&
|
||||||
memcmp(file_data, conn->content, file_size) == 0 ? 1 : 0,
|
memcmp(file_data, conn->content, file_size) == 0 ? 1 : 0,
|
||||||
conn->query_string == NULL ? "?" : conn->query_string);
|
conn->query_string == NULL ? "?" : conn->query_string);
|
||||||
free(file_data);
|
free(file_data);
|
||||||
} else if (conn->status_code == MG_CONNECT_FAILURE) {
|
break;
|
||||||
sprintf(cp, "%s", "cf");
|
default:
|
||||||
} else {
|
break;
|
||||||
sprintf(cp, "%s", "df");
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MONGOOSE_USE_SSL
|
return result;
|
||||||
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 +473,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,7 +496,6 @@ 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_base64_encode);
|
RUN_TEST(test_base64_encode);
|
||||||
@@ -626,13 +503,9 @@ static const char *run_all_tests(void) {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
+12
-4
@@ -5,7 +5,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
static int index_html(struct mg_connection *conn) {
|
static void send_index_page(struct mg_connection *conn) {
|
||||||
const char *data;
|
const char *data;
|
||||||
int data_len;
|
int data_len;
|
||||||
char var_name[100], file_name[100];
|
char var_name[100], file_name[100];
|
||||||
@@ -29,17 +29,25 @@ static int index_html(struct mg_connection *conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mg_printf_data(conn, "%s", "</body></html>");
|
mg_printf_data(conn, "%s", "</body></html>");
|
||||||
|
}
|
||||||
|
|
||||||
return MG_REQUEST_PROCESSED;
|
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
|
||||||
|
if (ev == MG_REQUEST) {
|
||||||
|
send_index_page(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;
|
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"));
|
||||||
|
|||||||
+28
-12
@@ -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) {
|
static int counter = 0;
|
||||||
|
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), "%d", counter++);
|
||||||
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,27 +28,37 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+1529
-915
File diff suppressed because it is too large.
Load diff
+27
-21
@@ -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.3"
|
||||||
|
|
||||||
#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
|
||||||
@@ -58,21 +58,31 @@ struct mg_connection {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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
|
||||||
|
MG_LUA, // Called before LSP page invoked
|
||||||
|
MG_HTTP_ERROR // If callback returns MG_FALSE, Mongoose continues with err
|
||||||
|
};
|
||||||
|
typedef int (*mg_handler_t)(struct mg_connection *, enum mg_event);
|
||||||
|
|
||||||
// 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 mg_wakeup_server(struct mg_server *);
|
||||||
|
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);
|
||||||
@@ -102,19 +112,15 @@ 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);
|
||||||
|
|
||||||
// Callback function return codes
|
// Lua utility functions
|
||||||
enum { MG_REQUEST_NOT_PROCESSED, MG_REQUEST_PROCESSED, MG_REQUEST_CALL_AGAIN };
|
#ifdef MONGOOSE_USE_LUA
|
||||||
enum { MG_AUTH_FAIL, MG_AUTH_OK };
|
#include <lua.h>
|
||||||
enum { MG_ERROR_NOT_PROCESSED, MG_ERROR_PROCESSED };
|
#include <lauxlib.h>
|
||||||
enum { MG_CLIENT_CONTINUE, MG_CLIENT_CLOSE };
|
void reg_string(lua_State *L, const char *name, const char *val);
|
||||||
|
void reg_int(lua_State *L, const char *name, int val);
|
||||||
// HTTP client events
|
void reg_function(lua_State *L, const char *,
|
||||||
enum {
|
lua_CFunction, struct mg_connection *);
|
||||||
MG_CONNECT_SUCCESS, MG_CONNECT_FAILURE,
|
#endif
|
||||||
MG_DOWNLOAD_SUCCESS, MG_DOWNLOAD_FAILURE
|
|
||||||
};
|
|
||||||
int mg_connect(struct mg_server *, const char *host, int port, int use_ssl,
|
|
||||||
mg_handler_t handler, void *param);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user