Compare commits

..
62 Commits
Author SHA1 Message Date
Sergey Lyubka 13d0b67c00 More updates to User Manual 2013-02-05 18:00:49 +00:00
Sergey Lyubka bda8e90733 Added protection against running under launchd 2013-02-05 17:59:56 +00:00
Sergey Lyubka fed2057a33 Added comment 2013-02-05 17:59:16 +00:00
Sergey Lyubka a27f62f89f More options 2013-02-05 14:04:38 +00:00
Sergey Lyubka d53902844c Squashed warning for lua+sqlite code path 2013-02-04 22:14:42 +00:00
Sergey Lyubka 9870b80c65 Updated README 2013-02-04 20:18:07 +00:00
Sergey Lyubka cde25a8b82 Removed embedded test 2013-02-03 19:56:24 +00:00
Sergey Lyubka 1b7f036199 Removed embedded test 2013-02-03 19:56:16 +00:00
Sergey Lyubka b80157b7ea Setting user data pointer properly 2013-02-03 19:15:27 +00:00
Sergey Lyubka f9311f0b30 Building cocoa version with lua + sqlite 2013-02-02 16:42:01 +00:00
Sergey Lyubka bdd933f597 Fixed UNIX code path for handle_lsp_request() 2013-02-02 16:41:43 +00:00
Sergey Lyubka ee55d38b55 API CHANGE: using struct mg_callbacks 2013-02-01 16:48:30 +00:00
Sergey Lyubka 62162ff3e4 Added /DNO_SSL_DL to w target 2013-02-01 13:53:31 +00:00
Sergey Lyubka 98f66c8867 Updated User Manual 2013-01-31 22:33:08 +00:00
Sergey Lyubka c132191cad Prepare to change the API to use explicit callbacks 2013-01-31 18:29:22 +00:00
Sergey Lyubka d73aa1ad09 Added skeleton for regex checks 2013-01-31 18:06:05 +00:00
Sergey Lyubka d5bc58c38d Using sqlite as an example 2013-01-31 15:17:40 +00:00
Sergey Lyubka 51a80e4844 Do not output headers when serving Lua server pages 2013-01-31 15:17:19 +00:00
Sergey Lyubka 54f15a8fe3 Adding lua+sqlite functionality in the default windows binary build 2013-01-31 13:38:57 +00:00
Sergey Lyubka ce0826e285 Adding lua+sqlite functionality in the default windows binary build 2013-01-31 13:38:32 +00:00
Sergey Lyubka b21a8b2193 Removed wait_until_socket_is_readable(). Rely on socket timeout. 2013-01-29 11:21:23 +00:00
Sergey Lyubka 8bb49642da Merge pull request #86 from thekvs/master
I've just fixed a typo.
2013-01-28 12:06:28 -08:00
Konstantin Sorokin 3f78df86ac fixed typo 2013-01-28 18:49:19 +04:00
Sergey Lyubka c30f7a5258 Merge pull request #77 from bamiaux/add_request_timeout
Add request_timeout option
2013-01-28 03:13:28 -08:00
Sergey Lyubka 7ed94d1918 Unit test call fixed 2013-01-27 20:49:30 +00:00
Sergey Lyubka dc513a9a15 Wrapped SSL code into #ifndef NO_SSL 2013-01-27 20:49:17 +00:00
Sergey Lyubka 8ce6cf61b7 Using relative path to yassl 2013-01-27 20:48:52 +00:00
Sergey Lyubka e63f7fe33a Always initializing struct file on stack 2013-01-25 21:50:00 +00:00
Sergey Lyubka b589e0cdf4 Fixed mg_upload() and some SSL related issues 2013-01-25 20:28:09 +00:00
Sergey Lyubka 2b87f909ce Makefile refactored 2013-01-25 20:27:52 +00:00
Sergey Lyubka ec7d0b1abd Added test_mg_upload() 2013-01-25 20:27:08 +00:00
Sergey Lyubka 5bb3ede46d Removed mongoose.1 2013-01-25 13:21:19 +00:00
Sergey Lyubka 5105717f78 Added printf checkers for mg_download 2013-01-24 09:35:54 +00:00
Sergey Lyubka 9d1e356709 changed README 2013-01-24 09:35:11 +00:00
Sergey Lyubka 3d7192d9fa Typo in mongoose.h fixed 2013-01-24 01:16:23 +00:00
Sergey Lyubka 1b3cbb17c1 Updated README 2013-01-23 23:48:07 +00:00
Sergey Lyubka f1f03e4378 Updated README 2013-01-23 23:43:59 +00:00
Sergey Lyubka 625a2a3652 Updated README 2013-01-23 23:42:46 +00:00
Sergey Lyubka 33a31d9308 Updated README 2013-01-23 23:39:55 +00:00
Sergey Lyubka 5e9bd1f05e Update README.md 2013-01-23 23:34:01 +00:00
Sergey Lyubka 75d9a6c844 Removed mg_connect() and mg_fetch(). Added mg_download() 2013-01-23 21:54:27 +00:00
Sergey Lyubka 179761fd99 Removed LUA_FLAGS from CL flags 2013-01-23 21:14:11 +00:00
Sergey Lyubka b783813e54 Always sending MG_NEW_REQUEST, as first step in processing the request 2013-01-22 12:28:22 +00:00
Sergey Lyubka 4b29689a42 Binding to 127.0.0.1 to prevent popup windows on mac 2013-01-22 12:26:56 +00:00
Sergey Lyubka 9c4853471c Added stub for test_mg_upload() 2013-01-22 09:08:00 +00:00
Sergey Lyubka 81e21a3a0b Using get_url_to_first_open_port() in cocoa build 2013-01-22 09:00:17 +00:00
Sergey Lyubka bec6ea00ea Using unit test targets 2013-01-22 08:59:42 +00:00
Sergey Lyubka ed32e3be0f Added unix_unit_test and windows_unit_test targets 2013-01-22 08:59:13 +00:00
Sergey Lyubka 83fae879bf Binding to 127.0.0.1 to prevent annoying popups on mac 2013-01-21 22:29:31 +00:00
Sergey Lyubka 2169e6d77f Added -pthread to the CFLAGS 2013-01-21 17:51:56 +00:00
Sergey Lyubka 850e0a5922 Updated readme 2013-01-21 09:11:58 +00:00
Sergey Lyubka 322ed3cc6f fflush()-ing uploaded file before calling MG_UPLOAD callback 2013-01-21 01:39:14 +00:00
Sergey Lyubka 6d4d53c247 Squashed linux warning in process_new_connection() 2013-01-20 10:11:57 +00:00
Sergey Lyubka e6f8d40942 SSL redirect, using Host: header 2013-01-20 10:06:31 +00:00
Sergey Lyubka 7e051206ad Removed obsolete manual reference 2013-01-20 00:21:57 +00:00
Sergey Lyubka 76d913796c Getting first SSL listener dynamically 2013-01-19 22:32:26 +00:00
Sergey Lyubka 4c2609df2c Merge branch 'master' of ssh://github.com/valenok/mongoose 2013-01-19 10:46:10 +00:00
Sergey Lyubka f79bd71fd4 Implemented ssl redirection 2013-01-19 10:45:58 +00:00
Sergey Lyubka 5d4f5b6000 Merge pull request #83 from rogerz/mingw-build-error
Fix building error under mingw
2013-01-18 05:36:50 -08:00
Rogerz Zhang fe7c245a6b Fix building error under mingw 2013-01-18 21:22:44 +08:00
Sergey Lyubka f24bebaca8 3.6 -> 3.7 2013-01-18 11:08:20 +00:00
Benoît Amiaux acf72a6109 Add request_timeout option 2013-01-09 17:09:04 +01:00
19 changed files with 147569 additions and 1239 deletions

No files matched your search

+100 -100
View File
@@ -1,12 +1,10 @@
# This file is part of Mongoose project, http://code.google.com/p/mongoose
# $Id: Makefile 473 2009-09-02 11:20:06Z valenok $
PROG= mongoose
all:
@echo "make (linux|bsd|solaris|mac|windows|mingw|cygwin)"
# Possible COPT values: (in brackets are rough numbers for 'gcc -O2' on i386)
# This Makefile is part of Mongoose web server project,
# https://github.com/valenok/mongoose
#
# Example custom build:
# COPT="-g -O0 -DNO_SSL_DL -DUSE_LUA -llua -lcrypto -lssl" make linux
#
# Flags are:
# -DHAVE_MD5 - use system md5 library (-2kb)
# -DNDEBUG - strip off all debug code (-5kb)
# -DDEBUG - build debug version (very noisy) (+7kb)
@@ -18,60 +16,12 @@ all:
# -DCRYPTO_LIB=\"libcrypto.so.<version>\" - use system versioned CRYPTO so
# -DUSE_LUA - embed Lua in Mongoose (+100kb)
##########################################################################
### UNIX build: linux, bsd, mac, rtems
##########################################################################
PROG = mongoose
CFLAGS = -std=c99 -O2 -W -Wall -pedantic -pthread $(COPT)
# To build with Lua, download and unzip Lua 5.2.1 source code into the
# mongoose directory, and then add $(LUA_FLAGS) to CFLAGS below
# mongoose directory, and then add $(LUA_SOURCES) to CFLAGS
LUA = lua-5.2.1/src
LUA_FLAGS = -DUSE_LUA -I$(LUA) -L$(LUA) -llua -lm
CFLAGS = -std=c99 -O2 -W -Wall -pedantic $(COPT)
LIB = lib$(PROG).so$(MONGOOSE_LIB_SUFFIX)
# 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
# "-Wl,--as-needed" turned on by default in cc command.
# Also, this is turned in many other distros in static linkage builds.
linux:
$(CC) mongoose.c main.c -o $(PROG) -ldl -pthread $(CFLAGS)
bsd:
$(CC) mongoose.c main.c -pthread -o $(PROG) $(CFLAGS)
mac:
$(CC) mongoose.c main.c -pthread $(CFLAGS) -o $(PROG)
cocoa:
$(CC) mongoose.c main.c -DUSE_COCOA -pthread $(CFLAGS) -framework Cocoa -ObjC -arch i386 -arch x86_64 -o Mongoose
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`; DIR=dmg/Mongoose.app && rm -rf $$DIR && mkdir -p $$DIR/Contents/{MacOS,Resources} && install -m 644 build/mongoose_*.png $$DIR/Contents/Resources/ && install -m 644 build/Info.plist $$DIR/Contents/ && install -m 755 Mongoose $$DIR/Contents/MacOS/ && ln -fs /Applications dmg/ ; hdiutil create Mongoose_$$V.dmg -volname "Mongoose $$V" -srcfolder dmg -ov #; rm -rf dmg
solaris:
$(CC) mongoose.c main.c -pthread -lnsl -lsocket -o $(PROG) $(CFLAGS)
##########################################################################
### WINDOWS build: Using Visual Studio or Mingw
##########################################################################
# Using Visual Studio 6.0. To build Mongoose:
# o Set MSVC variable below to where VS 6.0 is installed on your system
# o Run "PATH_TO_VC6\bin\nmake windows"
MSVC = e:/vc6
CYA = e:/cyassl-2.0.0rc2
#DBG = /Zi /DDEBUG /Od
DBG = /DNDEBUG /O1
CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /Gz /W3 /DNO_SSL_DL \
/I$(MSVC)/include /DUSE_LUA /I$(LUA)
GUILIB= user32.lib shell32.lib comdlg32.lib
LINK = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
/subsystem:windows ws2_32.lib advapi32.lib cyassl.lib lua.lib
CYAFL = /c /I $(CYA)/include -I $(CYA)/include/openssl /I$(MSVC)/INCLUDE \
/I $(CYA)/ctaocrypt/include /D _LIB /D OPENSSL_EXTRA
LUA_SOURCES = $(LUA)/lapi.c $(LUA)/lcode.c $(LUA)/lctype.c \
$(LUA)/ldebug.c $(LUA)/ldo.c $(LUA)/ldump.c \
$(LUA)/lfunc.c $(LUA)/lgc.c $(LUA)/llex.c \
@@ -85,47 +35,104 @@ LUA_SOURCES = $(LUA)/lapi.c $(LUA)/lcode.c $(LUA)/lctype.c \
$(LUA)/loadlib.c $(LUA)/linit.c
LUA_OBJECTS = $(LUA_SOURCES:%.c=%.o)
CYA_SOURCES = $(CYA)/src/cyassl_int.c $(CYA)/src/cyassl_io.c \
$(CYA)/src/keys.c $(CYA)/src/tls.c $(CYA)/src/ssl.c \
$(CYA)/ctaocrypt/src/aes.c $(CYA)/ctaocrypt/src/arc4.c \
$(CYA)/ctaocrypt/src/asn.c $(CYA)/ctaocrypt/src/coding.c \
$(CYA)/ctaocrypt/src/ctc_asm.c $(CYA)/ctaocrypt/src/ctc_misc.c \
$(CYA)/ctaocrypt/src/cyassl_memory.c $(CYA)/ctaocrypt/src/des3.c \
$(CYA)/ctaocrypt/src/dh.c $(CYA)/ctaocrypt/src/dsa.c \
$(CYA)/ctaocrypt/src/ecc.c $(CYA)/ctaocrypt/src/hc128.c \
$(CYA)/ctaocrypt/src/hmac.c $(CYA)/ctaocrypt/src/integer.c \
$(CYA)/ctaocrypt/src/md4.c $(CYA)/ctaocrypt/src/md5.c \
$(CYA)/ctaocrypt/src/pwdbased.c $(CYA)/ctaocrypt/src/rabbit.c \
$(CYA)/ctaocrypt/src/random.c $(CYA)/ctaocrypt/src/ripemd.c \
$(CYA)/ctaocrypt/src/rsa.c $(CYA)/ctaocrypt/src/sha.c \
$(CYA)/ctaocrypt/src/sha256.c $(CYA)/ctaocrypt/src/sha512.c \
$(CYA)/ctaocrypt/src/tfm.c
# Using Visual Studio 6.0. To build Mongoose:
# Set MSVC variable below to where VS 6.0 is installed on your system
# Run "PATH_TO_VC6\bin\nmake windows"
MSVC = ../vc6
#DBG = /Zi /Od
DBG = /DNDEBUG /O1
CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /Gz /W3 \
/I$(MSVC)/include /I$(LUA) /I. /I$(YASSL) /I$(YASSL)/cyassl /GA
MSLIB = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
user32.lib shell32.lib comdlg32.lib ws2_32.lib advapi32.lib
cyassl.lib:
$(CL) /Fo$(CYA)/ $(CYA_SOURCES) $(CYAFL) $(DEF)
$(MSVC)/bin/lib $(CYA)/*.obj /out:$@
# Stock windows binary builds with Lua and YASSL library.
YASSL = ../cyassl-2.4.6
YASSL_FLAGS = -I $(YASSL) -I $(YASSL)/cyassl \
-D _LIB -D OPENSSL_EXTRA -D HAVE_ERRNO_H \
-D HAVE_GETHOSTBYNAME -D HAVE_INET_NTOA -D HAVE_LIMITS_H \
-D HAVE_MEMSET -D HAVE_SOCKET -D HAVE_STDDEF_H -D HAVE_STDLIB_H \
-D HAVE_STRING_H -D HAVE_SYS_STAT_H -D HAVE_SYS_TYPES_H
YASSL_SOURCES = \
$(YASSL)/src/internal.c $(YASSL)/src/io.c $(YASSL)/src/keys.c \
$(YASSL)/src/ssl.c $(YASSL)/src/tls.c $(YASSL)/ctaocrypt/src/hmac.c \
$(YASSL)/ctaocrypt/src/random.c $(YASSL)/ctaocrypt/src/sha.c \
$(YASSL)/ctaocrypt/src/sha256.c $(YASSL)/ctaocrypt/src/logging.c \
$(YASSL)/ctaocrypt/src/error.c $(YASSL)/ctaocrypt/src/rsa.c \
$(YASSL)/ctaocrypt/src/des3.c $(YASSL)/ctaocrypt/src/asn.c \
$(YASSL)/ctaocrypt/src/coding.c $(YASSL)/ctaocrypt/src/arc4.c \
$(YASSL)/ctaocrypt/src/md4.c $(YASSL)/ctaocrypt/src/md5.c \
$(YASSL)/ctaocrypt/src/dh.c $(YASSL)/ctaocrypt/src/dsa.c \
$(YASSL)/ctaocrypt/src/pwdbased.c $(YASSL)/ctaocrypt/src/aes.c \
$(YASSL)/ctaocrypt/src/md2.c $(YASSL)/ctaocrypt/src/ripemd.c \
$(YASSL)/ctaocrypt/src/sha512.c $(YASSL)/src/sniffer.c \
$(YASSL)/ctaocrypt/src/rabbit.c $(YASSL)/ctaocrypt/src/misc.c \
$(YASSL)/ctaocrypt/src/tfm.c $(YASSL)/ctaocrypt/src/integer.c \
$(YASSL)/ctaocrypt/src/ecc.c $(YASSL)/src/ocsp.c $(YASSL)/src/crl.c \
$(YASSL)/ctaocrypt/src/hc128.c $(YASSL)/ctaocrypt/src/memory.c
lua.lib:
$(CL) /c /Fo$(LUA)/ $(LUA_SOURCES)
$(MSVC)/bin/lib $(LUA_SOURCES:%.c=%.obj) /out:$@
all:
@echo "make (linux|bsd|solaris|mac|windows|mingw|cygwin)"
windows: cyassl.lib lua.lib
# 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
# "-Wl,--as-needed" turned on by default in cc command.
# Also, this is turned in many other distros in static linkage builds.
linux:
$(CC) mongoose.c main.c -o $(PROG) -ldl $(CFLAGS)
mac: bsd
bsd:
$(CC) mongoose.c main.c -o $(PROG) $(CFLAGS)
bsd_yassl:
$(CC) mongoose.c main.c build/lsqlite3.c build/sqlite3.c -o $(PROG) \
$(CFLAGS) -I$(LUA) -Ibuild \
$(YASSL_SOURCES) $(YASSL_FLAGS) -DNO_SSL_DL \
$(LUA_SOURCES) -DUSE_LUA -DUSE_LUA_SQLITE3 -DLUA_COMPAT_ALL
solaris:
$(CC) mongoose.c main.c -lnsl -lsocket -o $(PROG) $(CFLAGS)
# For codesign to work in non-interactive mode, unlock login keychain:
# security unlock ~/Library/Keychains/login.keychain
# See e.g. http://lists.apple.com/archives/apple-cdsa/2008/Jan/msg00027.html
cocoa:
$(CC) mongoose.c main.c build/lsqlite3.c build/sqlite3.c \
-DUSE_COCOA $(CFLAGS) -I$(LUA) -Ibuild \
$(YASSL_SOURCES) $(YASSL_FLAGS) -DNO_SSL_DL \
$(LUA_SOURCES) -DUSE_LUA -DUSE_LUA_SQLITE3 -DLUA_COMPAT_ALL \
-framework Cocoa -ObjC -arch i386 -arch x86_64 -o Mongoose
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`; DIR=dmg/Mongoose.app && rm -rf $$DIR && mkdir -p $$DIR/Contents/{MacOS,Resources} && install -m 644 build/mongoose_*.png $$DIR/Contents/Resources/ && install -m 644 build/Info.plist $$DIR/Contents/ && install -m 755 Mongoose $$DIR/Contents/MacOS/ && ln -fs /Applications dmg/ ; hdiutil create Mongoose_$$V.dmg -volname "Mongoose $$V" -srcfolder dmg -ov #; rm -rf dmg
u:
$(CC) test/unit_test.c -o unit_test -I. -I$(LUA) $(LUA_SOURCES) \
$(CFLAGS) -g -O0
./unit_test
w:
$(CL) test/unit_test.c $(LUA_SOURCES) \
$(YASSL_SOURCES) $(YASSL_FLAGS) /DNO_SSL_DL \
$(MSLIB) /out:unit_test.exe
./unit_test.exe
windows:
$(MSVC)/bin/rc build\res.rc
$(CL) /Ibuild main.c mongoose.c /GA $(LINK) build\res.res \
$(GUILIB) /out:$(PROG).exe
# $(CL) mongoose.c /GD $(LINK) /DLL /DEF:build\dll.def /out:$(PROG).dll
$(CL) main.c mongoose.c build/lsqlite3.c build/sqlite3.c \
$(YASSL_SOURCES) $(YASSL_FLAGS) /DNO_SSL_DL \
$(LUA_SOURCES) /DUSE_LUA /DUSE_LUA_SQLITE3 /DLUA_COMPAT_ALL \
$(MSLIB) build\res.res /out:$(PROG).exe /subsystem:windows
# Build for Windows under MinGW
#MINGWDBG= -DDEBUG -O0 -ggdb
MINGWDBG= -DNDEBUG -Os
MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,console $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
#MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,windows $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
mingw:
windres build\res.rc build\res.o
$(CC) $(MINGWOPT) mongoose.c -lws2_32 \
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
$(CC) $(MINGWOPT) -build mongoose.c main.c build\res.o \
-lws2_32 -ladvapi32 -o $(PROG).exe
$(CC) $(MINGWOPT) mongoose.c main.c build\res.o \
-lws2_32 -ladvapi32 -lcomdlg32 -o $(PROG).exe
# Build for Windows under Cygwin
#CYGWINDBG= -DDEBUG -O0 -ggdb
@@ -138,23 +145,16 @@ cygwin:
$(CC) $(CYGWINOPT) -Ibuild mongoose.c main.c ./build/res.o \
-lws2_32 -ladvapi32 -o $(PROG).exe
##########################################################################
### Manuals, cleanup, test, release
##########################################################################
man:
groff -man -T ascii mongoose.1 | col -b > mongoose.txt
groff -man -T ascii mongoose.1 | less
# "TEST=unit make test" - perform unit test only
# "TEST=embedded" - test embedded API by building and testing test/embed.c
# "TEST=basic_tests" - perform basic tests only (no CGI, SSI..)
tests:
perl test/test.pl $(TEST)
release: clean
tarball: clean
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`.tgz ; cd .. && tar -czf x mongoose/{LICENSE,Makefile,examples,test,build,*.[ch],*.md} && mv x mongoose/$$F
release: tarball cocoa
wine make windows
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`; upx mongoose.exe; cp mongoose.exe mongoose-$$V.exe; cp mongoose.exe mongoose_php_bundle/; zip -r mongoose_php_bundle_$$V.zip mongoose_php_bundle/
clean:
cd examples && $(MAKE) clean
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz \
+16 -5
View File
@@ -24,15 +24,17 @@ simplicity by carefully selected list of features:
- Small footprint: executable size is 50 kB on Linux 2.6 i386 system
- 130 kilobytes Windows executable with all of the above and no dependencies
- Simple and clean embedding API,
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)).
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h).
The source is in single
[mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file
to make things easy.
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
- Extensive documentation in form of
[User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md)
@@ -44,8 +46,9 @@ mailing list.
Keep Sergey happy
-----------------
Since 2004, Mongoose is being constantly improved by me, Sergey Lyubka, a
software engineer from Galway, Ireland. My other software I give to the
I am Sergey Lyubka, a software engineer from Galway, Ireland. I started
working on Mongoose in 2004, and since then continuously improving it,
investing thousands of hours of work. My other project I'm contributing to the
community for free is
[Super Light Regular Expression library](http://code.google.com/p/slre).
@@ -53,4 +56,12 @@ community for free is
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 already did so: T.Barmann, D.Hughes, J.C.Sloan, R.Romeo,
L.E.Spencer, and 4 others. Appreciated guys, you keep my brains going!
L.E.Spencer, S.Kotay, B.Fjukstad and 6 others.
Appreciated guys, you keep my brains going!
Cash is also appreciated 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: 4/1000 &euro;
(thanks to O.M.Vilhunen, C.Radik)
![Progress](http://chart.googleapis.com/chart?chxr=0,0,1000&chxt=x&chbh=30,0,0&chs=300x35&cht=bhs&chco=90c0f0&chd=s:B)
+25 -17
View File
@@ -1,8 +1,7 @@
**NOTE: THIS MANUAL IS WORK IN PROGRESS**
Overview
--------
## Overview
Mongoose is small and easy to use web server. It is self-contained, and does
not require any external software to run.
@@ -45,8 +44,7 @@ Mongoose can also be used to modify `.htpasswd` passwords file:
mongoose -A <htpasswd_file> <realm> <user> <passwd>
Usage Examples
--------------
## Usage Examples
- How to share a Windows folder: copy mongoose executable to a folder and
double-click the executable. The folder should be accessible via
@@ -59,15 +57,8 @@ Usage Examples
- How to serve user home directories using URL rewrite:
`mongoose -url_rewrite_patterns /~joe/=/home/joe/,/~bill=/home/bill/`
Command Line Options
--------------------
## Configuration Options
```
-A 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 htdigest utility.
-C cgi_pattern
All files that fully match cgi_pattern are treated as CGI.
Default pattern allows CGI files be anywhere. To restrict CGIs to
@@ -190,14 +181,31 @@ Command Line Options
```
Common Problems
## Common Problems
---------------
- PHP doesn't work - getting empty page, or 'File not found' error. The
reason for that is wrong paths to the interpreter. Remember that with PHP,
correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify
full path to the PHP interpreter, e.g.
`mongoose -cgi_interpreter /full/path/to/php-cgi`
Embedding
---------
## Embedding
Embedding Mongoose is easy. Somewhere in the application code, `mg_start()`
function must be called. That starts the web server in a separate thread.
When it is not needed anymore, `mg_stop()` must be called. Application code
can pass configuration options to `mg_start()`, and also specify callback
functions that Mongoose should call at certain events.
[hello.c](http://a.c) provides a minimalistic example.
Other Resources
---------------
Common pattern is to implement `begin_request` callback, and serve static files
from memory, and/or construct dynamic replies on the fly. Here is
my [embed.c](https://gist.github.com/valenok/4714740) gist
that shows how to easily any data can be embedded
directly into the executable. If such data needs to be encrypted, then
encrypted database or encryption dongles would be a better choice.
## Other Resources
- Presentation made by Arnout Vandecappelle at FOSDEM 2011 on 2011-02-06
in Brussels, Belgium, called
"Creating secure web based user interfaces for Embedded Devices"
+14 -12
View File
@@ -2,17 +2,19 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>mongoose</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleTypeRole</key>
<string>None</string>
<key>CFBundleIconFiles</key>
<array>
<string>mongoose_16x16.png</string>
</array>
<key>LSUIElement</key>
<true/>
<key>CFBundleExecutable</key> <string>Mongoose</string>
<key>CFBundlePackageType</key> <string>APPL</string>
<key>CFBundleTypeRole</key> <string>None</string>
<key>CFBundleIconFiles</key> <array>
<string>mongoose_16x16.png</string>
<string>mongoose_22x22.png</string>
<string>mongoose_32x32.png</string>
<string>mongoose_64x64.png</string>
</array>
<key>LSUIElement</key> <true/>
<key>RunAtLoad</key> <true/>
<key>Label</key> <string>com.kolkin.mongoose</string>
<key>ProgramArguments</key> <array> </array>
<key>KeepAlive</key> <true/>
</dict>
</plist>
+1885
View File
File diff suppressed because it is too large. Load diff
+137414
View File
File diff suppressed because it is too large. Load diff
+7160
View File
File diff suppressed because it is too large. Load diff
+19 -25
View File
@@ -325,34 +325,25 @@ static void redirect_to_ssl(struct mg_connection *conn,
}
}
static void *event_handler(enum mg_event event,
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);
void *processed = "yes";
int processed = 1;
if (event == MG_NEW_REQUEST) {
if (!request_info->is_ssl) {
redirect_to_ssl(conn, request_info);
} else if (!is_authorized(conn, request_info)) {
redirect_to_login(conn, request_info);
} else if (strcmp(request_info->uri, authorize_url) == 0) {
authorize(conn, request_info);
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
ajax_get_messages(conn, request_info);
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
ajax_send_message(conn, request_info);
} else {
// No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
processed = NULL;
}
} else if (event == MG_EVENT_LOG) {
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
processed = NULL;
if (!request_info->is_ssl) {
redirect_to_ssl(conn, request_info);
} else if (!is_authorized(conn, request_info)) {
redirect_to_login(conn, request_info);
} else if (strcmp(request_info->uri, authorize_url) == 0) {
authorize(conn, request_info);
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
ajax_get_messages(conn, request_info);
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
ajax_send_message(conn, request_info);
} else {
processed = NULL;
// No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
processed = 0;
}
return processed;
}
@@ -365,6 +356,7 @@ static const char *options[] = {
};
int main(void) {
struct mg_callbacks callbacks;
struct mg_context *ctx;
// Initialize random number generator. It will be used later on for
@@ -372,7 +364,9 @@ int main(void) {
srand((unsigned) time(0));
// 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");
exit(EXIT_FAILURE);
}
+35 -21
View File
@@ -2,35 +2,49 @@
#include <string.h>
#include "mongoose.h"
static void *callback(enum mg_event event,
struct mg_connection *conn) {
// This function will be called by mongoose on every new request.
static int begin_request_handler(struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn);
char content[100];
if (event == MG_NEW_REQUEST) {
char content[1024];
int content_length = snprintf(content, sizeof(content),
"Hello from mongoose! Remote port: %d",
request_info->remote_port);
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: %d\r\n" // Always set Content-Length
"\r\n"
"%s",
content_length, content);
// Mark as processed
return "";
} else {
return NULL;
}
// Prepare the message we're going to send
int content_length = snprintf(content, sizeof(content),
"Hello from mongoose! Remote port: %d",
request_info->remote_port);
// Send HTTP reply to the client
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: %d\r\n" // Always set Content-Length
"\r\n"
"%s",
content_length, content);
// Returning non-zero tells mongoose that our function has replied to
// the client, and mongoose should not send client any more data.
return 1;
}
int main(void) {
struct mg_context *ctx;
struct mg_callbacks callbacks;
// List of options. Last element must be NULL.
const char *options[] = {"listening_ports", "8080", NULL};
ctx = mg_start(&callback, NULL, options);
getchar(); // Wait until user hits "enter"
// Prepare callbacks structure. We have only one callback, the rest are NULL.
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);
return 0;
+27 -32
View File
@@ -10,50 +10,45 @@ static const char *html_form =
"<input type=\"submit\" />"
"</form></body></html>";
static void *callback(enum mg_event event,
struct mg_connection *conn) {
static int begin_request_handler(struct mg_connection *conn) {
const struct mg_request_info *ri = mg_get_request_info(conn);
char post_data[1024], input1[sizeof(post_data)], input2[sizeof(post_data)];
int post_data_len;
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;
if (!strcmp(ri->uri, "/handle_post_request")) {
// User has submitted a form, show submitted data and a variable value
post_data_len = mg_read(conn, post_data, sizeof(post_data));
// 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));
// 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 "";
// Send reply to the client, showing submitted form values.
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 {
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) {
struct mg_context *ctx;
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"
mg_stop(ctx);
+28 -26
View File
@@ -17,42 +17,44 @@ typedef __int64 int64_t;
#include "mongoose.h"
static void *callback(enum mg_event event, struct mg_connection *conn) {
if (event == MG_NEW_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_upload(conn, "/tmp");
} else {
// Show HTML form. Make sure it has enctype="multipart/form-data" attr.
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 int begin_request_handler(struct mg_connection *conn) {
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_upload(conn, "/tmp");
} else {
// Show HTML form. Make sure it has enctype="multipart/form-data" attr.
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>";
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 if (event == MG_UPLOAD) {
mg_printf(conn, "Saved [%s]", mg_get_request_info(conn)->ev_data);
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 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) {
struct mg_context *ctx;
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"
pause();
mg_stop(ctx);
return 0;
+49 -48
View File
@@ -5,69 +5,70 @@
#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;
static void websocket_ready_handler(struct mg_connection *conn) {
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]);
}
// 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
static int websocket_data_handler(struct mg_connection *conn) {
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 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 (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;
}
// 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;
}
// Prepare frame
reply[0] = 0x81; // text, FIN set
reply[1] = msg_len;
// Copy message from request to reply, applying the mask if required.
for (i = 0; i < msg_len; i++) {
xor = mask_len == 0 ? 0 : buf[2 + (i % 4)];
reply[i + 2] = buf[i + 2 + mask_len] ^ xor;
}
// Echo the message back to the client
mg_write(conn, reply, 2 + msg_len);
// Returnint zero means stoping websocket conversation.
// Close the conversation if client has sent us "exit" string.
return memcmp(reply + 2, "exit", 4);
}
int main(void) {
struct mg_context *ctx;
struct mg_callbacks callbacks;
const char *options[] = {
"listening_ports", "8080",
"document_root", "websocket_html_root",
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"
mg_stop(ctx);
+20 -18
View File
@@ -105,9 +105,6 @@ static void show_usage_and_exit(void) {
fprintf(stderr, " -%s %s (default: \"%s\")\n",
names[i], names[i + 1], names[i + 2] == NULL ? "" : names[i + 2]);
}
fprintf(stderr, "\nSee http://code.google.com/p/mongoose/wiki/MongooseManual"
" for more details.\n");
fprintf(stderr, "Example:\n mongoose -s cert.pem -p 80,443s -d no\n");
exit(EXIT_FAILURE);
}
@@ -255,12 +252,18 @@ static void process_command_line_arguments(char *argv[], char **options) {
(void) fclose(fp);
}
// Handle command line flags. They override config file and default settings.
for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
if (argv[i][0] != '-' || argv[i + 1] == NULL) {
show_usage_and_exit();
// If we're under MacOS and started by launchd, then the second
// argument is process serial number, -psn_.....
// In this case, don't process arguments at all.
if (argv[1] == NULL || memcmp(argv[1], "-psn_", 5) != 0) {
// Handle command line flags.
// They override config file and default settings.
for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
if (argv[i][0] != '-' || argv[i + 1] == NULL) {
show_usage_and_exit();
}
set_option(options, &argv[i][1], argv[i + 1]);
}
set_option(options, &argv[i][1], argv[i + 1]);
}
}
@@ -269,17 +272,14 @@ static void init_server_name(void) {
mg_version());
}
static void *mongoose_callback(enum mg_event ev, struct mg_connection *conn) {
if (ev == MG_EVENT_LOG) {
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
}
// Returning NULL marks request as not handled, signalling mongoose to
// proceed with handling it.
return NULL;
static int log_message(const struct mg_connection *conn, const char *message) {
(void) conn;
printf("%s\n", message);
return 0;
}
static void start_mongoose(int argc, char *argv[]) {
struct mg_callbacks callbacks;
char *options[MAX_OPTIONS];
int i;
@@ -305,7 +305,9 @@ static void start_mongoose(int argc, char *argv[]) {
signal(SIGINT, signal_handler);
/* Start Mongoose */
ctx = mg_start(&mongoose_callback, NULL, (const char **) options);
memset(&callbacks, 0, sizeof(callbacks));
callbacks.log_message = &log_message;
ctx = mg_start(&callbacks, NULL, (const char **) options);
for (i = 0; options[i] != NULL; i++) {
free(options[i]);
}
@@ -807,7 +809,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show) {
- (void) openBrowser {
[[NSWorkspace sharedWorkspace]
openURL:[NSURL URLWithString:
[NSString stringWithUTF8String:"http://www.yahoo.com"]]];
[NSString stringWithUTF8String:get_url_to_first_open_port(ctx)]]];
}
- (void) editConfig {
create_config_file(config_file);
-184
View File
@@ -1,184 +0,0 @@
.\" 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 .
+361 -384
View File
File diff suppressed because it is too large. Load diff
+45 -132
View File
@@ -42,122 +42,37 @@ struct mg_request_info {
long remote_ip; // Client's IP address
int remote_port; // Client's port
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 {
const char *name; // HTTP header name
const char *value; // HTTP header value
} 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.
enum mg_event {
// New HTTP request has arrived from the client.
// If callback returns non-NULL, Mongoose stops handling current request.
// ev_data contains NULL.
MG_NEW_REQUEST,
// Mongoose has finished handling the request.
// Callback return value is ignored.
// ev_data contains integer HTTP status code:
// int http_reply_status_code = (long) request_info->ev_data;
MG_REQUEST_COMPLETE,
// HTTP error must be returned to the client.
// If callback returns non-NULL, Mongoose stops handling error.
// 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,
// This structure needs to be passed to mg_start(), to let mongoose know
// which callbacks to invoke. For detailed description, see
// https://github.com/valenok/mongoose/blob/master/UserManual.md
struct mg_callbacks {
int (*begin_request)(struct mg_connection *);
void (*end_request)(const struct mg_connection *, int reply_status_code);
int (*log_message)(const struct mg_connection *, const char *message);
int (*init_ssl)(void *ssl_context);
int (*websocket_connect)(const struct mg_connection *);
void (*websocket_ready)(struct mg_connection *);
int (*websocket_data)(struct mg_connection *);
const char * (*open_file)(const struct mg_connection *,
const char *path, size_t *data_len);
void (*init_lua)(struct mg_connection *, void *lua_context);
void (*upload)(struct mg_connection *, const char *file_name);
};
// 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.
//
// 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
// specify Mongoose configuration parameters.
//
@@ -179,8 +94,9 @@ typedef void *(*mg_callback_t)(enum mg_event event, struct mg_connection *conn);
//
// Return:
// web server context, or NULL on error.
struct mg_context *mg_start(mg_callback_t callback, void *user_data,
const char **options);
struct mg_context *mg_start(const struct mg_callbacks *callbacks,
void *user_data,
const char **configuration_options);
// Stop the web server.
@@ -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);
// 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
#if _MSC_VER >= 1400
#include <sal.h>
@@ -260,6 +170,11 @@ int mg_write(struct mg_connection *, const void *buf, size_t len);
#define PRINTF_ARGS(x, y)
#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 *,
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
@@ -316,32 +231,30 @@ int mg_get_cookie(const struct mg_connection *,
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:
// On success, valid pointer to the new connection
// On error, NULL
struct mg_connection *mg_connect(struct mg_context *ctx,
const char *host, int port, int use_ssl);
// On success, valid pointer to the new connection, suitable for mg_read().
// On error, NULL. error_buffer contains error message.
// Example:
// 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);
// 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 and MG_UPLOAD event is sent.
// Return number of uploaded files.
+45 -33
View File
@@ -1,40 +1,52 @@
<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)
if n <= 0 then return false end
if n <= 2 then return true end
if (n % 2 == 0) then return false end
for i = 3, n / 2, 2 do
if (n % i == 0) then return false end
end
return true
end
<p>This is an example Lua server page served by
<a href="http://code.google.com/p/mongoose">Mongoose web server</a>.
Mongoose has Lua, Sqlite, and other functionality built in the binary.
This example page stores the request in the Sqlite database, and shows
all requests done previously.</p>
for i = 1, 100 do
if is_prime(i) then print('<span>' .. i .. '</span>&nbsp;') end
end
<pre>
<?
-- 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>
<form method="POST"><input type="text" name="t1"/><input type="submit"></form>
-- Create a table if it is not created already
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>
POST data: [<? post_data = read() print(post_data) ?>]
request method: [<? print(request_info.request_method) ?>]
IP/port: [<? print(request_info.remote_ip, ':', request_info.remote_port) ?>]
URI: [<? print(request_info.uri) ?>]
HTTP version [<? print(request_info.http_version) ?>]
HEADERS:
<?
for name, value in pairs(request_info.http_headers) do
print(name, ':', value, '\n')
end
?>
</pre>
-- Add entry about this request
local stmt = db:prepare(
'INSERT INTO requests VALUES(NULL, datetime("now"), ?, ?, ?);');
stmt:bind_values(request_info.request_method, request_info.uri,
request_info.http_headers['User-Agent'])
stmt:step()
stmt:finalize()
-- 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>
+10 -87
View File
@@ -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') {
do_unit_test();
exit 0;
@@ -158,15 +153,16 @@ if (scalar(@ARGV) > 0 and $ARGV[0] eq 'unit') {
# Make sure we load config file if no options are given.
# Command line options override config files settings
write_file($config, "access_log_file access.log\nlistening_ports 12345\n");
spawn("$exe -p $port");
write_file($config, "access_log_file access.log\n" .
"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');
unlink $config;
kill_spawned_child();
# Spawn the server on port $port
my $cmd = "$exe ".
"-listening_ports $port ".
"-listening_ports 127.0.0.1:$port ".
"-access_log_file access.log ".
"-error_log_file debug.log ".
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
@@ -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');
# Test HTTP version parsing
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/1.1z\r\n\r\n", '505 HTTP', 'Bad HTTP min Version');
o("GET / HTTP/02.0\r\n\r\n", '505 HTTP version not supported',
'HTTP Version >1.1');
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", '^HTTP/1.1 505', 'Bad HTTP maj Version', 0);
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", '^HTTP/1.1 505', 'HTTP Version >1.1', 0);
# File with leading single dot
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") {
do_PUT_test();
kill_spawned_child();
do_unit_test();
do_embedded_test();
}
sub do_PUT_test {
@@ -456,79 +450,8 @@ sub do_PUT_test {
}
sub do_unit_test {
my $cmd = "cc -g -W -Wall -o $unit_test_exe $root/unit_test.c -I. ".
"-pthread -DNO_SSL ";
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();
my $target = on_windows() ? 'w' : 'u';
system("make $target") == 0 or fail("Unit test failed!");
}
print "SUCCESS! All tests passed.\n";
+316 -115
View File
@@ -1,4 +1,27 @@
// Copyright (c) 2004-2013 Sergey Lyubka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// Unit test for the mongoose web server. Tests embedded API.
#define USE_WEBSOCKET
#define USE_LUA
#include "mongoose.c"
#define FATAL(str, line) do { \
@@ -7,24 +30,43 @@
} while (0)
#define ASSERT(expr) do { if (!(expr)) FATAL(#expr, __LINE__); } while (0)
#define UNUSED_PORT "33796"
#define HTTP_PORT "56789"
#define HTTPS_PORT "56790"
#define HTTP_PORT2 "56791"
#define LISTENING_ADDR \
"127.0.0.1:" HTTP_PORT "r" \
",127.0.0.1:" HTTPS_PORT "s" \
",127.0.0.1:" HTTP_PORT2
static void test_parse_http_request() {
#ifndef _WIN32
#define __cdecl
#endif
static void test_parse_http_message() {
struct mg_request_info ri;
char req1[] = "GET / HTTP/1.1\r\n\r\n";
char req2[] = "BLAH / HTTP/1.1\r\n\r\n";
char req3[] = "GET / HTTP/1.1\r\nBah\r\n";
char req4[] = "GET / HTTP/1.1\r\nA: foo bar\r\nB: bar\r\nbaz\r\n\r\n";
char req5[] = "GET / HTTP/1.1\r\n\r\n";
char req6[] = "G";
char req7[] = " blah ";
char req8[] = " HTTP/1.1 200 OK \n\n";
ASSERT(parse_http_request(req1, sizeof(req1), &ri) == sizeof(req1) - 1);
ASSERT(parse_http_message(req1, sizeof(req1), &ri) == sizeof(req1) - 1);
ASSERT(strcmp(ri.http_version, "1.1") == 0);
ASSERT(ri.num_headers == 0);
ASSERT(parse_http_request(req2, sizeof(req2), &ri) == -1);
ASSERT(parse_http_request(req3, sizeof(req3), &ri) == -1);
ASSERT(parse_http_message(req2, sizeof(req2), &ri) == -1);
ASSERT(parse_http_message(req3, sizeof(req3), &ri) == 0);
ASSERT(parse_http_message(req6, sizeof(req6), &ri) == 0);
ASSERT(parse_http_message(req7, sizeof(req7), &ri) == 0);
ASSERT(parse_http_message("", 0, &ri) == 0);
ASSERT(parse_http_message(req8, sizeof(req8), &ri) == sizeof(req8) - 1);
// TODO(lsm): Fix this. Header value may span multiple lines.
ASSERT(parse_http_request(req4, sizeof(req4), &ri) == sizeof(req4) - 1);
ASSERT(parse_http_message(req4, sizeof(req4), &ri) == sizeof(req4) - 1);
ASSERT(strcmp(ri.http_version, "1.1") == 0);
ASSERT(ri.num_headers == 3);
ASSERT(strcmp(ri.http_headers[0].name, "A") == 0);
ASSERT(strcmp(ri.http_headers[0].value, "foo bar") == 0);
@@ -33,7 +75,9 @@ static void test_parse_http_request() {
ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0);
ASSERT(strcmp(ri.http_headers[2].value, "") == 0);
// TODO(lsm): add more tests.
ASSERT(parse_http_message(req5, sizeof(req5), &ri) == sizeof(req5) - 1);
ASSERT(strcmp(ri.request_method, "GET") == 0);
ASSERT(strcmp(ri.http_version, "1.1") == 0);
}
static void test_should_keep_alive(void) {
@@ -46,7 +90,8 @@ static void test_should_keep_alive(void) {
memset(&conn, 0, sizeof(conn));
conn.ctx = &ctx;
parse_http_request(req1, sizeof(req1), &conn.request_info);
ASSERT(parse_http_message(req1, sizeof(req1), &conn.request_info) ==
sizeof(req1) - 1);
ctx.config[ENABLE_KEEP_ALIVE] = "no";
ASSERT(should_keep_alive(&conn) == 0);
@@ -58,13 +103,13 @@ static void test_should_keep_alive(void) {
ASSERT(should_keep_alive(&conn) == 0);
conn.must_close = 0;
parse_http_request(req2, sizeof(req2), &conn.request_info);
parse_http_message(req2, sizeof(req2), &conn.request_info);
ASSERT(should_keep_alive(&conn) == 0);
parse_http_request(req3, sizeof(req3), &conn.request_info);
parse_http_message(req3, sizeof(req3), &conn.request_info);
ASSERT(should_keep_alive(&conn) == 0);
parse_http_request(req4, sizeof(req4), &conn.request_info);
parse_http_message(req4, sizeof(req4), &conn.request_info);
ASSERT(should_keep_alive(&conn) == 1);
conn.status_code = 401;
@@ -121,106 +166,204 @@ static void test_remove_double_dots() {
size_t i;
for (i = 0; i < ARRAY_SIZE(data); i++) {
printf("[%s] -> [%s]\n", data[i].before, data[i].after);
remove_double_dots_and_double_slashes(data[i].before);
ASSERT(strcmp(data[i].before, data[i].after) == 0);
}
}
static char *read_file(const char *path, int *size) {
FILE *fp;
struct stat st;
char *data = NULL;
if ((fp = fopen(path, "rb")) != NULL && !fstat(fileno(fp), &st)) {
*size = (int) st.st_size;
ASSERT((data = malloc(*size)) != NULL);
ASSERT(fread(data, 1, *size, fp) == (size_t) *size);
fclose(fp);
}
return data;
}
static const char *fetch_data = "hello world!\n";
static const char *inmemory_file_data = "hi there";
static void *event_handler(enum mg_event event, struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn);
static const char *upload_filename = "upload_test.txt";
static const char *upload_ok_message = "upload successful";
if (event == MG_NEW_REQUEST && !strcmp(request_info->uri, "/data")) {
static const char *open_file_cb(const struct mg_connection *conn,
const char *path, size_t *size) {
(void) conn;
if (!strcmp(path, "./blah")) {
*size = strlen(inmemory_file_data);
return inmemory_file_data;
}
return NULL;
}
static void upload_cb(struct mg_connection *conn, const char *path) {
char *p1, *p2;
int len1, len2;
ASSERT(!strcmp(path, "./upload_test.txt"));
ASSERT((p1 = read_file("mongoose.c", &len1)) != NULL);
ASSERT((p2 = read_file(path, &len2)) != NULL);
ASSERT(len1 == len2);
ASSERT(memcmp(p1, p2, len1) == 0);
free(p1), free(p2);
remove(upload_filename);
mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n\r\n%s",
(int) strlen(upload_ok_message), upload_ok_message);
}
static int begin_request_handler_cb(struct mg_connection *conn) {
const struct mg_request_info *ri = mg_get_request_info(conn);
if (!strcmp(ri->uri, "/data")) {
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
"Content-Length: %d\r\n"
"Content-Type: text/plain\r\n\r\n"
"%s", (int) strlen(fetch_data), fetch_data);
return "";
} else if (event == MG_OPEN_FILE) {
const char *path = request_info->ev_data;
printf("%s: [%s]\n", __func__, path);
if (strcmp(path, "./blah") == 0) {
mg_get_request_info(conn)->ev_data =
(void *) (int) strlen(inmemory_file_data);
return (void *) inmemory_file_data;
}
} else if (event == MG_EVENT_LOG) {
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
return 1;
}
return NULL;
if (!strcmp(ri->uri, "/upload")) {
ASSERT(mg_upload(conn, ".") == 1);
}
return 0;
}
static void test_mg_fetch(void) {
static const char *options[] = {
"document_root", ".",
"listening_ports", UNUSED_PORT,
NULL,
};
char buf[2000], buf2[2000];
int n, length;
static int log_message_cb(const struct mg_connection *conn, const char *msg) {
(void) conn;
printf("%s\n", msg);
return 0;
}
static const struct mg_callbacks CALLBACKS = {
&begin_request_handler_cb, NULL, &log_message_cb, NULL, NULL, NULL, NULL,
&open_file_cb, NULL, &upload_cb
};
static const char *OPTIONS[] = {
"document_root", ".",
"listening_ports", LISTENING_ADDR,
"ssl_certificate", "build/ssl_cert.pem",
NULL,
};
static char *read_conn(struct mg_connection *conn, int *size) {
char buf[100], *data = NULL;
int len;
*size = 0;
while ((len = mg_read(conn, buf, sizeof(buf))) > 0) {
*size += len;
ASSERT((data = realloc(data, *size)) != NULL);
memcpy(data + *size - len, buf, len);
}
return data;
}
static void test_mg_download(void) {
char *p1, *p2, ebuf[100];
int len1, len2, port = atoi(HTTPS_PORT);
struct mg_connection *conn;
struct mg_context *ctx;
struct mg_request_info ri;
const char *tmp_file = "temporary_file_name_for_unit_test.txt";
struct file file;
FILE *fp;
ASSERT((ctx = mg_start(event_handler, NULL, options)) != NULL);
ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
// Failed fetch, pass invalid URL
ASSERT(mg_fetch(ctx, "localhost", tmp_file, buf, sizeof(buf), &ri) == NULL);
ASSERT(mg_fetch(ctx, "localhost:" UNUSED_PORT, tmp_file,
buf, sizeof(buf), &ri) == NULL);
ASSERT(mg_fetch(ctx, "http://$$$.$$$", tmp_file,
buf, sizeof(buf), &ri) == NULL);
ASSERT(mg_download(NULL, port, 0, ebuf, sizeof(ebuf), "%s", "") == NULL);
ASSERT(mg_download("localhost", 0, 0, ebuf, sizeof(ebuf), "%s", "") == NULL);
ASSERT(mg_download("localhost", port, 1, ebuf, sizeof(ebuf),
"%s", "") == NULL);
// Failed fetch, pass invalid file name
ASSERT(mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/data",
"/this/file/must/not/exist/ever",
buf, sizeof(buf), &ri) == NULL);
// Fetch nonexistent file, should see 404
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
"GET /gimbec HTTP/1.0\r\n\r\n")) != NULL);
ASSERT(strcmp(conn->request_info.uri, "404") == 0);
mg_close_connection(conn);
// Successful fetch
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/data",
tmp_file, buf, sizeof(buf), &ri)) != NULL);
ASSERT(ri.num_headers == 2);
ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));
ASSERT(!strcmp(ri.uri, "200"));
ASSERT(!strcmp(ri.http_version, "OK"));
ASSERT((length = ftell(fp)) == (int) strlen(fetch_data));
fseek(fp, 0, SEEK_SET);
ASSERT(fread(buf2, 1, length, fp) == (size_t) length);
ASSERT(memcmp(buf2, fetch_data, length) == 0);
fclose(fp);
ASSERT((conn = mg_download("google.com", 443, 1, ebuf, sizeof(ebuf), "%s",
"GET / HTTP/1.0\r\n\r\n")) != NULL);
mg_close_connection(conn);
// Fetch big file, mongoose.c
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/mongoose.c",
tmp_file, buf, sizeof(buf), &ri)) != NULL);
ASSERT(mg_stat(fc(ctx), "mongoose.c", &file));
ASSERT(file.size == ftell(fp));
ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));
// Fetch mongoose.c, should succeed
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
"GET /mongoose.c HTTP/1.0\r\n\r\n")) != NULL);
ASSERT(!strcmp(conn->request_info.uri, "200"));
ASSERT((p1 = read_conn(conn, &len1)) != NULL);
ASSERT((p2 = read_file("mongoose.c", &len2)) != NULL);
ASSERT(len1 == len2);
ASSERT(memcmp(p1, p2, len1) == 0);
free(p1), free(p2);
mg_close_connection(conn);
// Fetch nonexistent file, /blah
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/boo",
tmp_file, buf, sizeof(buf), &ri)) != NULL);
ASSERT(!mg_strcasecmp(ri.uri, "404"));
fclose(fp);
// Fetch in-memory file, should succeed.
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
"GET /blah HTTP/1.1\r\n\r\n")) != NULL);
ASSERT((p1 = read_conn(conn, &len1)) != NULL);
ASSERT(len1 == (int) strlen(inmemory_file_data));
ASSERT(memcmp(p1, inmemory_file_data, len1) == 0);
free(p1);
mg_close_connection(conn);
// Fetch existing inmemory file
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/blah",
tmp_file, buf, sizeof(buf), &ri)) != NULL);
ASSERT(!mg_strcasecmp(ri.uri, "200"));
n = ftell(fp);
fseek(fp, 0, SEEK_SET);
printf("%s %d %d [%.*s]\n", __func__, n, (int) feof(fp), n, buf2);
n = fread(buf2, 1, n, fp);
printf("%s %d %d [%.*s]\n", __func__, n, (int) feof(fp), n, buf2);
ASSERT((size_t) ftell(fp) == (size_t) strlen(inmemory_file_data));
ASSERT(!memcmp(inmemory_file_data, buf2, ftell(fp)));
fclose(fp);
// Test SSL redirect, IP address
ASSERT((conn = mg_download("localhost", atoi(HTTP_PORT), 0,
ebuf, sizeof(ebuf), "%s",
"GET /foo HTTP/1.1\r\n\r\n")) != NULL);
ASSERT(strcmp(conn->request_info.uri, "302") == 0);
ASSERT(strcmp(mg_get_header(conn, "Location"),
"https://127.0.0.1:" HTTPS_PORT "/foo") == 0);
mg_close_connection(conn);
remove(tmp_file);
// Test SSL redirect, Host:
ASSERT((conn = mg_download("localhost", atoi(HTTP_PORT), 0,
ebuf, sizeof(ebuf), "%s",
"GET /foo HTTP/1.1\r\nHost: a.b:77\n\n")) != NULL);
ASSERT(strcmp(conn->request_info.uri, "302") == 0);
ASSERT(strcmp(mg_get_header(conn, "Location"),
"https://a.b:" HTTPS_PORT "/foo") == 0);
mg_close_connection(conn);
mg_stop(ctx);
}
static int alloc_printf(char **buf, size_t size, char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
return alloc_vprintf(buf, size, fmt, ap);
}
static void test_mg_upload(void) {
static const char *boundary = "OOO___MY_BOUNDARY___OOO";
struct mg_context *ctx;
struct mg_connection *conn;
char ebuf[100], buf[20], *file_data, *post_data = NULL;
int file_len, post_data_len;
ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
ASSERT((file_data = read_file("mongoose.c", &file_len)) != NULL);
post_data_len = alloc_printf(&post_data, 0,
"--%s\r\n"
"Content-Disposition: form-data; "
"name=\"file\"; "
"filename=\"%s\"\r\n\r\n"
"%.*s\r\n"
"--%s\r\n",
boundary, upload_filename,
file_len, file_data, boundary);
ASSERT(post_data_len > 0);
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
ebuf, sizeof(ebuf),
"POST /upload HTTP/1.1\r\n"
"Content-Length: %d\r\n"
"Content-Type: multipart/form-data; "
"boundary=%s\r\n\r\n"
"%.*s", post_data_len, boundary,
post_data_len, post_data)) != NULL);
free(file_data), free(post_data);
ASSERT(mg_read(conn, buf, sizeof(buf)) == (int) strlen(upload_ok_message));
ASSERT(memcmp(buf, upload_ok_message, strlen(upload_ok_message)) == 0);
mg_close_connection(conn);
mg_stop(ctx);
}
@@ -232,7 +375,6 @@ static void test_base64_encode(void) {
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]));
}
}
@@ -297,10 +439,9 @@ static void check_lua_expr(lua_State *L, const char *expr, const char *value) {
char buf[100];
snprintf(buf, sizeof(buf), "%s = %s", var_name, expr);
luaL_dostring(L, buf);
(void) luaL_dostring(L, buf);
lua_getglobal(L, var_name);
v = lua_tostring(L, -1);
printf("%s: %s: [%s] [%s]\n", __func__, expr, v == NULL ? "null" : v, value);
ASSERT((value == NULL && v == NULL) ||
(value != NULL && v != NULL && !strcmp(value, v)));
}
@@ -312,13 +453,12 @@ static void test_lua(void) {
char http_request[] = "POST /foo/bar HTTP/1.1\r\n"
"Content-Length: 12\r\n"
"Connection: close\r\n\r\nhello world!";
const char *page = "<? print('hi') ?>";
lua_State *L = luaL_newstate();
conn.ctx = &ctx;
conn.buf = http_request;
conn.buf_size = conn.data_len = strlen(http_request);
conn.request_len = parse_http_request(conn.buf, conn.data_len,
conn.request_len = parse_http_message(conn.buf, conn.data_len,
&conn.request_info);
conn.content_len = conn.data_len - conn.request_len;
@@ -332,33 +472,16 @@ static void test_lua(void) {
check_lua_expr(L, "request_info.remote_ip", "0");
check_lua_expr(L, "request_info.http_headers['Content-Length']", "12");
check_lua_expr(L, "request_info.http_headers['Connection']", "close");
luaL_dostring(L, "post = read()");
(void) luaL_dostring(L, "post = read()");
check_lua_expr(L, "# post", "12");
check_lua_expr(L, "post", "hello world!");
lua_close(L);
}
#endif
static void *user_data_tester(enum mg_event event, struct mg_connection *conn) {
struct mg_request_info *ri = mg_get_request_info(conn);
ASSERT(ri->user_data == (void *) 123);
ASSERT(event == MG_NEW_REQUEST);
return NULL;
}
static void test_user_data(void) {
static const char *options[] = {"listening_ports", UNUSED_PORT, NULL};
struct mg_context *ctx;
ASSERT((ctx = mg_start(user_data_tester, (void *) 123, options)) != NULL);
ASSERT(ctx->user_data == (void *) 123);
call_user(fc(ctx), MG_NEW_REQUEST);
}
static void test_mg_stat(void) {
static struct mg_context ctx;
struct file file;
memset(&file, 'A', sizeof(file));
struct file file = STRUCT_FILE_INITIALIZER;
ASSERT(!mg_stat(fc(&ctx), " does not exist ", &file));
}
@@ -380,21 +503,99 @@ static void test_skip_quoted(void) {
#endif
}
int main(void) {
static void test_alloc_vprintf(void) {
char buf[MG_BUF_LEN], *p = buf;
ASSERT(alloc_printf(&p, sizeof(buf), "%s", "hi") == 2);
ASSERT(p == buf);
ASSERT(alloc_printf(&p, sizeof(buf), "%s", "") == 0);
ASSERT(alloc_printf(&p, sizeof(buf), "") == 0);
// Pass small buffer, make sure alloc_printf allocates
ASSERT(alloc_printf(&p, 1, "%s", "hello") == 5);
ASSERT(p != buf);
free(p);
}
static void test_request_replies(void) {
char ebuf[100];
int i, port = atoi(HTTPS_PORT);
struct mg_connection *conn;
struct mg_context *ctx;
static struct { const char *request, *reply_regex; } tests[] = {
{
"GET test/hello.txt HTTP/1.0\r\nRange: bytes=3-5\r\n\r\n",
"^HTTP/1.1 206 Partial Content"
},
{NULL, NULL},
};
ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
for (i = 0; tests[i].request != NULL; i++) {
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
tests[i].request)) != NULL);
mg_close_connection(conn);
}
mg_stop(ctx);
}
static int api_callback(struct mg_connection *conn) {
struct mg_request_info *ri = mg_get_request_info(conn);
char post_data[100] = "";
ASSERT(ri->user_data == (void *) 123);
ASSERT(ri->num_headers == 2);
ASSERT(strcmp(mg_get_header(conn, "host"), "blah.com") == 0);
ASSERT(mg_read(conn, post_data, sizeof(post_data)) == 3);
ASSERT(memcmp(post_data, "b=1", 3) == 0);
ASSERT(ri->query_string != NULL);
ASSERT(ri->remote_ip > 0);
ASSERT(ri->remote_port > 0);
ASSERT(strcmp(ri->http_version, "1.0") == 0);
mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n");
return 1;
}
static void test_api_calls(void) {
char ebuf[100];
struct mg_callbacks callbacks;
struct mg_connection *conn;
struct mg_context *ctx;
static const char *request = "POST /?a=%20&b=&c=xx HTTP/1.0\r\n"
"Host: blah.com\n" // More spaces before
"content-length: 3\r\n" // Lower case header name
"\r\nb=123456"; // Content size > content-length, test for mg_read()
memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = api_callback;
ASSERT((ctx = mg_start(&callbacks, (void *) 123, OPTIONS)) != NULL);
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
ebuf, sizeof(ebuf), "%s", request)) != NULL);
mg_close_connection(conn);
mg_stop(ctx);
}
int __cdecl main(void) {
test_alloc_vprintf();
test_base64_encode();
test_match_prefix();
test_remove_double_dots();
test_should_keep_alive();
test_parse_http_request();
test_mg_fetch();
test_parse_http_message();
test_mg_download();
test_mg_get_var();
test_set_throttle();
test_next_option();
test_user_data();
test_mg_stat();
test_skip_quoted();
test_mg_upload();
test_request_replies();
test_api_calls();
#ifdef USE_LUA
test_lua();
#endif
test_skip_quoted();
printf("%s\n", "PASSED");
return 0;
}