Commit 9178fbd1 authored by jmucchiello's avatar jmucchiello

Merge pull request #1 from valenok/master

update
parents fcbc36ec b2b43ab0
This diff is collapsed.
Project Mission # Project Mission
---------------
Project mission is to provide simple, functional, embeddable web server to Project mission is to provide simple, functional, embeddable web server to
make it easy for application and device developers to implement web interface for their make it easy for application and device developers to implement web interface
application and devices, and to offer a simple web development environment. for their application and devices, and to offer a simple web development
environment.
Overview # Overview
--------
To accomplish it's mission, Mongoose keeps balance on functionality and To accomplish it's mission, Mongoose keeps balance on functionality and
simplicity by carefully selected list of features: simplicity by carefully selected list of features:
- Liberal, commercial-friendly [MIT license](http://en.wikipedia.org/wiki/MIT_License) - Liberal, commercial-friendly
[MIT license](http://en.wikipedia.org/wiki/MIT_License)
- Works on Windows, Mac, UNIX, iPhone, Android, and many other platforms - Works on Windows, Mac, UNIX, iPhone, Android, and many other platforms
- Support for CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV - Support for CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV
- Lua server pages (PHP-like functionality using Lua), see [page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp) - Lua server pages (PHP-like functionality using Lua), see
[page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp)
- Resumed download, URL rewrite, IP-based ACL, Windows service - Resumed download, URL rewrite, IP-based ACL, Windows service
- Excluding files from serving by URI pattern (file blacklist) - Excluding files from serving by URI pattern (file blacklist)
- Download speed limit based on client subnet or URI pattern - Download speed limit based on client subnet or URI pattern
- Small footprint: executable size is 50 kB on Linux 2.6 i386 system - Small footprint: executable size is 50 kB on Linux 2.6 i386 system
- 130 kilobytes Windows executable with all of the above and no dependencies - 130 kilobytes Windows executable with all of the above and no dependencies
- Simple and clean embedding API ([mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)). The source is in single [mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file to make things easy. - Simple and clean embedding API,
- Embedding examples: [hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c), [post.c](https://github.com/valenok/mongoose/blob/master/examples/post.c), [upload.c](https://github.com/valenok/mongoose/blob/master/examples/upload.c), [websocket.c](https://github.com/valenok/mongoose/blob/master/examples/websocket.c) [mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h).
- Extensive documentation in form of [User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md) The source is in single
[mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file
to make things easy
- Embedding examples:
[hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c),
[post.c](https://github.com/valenok/mongoose/blob/master/examples/post.c),
[upload.c](https://github.com/valenok/mongoose/blob/master/examples/upload.c),
[websocket.c](https://github.com/valenok/mongoose/blob/master/examples/websocket.c)
- HTTP client functionality for embedded usage, capable of
sending arbitrary HTTP/HTTPS requests
- [User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md)
Questions can be asked at Questions can be asked at
[mongoose-users@google.com](http://groups.google.com/group/mongoose-users) mailing list. [mongoose-users@google.com](http://groups.google.com/group/mongoose-users)
mailing list.
Keep Sergey happy # Keep Sergey happy
-----------------
Since 2004, Mongoose is being constantly improved by me, Sergey Lyubka, a software engineer I am Sergey Lyubka, a software engineer from Galway, Ireland. I started
from Galway, Ireland. My other software I give to the community for free is working on Mongoose in 2004, and since then continuously improve it,
investing thousands of hours of work. My other project I'm contributing to the
community for free is
[Super Light Regular Expression library](http://code.google.com/p/slre). [Super Light Regular Expression library](http://code.google.com/p/slre).
If you feel grateful for the stuff I've done, you can buy me a book from my If you feel grateful for the stuff I've done, you can buy me a book from my
[Amazon wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority). Many thanks to all who [Amazon wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority). Many thanks
already did so: T.Barmann, D.Hughes, J.C.Sloan, R.Romeo and 4 others. to all who already did so: T.Barmann, D.Hughes, J.C.Sloan, R.Romeo,
Appreciated guys, you keep my brains going! L.E.Spencer, S.Kotay and 7 others.
Appreciated guys, you keep my brains going! Cash is also welcome indeed.
Press [<img src="http://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif">](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DGZ2FMP95TAL6)
button to donate. Donation progress: 39/1000 &euro;
(thanks to O.M.Vilhunen, C.Radik, G.Woodcock, M.Szczepkowski,
Eternal Lands Development Team)
![Progress](http://chart.googleapis.com/chart?chxr=0,0,1000&chxt=x&chbh=30,0,0&chs=300x35&cht=bhs&chco=90c0f0&chd=t:3.9)
This diff is collapsed.
...@@ -2,17 +2,19 @@ ...@@ -2,17 +2,19 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key> <string>Mongoose</string>
<string>mongoose</string> <key>CFBundlePackageType</key> <string>APPL</string>
<key>CFBundlePackageType</key> <key>CFBundleTypeRole</key> <string>None</string>
<string>APPL</string> <key>CFBundleIconFiles</key> <array>
<key>CFBundleTypeRole</key> <string>mongoose_16x16.png</string>
<string>None</string> <string>mongoose_22x22.png</string>
<key>CFBundleIconFiles</key> <string>mongoose_32x32.png</string>
<array> <string>mongoose_64x64.png</string>
<string>mongoose_16x16.png</string> </array>
</array> <key>LSUIElement</key> <true/>
<key>LSUIElement</key> <key>RunAtLoad</key> <true/>
<true/> <key>Label</key> <string>com.kolkin.mongoose</string>
<key>ProgramArguments</key> <array> </array>
<key>KeepAlive</key> <true/>
</dict> </dict>
</plist> </plist>
This diff is collapsed.
200 ICON DISCARDABLE "systray.ico" 100 ICON DISCARDABLE "systray.ico"
This diff is collapsed.
This diff is collapsed.
build/systray.ico

1.05 KB | W: | H:

build/systray.ico

16.6 KB | W: | H:

build/systray.ico
build/systray.ico
build/systray.ico
build/systray.ico
  • 2-up
  • Swipe
  • Onion skin
...@@ -25,4 +25,4 @@ windows: ...@@ -25,4 +25,4 @@ windows:
$(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS) $(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS)
clean: clean:
rm -rf hello hello.exe upload upload.exe post post.exe websocket websocket.exe chat chat.exe *.dSYM *.obj rm -rf hello upload post websocket chat *.exe *.dSYM *.obj
...@@ -325,34 +325,25 @@ static void redirect_to_ssl(struct mg_connection *conn, ...@@ -325,34 +325,25 @@ static void redirect_to_ssl(struct mg_connection *conn,
} }
} }
static void *event_handler(enum mg_event event, static int begin_request_handler(struct mg_connection *conn) {
struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn); const struct mg_request_info *request_info = mg_get_request_info(conn);
void *processed = "yes"; int processed = 1;
if (event == MG_NEW_REQUEST) { if (!request_info->is_ssl) {
if (!request_info->is_ssl) { redirect_to_ssl(conn, request_info);
redirect_to_ssl(conn, request_info); } else if (!is_authorized(conn, request_info)) {
} else if (!is_authorized(conn, request_info)) { redirect_to_login(conn, request_info);
redirect_to_login(conn, request_info); } else if (strcmp(request_info->uri, authorize_url) == 0) {
} else if (strcmp(request_info->uri, authorize_url) == 0) { authorize(conn, request_info);
authorize(conn, request_info); } else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) { ajax_get_messages(conn, request_info);
ajax_get_messages(conn, request_info); } else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) { ajax_send_message(conn, request_info);
ajax_send_message(conn, request_info);
} else {
// No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
processed = NULL;
}
} else if (event == MG_EVENT_LOG) {
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
processed = NULL;
} else { } else {
processed = NULL; // No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
processed = 0;
} }
return processed; return processed;
} }
...@@ -365,6 +356,7 @@ static const char *options[] = { ...@@ -365,6 +356,7 @@ static const char *options[] = {
}; };
int main(void) { int main(void) {
struct mg_callbacks callbacks;
struct mg_context *ctx; struct mg_context *ctx;
// Initialize random number generator. It will be used later on for // Initialize random number generator. It will be used later on for
...@@ -372,7 +364,9 @@ int main(void) { ...@@ -372,7 +364,9 @@ int main(void) {
srand((unsigned) time(0)); srand((unsigned) time(0));
// Setup and start Mongoose // Setup and start Mongoose
if ((ctx = mg_start(&event_handler, NULL, options)) == NULL) { memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler;
if ((ctx = mg_start(&callbacks, NULL, options)) == NULL) {
printf("%s\n", "Cannot start chat server, fatal exit"); printf("%s\n", "Cannot start chat server, fatal exit");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
......
...@@ -2,35 +2,49 @@ ...@@ -2,35 +2,49 @@
#include <string.h> #include <string.h>
#include "mongoose.h" #include "mongoose.h"
static void *callback(enum mg_event event, // This function will be called by mongoose on every new request.
struct mg_connection *conn) { static int begin_request_handler(struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn); const struct mg_request_info *request_info = mg_get_request_info(conn);
char content[100];
if (event == MG_NEW_REQUEST) { // Prepare the message we're going to send
char content[1024]; int content_length = snprintf(content, sizeof(content),
int content_length = snprintf(content, sizeof(content), "Hello from mongoose! Remote port: %d",
"Hello from mongoose! Remote port: %d", request_info->remote_port);
request_info->remote_port);
mg_printf(conn, // Send HTTP reply to the client
"HTTP/1.1 200 OK\r\n" mg_printf(conn,
"Content-Type: text/plain\r\n" "HTTP/1.1 200 OK\r\n"
"Content-Length: %d\r\n" // Always set Content-Length "Content-Type: text/plain\r\n"
"\r\n" "Content-Length: %d\r\n" // Always set Content-Length
"%s", "\r\n"
content_length, content); "%s",
// Mark as processed content_length, content);
return "";
} else { // Returning non-zero tells mongoose that our function has replied to
return NULL; // the client, and mongoose should not send client any more data.
} return 1;
} }
int main(void) { int main(void) {
struct mg_context *ctx; struct mg_context *ctx;
struct mg_callbacks callbacks;
// List of options. Last element must be NULL.
const char *options[] = {"listening_ports", "8080", NULL}; const char *options[] = {"listening_ports", "8080", NULL};
ctx = mg_start(&callback, NULL, options); // Prepare callbacks structure. We have only one callback, the rest are NULL.
getchar(); // Wait until user hits "enter" memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler;
// Start the web server.
ctx = mg_start(&callbacks, NULL, options);
// Wait until user hits "enter". Server is running in separate thread.
// Navigating to http://localhost:8080 will invoke begin_request_handler().
getchar();
// Stop the server.
mg_stop(ctx); mg_stop(ctx);
return 0; return 0;
......
...@@ -10,50 +10,45 @@ static const char *html_form = ...@@ -10,50 +10,45 @@ static const char *html_form =
"<input type=\"submit\" />" "<input type=\"submit\" />"
"</form></body></html>"; "</form></body></html>";
static void *callback(enum mg_event event, static int begin_request_handler(struct mg_connection *conn) {
struct mg_connection *conn) {
const struct mg_request_info *ri = mg_get_request_info(conn); const struct mg_request_info *ri = mg_get_request_info(conn);
char post_data[1024], input1[sizeof(post_data)], input2[sizeof(post_data)];
if (event == MG_NEW_REQUEST) { int post_data_len;
if (!strcmp(ri->uri, "/handle_post_request")) {
// User has submitted a form, show submitted data and a variable value if (!strcmp(ri->uri, "/handle_post_request")) {
char post_data[1024], // User has submitted a form, show submitted data and a variable value
input1[sizeof(post_data)], input2[sizeof(post_data)]; post_data_len = mg_read(conn, post_data, sizeof(post_data));
int post_data_len;
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated
// Read POST data mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1));
post_data_len = mg_read(conn, post_data, sizeof(post_data)); mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2));
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated // Send reply to the client, showing submitted form values.
mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1)); mg_printf(conn, "HTTP/1.0 200 OK\r\n"
mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2)); "Content-Type: text/plain\r\n\r\n"
"Submitted data: [%.*s]\n"
mg_printf(conn, "HTTP/1.0 200 OK\r\n" "Submitted data length: %d bytes\n"
"Content-Type: text/plain\r\n\r\n" "input_1: [%s]\n"
"Submitted data: [%.*s]\n" "input_2: [%s]\n",
"Submitted data length: %d bytes\n" post_data_len, post_data, post_data_len, input1, input2);
"input_1: [%s]\n"
"input_2: [%s]\n",
post_data_len, post_data, post_data_len, input1, input2);
} else {
// Show HTML form.
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
"Content-Length: %d\r\n"
"Content-Type: text/html\r\n\r\n%s",
(int) strlen(html_form), html_form);
}
// Mark as processed
return "";
} else { } else {
return NULL; // Show HTML form.
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
"Content-Length: %d\r\n"
"Content-Type: text/html\r\n\r\n%s",
(int) strlen(html_form), html_form);
} }
return 1; // Mark request as processed
} }
int main(void) { int main(void) {
struct mg_context *ctx; struct mg_context *ctx;
const char *options[] = {"listening_ports", "8080", NULL}; const char *options[] = {"listening_ports", "8080", NULL};
struct mg_callbacks callbacks;
ctx = mg_start(&callback, NULL, options); memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler;
ctx = mg_start(&callbacks, NULL, options);
getchar(); // Wait until user hits "enter" getchar(); // Wait until user hits "enter"
mg_stop(ctx); mg_stop(ctx);
......
...@@ -17,42 +17,44 @@ typedef __int64 int64_t; ...@@ -17,42 +17,44 @@ typedef __int64 int64_t;
#include "mongoose.h" #include "mongoose.h"
static void *callback(enum mg_event event, struct mg_connection *conn) { static int begin_request_handler(struct mg_connection *conn) {
if (event == MG_NEW_REQUEST) { if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) {
if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) { mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n");
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n"); mg_upload(conn, "/tmp");
mg_upload(conn, "/tmp"); } else {
} else { // Show HTML form. Make sure it has enctype="multipart/form-data" attr.
// Show HTML form. Make sure it has enctype="multipart/form-data" attr. static const char *html_form =
static const char *html_form = "<html><body>Upload example."
"<html><body>Upload example." "<form method=\"POST\" action=\"/handle_post_request\" "
"<form method=\"POST\" action=\"/handle_post_request\" " " enctype=\"multipart/form-data\">"
" enctype=\"multipart/form-data\">" "<input type=\"file\" name=\"file\" /> <br/>"
"<input type=\"file\" name=\"file\" /> <br/>" "<input type=\"submit\" value=\"Upload\" />"
"<input type=\"submit\" value=\"Upload\" />" "</form></body></html>";
"</form></body></html>";
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
mg_printf(conn, "HTTP/1.0 200 OK\r\n" "Content-Length: %d\r\n"
"Content-Length: %d\r\n" "Content-Type: text/html\r\n\r\n%s",
"Content-Type: text/html\r\n\r\n%s", (int) strlen(html_form), html_form);
(int) strlen(html_form), html_form);
}
// Mark as processed
return "";
} else if (event == MG_UPLOAD) {
mg_printf(conn, "Saved [%s]", mg_get_request_info(conn)->ev_data);
} }
return NULL; // Mark request as processed
return 1;
}
static void upload_handler(struct mg_connection *conn, const char *path) {
mg_printf(conn, "Saved [%s]", path);
} }
int main(void) { int main(void) {
struct mg_context *ctx; struct mg_context *ctx;
const char *options[] = {"listening_ports", "8080", NULL}; const char *options[] = {"listening_ports", "8080", NULL};
struct mg_callbacks callbacks;
ctx = mg_start(&callback, NULL, options); memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler;
callbacks.upload = upload_handler;
ctx = mg_start(&callbacks, NULL, options);
getchar(); // Wait until user hits "enter" getchar(); // Wait until user hits "enter"
pause();
mg_stop(ctx); mg_stop(ctx);
return 0; return 0;
......
...@@ -5,69 +5,70 @@ ...@@ -5,69 +5,70 @@
#include <string.h> #include <string.h>
#include "mongoose.h" #include "mongoose.h"
static void *callback(enum mg_event event, struct mg_connection *conn) { static void websocket_ready_handler(struct mg_connection *conn) {
if (event == MG_WEBSOCKET_READY) { unsigned char buf[40];
unsigned char buf[40]; buf[0] = 0x81;
buf[0] = 0x81; buf[1] = snprintf((char *) buf + 2, sizeof(buf) - 2, "%s", "server ready");
buf[1] = snprintf((char *) buf + 2, sizeof(buf) - 2, "%s", "server ready"); mg_write(conn, buf, 2 + buf[1]);
mg_write(conn, buf, 2 + buf[1]); }
return ""; // MG_WEBSOCKET_READY return value is ignored
} else if (event == MG_WEBSOCKET_MESSAGE) { static int websocket_data_handler(struct mg_connection *conn) {
unsigned char buf[200], reply[200]; unsigned char buf[200], reply[200];
int n, i, mask_len, xor, msg_len, len; int n, i, mask_len, xor, msg_len, len;
// Read message from the client. // Read message from the client.
// Accept only small (<126 bytes) messages. // Accept only small (<126 bytes) messages.
len = 0; len = 0;
msg_len = mask_len = 0; msg_len = mask_len = 0;
for (;;) { for (;;) {
if ((n = mg_read(conn, buf + len, sizeof(buf) - len)) <= 0) { if ((n = mg_read(conn, buf + len, sizeof(buf) - len)) <= 0) {
return ""; // Read error, close websocket return 0; // Read error, close websocket
}
len += n;
if (len >= 2) {
msg_len = buf[1] & 127;
mask_len = (buf[1] & 128) ? 4 : 0;
if (msg_len > 125) {
return 0; // Message is too long, close websocket
} }
len += n; // If we've buffered the whole message, exit the loop
if (len >= 2) { if (len >= 2 + mask_len + msg_len) {
msg_len = buf[1] & 127; break;
mask_len = (buf[1] & 128) ? 4 : 0;
if (msg_len > 125) {
return ""; // Message is too long, close websocket
}
// If we've buffered the whole message, exit the loop
if (len >= 2 + mask_len + msg_len) {
break;
}
} }
} }
}
// Prepare frame // Prepare frame
reply[0] = 0x81; // text, FIN set reply[0] = 0x81; // text, FIN set
reply[1] = msg_len; reply[1] = msg_len;
// Copy message from request to reply, applying the mask if required. // Copy message from request to reply, applying the mask if required.
for (i = 0; i < msg_len; i++) { for (i = 0; i < msg_len; i++) {
xor = mask_len == 0 ? 0 : buf[2 + (i % 4)]; xor = mask_len == 0 ? 0 : buf[2 + (i % 4)];
reply[i + 2] = buf[i + 2 + mask_len] ^ xor; reply[i + 2] = buf[i + 2 + mask_len] ^ xor;
} }
// Echo the message back to the client // Echo the message back to the client
mg_write(conn, reply, 2 + msg_len); mg_write(conn, reply, 2 + msg_len);
// Return non-NULL means stoping websocket conversation. // Returnint zero means stoping websocket conversation.
// Close the conversation if client has sent us "exit" string. // Close the conversation if client has sent us "exit" string.
return memcmp(reply + 2, "exit", 4) == 0 ? "" : NULL; return memcmp(reply + 2, "exit", 4);
} else {
return NULL;
}
} }
int main(void) { int main(void) {
struct mg_context *ctx; struct mg_context *ctx;
struct mg_callbacks callbacks;
const char *options[] = { const char *options[] = {
"listening_ports", "8080", "listening_ports", "8080",
"document_root", "websocket_html_root", "document_root", "websocket_html_root",
NULL NULL
}; };
ctx = mg_start(&callback, NULL, options); memset(&callbacks, 0, sizeof(callbacks));
callbacks.websocket_ready = websocket_ready_handler;
callbacks.websocket_data = websocket_data_handler;
ctx = mg_start(&callbacks, NULL, options);
getchar(); // Wait until user hits "enter" getchar(); // Wait until user hits "enter"
mg_stop(ctx); mg_stop(ctx);
......
This diff is collapsed.
.\" Process this file with
.\" groff -man -Tascii mongoose.1
.\" $Id: mongoose.1,v 1.12 2008/11/29 15:32:42 drozd Exp $
.Dd Sep 23, 2012
.Dt mongoose 1
.Sh NAME
.Nm mongoose
.Nd lightweight web server
.Sh SYNOPSIS
.Nm
.Op Ar config_file
.Op Ar OPTIONS
.Nm
.Fl A Ar htpasswd_file domain_name user_name password
.Sh DESCRIPTION
.Nm
is small, fast and easy to use web server with CGI, SSL, MD5 authorization,
and basic SSI support.
.Pp
.Nm
does not detach from terminal, and uses current working directory
as the web root, unless
.Fl r
option is specified.
It is possible to specify multiple ports to listen on. For example, to make
mongoose listen on HTTP port 80 and HTTPS port 443, one should start it as:
.Nm
.Fl s Ar cert.pem Fl p Ar 80,443s
.Pp
Unlike other web servers,
.Nm
does not require CGI scripts be put in a special directory. CGI scripts can
be anywhere. CGI (and SSI) files are recognized by the file name pattern.
.Nm
uses shell-like glob patterns with the following syntax:
.Bl -tag -compact -width indent
.It **
Matches everything
.It *
Matches everything but slash character, '/'
.It ?
Matches any character
.It $
Matches the end of the string
.It |
Matches if pattern on the left side or the right side matches. Pattern on the
left side is matched first
.El
All other characters in the pattern match themselves.
.Pp
If no arguments are given,
.Nm
searches for a configuration file called "mongoose.conf" in the same directory
where mongoose binary is located. Alternatively, a file name could be
specified in the command line. Format of the configuration file is the same
as for the command line options except that each option must be specified
on a separate line, leading dashes for option names must be omitted.
Lines beginning with '#' and empty lines are ignored.
.Pp
.Sh OPTIONS
.Bl -tag -width indent
.It Fl A Ar htpasswd_file domain_name user_name password
Add/edit user's password in the passwords file. Deleting users can be done
with any text editor. Functionality is similar to Apache's
.Ic htdigest
utility.
.It Fl C Ar cgi_pattern
All files that fully match cgi_pattern are treated as CGI.
Default pattern allows CGI files be
anywhere. To restrict CGIs to certain directory, use e.g. "-C /cgi-bin/**.cgi".
Default: "**.cgi$|**.pl$|**.php$"
.It Fl E Ar cgi_environment
Extra environment variables to be passed to the CGI script in addition to
standard ones. The list must be comma-separated list of X=Y pairs, like this:
"VARIABLE1=VALUE1,VARIABLE2=VALUE2". Default: ""
.It Fl G Ar put_delete_passwords_file
PUT and DELETE passwords file. This must be specified if PUT or
DELETE methods are used. Default: ""
.It Fl I Ar cgi_interpreter
Use
.Ar cgi_interpreter
as a CGI interpreter for all CGI scripts regardless script extension.
Mongoose decides which interpreter to use by looking at
the first line of a CGI script. Default: "".
.It Fl P Ar protect_uri
Comma separated list of URI=PATH pairs, specifying that given URIs
must be protected with respected password files. Default: ""
.It Fl R Ar authentication_domain
Authorization realm. Default: "mydomain.com"
.It Fl S Ar ssi_pattern
All files that fully match ssi_pattern are treated as SSI.
Unknown SSI directives are silently ignored. Currently, two SSI directives
are supported, "include" and "exec". Default: "**.shtml$|**.shtm$"
.It Fl T Ar throttle
Limit download speed for clients.
.Ar throttle
is a comma-separated list of key=value pairs, where
key could be a '*' character (limit for all connections), a subnet in form
x.x.x.x/mask (limit for a given subnet, for example 10.0.0.0/8), or an
URI prefix pattern (limit for the set of URIs, for example /foo/**). The value
is a floating-point number of bytes per second, optionally followed by a
`k' or `m' character, meaning kilobytes and megabytes respectively. A limit
of 0 means unlimited rate. The last matching rule wins. For example,
"*=1k,10.0.0.0/8" means limit everybody to 1 kilobyte per second, but give
people from 10/8 subnet unlimited speed. Default: ""
.It Fl a Ar access_log_file
Access log file. Default: "", no logging is done.
.It Fl d Ar enable_directory_listing
Enable/disable directory listing. Default: "yes"
.It Fl e Ar error_log_file
Error log file. Default: "", no errors are logged.
.It Fl g Ar global_passwords_file
Location of a global passwords file. If set, per-directory .htpasswd files are
ignored, and all requests must be authorised against that file. Default: ""
.It Fl i Ar index_files
Comma-separated list of files to be treated as directory index files.
Default: "index.html,index.htm,index.cgi"
.It Fl l Ar access_control_list
Specify access control list (ACL). ACL is a comma separated list
of IP subnets, each subnet is prepended by '-' or '+' sign. Plus means allow,
minus means deny. If subnet mask is
omitted, like "-1.2.3.4", then it means single IP address. Mask may vary
from 0 to 32 inclusive. On each request, full list is traversed, and
last match wins. Default setting is to allow all. For example, to allow only
192.168/16 subnet to connect, run "mongoose -0.0.0.0/0,+192.168/16".
Default: ""
.It Fl m Ar extra_mime_types
Extra mime types to recognize, in form
"extension1=type1,extension2=type2,...". Extension must include dot.
Example: "mongoose -m .cpp=plain/text,.java=plain/text". Default: ""
.It Fl p Ar listening_ports
Comma-separated list of ports to listen on. If the port is SSL, a letter 's'
must be appeneded, for example, "-p 80,443s" will open port 80 and port 443,
and connections on port 443 will be SSL-ed. It is possible to specify an
IP address to bind to. In this case, an IP address and a colon must be
prepended to the port number. For example, to bind to a loopback interface
on port 80 and to all interfaces on HTTPS port 443, use
"mongoose -p 127.0.0.1:80,443s". Default: "8080"
.It Fl r Ar document_root
Location of the WWW root directory. Default: "."
.It Fl s Ar ssl_certificate
Location of SSL certificate file. Default: ""
.It Fl t Ar num_threads
Number of worker threads to start. Default: "10"
.It Fl u Ar run_as_user
Switch to given user's credentials after startup. Default: ""
.It Fl w Ar url_rewrite_patterns
Comma-separated list of URL rewrites in the form of
"pattern=substitution,..." If the "pattern" matches some prefix
of the requested URL, then matched prefix gets substituted with "substitution".
For example, "-w /config=/etc,**.doc|**.rtf=/path/to/cgi-bin/handle_doc.cgi"
will serve all URLs that start with "/config" from the "/etc" directory, and
call handle_doc.cgi script for .doc and .rtf file requests. If some pattern
matches, no further matching/substitution is performed
(first matching pattern wins). Use full paths in substitutions. Default: ""
.It Fl x Ar hide_files_patterns
A prefix pattern for the files to hide. Files that match the pattern will not
show up in directory listing and return 404 Not Found if requested. Default: ""
.El
.Pp
.Sh EMBEDDING
.Nm
was designed to be embeddable into C/C++ applications. Since the
source code is contained in single C file, it is fairly easy to embed it
and follow the updates. Please refer to http://code.google.com/p/mongoose
for details.
.Pp
.Sh EXAMPLES
.Bl -tag -width indent
.It Nm Fl r Ar /var/www Fl s Ar /etc/cert.pem Fl p Ar 8080,8043s
Start serving files from /var/www. Listen on port 8080 for HTTP, and 8043
for HTTPS connections. Use /etc/cert.pem as SSL certificate file.
.It Nm Fl l Ar -0.0.0.0/0,+10.0.0.0/8,+1.2.3.4
Deny connections from everywhere, allow only IP address 1.2.3.4 and
all IP addresses from 10.0.0.0/8 subnet to connect.
.It Nm Fl w Ar **=/usr/bin/script.cgi
Invoke /usr/bin/script.cgi for every incoming request, regardless of the URL.
.El
.Pp
.Sh COPYRIGHT
.Nm
is licensed under the terms of the MIT license.
.Sh AUTHOR
.An Sergey Lyubka Aq valenok@gmail.com .
This diff is collapsed.
...@@ -42,122 +42,37 @@ struct mg_request_info { ...@@ -42,122 +42,37 @@ struct mg_request_info {
long remote_ip; // Client's IP address long remote_ip; // Client's IP address
int remote_port; // Client's port int remote_port; // Client's port
int is_ssl; // 1 if SSL-ed, 0 if not int is_ssl; // 1 if SSL-ed, 0 if not
int num_headers; // Number of headers void *user_data; // User data pointer passed to mg_start()
int num_headers; // Number of HTTP headers
struct mg_header { struct mg_header {
const char *name; // HTTP header name const char *name; // HTTP header name
const char *value; // HTTP header value const char *value; // HTTP header value
} http_headers[64]; // Maximum 64 headers } http_headers[64]; // Maximum 64 headers
void *user_data; // User data pointer passed to mg_start()
void *ev_data; // Event-specific data pointer
}; };
// Various events on which user-defined callback function is called by Mongoose. // This structure needs to be passed to mg_start(), to let mongoose know
enum mg_event { // which callbacks to invoke. For detailed description, see
// New HTTP request has arrived from the client. // https://github.com/valenok/mongoose/blob/master/UserManual.md
// If callback returns non-NULL, Mongoose stops handling current request. struct mg_callbacks {
// ev_data contains NULL. int (*begin_request)(struct mg_connection *);
MG_NEW_REQUEST, void (*end_request)(const struct mg_connection *, int reply_status_code);
int (*log_message)(const struct mg_connection *, const char *message);
// Mongoose has finished handling the request. int (*init_ssl)(void *ssl_context);
// Callback return value is ignored. int (*websocket_connect)(const struct mg_connection *);
// ev_data contains integer HTTP status code: void (*websocket_ready)(struct mg_connection *);
// int http_reply_status_code = (long) request_info->ev_data; int (*websocket_data)(struct mg_connection *);
MG_REQUEST_COMPLETE, const char * (*open_file)(const struct mg_connection *,
const char *path, size_t *data_len);
// HTTP error must be returned to the client. void (*init_lua)(struct mg_connection *, void *lua_context);
// If callback returns non-NULL, Mongoose stops handling error. void (*upload)(struct mg_connection *, const char *file_name);
// ev_data contains HTTP error code:
// int http_reply_status_code = (long) request_info->ev_data;
MG_HTTP_ERROR,
// Mongoose logs a message.
// If callback returns non-NULL, Mongoose stops handling that event.
// ev_data contains a message to be logged:
// const char *log_message = request_info->ev_data;
MG_EVENT_LOG,
// SSL initialization, sent before certificate setup.
// If callback returns non-NULL, Mongoose does not set up certificates.
// ev_data contains server's OpenSSL context:
// SSL_CTX *ssl_context = request_info->ev_data;
MG_INIT_SSL,
// Sent on HTTP connect, before websocket handshake.
// If user callback returns NULL, then mongoose proceeds
// with handshake, otherwise it closes the connection.
// ev_data contains NULL.
MG_WEBSOCKET_CONNECT,
// Handshake has been successfully completed.
// Callback's return value is ignored.
// ev_data contains NULL.
MG_WEBSOCKET_READY,
// Incoming message from the client, data could be read with mg_read().
// If user callback returns non-NULL, mongoose closes the websocket.
// ev_data contains NULL.
MG_WEBSOCKET_MESSAGE,
// Client has closed the connection.
// Callback's return value is ignored.
// ev_data contains NULL.
MG_WEBSOCKET_CLOSE,
// Mongoose tries to open file.
// If callback returns non-NULL, Mongoose will not try to open it, but
// will use the returned value as a pointer to the file data. This allows
// for example to serve files from memory.
// ev_data contains file path, including document root path.
// Upon return, ev_data should return file size, which should be a long int.
//
// const char *file_name = request_info->ev_data;
// if (strcmp(file_name, "foo.txt") == 0) {
// request_info->ev_data = (void *) (long) 4;
// return "data";
// }
// return NULL;
//
// Note that this even is sent multiple times during one request. Each
// time mongoose tries to open or stat the file, this event is sent, e.g.
// for opening .htpasswd file, stat-ting requested file, opening requested
// file, etc.
MG_OPEN_FILE,
// Mongoose initializes Lua server page. Sent only if Lua support is enabled.
// Callback's return value is ignored.
// ev_data contains lua_State pointer.
MG_INIT_LUA,
// Mongoose has uploaded file to a temporary directory.
// Callback's return value is ignored.
// ev_data contains NUL-terminated file name.
MG_UPLOAD,
}; };
// Prototype for the user-defined function. Mongoose calls this function
// on every MG_* event.
//
// Parameters:
// event: which event has been triggered.
// conn: opaque connection handler. Could be used to read, write data to the
// client, etc. See functions below that have "mg_connection *" arg.
//
// Return:
// If handler returns non-NULL, that means that handler has processed the
// request by sending appropriate HTTP reply to the client. Mongoose treats
// the request as served.
// If handler returns NULL, that means that handler has not processed
// the request. Handler must not send any data to the client in this case.
// Mongoose proceeds with request handling as if nothing happened.
typedef void *(*mg_callback_t)(enum mg_event event, struct mg_connection *conn);
// Start web server. // Start web server.
// //
// Parameters: // Parameters:
// callback: user defined event handling function or NULL. // callbacks: mg_callbacks structure with user-defined callbacks.
// options: NULL terminated list of option_name, option_value pairs that // options: NULL terminated list of option_name, option_value pairs that
// specify Mongoose configuration parameters. // specify Mongoose configuration parameters.
// //
...@@ -179,8 +94,9 @@ typedef void *(*mg_callback_t)(enum mg_event event, struct mg_connection *conn); ...@@ -179,8 +94,9 @@ typedef void *(*mg_callback_t)(enum mg_event event, struct mg_connection *conn);
// //
// Return: // Return:
// web server context, or NULL on error. // web server context, or NULL on error.
struct mg_context *mg_start(mg_callback_t callback, void *user_data, struct mg_context *mg_start(const struct mg_callbacks *callbacks,
const char **options); void *user_data,
const char **configuration_options);
// Stop the web server. // Stop the web server.
...@@ -236,12 +152,6 @@ struct mg_request_info *mg_get_request_info(struct mg_connection *); ...@@ -236,12 +152,6 @@ struct mg_request_info *mg_get_request_info(struct mg_connection *);
int mg_write(struct mg_connection *, const void *buf, size_t len); int mg_write(struct mg_connection *, const void *buf, size_t len);
// Send data to the browser using printf() semantics.
//
// Works exactly like mg_write(), but allows to do message formatting.
// Below are the macros for enabling compiler-specific checks for
// printf-like arguments.
#undef PRINTF_FORMAT_STRING #undef PRINTF_FORMAT_STRING
#if _MSC_VER >= 1400 #if _MSC_VER >= 1400
#include <sal.h> #include <sal.h>
...@@ -260,6 +170,11 @@ int mg_write(struct mg_connection *, const void *buf, size_t len); ...@@ -260,6 +170,11 @@ int mg_write(struct mg_connection *, const void *buf, size_t len);
#define PRINTF_ARGS(x, y) #define PRINTF_ARGS(x, y)
#endif #endif
// Send data to the browser using printf() semantics.
//
// Works exactly like mg_write(), but allows to do message formatting.
// Below are the macros for enabling compiler-specific checks for
// printf-like arguments.
int mg_printf(struct mg_connection *, int mg_printf(struct mg_connection *,
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
...@@ -316,32 +231,30 @@ int mg_get_cookie(const struct mg_connection *, ...@@ -316,32 +231,30 @@ int mg_get_cookie(const struct mg_connection *,
const char *cookie_name, char *buf, size_t buf_len); const char *cookie_name, char *buf, size_t buf_len);
// Connect to the remote web server. // Download data from the remote web server.
// host: host name to connect to, e.g. "foo.com", or "10.12.40.1".
// port: port number, e.g. 80.
// use_ssl: wether to use SSL connection.
// error_buffer, error_buffer_size: error message placeholder.
// request_fmt,...: HTTP request.
// Return: // Return:
// On success, valid pointer to the new connection // On success, valid pointer to the new connection, suitable for mg_read().
// On error, NULL // On error, NULL. error_buffer contains error message.
struct mg_connection *mg_connect(struct mg_context *ctx, // Example:
const char *host, int port, int use_ssl); // char ebuf[100];
// struct mg_connection *conn;
// conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
// "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
struct mg_connection *mg_download(const char *host, int port, int use_ssl,
char *error_buffer, size_t error_buffer_size,
PRINTF_FORMAT_STRING(const char *request_fmt),
...) PRINTF_ARGS(6, 7);
// Close the connection opened by mg_connect(). // Close the connection opened by mg_download().
void mg_close_connection(struct mg_connection *conn); void mg_close_connection(struct mg_connection *conn);
// Download given URL to a given file.
// url: URL to download
// path: file name where to save the data
// request_info: pointer to a structure that will hold parsed reply headers
// buf, bul_len: a buffer for the reply headers
// Return:
// On error, NULL
// On success, opened file stream to the downloaded contents. The stream
// is positioned to the end of the file. It is the user's responsibility
// to fclose() the opened file stream.
FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
char *buf, size_t buf_len, struct mg_request_info *request_info);
// File upload functionality. Each uploaded file gets saved into a temporary // File upload functionality. Each uploaded file gets saved into a temporary
// file and MG_UPLOAD event is sent. // file and MG_UPLOAD event is sent.
// Return number of uploaded files. // Return number of uploaded files.
......
<html> <?
<p>Prime numbers from 0 to 100, calculated by Lua:</p> -- Lua server pages have full control over the output, including HTTP
<? -- headers they send to the client. Send HTTP headers:
print('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n')
?><html><body>
function is_prime(n) <p>This is an example Lua server page served by
if n <= 0 then return false end <a href="http://code.google.com/p/mongoose">Mongoose web server</a>.
if n <= 2 then return true end Mongoose has Lua, Sqlite, and other functionality built in the binary.
if (n % 2 == 0) then return false end This example page stores the request in the Sqlite database, and shows
for i = 3, n / 2, 2 do all requests done previously.</p>
if (n % i == 0) then return false end
end
return true
end
for i = 1, 100 do <pre>
if is_prime(i) then print('<span>' .. i .. '</span>&nbsp;') end <?
end -- Open database
local db = sqlite3.open('requests.db')
?> -- Setup a trace callback, to show SQL statements we'll be executing.
-- db:trace(function(data, sql) print('Executing: ' .. sql .. '\n') end, nil)
<p>Reading POST data from Lua (click submit):</p> -- Create a table if it is not created already
<form method="POST"><input type="text" name="t1"/><input type="submit"></form> db:exec([[
CREATE TABLE IF NOT EXISTS requests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp NOT NULL,
method NOT NULL,
uri NOT NULL,
user_agent
);
]])
<pre> -- Add entry about this request
POST data: [<? post_data = read() print(post_data) ?>] local stmt = db:prepare(
request method: [<? print(request_info.request_method) ?>] 'INSERT INTO requests VALUES(NULL, datetime("now"), ?, ?, ?);');
IP/port: [<? print(request_info.remote_ip, ':', request_info.remote_port) ?>] stmt:bind_values(request_info.request_method, request_info.uri,
URI: [<? print(request_info.uri) ?>] request_info.http_headers['User-Agent'])
HTTP version [<? print(request_info.http_version) ?>] stmt:step()
HEADERS: stmt:finalize()
<?
for name, value in pairs(request_info.http_headers) do
print(name, ':', value, '\n')
end
?>
</pre>
-- Show all previous records
print('Previous requests:\n')
stmt = db:prepare('SELECT * FROM requests ORDER BY id DESC;')
while stmt:step() == sqlite3.ROW do
local v = stmt:get_values()
print(v[1] .. ' ' .. v[2] .. ' ' .. v[3] .. ' '
.. v[4] .. ' ' .. v[5] .. '\n')
end
</html> -- Close database
db:close()
?>
</pre></body></html>
...@@ -146,11 +146,6 @@ if ($^O =~ /darwin|bsd|linux/) { ...@@ -146,11 +146,6 @@ if ($^O =~ /darwin|bsd|linux/) {
} }
} }
if (scalar(@ARGV) > 0 and $ARGV[0] eq 'embedded') {
do_embedded_test();
exit 0;
}
if (scalar(@ARGV) > 0 and $ARGV[0] eq 'unit') { if (scalar(@ARGV) > 0 and $ARGV[0] eq 'unit') {
do_unit_test(); do_unit_test();
exit 0; exit 0;
...@@ -158,20 +153,21 @@ if (scalar(@ARGV) > 0 and $ARGV[0] eq 'unit') { ...@@ -158,20 +153,21 @@ if (scalar(@ARGV) > 0 and $ARGV[0] eq 'unit') {
# Make sure we load config file if no options are given. # Make sure we load config file if no options are given.
# Command line options override config files settings # Command line options override config files settings
write_file($config, "access_log_file access.log\nlistening_ports 12345\n"); write_file($config, "access_log_file access.log\n" .
spawn("$exe -p $port"); "listening_ports 127.0.0.1:12345\n");
spawn("$exe -p 127.0.0.1:$port");
o("GET /test/hello.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'Loading config file'); o("GET /test/hello.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'Loading config file');
unlink $config; unlink $config;
kill_spawned_child(); kill_spawned_child();
# Spawn the server on port $port # Spawn the server on port $port
my $cmd = "$exe ". my $cmd = "$exe ".
"-listening_ports $port ". "-listening_ports 127.0.0.1:$port ".
"-access_log_file access.log ". "-access_log_file access.log ".
"-error_log_file debug.log ". "-error_log_file debug.log ".
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " . "-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " . "-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
'-put_delete_passwords_file test/passfile ' . '-put_delete_auth_file test/passfile ' .
'-access_control_list -0.0.0.0/0,+127.0.0.1 ' . '-access_control_list -0.0.0.0/0,+127.0.0.1 ' .
"-document_root $root ". "-document_root $root ".
"-hide_files_patterns **exploit.pl ". "-hide_files_patterns **exploit.pl ".
...@@ -219,11 +215,10 @@ write_file("$root/a+.txt", ''); ...@@ -219,11 +215,10 @@ write_file("$root/a+.txt", '');
o("GET /a+.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'URL-decoding, + in URI'); o("GET /a+.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'URL-decoding, + in URI');
# Test HTTP version parsing # Test HTTP version parsing
o("GET / HTTPX/1.0\r\n\r\n", '400 Bad Request', 'Bad HTTP Version', 0); o("GET / HTTPX/1.0\r\n\r\n", '^HTTP/1.1 500', 'Bad HTTP Version', 0);
o("GET / HTTP/x.1\r\n\r\n", '505 HTTP', 'Bad HTTP maj Version'); o("GET / HTTP/x.1\r\n\r\n", '^HTTP/1.1 505', 'Bad HTTP maj Version', 0);
o("GET / HTTP/1.1z\r\n\r\n", '505 HTTP', 'Bad HTTP min Version'); o("GET / HTTP/1.1z\r\n\r\n", '^HTTP/1.1 505', 'Bad HTTP min Version', 0);
o("GET / HTTP/02.0\r\n\r\n", '505 HTTP version not supported', o("GET / HTTP/02.0\r\n\r\n", '^HTTP/1.1 505', 'HTTP Version >1.1', 0);
'HTTP Version >1.1');
# File with leading single dot # File with leading single dot
o("GET /.leading.dot.txt HTTP/1.0\n\n", 'abc123', 'Leading dot 1'); o("GET /.leading.dot.txt HTTP/1.0\n\n", 'abc123', 'Leading dot 1');
...@@ -426,7 +421,6 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") { ...@@ -426,7 +421,6 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
do_PUT_test(); do_PUT_test();
kill_spawned_child(); kill_spawned_child();
do_unit_test(); do_unit_test();
do_embedded_test();
} }
sub do_PUT_test { sub do_PUT_test {
...@@ -456,79 +450,8 @@ sub do_PUT_test { ...@@ -456,79 +450,8 @@ sub do_PUT_test {
} }
sub do_unit_test { sub do_unit_test {
my $cmd = "cc -g -W -Wall -o $unit_test_exe $root/unit_test.c -I. ". my $target = on_windows() ? 'w' : 'u';
"-pthread -DNO_SSL "; system("make $target") == 0 or fail("Unit test failed!");
if (on_windows()) {
$cmd = "cl $root/embed.c mongoose.c /I. /nologo /DNO_SSL ".
"/DLISTENING_PORT=\\\"$port\\\" /link /out:$embed_exe.exe ws2_32.lib ";
}
print $cmd, "\n";
system($cmd) == 0 or fail("Cannot compile unit test");
system($unit_test_exe) == 0 or fail("Unit test failed!");
}
sub do_embedded_test {
my $cmd = "cc -W -Wall -o $embed_exe $root/embed.c mongoose.c -I. ".
"-pthread -DNO_SSL -DLISTENING_PORT=\\\"$port\\\"";
if (on_windows()) {
$cmd = "cl $root/embed.c mongoose.c /I. /nologo /DNO_SSL ".
"/DLISTENING_PORT=\\\"$port\\\" /link /out:$embed_exe.exe ws2_32.lib ";
}
print $cmd, "\n";
system($cmd) == 0 or fail("Cannot compile embedded unit test");
spawn("./$embed_exe");
o("GET /test_get_header HTTP/1.0\nHost: blah\n\n",
'Value: \[blah\]', 'mg_get_header', 0);
o("GET /test_get_var?a=b&my_var=foo&c=d HTTP/1.0\n\n",
'Value: \[foo\]', 'mg_get_var 1', 0);
o("GET /test_get_var?my_var=foo&c=d HTTP/1.0\n\n",
'Value: \[foo\]', 'mg_get_var 2', 0);
o("GET /test_get_var?a=b&my_var=foo HTTP/1.0\n\n",
'Value: \[foo\]', 'mg_get_var 3', 0);
o("POST /test_get_var HTTP/1.0\nContent-Length: 10\n\n".
"my_var=foo", 'Value: \[foo\]', 'mg_get_var 4', 0);
o("POST /test_get_var HTTP/1.0\nContent-Length: 18\n\n".
"a=b&my_var=foo&c=d", 'Value: \[foo\]', 'mg_get_var 5', 0);
o("POST /test_get_var HTTP/1.0\nContent-Length: 14\n\n".
"a=b&my_var=foo", 'Value: \[foo\]', 'mg_get_var 6', 0);
o("GET /test_get_var?a=one%2btwo&my_var=foo& HTTP/1.0\n\n",
'Value: \[foo\]', 'mg_get_var 7', 0);
o("GET /test_get_var?my_var=one%2btwo&b=two%2b HTTP/1.0\n\n",
'Value: \[one\+two\]', 'mg_get_var 8', 0);
# + in form data MUST be decoded to space
o("POST /test_get_var HTTP/1.0\nContent-Length: 10\n\n".
"my_var=b+c", 'Value: \[b c\]', 'mg_get_var 9', 0);
# Test that big POSTed vars are not truncated
my $my_var = 'x' x 64000;
o("POST /test_get_var HTTP/1.0\nContent-Length: 64007\n\n".
"my_var=$my_var", 'Value size: \[64000\]', 'mg_get_var 10', 0);
# Other methods should also work
o("PUT /test_get_var HTTP/1.0\nContent-Length: 10\n\n".
"my_var=foo", 'Value: \[foo\]', 'mg_get_var 11', 0);
o("POST /test_get_request_info?xx=yy HTTP/1.0\nFoo: bar\n".
"Content-Length: 3\n\na=b",
'Method: \[POST\].URI: \[/test_get_request_info\].'.
'HTTP version: \[1.0\].HTTP header \[Foo\]: \[bar\].'.
'HTTP header \[Content-Length\]: \[3\].'.
'Query string: \[xx=yy\].'.
'Remote IP: \[\d+\].Remote port: \[\d+\].'.
'Remote user: \[\]'
, 'request_info', 0);
o("GET /not_exist HTTP/1.0\n\n", 'Error: \[404\]', '404 handler', 0);
o("bad request\n\n", 'Error: \[400\]', '* error handler', 0);
# o("GET /foo/secret HTTP/1.0\n\n",
# '401 Unauthorized', 'mg_protect_uri', 0);
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=bill\n\n",
# '401 Unauthorized', 'mg_protect_uri (bill)', 0);
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=joe\n\n",
# '200 OK', 'mg_protect_uri (joe)', 0);
kill_spawned_child();
} }
print "SUCCESS! All tests passed.\n"; print "SUCCESS! All tests passed.\n";
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment