mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0804ce71d5 | ||
|
|
2ffb558e30 | ||
|
|
05062be889 | ||
|
|
ba6c01e602 | ||
|
|
dfc0f00478 | ||
|
|
ecb2b0e53d | ||
|
|
ed74cd82f5 | ||
|
|
d2be594262 | ||
|
|
1e632d3215 | ||
|
|
5642dfcf38 | ||
|
|
4dff36ffde | ||
|
|
771ce9059c | ||
|
|
e490b30c38 | ||
|
|
f935ef832b | ||
|
|
79cdb77e5d | ||
|
|
ff0c2d3f91 | ||
|
|
6f9901b65c | ||
|
|
bce3a85702 | ||
|
|
f741349d51 | ||
|
|
25863b59a6 | ||
|
|
72c861bb83 | ||
|
|
586574e176 | ||
|
|
0f15954727 | ||
|
|
167338f216 | ||
|
|
d82f6b7607 | ||
|
|
614b219a27 | ||
|
|
35cf8687df | ||
|
|
d883b0f0f9 | ||
|
|
0b942aaaba | ||
|
|
364235e7ae | ||
|
|
ba1f2ced8e | ||
|
|
c4c34a8f4d | ||
|
|
a866f4e174 | ||
|
|
c5916814cd | ||
|
|
349717ac0e | ||
|
|
5d27766424 | ||
|
|
97668ba6c9 | ||
|
|
98fb44f836 | ||
|
|
2260c8ddc0 | ||
|
|
edffc46b98 | ||
|
|
7bd8cc3c0c | ||
|
|
828f6cd7fe | ||
|
|
f9873eb87a | ||
|
|
358e4ffbf5 | ||
|
|
9aad922983 | ||
|
|
d9ab7af8b4 | ||
|
|
05b2a04267 | ||
|
|
4408083b4d | ||
|
|
4f01f101fc | ||
|
|
d9f9911d9b | ||
|
|
93b4562515 | ||
|
|
0bb67e06c1 | ||
|
|
518b3aa726 | ||
|
|
07f3554db3 | ||
|
|
acab70ee7c | ||
|
|
c388bc2dd3 | ||
|
|
8072b0a7d4 | ||
|
|
3f4be595a6 | ||
|
|
5d11901af8 | ||
|
|
43953782f9 | ||
|
|
12e133a5e9 | ||
|
|
e92d4451ef | ||
|
|
fadb60d776 | ||
|
|
0521d78982 | ||
|
|
3602d54a5b | ||
|
|
8428f362c1 | ||
|
|
fafc9c6a20 | ||
|
|
66e088d843 | ||
|
|
4a760ad324 | ||
|
|
c92d7c3bc8 | ||
|
|
ee91109d9f | ||
|
|
30fe257691 | ||
|
|
ab6f145e3e | ||
|
|
83dc425e25 | ||
|
|
001cd78b45 | ||
|
|
3d3359910f | ||
|
|
ee8111e424 | ||
|
|
1861b1137b | ||
|
|
ccf43fe3e3 | ||
|
|
8b491d75b5 | ||
|
|
d70c18d692 |
No files matched your search
@@ -22,18 +22,19 @@ all:
|
|||||||
### UNIX build: linux, bsd, mac, rtems
|
### UNIX build: linux, bsd, mac, rtems
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
CFLAGS = -W -Wall -std=c99 -pedantic -O2 $(COPT)
|
GCC_WARNS = -W -Wall -pedantic
|
||||||
|
# -Wno-missing-field-initializers -Wno-unused-parameter -Wno-format-zero-length -Wno-missing-braces
|
||||||
|
CFLAGS = -std=c99 -O2 $(GCC_WARNS) $(COPT)
|
||||||
MAC_SHARED = -flat_namespace -bundle -undefined suppress
|
MAC_SHARED = -flat_namespace -bundle -undefined suppress
|
||||||
LINFLAGS = -ldl -pthread $(CFLAGS)
|
LINFLAGS = -ldl -pthread $(CFLAGS)
|
||||||
LIB = _$(PROG).so
|
LIB = lib$(PROG).so$(MONGOOSE_LIB_SUFFIX)
|
||||||
#CC = g++
|
|
||||||
|
|
||||||
# Make sure that the compiler flags come last in the compilation string.
|
# Make sure that the compiler flags come last in the compilation string.
|
||||||
# If not so, this can break some on some Linux distros which use
|
# If not so, this can break some on some Linux distros which use
|
||||||
# "-Wl,--as-needed" turned on by default in cc command.
|
# "-Wl,--as-needed" turned on by default in cc command.
|
||||||
# Also, this is turned in many other distros in static linkage builds.
|
# Also, this is turned in many other distros in static linkage builds.
|
||||||
linux:
|
linux:
|
||||||
$(CC) mongoose.c -shared -fPIC -fpic -o $(LIB) $(LINFLAGS)
|
$(CC) mongoose.c -shared -fPIC -fpic -o $(LIB) -Wl,-soname,$(LIB) $(LINFLAGS)
|
||||||
$(CC) mongoose.c main.c -o $(PROG) $(LINFLAGS)
|
$(CC) mongoose.c main.c -o $(PROG) $(LINFLAGS)
|
||||||
|
|
||||||
bsd:
|
bsd:
|
||||||
@@ -45,9 +46,9 @@ mac:
|
|||||||
$(CC) mongoose.c main.c -pthread -o $(PROG) $(CFLAGS)
|
$(CC) mongoose.c main.c -pthread -o $(PROG) $(CFLAGS)
|
||||||
|
|
||||||
solaris:
|
solaris:
|
||||||
gcc mongoose.c -pthread -lnsl \
|
$(CC) mongoose.c -pthread -lnsl \
|
||||||
-lsocket -fpic -fPIC -shared -o $(LIB) $(CFLAGS)
|
-lsocket -fpic -fPIC -shared -o $(LIB) $(CFLAGS)
|
||||||
gcc mongoose.c main.c -pthread -lnsl -lsocket -o $(PROG) $(CFLAGS)
|
$(CC) mongoose.c main.c -pthread -lnsl -lsocket -o $(PROG) $(CFLAGS)
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@@ -55,18 +56,19 @@ solaris:
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
# Using Visual Studio 6.0. To build Mongoose:
|
# Using Visual Studio 6.0. To build Mongoose:
|
||||||
# o Set VC variable below to where VS 6.0 is installed on your system
|
# o Set MSVC variable below to where VS 6.0 is installed on your system
|
||||||
# o Run "PATH_TO_VC6\bin\nmake windows"
|
# o Run "PATH_TO_VC6\bin\nmake windows"
|
||||||
|
|
||||||
VC= z:
|
MSVC = e:/vc6
|
||||||
CYA= y:
|
CYA = e:/cyassl-2.0.0rc2
|
||||||
#DBG= /Zi /DDEBUG /Od
|
#DBG = /Zi /DDEBUG /Od
|
||||||
DBG= /DNDEBUG /O1
|
DBG = /DNDEBUG /O1
|
||||||
CL= cl /MD /TC /nologo $(DBG) /Gz /W3 /DNO_SSL_DL
|
CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /Gz /W3 /DNO_SSL_DL \
|
||||||
|
/I$(MSVC)/include
|
||||||
GUILIB= user32.lib shell32.lib
|
GUILIB= user32.lib shell32.lib
|
||||||
LINK= /link /incremental:no /libpath:$(VC)\lib /subsystem:windows \
|
LINK = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
|
||||||
ws2_32.lib advapi32.lib cyassl.lib
|
/subsystem:windows ws2_32.lib advapi32.lib cyassl.lib
|
||||||
CYAFL = /c /I $(CYA)/include -I $(CYA)/include/openssl \
|
CYAFL = /c /I $(CYA)/include -I $(CYA)/include/openssl /I$(MSVC)/INCLUDE \
|
||||||
/I $(CYA)/ctaocrypt/include /D _LIB /D OPENSSL_EXTRA
|
/I $(CYA)/ctaocrypt/include /D _LIB /D OPENSSL_EXTRA
|
||||||
|
|
||||||
CYASRC= \
|
CYASRC= \
|
||||||
@@ -101,26 +103,26 @@ CYASRC= \
|
|||||||
$(CYA)/ctaocrypt/src/sha512.c \
|
$(CYA)/ctaocrypt/src/sha512.c \
|
||||||
$(CYA)/ctaocrypt/src/tfm.c
|
$(CYA)/ctaocrypt/src/tfm.c
|
||||||
|
|
||||||
cyassl:
|
cyassl.lib:
|
||||||
$(CL) $(CYASRC) $(CYAFL) $(DEF)
|
$(CL) $(CYASRC) $(CYAFL) $(DEF)
|
||||||
lib *.obj /out:cyassl.lib
|
$(MSVC)/bin/lib *.obj /out:$@
|
||||||
|
|
||||||
windows:
|
windows: cyassl.lib
|
||||||
rc win32\res.rc
|
$(MSVC)/bin/rc win32\res.rc
|
||||||
$(CL) main.c mongoose.c /GA $(LINK) win32\res.res \
|
$(CL) /I win32 main.c mongoose.c /GA $(LINK) win32\res.res \
|
||||||
$(GUILIB) /out:$(PROG).exe
|
$(GUILIB) /out:$(PROG).exe
|
||||||
$(CL) mongoose.c /GD $(LINK) /DLL /DEF:win32\dll.def /out:_$(PROG).dll
|
$(CL) mongoose.c /GD $(LINK) /DLL /DEF:win32\dll.def /out:$(PROG).dll
|
||||||
|
|
||||||
# Build for Windows under MinGW
|
# Build for Windows under MinGW
|
||||||
#MINGWDBG= -DDEBUG -O0
|
#MINGWDBG= -DDEBUG -O0 -ggdb
|
||||||
MINGWDBG= -DNDEBUG -Os
|
MINGWDBG= -DNDEBUG -Os
|
||||||
#MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,console $(MINGWDBG) -DHAVE_STDINT
|
MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,console $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
|
||||||
MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,windows $(MINGWDBG)
|
#MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,windows $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
|
||||||
mingw:
|
mingw:
|
||||||
windres win32\res.rc win32\res.o
|
windres win32\res.rc win32\res.o
|
||||||
gcc $(MINGWOPT) mongoose.c -lws2_32 \
|
$(CC) $(MINGWOPT) mongoose.c -lws2_32 \
|
||||||
-shared -Wl,--out-implib=$(PROG).lib -o _$(PROG).dll
|
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
|
||||||
gcc $(MINGWOPT) mongoose.c main.c win32\res.o -lws2_32 -ladvapi32 \
|
$(CC) $(MINGWOPT) -Iwin32 mongoose.c main.c win32\res.o -lws2_32 -ladvapi32 \
|
||||||
-o $(PROG).exe
|
-o $(PROG).exe
|
||||||
|
|
||||||
|
|
||||||
@@ -135,12 +137,11 @@ man:
|
|||||||
# "TEST=unit make test" - perform unit test only
|
# "TEST=unit make test" - perform unit test only
|
||||||
# "TEST=embedded" - test embedded API by building and testing test/embed.c
|
# "TEST=embedded" - test embedded API by building and testing test/embed.c
|
||||||
# "TEST=basic_tests" - perform basic tests only (no CGI, SSI..)
|
# "TEST=basic_tests" - perform basic tests only (no CGI, SSI..)
|
||||||
test: do_test
|
tests:
|
||||||
do_test:
|
|
||||||
perl test/test.pl $(TEST)
|
perl test/test.pl $(TEST)
|
||||||
|
|
||||||
release: clean
|
release: clean
|
||||||
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`.tgz ; cd .. && tar -czf x mongoose/{LICENSE,Makefile,bindings,examples,test,win32,mongoose.c,mongoose.h,mongoose.1,main.c} && mv x mongoose/$$F
|
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`.tgz ; cd .. && tar -czf x mongoose/{LICENSE,Makefile,bindings,examples,test,win32,mongoose.c,mongoose.h,mongoose.1,main.c} && mv x mongoose/$$F
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz
|
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz $(PROG).exe *.dll *.lib
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
Mongoose is easy to use web server. It also can be used as embedded web server library to provide web interface to applications.
|
||||||
|
|
||||||
|
Mongoose executable does not depend on any external library or configuration. If it is copied to any directory and launched from there, it starts to serve that directory on port 8080 (so to access files, go to http://localhost:8080). If some additional config is required - for example, different listening port or IP-based access control, then a mongoose.conf file with respective options can be created in the same directory where executable lives. This makes Mongoose perfect for all sorts of demos, quick tests, file sharing, and Web programming.
|
||||||
|
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Crossplatform - works on Windows, MacOS and most flavors of UNIX
|
||||||
|
- CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV support
|
||||||
|
- Resumed download, URL rewrite support
|
||||||
|
- IP-based ACL, Windows service, GET, POST, HEAD, PUT, DELETE methods
|
||||||
|
- Small footprint: executable size is 40 kB on Linux 2.6 i386 system
|
||||||
|
- Embeddable with simple and clean 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.
|
||||||
|
- 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)
|
||||||
|
- Python and C# bindings
|
||||||
|
|
||||||
|
|
||||||
|
Mailing list
|
||||||
|
------------
|
||||||
|
|
||||||
|
You can read it online, subscribe to, or send a message at [mongoose-users](http://groups.google.com/group/mongoose-users).
|
||||||
|
|
||||||
|
|
||||||
|
Keep Sergey happy
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
I have a [books wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority) on Amazon. If you feel brave, you can buy me a book!
|
||||||
@@ -13,7 +13,7 @@ def EventHandler(event, conn):
|
|||||||
if event == mongoose.HTTP_ERROR:
|
if event == mongoose.HTTP_ERROR:
|
||||||
conn.printf('%s', 'HTTP/1.0 200 OK\r\n')
|
conn.printf('%s', 'HTTP/1.0 200 OK\r\n')
|
||||||
conn.printf('%s', 'Content-Type: text/plain\r\n\r\n')
|
conn.printf('%s', 'Content-Type: text/plain\r\n\r\n')
|
||||||
conn.printf('HTTP error: %d\n', info.status_code)
|
conn.printf('HTTP error: %d\n', conn.reply_status_code)
|
||||||
return True
|
return True
|
||||||
elif event == mongoose.NEW_REQUEST and info.uri == '/show':
|
elif event == mongoose.NEW_REQUEST and info.uri == '/show':
|
||||||
conn.printf('%s', 'HTTP/1.0 200 OK\r\n')
|
conn.printf('%s', 'HTTP/1.0 200 OK\r\n')
|
||||||
|
|||||||
@@ -41,9 +41,14 @@ import os
|
|||||||
|
|
||||||
|
|
||||||
NEW_REQUEST = 0
|
NEW_REQUEST = 0
|
||||||
HTTP_ERROR = 1
|
REQUEST_COMPLETE = 1
|
||||||
EVENT_LOG = 2
|
HTTP_ERROR = 2
|
||||||
INIT_SSL = 3
|
EVENT_LOG = 3
|
||||||
|
INIT_SSL = 4
|
||||||
|
WEBSOCKET_CONNECT = 5
|
||||||
|
WEBSOCKET_READY = 6
|
||||||
|
WEBSOCKET_MESSAGE = 7
|
||||||
|
WEBSOCKET_CLOSE = 8
|
||||||
|
|
||||||
|
|
||||||
class mg_header(ctypes.Structure):
|
class mg_header(ctypes.Structure):
|
||||||
@@ -57,16 +62,13 @@ class mg_header(ctypes.Structure):
|
|||||||
class mg_request_info(ctypes.Structure):
|
class mg_request_info(ctypes.Structure):
|
||||||
"""A wrapper for struct mg_request_info."""
|
"""A wrapper for struct mg_request_info."""
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
('user_data', ctypes.c_char_p),
|
|
||||||
('request_method', ctypes.c_char_p),
|
('request_method', ctypes.c_char_p),
|
||||||
('uri', ctypes.c_char_p),
|
('uri', ctypes.c_char_p),
|
||||||
('http_version', ctypes.c_char_p),
|
('http_version', ctypes.c_char_p),
|
||||||
('query_string', ctypes.c_char_p),
|
('query_string', ctypes.c_char_p),
|
||||||
('remote_user', ctypes.c_char_p),
|
('remote_user', ctypes.c_char_p),
|
||||||
('log_message', ctypes.c_char_p),
|
|
||||||
('remote_ip', ctypes.c_long),
|
('remote_ip', ctypes.c_long),
|
||||||
('remote_port', ctypes.c_int),
|
('remote_port', ctypes.c_int),
|
||||||
('status_code', ctypes.c_int),
|
|
||||||
('is_ssl', ctypes.c_int),
|
('is_ssl', ctypes.c_int),
|
||||||
('num_headers', ctypes.c_int),
|
('num_headers', ctypes.c_int),
|
||||||
('http_headers', mg_header * 64),
|
('http_headers', mg_header * 64),
|
||||||
@@ -84,6 +86,10 @@ class Connection(object):
|
|||||||
self.m = mongoose
|
self.m = mongoose
|
||||||
self.conn = ctypes.c_void_p(connection)
|
self.conn = ctypes.c_void_p(connection)
|
||||||
self.info = self.m.dll.mg_get_request_info(self.conn).contents
|
self.info = self.m.dll.mg_get_request_info(self.conn).contents
|
||||||
|
self.user_data = self.m.dll.mg_get_user_data(self.conn)
|
||||||
|
self.log_message = self.m.dll.mg_get_log_message(self.conn)
|
||||||
|
self.reply_status_code = self.m.dll.mg_get_reply_status_code(self.conn)
|
||||||
|
self.ssl_context = self.m.dll.mg_get_ssl_context(self.conn)
|
||||||
|
|
||||||
def get_header(self, name):
|
def get_header(self, name):
|
||||||
val = self.m.dll.mg_get_header(self.conn, name)
|
val = self.m.dll.mg_get_header(self.conn, name)
|
||||||
@@ -117,9 +123,9 @@ class Mongoose(object):
|
|||||||
|
|
||||||
def __init__(self, callback, **kwargs):
|
def __init__(self, callback, **kwargs):
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
self.dll = ctypes.WinDLL('_mongoose.dll')
|
self.dll = ctypes.WinDLL('mongoose.dll')
|
||||||
else:
|
else:
|
||||||
self.dll = ctypes.CDLL('_mongoose.so')
|
self.dll = ctypes.CDLL('libmongoose.so')
|
||||||
|
|
||||||
self.dll.mg_start.restype = ctypes.c_void_p
|
self.dll.mg_start.restype = ctypes.c_void_p
|
||||||
self.dll.mg_modify_passwords_file.restype = ctypes.c_int
|
self.dll.mg_modify_passwords_file.restype = ctypes.c_int
|
||||||
@@ -131,6 +137,10 @@ class Mongoose(object):
|
|||||||
self.dll.mg_get_cookie.restype = ctypes.c_int
|
self.dll.mg_get_cookie.restype = ctypes.c_int
|
||||||
self.dll.mg_get_option.restype = ctypes.c_char_p
|
self.dll.mg_get_option.restype = ctypes.c_char_p
|
||||||
self.dll.mg_get_request_info.restype = ctypes.POINTER(mg_request_info)
|
self.dll.mg_get_request_info.restype = ctypes.POINTER(mg_request_info)
|
||||||
|
self.dll.mg_get_user_data.restype = ctypes.c_void_p
|
||||||
|
self.dll.mg_get_reply_status_code.restype = ctypes.c_int
|
||||||
|
self.dll.mg_get_log_message.restype = ctypes.c_char_p
|
||||||
|
self.dll.mg_get_ssl_context.restype = ctypes.c_void_p
|
||||||
|
|
||||||
if callback:
|
if callback:
|
||||||
# Create a closure that will be called by the shared library.
|
# Create a closure that will be called by the shared library.
|
||||||
|
|||||||
@@ -4,4 +4,7 @@ all:
|
|||||||
OS=`uname`; \
|
OS=`uname`; \
|
||||||
test "$$OS" = Linux && LIBS="-ldl" ; \
|
test "$$OS" = Linux && LIBS="-ldl" ; \
|
||||||
$(CC) $(CFLAGS) hello.c ../mongoose.c $$LIBS $(ADD) -o hello;
|
$(CC) $(CFLAGS) hello.c ../mongoose.c $$LIBS $(ADD) -o hello;
|
||||||
|
$(CC) $(CFLAGS) upload.c ../mongoose.c $$LIBS $(ADD) -o upload;
|
||||||
|
$(CC) $(CFLAGS) post.c ../mongoose.c $$LIBS $(ADD) -o post;
|
||||||
|
$(CC) $(CFLAGS) -DUSE_WEBSOCKET websocket.c ../mongoose.c $$LIBS $(ADD) -o websocket;
|
||||||
$(CC) $(CFLAGS) chat.c ../mongoose.c $$LIBS $(ADD) -o chat
|
$(CC) $(CFLAGS) chat.c ../mongoose.c $$LIBS $(ADD) -o chat
|
||||||
+4
-2
@@ -369,8 +369,10 @@ int main(void) {
|
|||||||
srand((unsigned) time(0));
|
srand((unsigned) time(0));
|
||||||
|
|
||||||
// Setup and start Mongoose
|
// Setup and start Mongoose
|
||||||
ctx = mg_start(&event_handler, NULL, options);
|
if ((ctx = mg_start(&event_handler, NULL, options)) == NULL) {
|
||||||
assert(ctx != NULL);
|
printf("%s\n", "Cannot start chat server, fatal exit");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
// Wait until enter is pressed, then exit
|
// Wait until enter is pressed, then exit
|
||||||
printf("Chat server started on ports %s, press enter to quit.\n",
|
printf("Chat server started on ports %s, press enter to quit.\n",
|
||||||
|
|||||||
+11
-4
@@ -16,6 +16,11 @@ chat.normalizeText = function(text) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
chat.refresh = function(data) {
|
chat.refresh = function(data) {
|
||||||
|
|
||||||
|
if (data === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$.each(data, function(index, entry) {
|
$.each(data, function(index, entry) {
|
||||||
var row = $('<div>').addClass('message-row').appendTo('#mml');
|
var row = $('<div>').addClass('message-row').appendTo('#mml');
|
||||||
var timestamp = (new Date(entry.timestamp * 1000)).toLocaleTimeString();
|
var timestamp = (new Date(entry.timestamp * 1000)).toLocaleTimeString();
|
||||||
@@ -42,7 +47,7 @@ chat.refresh = function(data) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
chat.getMessages = function() {
|
chat.getMessages = function(enter_loop) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
dataType: 'jsonp',
|
dataType: 'jsonp',
|
||||||
url: chat.backendUrl + '/ajax/get_messages',
|
url: chat.backendUrl + '/ajax/get_messages',
|
||||||
@@ -51,7 +56,9 @@ chat.getMessages = function() {
|
|||||||
error: function() {
|
error: function() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
window.setTimeout(chat.getMessages, chat.getMessagesIntervalMs);
|
if (enter_loop) {
|
||||||
|
window.setTimeout('chat.getMessages(true)', chat.getMessagesIntervalMs);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
chat.handleMenuItemClick = function(ev) {
|
chat.handleMenuItemClick = function(ev) {
|
||||||
@@ -81,7 +88,7 @@ chat.handleMessageInput = function(ev) {
|
|||||||
success: function(ev) {
|
success: function(ev) {
|
||||||
input.value = '';
|
input.value = '';
|
||||||
input.disabled = false;
|
input.disabled = false;
|
||||||
chat.getMessages();
|
chat.getMessages(false);
|
||||||
},
|
},
|
||||||
error: function(ev) {
|
error: function(ev) {
|
||||||
chat.showError('Error sending message');
|
chat.showError('Error sending message');
|
||||||
@@ -93,7 +100,7 @@ chat.handleMessageInput = function(ev) {
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('.menu-item').click(chat.handleMenuItemClick);
|
$('.menu-item').click(chat.handleMenuItemClick);
|
||||||
$('.message-input').keypress(chat.handleMessageInput);
|
$('.message-input').keypress(chat.handleMessageInput);
|
||||||
chat.getMessages();
|
chat.getMessages(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// vim:ts=2:sw=2:et
|
// vim:ts=2:sw=2:et
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static const char *html_form =
|
||||||
|
"<html><body>POST example."
|
||||||
|
"<form method=\"POST\" action=\"/handle_post_request\">"
|
||||||
|
"Input 1: <input type=\"text\" name=\"input_1\" /> <br/>"
|
||||||
|
"Input 2: <input type=\"text\" name=\"input_2\" /> <br/>"
|
||||||
|
"<input type=\"submit\" />"
|
||||||
|
"</form></body></html>";
|
||||||
|
|
||||||
|
static void *callback(enum mg_event event,
|
||||||
|
struct mg_connection *conn) {
|
||||||
|
const struct mg_request_info *ri = mg_get_request_info(conn);
|
||||||
|
|
||||||
|
if (event == MG_NEW_REQUEST) {
|
||||||
|
if (!strcmp(ri->uri, "/handle_post_request")) {
|
||||||
|
// User has submitted a form, show submitted data and a variable value
|
||||||
|
char post_data[1024],
|
||||||
|
input1[sizeof(post_data)], input2[sizeof(post_data)];
|
||||||
|
int post_data_len;
|
||||||
|
|
||||||
|
// Read POST data
|
||||||
|
post_data_len = mg_read(conn, post_data, sizeof(post_data));
|
||||||
|
|
||||||
|
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated
|
||||||
|
mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1));
|
||||||
|
mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2));
|
||||||
|
|
||||||
|
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||||
|
"Content-Type: text/plain\r\n\r\n"
|
||||||
|
"Submitted data: [%.*s]\n"
|
||||||
|
"Submitted data length: %d bytes\n"
|
||||||
|
"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 {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_context *ctx;
|
||||||
|
const char *options[] = {"listening_ports", "8080", NULL};
|
||||||
|
|
||||||
|
ctx = mg_start(&callback, NULL, options);
|
||||||
|
getchar(); // Wait until user hits "enter"
|
||||||
|
mg_stop(ctx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
// Copyright (c) 2004-2012 Sergey Lyubka
|
||||||
|
// This file is a part of mongoose project, http://github.com/valenok/mongoose
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
// Make sure that form has enctype="multipart/form-data" attribute
|
||||||
|
static const char *html_form =
|
||||||
|
"<html><body>Upload example."
|
||||||
|
"<form method=\"POST\" action=\"/handle_post_request\" "
|
||||||
|
" enctype=\"multipart/form-data\">"
|
||||||
|
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||||
|
"<input type=\"submit\" value=\"Upload\" />"
|
||||||
|
"</form></body></html>";
|
||||||
|
|
||||||
|
static const char *HTTP_500 = "HTTP/1.0 500 Server Error\r\n\r\n";
|
||||||
|
|
||||||
|
static void handle_file_upload(struct mg_connection *conn) {
|
||||||
|
const char *cl_header;
|
||||||
|
char post_data[16 * 1024], path[999], file_name[1024], mime_type[100],
|
||||||
|
buf[BUFSIZ], *eop, *s, *p;
|
||||||
|
FILE *fp;
|
||||||
|
long long int cl, written;
|
||||||
|
int fd, n, post_data_len;
|
||||||
|
|
||||||
|
// Figure out total content length. Return if it is not present or invalid.
|
||||||
|
cl_header = mg_get_header(conn, "Content-Length");
|
||||||
|
if (cl_header == NULL || (cl = strtoll(cl_header, NULL, 10)) <= 0) {
|
||||||
|
mg_printf(conn, "%s%s", HTTP_500, "Invalid Conent-Length");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the initial chunk into memory. This should be multipart POST data.
|
||||||
|
// Parse headers, where we should find file name and content-type.
|
||||||
|
post_data_len = mg_read(conn, post_data, sizeof(post_data));
|
||||||
|
file_name[0] = mime_type[0] = '\0';
|
||||||
|
for (s = p = post_data; p < &post_data[post_data_len]; p++) {
|
||||||
|
if (p[0] == '\r' && p[1] == '\n') {
|
||||||
|
if (s == p) {
|
||||||
|
p += 2;
|
||||||
|
break; // End of headers
|
||||||
|
}
|
||||||
|
p[0] = p[1] = '\0';
|
||||||
|
sscanf(s, "Content-Type: %99s", mime_type);
|
||||||
|
// TODO(lsm): don't expect filename to be the 3rd field,
|
||||||
|
// parse the header properly instead.
|
||||||
|
sscanf(s, "Content-Disposition: %*s %*s filename=\"%1023[^\"]",
|
||||||
|
file_name);
|
||||||
|
s = p + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finished parsing headers. Now "p" points to the first byte of data.
|
||||||
|
// Calculate file size
|
||||||
|
cl -= p - post_data; // Subtract headers size
|
||||||
|
cl -= strlen(post_data); // Subtract the boundary marker at the end
|
||||||
|
cl -= 6; // Subtract "\r\n" before and after boundary
|
||||||
|
|
||||||
|
// Construct destination file name. Write to /tmp, do not allow
|
||||||
|
// paths that contain slashes.
|
||||||
|
if ((s = strrchr(file_name, '/')) == NULL) {
|
||||||
|
s = file_name;
|
||||||
|
}
|
||||||
|
snprintf(path, sizeof(path), "/tmp/%s", s);
|
||||||
|
|
||||||
|
if (file_name[0] == '\0') {
|
||||||
|
mg_printf(conn, "%s%s", HTTP_500, "Can't get file name");
|
||||||
|
} else if (cl <= 0) {
|
||||||
|
mg_printf(conn, "%s%s", HTTP_500, "Empty file");
|
||||||
|
} else if ((fd = open(path, O_CREAT | O_TRUNC |
|
||||||
|
O_WRONLY | O_EXLOCK | O_CLOEXEC)) < 0) {
|
||||||
|
// We're opening the file with exclusive lock held. This guarantee us that
|
||||||
|
// there is no other thread can save into the same file simultaneously.
|
||||||
|
mg_printf(conn, "%s%s", HTTP_500, "Cannot open file");
|
||||||
|
} else if ((fp = fdopen(fd, "w")) == NULL) {
|
||||||
|
mg_printf(conn, "%s%s", HTTP_500, "Cannot reopen file stream");
|
||||||
|
close(fd);
|
||||||
|
} else {
|
||||||
|
// Success. Write data into the file.
|
||||||
|
eop = post_data + post_data_len;
|
||||||
|
n = p + cl > eop ? (int) (eop - p) : (int) cl;
|
||||||
|
(void) fwrite(p, 1, n, fp);
|
||||||
|
written = n;
|
||||||
|
while (written < cl &&
|
||||||
|
(n = mg_read(conn, buf, cl - written > (long long) sizeof(buf) ?
|
||||||
|
sizeof(buf) : cl - written)) > 0) {
|
||||||
|
(void) fwrite(buf, 1, n, fp);
|
||||||
|
written += n;
|
||||||
|
}
|
||||||
|
(void) fclose(fp);
|
||||||
|
mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n"
|
||||||
|
"Saved to [%s], written %llu bytes", path, cl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *callback(enum mg_event event, struct mg_connection *conn) {
|
||||||
|
const struct mg_request_info *ri = mg_get_request_info(conn);
|
||||||
|
|
||||||
|
if (event == MG_NEW_REQUEST) {
|
||||||
|
if (!strcmp(ri->uri, "/handle_post_request")) {
|
||||||
|
handle_file_upload(conn);
|
||||||
|
} 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 {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_context *ctx;
|
||||||
|
const char *options[] = {"listening_ports", "8080", NULL};
|
||||||
|
|
||||||
|
ctx = mg_start(&callback, NULL, options);
|
||||||
|
getchar(); // Wait until user hits "enter"
|
||||||
|
mg_stop(ctx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
// Copyright (c) 2004-2012 Sergey Lyubka
|
||||||
|
// This file is a part of mongoose project, http://github.com/valenok/mongoose
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static void *callback(enum mg_event event, struct mg_connection *conn) {
|
||||||
|
if (event == MG_WEBSOCKET_READY) {
|
||||||
|
unsigned char buf[40];
|
||||||
|
buf[0] = 0x81;
|
||||||
|
buf[1] = snprintf((char *) buf + 2, sizeof(buf) - 2, "%s", "server ready");
|
||||||
|
mg_write(conn, buf, 2 + buf[1]);
|
||||||
|
return ""; // MG_WEBSOCKET_READY return value is ignored
|
||||||
|
} else if (event == MG_WEBSOCKET_MESSAGE) {
|
||||||
|
unsigned char buf[200], reply[200];
|
||||||
|
int n, i, mask_len, xor, msg_len, len;
|
||||||
|
|
||||||
|
// Read message from the client.
|
||||||
|
// Accept only small (<126 bytes) messages.
|
||||||
|
len = 0;
|
||||||
|
msg_len = mask_len = 0;
|
||||||
|
for (;;) {
|
||||||
|
if ((n = mg_read(conn, buf + len, sizeof(buf) - len)) <= 0) {
|
||||||
|
return ""; // 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 ""; // Message is too long, close websocket
|
||||||
|
}
|
||||||
|
// If we've buffered the whole message, exit the loop
|
||||||
|
if (len >= 2 + mask_len + msg_len) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare frame
|
||||||
|
reply[0] = 0x81; // text, FIN set
|
||||||
|
reply[1] = msg_len;
|
||||||
|
|
||||||
|
// Copy message from request to reply, applying the mask if required.
|
||||||
|
for (i = 0; i < msg_len; i++) {
|
||||||
|
xor = mask_len == 0 ? 0 : buf[2 + (i % 4)];
|
||||||
|
reply[i + 2] = buf[i + 2 + mask_len] ^ xor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Echo the message back to the client
|
||||||
|
mg_write(conn, reply, 2 + msg_len);
|
||||||
|
|
||||||
|
// Return non-NULL means stoping websocket conversation.
|
||||||
|
// Close the conversation if client has sent us "exit" string.
|
||||||
|
return memcmp(reply + 2, "exit", 4) == 0 ? "" : NULL;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_context *ctx;
|
||||||
|
const char *options[] = {
|
||||||
|
"listening_ports", "8080",
|
||||||
|
"document_root", "websocket_html_root",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx = mg_start(&callback, NULL, options);
|
||||||
|
getchar(); // Wait until user hits "enter"
|
||||||
|
mg_stop(ctx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>WebSocket Test</title>
|
||||||
|
<script language="javascript" type="text/javascript">
|
||||||
|
|
||||||
|
var writeToScreen = function(message) {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.innerHTML = message;
|
||||||
|
document.getElementById('output').appendChild(div);
|
||||||
|
};
|
||||||
|
window.onload = function() {
|
||||||
|
var url = 'ws://' + window.location.host + '/foo';
|
||||||
|
websocket = new WebSocket(url);
|
||||||
|
websocket.onopen = function(ev) {
|
||||||
|
writeToScreen('CONNECTED');
|
||||||
|
var message = 'Не всё подчиняется разуму. Но всё подчиняется упорству. ';
|
||||||
|
writeToScreen('SENT: ' + message);
|
||||||
|
websocket.send(message);
|
||||||
|
};
|
||||||
|
websocket.onclose = function(ev) {
|
||||||
|
writeToScreen('DISCONNECTED');
|
||||||
|
};
|
||||||
|
websocket.onmessage = function(ev) {
|
||||||
|
writeToScreen('<span style="color: blue;">RESPONSE: ' + ev.data +
|
||||||
|
' </span>');
|
||||||
|
websocket.send('exit');
|
||||||
|
};
|
||||||
|
websocket.onerror = function(ev) {
|
||||||
|
writeToScreen('<span style="color: red; ">ERROR: </span> ' + ev.data);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style> div {font: small Verdana; } </style>
|
||||||
|
<h2>Mongoose WebSocket Test</h2>
|
||||||
|
|
||||||
|
<div style="width: 400px; color: #aaa; padding: 1em; ">
|
||||||
|
This page code creates websocket to the URI "/foo",
|
||||||
|
sends a message to it, waits for the reply, then sends an "exit" message.
|
||||||
|
Server must echo all messages back, and terminate the conversation after
|
||||||
|
receiving the "exit" message.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="output"></div>
|
||||||
|
</html>
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
@@ -191,7 +192,8 @@ static void process_command_line_arguments(char *argv[], char **options) {
|
|||||||
line_no++;
|
line_no++;
|
||||||
|
|
||||||
// Ignore empty lines and comments
|
// Ignore empty lines and comments
|
||||||
if (line[0] == '#' || line[0] == '\n')
|
for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
|
||||||
|
if (line[i] == '#' || line[i] == '\0')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
|
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
|
||||||
@@ -203,7 +205,7 @@ static void process_command_line_arguments(char *argv[], char **options) {
|
|||||||
(void) fclose(fp);
|
(void) fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now handle command line flags. They override config file settings.
|
// Handle command line flags. They override config file and default settings.
|
||||||
for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
|
for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
|
||||||
if (argv[i][0] != '-' || argv[i + 1] == NULL) {
|
if (argv[i][0] != '-' || argv[i + 1] == NULL) {
|
||||||
show_usage_and_exit();
|
show_usage_and_exit();
|
||||||
@@ -219,9 +221,11 @@ static void init_server_name(void) {
|
|||||||
|
|
||||||
static void *mongoose_callback(enum mg_event ev, struct mg_connection *conn) {
|
static void *mongoose_callback(enum mg_event ev, struct mg_connection *conn) {
|
||||||
if (ev == MG_EVENT_LOG) {
|
if (ev == MG_EVENT_LOG) {
|
||||||
printf("%s\n", mg_get_request_info(conn)->log_message);
|
printf("%s\n", mg_get_log_message(conn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returning NULL marks request as not handled, signalling mongoose to
|
||||||
|
// proceed with handling it.
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,6 +487,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show) {
|
|||||||
DispatchMessage(&msg);
|
DispatchMessage(&msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the WM_QUIT value.
|
||||||
return msg.wParam;
|
return msg.wParam;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|||||||
+13
-3
@@ -1,7 +1,7 @@
|
|||||||
.\" Process this file with
|
.\" Process this file with
|
||||||
.\" groff -man -Tascii mongoose.1
|
.\" groff -man -Tascii mongoose.1
|
||||||
.\" $Id: mongoose.1,v 1.12 2008/11/29 15:32:42 drozd Exp $
|
.\" $Id: mongoose.1,v 1.12 2008/11/29 15:32:42 drozd Exp $
|
||||||
.Dd Aug 31, 2010
|
.Dd Sep 23, 2012
|
||||||
.Dt mongoose 1
|
.Dt mongoose 1
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
.Nm mongoose
|
.Nm mongoose
|
||||||
@@ -82,8 +82,6 @@ Use
|
|||||||
as a CGI interpreter for all CGI scripts regardless script extension.
|
as a CGI interpreter for all CGI scripts regardless script extension.
|
||||||
Mongoose decides which interpreter to use by looking at
|
Mongoose decides which interpreter to use by looking at
|
||||||
the first line of a CGI script. Default: "".
|
the first line of a CGI script. Default: "".
|
||||||
.It Fl M Ar max_request_size
|
|
||||||
Maximum HTTP request size in bytes. Default: "16384"
|
|
||||||
.It Fl P Ar protect_uri
|
.It Fl P Ar protect_uri
|
||||||
Comma separated list of URI=PATH pairs, specifying that given URIs
|
Comma separated list of URI=PATH pairs, specifying that given URIs
|
||||||
must be protected with respected password files. Default: ""
|
must be protected with respected password files. Default: ""
|
||||||
@@ -93,6 +91,18 @@ Authorization realm. Default: "mydomain.com"
|
|||||||
All files that fully match ssi_pattern are treated as SSI.
|
All files that fully match ssi_pattern are treated as SSI.
|
||||||
Unknown SSI directives are silently ignored. Currently, two SSI directives
|
Unknown SSI directives are silently ignored. Currently, two SSI directives
|
||||||
are supported, "include" and "exec". Default: "**.shtml$|**.shtm$"
|
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
|
.It Fl a Ar access_log_file
|
||||||
Access log file. Default: "", no logging is done.
|
Access log file. Default: "", no logging is done.
|
||||||
.It Fl d Ar enable_directory_listing
|
.It Fl d Ar enable_directory_listing
|
||||||
|
|||||||
+696
-286
File diff suppressed because it is too large.
Load diff
+55
-27
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2004-2011 Sergey Lyubka
|
// Copyright (c) 2004-2012 Sergey Lyubka
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -34,16 +34,13 @@ struct mg_connection; // Handle for the individual connection
|
|||||||
|
|
||||||
// This structure contains information about the HTTP request.
|
// This structure contains information about the HTTP request.
|
||||||
struct mg_request_info {
|
struct mg_request_info {
|
||||||
void *user_data; // User-defined pointer passed to mg_start()
|
|
||||||
char *request_method; // "GET", "POST", etc
|
char *request_method; // "GET", "POST", etc
|
||||||
char *uri; // URL-decoded URI
|
char *uri; // URL-decoded URI
|
||||||
char *http_version; // E.g. "1.0", "1.1"
|
char *http_version; // E.g. "1.0", "1.1"
|
||||||
char *query_string; // URL part after '?' (not including '?') or NULL
|
char *query_string; // URL part after '?' (not including '?') or NULL
|
||||||
char *remote_user; // Authenticated user, or NULL if no auth used
|
char *remote_user; // Authenticated user, or NULL if no auth used
|
||||||
char *log_message; // Mongoose error log message, MG_EVENT_LOG only
|
|
||||||
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 status_code; // HTTP reply status code, e.g. 200
|
|
||||||
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
|
int num_headers; // Number of headers
|
||||||
struct mg_header {
|
struct mg_header {
|
||||||
@@ -52,16 +49,23 @@ struct mg_request_info {
|
|||||||
} http_headers[64]; // Maximum 64 headers
|
} http_headers[64]; // Maximum 64 headers
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Various events on which user-defined function is called by Mongoose.
|
// Various events on which user-defined function is called by Mongoose.
|
||||||
enum mg_event {
|
enum mg_event {
|
||||||
MG_NEW_REQUEST, // New HTTP request has arrived from the client
|
MG_NEW_REQUEST, // New HTTP request has arrived from the client
|
||||||
|
MG_REQUEST_COMPLETE, // Mongoose has finished handling the request
|
||||||
MG_HTTP_ERROR, // HTTP error must be returned to the client
|
MG_HTTP_ERROR, // HTTP error must be returned to the client
|
||||||
MG_EVENT_LOG, // Mongoose logs an event, request_info.log_message
|
MG_EVENT_LOG, // Mongoose logs an event, request_info.log_message
|
||||||
MG_INIT_SSL, // Mongoose initializes SSL. Instead of mg_connection *,
|
MG_INIT_SSL, // SSL initialization, sent before certificate setup
|
||||||
// SSL context is passed to the callback function.
|
MG_WEBSOCKET_CONNECT, // Sent on HTTP connect, before websocket handshake.
|
||||||
MG_REQUEST_COMPLETE // Mongoose has finished handling the request
|
// If user callback returns NULL, then mongoose proceeds
|
||||||
|
// with handshake, otherwise it closes the connection.
|
||||||
|
MG_WEBSOCKET_READY, // Handshake has been successfully completed.
|
||||||
|
MG_WEBSOCKET_MESSAGE, // Incoming message from the client
|
||||||
|
MG_WEBSOCKET_CLOSE, // Client has closed the connection
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Prototype for the user-defined function. Mongoose calls this function
|
// Prototype for the user-defined function. Mongoose calls this function
|
||||||
// on every MG_* event.
|
// on every MG_* event.
|
||||||
//
|
//
|
||||||
@@ -77,8 +81,7 @@ enum mg_event {
|
|||||||
// If handler returns NULL, that means that handler has not processed
|
// 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.
|
// the request. Handler must not send any data to the client in this case.
|
||||||
// Mongoose proceeds with request handling as if nothing happened.
|
// Mongoose proceeds with request handling as if nothing happened.
|
||||||
typedef void * (*mg_callback_t)(enum mg_event event,
|
typedef void *(*mg_callback_t)(enum mg_event event, struct mg_connection *conn);
|
||||||
struct mg_connection *conn);
|
|
||||||
|
|
||||||
|
|
||||||
// Start web server.
|
// Start web server.
|
||||||
@@ -151,26 +154,49 @@ int mg_modify_passwords_file(const char *passwords_file_name,
|
|||||||
const char *password);
|
const char *password);
|
||||||
|
|
||||||
|
|
||||||
// Return mg_request_info structure associated with the request.
|
// Return information associated with the request.
|
||||||
// Always succeeds.
|
// These functions always succeed.
|
||||||
const struct mg_request_info *mg_get_request_info(struct mg_connection *);
|
const struct mg_request_info *mg_get_request_info(const struct mg_connection *);
|
||||||
|
void *mg_get_user_data(struct mg_connection *);
|
||||||
|
const char *mg_get_log_message(const struct mg_connection *);
|
||||||
|
int mg_get_reply_status_code(const struct mg_connection *);
|
||||||
|
void *mg_get_ssl_context(const struct mg_connection *);
|
||||||
|
|
||||||
|
|
||||||
// Send data to the client.
|
// Send data to the client.
|
||||||
|
// Return:
|
||||||
|
// 0 when the connection has been closed
|
||||||
|
// -1 on error
|
||||||
|
// number of bytes written on success
|
||||||
int mg_write(struct mg_connection *, const void *buf, size_t len);
|
int mg_write(struct mg_connection *, const void *buf, size_t len);
|
||||||
|
|
||||||
|
|
||||||
// Send data to the browser using printf() semantics.
|
// Send data to the browser using printf() semantics.
|
||||||
//
|
//
|
||||||
// Works exactly like mg_write(), but allows to do message formatting.
|
// Works exactly like mg_write(), but allows to do message formatting.
|
||||||
// Note that mg_printf() uses internal buffer of size IO_BUF_SIZE
|
// Below are the macros for enabling compiler-specific checks for
|
||||||
// (8 Kb by default) as temporary message storage for formatting. Do not
|
// printf-like arguments.
|
||||||
// print data that is bigger than that, otherwise it will be truncated.
|
|
||||||
int mg_printf(struct mg_connection *, const char *fmt, ...)
|
#undef PRINTF_FORMAT_STRING
|
||||||
#ifdef __GNUC__
|
#if _MSC_VER >= 1400
|
||||||
__attribute__((format(printf, 2, 3)))
|
#include <sal.h>
|
||||||
|
#if _MSC_VER > 1400
|
||||||
|
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
|
||||||
|
#else
|
||||||
|
#define PRINTF_FORMAT_STRING(s) __format_string s
|
||||||
#endif
|
#endif
|
||||||
;
|
#else
|
||||||
|
#define PRINTF_FORMAT_STRING(s) s
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y)))
|
||||||
|
#else
|
||||||
|
#define PRINTF_ARGS(x, y)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int mg_printf(struct mg_connection *,
|
||||||
|
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
|
||||||
|
|
||||||
|
|
||||||
// Send contents of the entire file together with HTTP headers.
|
// Send contents of the entire file together with HTTP headers.
|
||||||
@@ -201,10 +227,12 @@ const char *mg_get_header(const struct mg_connection *, const char *name);
|
|||||||
//
|
//
|
||||||
// Return:
|
// Return:
|
||||||
// On success, length of the decoded variable.
|
// On success, length of the decoded variable.
|
||||||
// On error, -1 (variable not found, or destination buffer is too small).
|
// On error:
|
||||||
|
// -1 (variable not found, or destination buffer is too small).
|
||||||
|
// -2 (destination buffer is NULL or zero length).
|
||||||
//
|
//
|
||||||
// Destination buffer is guaranteed to be '\0' - terminated. In case of
|
// Destination buffer is guaranteed to be '\0' - terminated if it is not
|
||||||
// failure, dst[0] == '\0'.
|
// NULL or zero length. In case of failure, dst[0] == '\0'.
|
||||||
int mg_get_var(const char *data, size_t data_len,
|
int mg_get_var(const char *data, size_t data_len,
|
||||||
const char *var_name, char *buf, size_t buf_len);
|
const char *var_name, char *buf, size_t buf_len);
|
||||||
|
|
||||||
@@ -216,7 +244,7 @@ int mg_get_var(const char *data, size_t data_len,
|
|||||||
//
|
//
|
||||||
// Return:
|
// Return:
|
||||||
// On success, value length.
|
// On success, value length.
|
||||||
// On error, 0 (either "Cookie:" header is not present at all, or the
|
// On error, -1 (either "Cookie:" header is not present at all, or the
|
||||||
// requested parameter is not found, or destination buffer is too small
|
// requested parameter is not found, or destination buffer is too small
|
||||||
// to hold the value).
|
// to hold the value).
|
||||||
int mg_get_cookie(const struct mg_connection *,
|
int mg_get_cookie(const struct mg_connection *,
|
||||||
@@ -243,8 +271,8 @@ void mg_close_connection(struct mg_connection *conn);
|
|||||||
// Return:
|
// Return:
|
||||||
// On error, NULL
|
// On error, NULL
|
||||||
// On success, opened file stream to the downloaded contents. The stream
|
// On success, opened file stream to the downloaded contents. The stream
|
||||||
// is positioned to the end of the file. It is a user responsibility
|
// is positioned to the end of the file. It is the user's responsibility
|
||||||
// to fclose() opened file stream.
|
// to fclose() the opened file stream.
|
||||||
FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
|
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);
|
char *buf, size_t buf_len, struct mg_request_info *request_info);
|
||||||
|
|
||||||
@@ -266,11 +294,11 @@ const char *mg_version(void);
|
|||||||
|
|
||||||
// MD5 hash given strings.
|
// MD5 hash given strings.
|
||||||
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
|
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
|
||||||
// asciiz strings. When function returns, buf will contain human-readable
|
// ASCIIz strings. When function returns, buf will contain human-readable
|
||||||
// MD5 hash. Example:
|
// MD5 hash. Example:
|
||||||
// char buf[33];
|
// char buf[33];
|
||||||
// mg_md5(buf, "aa", "bb", NULL);
|
// mg_md5(buf, "aa", "bb", NULL);
|
||||||
void mg_md5(char *buf, ...);
|
void mg_md5(char buf[33], ...);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
blah
|
|
||||||
+3
-2
@@ -119,9 +119,10 @@ static void test_get_request_info(struct mg_connection *conn,
|
|||||||
|
|
||||||
static void test_error(struct mg_connection *conn,
|
static void test_error(struct mg_connection *conn,
|
||||||
const struct mg_request_info *ri) {
|
const struct mg_request_info *ri) {
|
||||||
|
(void) ri;
|
||||||
mg_printf(conn, "HTTP/1.1 %d XX\r\n"
|
mg_printf(conn, "HTTP/1.1 %d XX\r\n"
|
||||||
"Conntection: close\r\n\r\n", ri->status_code);
|
"Conntection: close\r\n\r\n", mg_get_reply_status_code(conn));
|
||||||
mg_printf(conn, "Error: [%d]", ri->status_code);
|
mg_printf(conn, "Error: [%d]", mg_get_reply_status_code(conn));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_post(struct mg_connection *conn,
|
static void test_post(struct mg_connection *conn,
|
||||||
|
|||||||
@@ -215,8 +215,6 @@ $num_requests++;
|
|||||||
write_file("$root/a+.txt", '');
|
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');
|
||||||
|
|
||||||
o("GET /%5c/a.txt HTTP/1.0\n\n", 'blah', 'GET dir backslash');
|
|
||||||
|
|
||||||
# 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", '400 Bad Request', '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", '505 HTTP', 'Bad HTTP maj Version');
|
||||||
|
|||||||
+71
-8
@@ -1,3 +1,4 @@
|
|||||||
|
#define USE_WEBSOCKET
|
||||||
#include "mongoose.c"
|
#include "mongoose.c"
|
||||||
|
|
||||||
#define FATAL(str, line) do { \
|
#define FATAL(str, line) do { \
|
||||||
@@ -64,10 +65,10 @@ static void test_should_keep_alive(void) {
|
|||||||
parse_http_request(req4, sizeof(req4), &conn.request_info);
|
parse_http_request(req4, sizeof(req4), &conn.request_info);
|
||||||
ASSERT(should_keep_alive(&conn) == 1);
|
ASSERT(should_keep_alive(&conn) == 1);
|
||||||
|
|
||||||
conn.request_info.status_code = 401;
|
conn.status_code = 401;
|
||||||
ASSERT(should_keep_alive(&conn) == 0);
|
ASSERT(should_keep_alive(&conn) == 0);
|
||||||
|
|
||||||
conn.request_info.status_code = 200;
|
conn.status_code = 200;
|
||||||
conn.must_close = 1;
|
conn.must_close = 1;
|
||||||
ASSERT(should_keep_alive(&conn) == 0);
|
ASSERT(should_keep_alive(&conn) == 0);
|
||||||
}
|
}
|
||||||
@@ -111,17 +112,14 @@ static void test_remove_double_dots() {
|
|||||||
{"/...///", "/./"},
|
{"/...///", "/./"},
|
||||||
{"/a...///", "/a.../"},
|
{"/a...///", "/a.../"},
|
||||||
{"/.x", "/.x"},
|
{"/.x", "/.x"},
|
||||||
#if defined(_WIN32)
|
|
||||||
{"/\\", "/"},
|
{"/\\", "/"},
|
||||||
#else
|
|
||||||
{"/\\", "/\\"},
|
|
||||||
#endif
|
|
||||||
{"/a\\", "/a\\"},
|
{"/a\\", "/a\\"},
|
||||||
|
{"/a\\\\...", "/a\\."},
|
||||||
};
|
};
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(data); i++) {
|
for (i = 0; i < ARRAY_SIZE(data); i++) {
|
||||||
//printf("[%s] -> [%s]\n", data[i].before, data[i].after);
|
printf("[%s] -> [%s]\n", data[i].before, data[i].after);
|
||||||
remove_double_dots_and_double_slashes(data[i].before);
|
remove_double_dots_and_double_slashes(data[i].before);
|
||||||
ASSERT(strcmp(data[i].before, data[i].after) == 0);
|
ASSERT(strcmp(data[i].before, data[i].after) == 0);
|
||||||
}
|
}
|
||||||
@@ -138,7 +136,7 @@ static void *event_handler(enum mg_event event,
|
|||||||
"%s", (int) strlen(fetch_data), fetch_data);
|
"%s", (int) strlen(fetch_data), fetch_data);
|
||||||
return "";
|
return "";
|
||||||
} else if (event == MG_EVENT_LOG) {
|
} else if (event == MG_EVENT_LOG) {
|
||||||
printf("%s\n", request_info->log_message);
|
printf("%s\n", mg_get_log_message(conn));
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -196,11 +194,76 @@ static void test_mg_fetch(void) {
|
|||||||
mg_stop(ctx);
|
mg_stop(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_base64_encode(void) {
|
||||||
|
const char *in[] = {"a", "ab", "abc", "abcd", NULL};
|
||||||
|
const char *out[] = {"YQ==", "YWI=", "YWJj", "YWJjZA=="};
|
||||||
|
char buf[100];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; in[i] != NULL; i++) {
|
||||||
|
base64_encode((unsigned char *) in[i], strlen(in[i]), buf);
|
||||||
|
printf("[%s] [%s]\n", out[i], buf);
|
||||||
|
ASSERT(!strcmp(buf, out[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_mg_get_var(void) {
|
||||||
|
static const char *post[] = {"a=1&&b=2&d&=&c=3%20&e=", NULL};
|
||||||
|
char buf[20];
|
||||||
|
|
||||||
|
ASSERT(mg_get_var(post[0], strlen(post[0]), "a", buf, sizeof(buf)) == 1);
|
||||||
|
ASSERT(buf[0] == '1' && buf[1] == '\0');
|
||||||
|
ASSERT(mg_get_var(post[0], strlen(post[0]), "b", buf, sizeof(buf)) == 1);
|
||||||
|
ASSERT(buf[0] == '2' && buf[1] == '\0');
|
||||||
|
ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, sizeof(buf)) == 2);
|
||||||
|
ASSERT(buf[0] == '3' && buf[1] == ' ' && buf[2] == '\0');
|
||||||
|
ASSERT(mg_get_var(post[0], strlen(post[0]), "e", buf, sizeof(buf)) == 0);
|
||||||
|
ASSERT(buf[0] == '\0');
|
||||||
|
|
||||||
|
ASSERT(mg_get_var(post[0], strlen(post[0]), "d", buf, sizeof(buf)) == -1);
|
||||||
|
ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, 2) == -1);
|
||||||
|
|
||||||
|
ASSERT(mg_get_var(post[0], strlen(post[0]), "x", NULL, 10) == -2);
|
||||||
|
ASSERT(mg_get_var(post[0], strlen(post[0]), "x", buf, 0) == -2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_set_throttle(void) {
|
||||||
|
ASSERT(set_throttle(NULL, 0x0a000001, "/") == 0);
|
||||||
|
ASSERT(set_throttle("10.0.0.0/8=20", 0x0a000001, "/") == 20);
|
||||||
|
ASSERT(set_throttle("10.0.0.0/8=0.5k", 0x0a000001, "/") == 512);
|
||||||
|
ASSERT(set_throttle("10.0.0.0/8=17m", 0x0a000001, "/") == 1048576 * 17);
|
||||||
|
ASSERT(set_throttle("10.0.0.0/8=1x", 0x0a000001, "/") == 0);
|
||||||
|
ASSERT(set_throttle("10.0.0.0/8=5,0.0.0.0/0=10", 0x0a000001, "/") == 10);
|
||||||
|
ASSERT(set_throttle("10.0.0.0/8=5,/foo/**=7", 0x0a000001, "/index") == 5);
|
||||||
|
ASSERT(set_throttle("10.0.0.0/8=5,/foo/**=7", 0x0a000001, "/foo/x") == 7);
|
||||||
|
ASSERT(set_throttle("10.0.0.0/8=5,/foo/**=7", 0x0b000001, "/foxo/x") == 0);
|
||||||
|
ASSERT(set_throttle("10.0.0.0/8=5,*=1", 0x0b000001, "/foxo/x") == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_next_option(void) {
|
||||||
|
const char *p, *list = "x/8,/y**=1;2k,z";
|
||||||
|
struct vec a, b;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ASSERT(next_option(NULL, &a, &b) == NULL);
|
||||||
|
for (i = 0, p = list; (p = next_option(p, &a, &b)) != NULL; i++) {
|
||||||
|
ASSERT(i != 0 || (a.ptr == list && a.len == 3 && b.len == 0));
|
||||||
|
ASSERT(i != 1 || (a.ptr == list + 4 && a.len == 4 && b.ptr == list + 9 &&
|
||||||
|
b.len == 4));
|
||||||
|
|
||||||
|
ASSERT(i != 2 || (a.ptr == list + 14 && a.len == 1 && b.len == 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
test_base64_encode();
|
||||||
test_match_prefix();
|
test_match_prefix();
|
||||||
test_remove_double_dots();
|
test_remove_double_dots();
|
||||||
test_should_keep_alive();
|
test_should_keep_alive();
|
||||||
test_parse_http_request();
|
test_parse_http_request();
|
||||||
test_mg_fetch();
|
test_mg_fetch();
|
||||||
|
test_mg_get_var();
|
||||||
|
test_set_throttle();
|
||||||
|
test_next_option();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<html>
|
||||||
|
<form method="post">
|
||||||
|
<input name="x" type="text" />
|
||||||
|
<input type="submit" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<? echo $_POST["x"]; ?>
|
||||||
|
|
||||||
|
</html>
|
||||||
Reference in new issue
Block a user